Lenor
项目描述Syntiant TinyML 是美国 Syntiant 公司开发的一款板子。搭载了 Syntiant 自制的 ultra-low-power Syntiant® NDP101 Neural Decision Processor 可以用以进行深度神经网络计算,并且有着超低的功耗,方便在边缘领域应用。
Syntiant是2017年成立于美国加州尔湾的一家专门提供高性能、低功耗深度神经网络处理器的人工智能芯片厂商。Syniant 的芯片可以轻松设计成电池供电的设备,包括耳塞、耳机、手机、遥控器、笔记本电脑和其他声控产品。
拿到这个板卡的第一感觉是:超级小,板卡尺寸仅有24mm x 28mm。麻雀虽小五脏俱全,板上配有神经决策处理器NDP101,它通过最常用的SPI总线可以跟几乎任何一款微控制器进行连接,通过计算和存储器高度耦合的架构来实现深度神经网路计算功能。可惜官方没有公布数据手册,所以连其他的板子也挺麻烦的。
板载麦克风和 BMI160 传感器可轻松配置任何语音、AED 或 6 轴运动和振动相关应用。训练后的模型可以通过 MicroUSB 连接轻松下载到 TinyML board上,无需任何专用硬件。
项目内容这次完成的内容是任务一,也就是用自带的麦克风,搭建机器学习模型,使板卡上的LED灯根据中文语音指令(不少于三个指令)呈现出不同效果,如开灯、关灯、闪烁、SOS、呼吸等。
具体的效果是,先说 “出来吧,大铸币” 对其唤醒,可以观察到亮彩灯。
之后可以使用 ”开灯、熄灭、闪烁“ 三个关键词,具体的效果就是字面意思。
演示效果可以说是还行,反正至少不差,不是不能用。
创建人工智能模型和烧录创建人工智能模型大致上就是跟着教程走。
首先是获取训练集,我用的是手机录音,一次说几十个几百个识别词,然后自己手写了一个 python 程序,可以把一个音频中的几百个识别词单独提取出来,成为几百个单独的音频文件,方便批量上传。
之后上传到 edge impulse 里面去,跟着教程七七八八走一通。
这里面有个挺奇怪的事情,就是官方教程里生成的 vector 预览图都是三维的,我的是二维的,看了半天也没看出来问题出在哪。而且好像最后也不是很影响使用,所以也没管它。就当是供应商出 bug 了吧。
之后下载创建的模型,然后打开万能的 Arduino CLI(咱就是说 Arduino 怎么还能干这事)
进行烧录。后来搞了半天我才知道,这个东西是分两步走的。第一步是训练人工智能模型,然后通过一次烧录把人工智能模型烧录到那个什么 NDP101 芯片里面去。之后再写代码,再把程序代码烧录到那个什么 SAMD21 里面去。
写代码也挺头疼的,Arduino 没怎么用过,不知道怎么写中断。外加这个 Syntiant 的库又很复杂,调用了一个 timer4,也不知道都干了啥,我也不敢自己乱写个什么中断。所以只好被迫非常耦合地在主循环里加了点屎山。
好就好在这个屎山可以工作,而且似乎还能用,还挺好用。所以后面也没有再去管这坨屎山了。万一轻轻管了一下,它不能用了怎么办?
哈哈,终于一千字了,乐!
各功能对应的主要代码片段及说明好像实在是没什么能放上去的代码。主要是训练的部分都在 edge impulse 上面弄了,而且完全是照着教程,没啥好说的。
那就勉为其难地放一下亮灯的代码吧。
extern int light;
extern int awake;
void on_classification_changed(const char *event, float confidence, float anomaly_score) {
if (strcmp(event, "awake") == 0) {
awake = 1;
light = 0;
return;
}
if (strcmp(event, "light") == 0 && awake) {
light = 1;
awake = 0;
return;
}
if (strcmp(event, "shut") == 0 && awake) {
light = 0;
awake = 0;
return;
}
if (strcmp(event, "blink") == 0 && awake) {
light = 2;
awake = 0;
return;
}
}
其实就是检测到对应的指令后,改变对应的状态位。
然后在主循环里判断亮灯。
digitalWrite(LED_RED, LOW);
if (awake){
awake_count ++;
if (awake_count == 6000){
awake_count = 0;
awake = 0;
}
if (awake_count / 500 % 2 == 0)
digitalWrite(LED_BLUE, HIGH);
else
digitalWrite(LED_BLUE, LOW);
}
else{
if (light == 0) {
digitalWrite(LED_RED, LOW);
if (!awake)
digitalWrite(LED_BLUE, LOW);
digitalWrite(LED_GREEN, LOW);
}
else if (light == 1) {
digitalWrite(LED_RED, LOW);
if (!awake)
digitalWrite(LED_BLUE, LOW);
digitalWrite(LED_GREEN, HIGH);
}
else if (light == 2) {
digitalWrite(LED_RED, LOW);
digitalWrite(LED_BLUE, LOW);
if (blink_count < 1000) {
digitalWrite(LED_GREEN, HIGH);
}
else {
digitalWrite(LED_GREEN, LOW);
}
blink_count ++;
if (blink_count == 2000)
blink_count = 0;
}
}
上面这段代码是真的乐,又想写,又不敢乱写。更搞笑的是这是个同步线程,所以不敢写 delay。那怎么实现延时呢?答案是我通过实验计算出了这个主循环循环一次大约需要的时间。然后通过异步的状态位来掐表。(就是那两个 count,没记错的话每 1000 是 0.5 秒)。唤醒的时候灯是在一直闪烁的,会占用灯的状态。唤醒结束且没有收到指令的时候,就会回到之前那个状态。
功能展示及说明就是说 “出来吧,大铸币” 然后就会唤醒并且亮一个彩灯。
在唤醒状态下,说 “开灯”,就会亮一个灯。说“熄灭”,灯就灭了。说 ”闪烁“,灯就开始闪了。
这是亮灯的状态
这是灯熄灭的状态
在唤醒的时候灯会闪烁
哈哈!太乐了。
快递也比想象中的快了一点。总之就是很乐。
最好就是下次能讲讲那个运动识别模块怎么用,看上去很高级的样子。