硬件介绍:
funpack 第二季第1期活动,推出了Syntiant TinyML Board 机器学习的板子。这个开发板非常小巧,板的尺寸为 24 毫米 x 28 毫米,是一个小型的独立系统,通过微型 USB 连接通过 Edge Impulse 轻松下载经过训练的模型,而无需任何专用硬件。板子自身配备超低功耗 Syntiant NDP101神经决策处理器,可以使语音和传感器应用程序分别在 140 和 100 微瓦以下运行。
任务:
本期选择任务1:用自带的麦克风,搭建机器学习模型,使板卡上的LED灯根据中文语音指令(不少于三个指令)呈现出不同效果,如开灯、关灯、闪烁、SOS、呼吸等.
这里我的目标是做一个用语音控制的玩具车。通过语音下达命令控制小车的移动。命令关键字有:向前进、向后退、向左转、向右转。在设计中允许命令字变形。比如:“向前走“、“前进”、”前进四”。 这些都能够作为小车前进的命令字。使用led灯来展示对应的命令字。
实现过程:
1、准备工作。先给开发板烧写官方的固件。访问这个页面:https://docs.edgeimpulse.com/docs/development-platforms/officially-supported-mcu-targets/syntiant-tinyml-board 从这个页面中下载Audio firmware固件。电脑上需要安装Arduino CLI。我这里使用的是ArduinoCLI 0.19.2。刷好固件后,Syntiant的板子接在电脑上,就能被识别成一个麦克风,用这个麦克风来收集语音数据。
2、数据收集。在网站https://studio.edgeimpulse.com 提供了完整的机器学习解决方案。登录这个网站,注册自己的用户,然后开始创建自己的工程。然后在Data acquistition中收集数据。我这里使用了四个标签:forward、back、left、right。每个标签,长度设置为1秒,采样率1600Hz。收集的训练样本是越多越好,最好有不同人的声音,每个标签对应的不同的命令字都要有足够多的样本。我这里一共采集了160个样本。
3、训练模型。使用收集来的语音数据,使用神经网络进行训练。网站提供了多种模型供选择,但是不太明白模型的本质,直接使用了例程中使用的NN神经网络来作为训练模型。 这里网站带来的便利性就体现出来了,不用自己手工去写复杂的神经网络,可以使用已经造好的车轮。降低了学习成本。选择好模型,就是配置模型参数了。选择impulse design下的Syntiant就可以选择配置参数。这里使用滑动窗口的方式截取声音的每个小片,将声音做了FFT变换,提取声音的频域信息,最终将声音转换成信息矩阵。可以配置转换参数,这里也是使用例程中的参数。这个网站的可视化做的很好,可以看见每一个样本的波形和fft信息。
配置好参数后,就是训练了,训练还是挺快的,几分钟就能完成训练了。但是看训练结果并不太理想。尤其是向右转正确识别率才66.7%。分析原因,应该和样本数量过少有关。如果想提高识别成功率,训练样本数量真的不能太少了,否则训练后的效果不佳。如果能够有合适的样本库就好了,收集训练样本实在是累而且无聊。
4 模型部署。训练完模型后,就可以部署到开发板上了。网页上提供了3个方法。最简单就是选择页面中的“build firmware”直接生成开发板的固件。生成固件后,能够下载到一个压缩包,解压后直接运行包内的批处理文件,就可以把固件烧写到开发板上。可以使用串口观察识别情况。
我这里选择的是生成syntiant NDP101 library,选择这里就能生成arduino可以使用的库文件。生成后,打卡压缩包,有个model-parameters的文件夹,在这个文件夹下有三个头文件,就是训练好了的模型文件。使用https://github.com/edgeimpulse/firmware-syntiant-tinyml这个工程作为基础,将src下model-parameters里的模型文件替换成自己的模型文件,然后修改model_variables.h这个文件(需要注释掉一个包含头文件的语句和一个函数)。接下来就可以使用arduino编译烧写了。
/* Generated by Edge Impulse
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#ifndef _EI_CLASSIFIER_MODEL_VARIABLES_H_
#define _EI_CLASSIFIER_MODEL_VARIABLES_H_
#include <stdint.h>
#include "model_metadata.h"
//#include "edge-impulse-sdk/classifier/ei_model_types.h"
const char* ei_classifier_inferencing_categories[] = { "back", "forward", "left", "right" };
uint8_t ei_dsp_config_14_axes[] = { 0 };
const uint32_t ei_dsp_config_14_axes_size = 1;
ei_dsp_config_audio_syntiant_t ei_dsp_config_14 = {
1,
1,
0.032f,
0.024f,
40,
512,
0,
0,
0.96875f
};
//const ei_model_performance_calibration_t ei_calibration = {
// 1, /* integer version number */
// (int32_t)(EI_CLASSIFIER_RAW_SAMPLE_COUNT / ((EI_CLASSIFIER_FREQUENCY > 0) ? EI_CLASSIFIER_FREQUENCY : 1)) * 1000, /* Model window */
// 0.8f, /* Default threshold */
// (int32_t)(EI_CLASSIFIER_RAW_SAMPLE_COUNT / ((EI_CLASSIFIER_FREQUENCY > 0) ? EI_CLASSIFIER_FREQUENCY : 1)) * 500, /* Half of model window */
// 0 /* Don't use flags */
//};
#endif // _EI_CLASSIFIER_MODEL_METADATA_H_
结果展示:
当开发板通过板载麦克风收集到语音信息后就会送到NDP101与加载的模型进行运算,然后得出最高概率的标签值返还。在arduino中,通过返还的标签来控制LED灯。
/**
* @brief Called when a inference matches 1 of the features
*
* @param[in] event The event
* @param[in] confidence The confidence
* @param[in] anomaly_score The anomaly score
*/
void on_classification_changed(const char *event, float confidence, float anomaly_score) {
Serial.print(confidence);
Serial.print(" ");
Serial.print(anomaly_score);
Serial.print(" ");
Serial.println(event);
// here you can write application code, e.g. to toggle LEDs based on keywords
if (strcmp(event, "forward") == 0) {
// Toggle LED
digitalWrite(LED_RED, HIGH);
digitalWrite(LED_GREEN, LOW);
digitalWrite(LED_BLUE, LOW);
}
if (strcmp(event, "back") == 0) {
// Toggle LED
digitalWrite(LED_RED, LOW);
digitalWrite(LED_GREEN, LOW);
digitalWrite(LED_BLUE, LOW);
}
if (strcmp(event, "left") == 0) {
// Toggle LED
digitalWrite(LED_RED, LOW);
digitalWrite(LED_GREEN, HIGH);
digitalWrite(LED_BLUE, LOW);
}
if (strcmp(event, "right") == 0) {
// Toggle LED
digitalWrite(LED_RED, LOW);
digitalWrite(LED_GREEN,LOW );
digitalWrite(LED_BLUE, HIGH);
}
}
向后退
向前进向右转
向左转
开发板的使用中遇到的问题。1、语音识别,只要麦克风接收到数据就会做识别,会计算出标签对应的概率,然后返回最大概率的标签。这样会导致没有命令时也会不停地识别出命令字来!2、工程文件中,调用命令字识别函数入口中有命令字识别的概率变量,但是按代码查询,这个概率被赋值为0了,这样就没有办法通过设定阈值来过滤环境噪音了。
/**
* @brief Called from the ndp101 read out. Print classification output
* and send matched output string to user callback
*
* @param[in] matched_feature
*/
void ei_classification_output(int matched_feature)
{
if (ei_run_impulse_active()) {
ei_printf("\nPredictions:\r\n");
for (size_t ix = 0; ix < EI_CLASSIFIER_LABEL_COUNT; ix++) {
ei_printf(" %s: \t%d\r\n", ei_classifier_inferencing_categories[ix],
(matched_feature == ix) ? 1 : 0);
}
on_classification_changed(ei_classifier_inferencing_categories[matched_feature], 0, 0);
}
}
/**
心得体会:突然发现机器学习已经开始渗透到我们的生活中来了。身边越来越多的电器开始开始用机器学习的方式来提供服务。机器学习很有意思,感谢funpack带来的活动,让我通过玩了解到了机器学习。谢谢!