1. 简介
构建内容
在此 Codelab 中,我们将学习如何使用适用于微控制器的 TensorFlow Lite 在 SparkFun Edge 开发板上运行深度学习模型。我们将使用开发板内置的语音检测模型,该模型使用卷积神经网络来检测单词“yes”和“no”通过 Jamboard 的两个麦克风讲话。
微控制器上的机器学习
机器学习技术可用于打造智能工具,让生活更轻松,就像 Google 助理一样。但通常情况下,这些体验需要大量的计算或资源,其中包括强大的云服务器或桌面设备。不过,现在可以在微小的低功耗硬件(例如微控制器)上运行机器学习推断。
微控制器极为常见、价格低廉、需要的能源非常少,并且非常可靠。它们是各种家用设备的一部分:Think 电器、汽车和玩具。事实上,每年生产大约 300 亿台微控制器驱动的设备。
通过将机器学习技术引入小型微控制器,我们可以提高日常使用的数十亿台设备的智能,而无需依赖昂贵的硬件或可靠的互联网连接。比如,有可以适应日常生活的智能电器、能够辨别问题和正常使用区别的智能工业传感器,以及可以帮助孩子以有趣和愉悦的方式学习的神奇玩具。
适用于微控制器的 TensorFlow Lite(软件)
TensorFlow 是 Google 的开源机器学习框架,用于训练和运行模型。TensorFlow Lite 是一个软件框架,它是 TensorFlow 的优化版本,旨在在手机等小型低功耗设备上运行 TensorFlow 模型。
适用于微控制器的 TensorFlow Lite 是一种软件框架,它是 TensorFlow 的优化版本,旨在在微型低功耗硬件(例如微控制器)上运行 TensorFlow 模型。它符合这些嵌入式环境中要求的约束条件,例如,它具有较小的二进制文件,不需要操作系统支持、任何标准 C/C++ 库或动态内存分配等。
SparkFun Edge(硬件)
SparkFun Edge 是一个基于微控制器的平台,即单块电路板上的小型计算机。它具有处理器、内存和 I/O 硬件,可向其他设备发送和接收数字信号。它配备了四个可通过软件控制的 LED 指示灯,颜色是您喜爱的 Google 颜色。
与计算机不同,微控制器不运行操作系统。相反,您编写的程序直接在硬件上运行。您需要在计算机上编写代码,然后通过一个名为程序员的设备将其下载到微控制器。
微控制器并不是功能强大的计算机。它们的处理器较小,内存又不多。但由于微控制器的设计尽可能简单,因此微控制器的能耗非常少。根据您的程序的功能,SparkFun Edge 可以使用一块纽扣电池运行数周!
学习内容
- 在计算机上编译 SparkFun Edge 的示例程序
- 将程序部署到您的设备
- 对程序进行更改并重新部署
所需条件
您将需要以下硬件:
- Linux 或 MacOS 计算机
- SparkFun Edge 看板
- SparkFun USB-C Serial Basic 编程器
- USB-C 转 USB-A 数据线(如果您使用的是 USB-C 计算机,请改为使用 USB-C 转 USB-C 数据线)
- (可选)3V 20 毫米纽扣式锂电池 (CR2032),无需编程器和数据线即可进行推理
您将需要以下软件:
- Git(通过在命令行中运行
git
来检查是否已安装该工具) - Python 3(通过在命令行中运行
python3
或python --version
来检查是否已安装 Python) - Python 3 版 Pip(实用的 StackOverflow 解答)
- Make 4.2.1 或更高版本(在命令行中运行
make --version
来检查是否已安装该版本) - SparkFun Serial Basic 驱动程序
2. 设置硬件
SparkFun Edge 微控制器预安装了一个可以运行语音模型的二进制文件。在我们自己的版本覆盖它之前,我们先运行这个模型。
可通过以下方式为您的开发板提供支持:
- 将一块纽扣电池插入板背上的电池接头中(电池“+”侧面朝上)。如果您的开发板原本就已插入电池,请拉出塑料片,推入电池,确保电池已完全插入)
- 如果您没有纽扣式电池,可以使用 SparkFun USB-C Serial Basic 编程器设备为开发板供电。如需将此设备连接到开发板,请执行以下步骤:
- 找到 SparkFun Edge 侧面的六针针脚。
- 将 SparkFun USB-C Serial Basic 端口插入这些引脚,确保标有“BLK”的引脚和“GRN”正确排列。
- 在 SparkFun USB-C 串行基本版和您的计算机之间连接一根 USB-C 数据线。
通过插入电池或连接 USB 编程器为开发板供电后,开发板就会被唤醒并开始通过麦克风监听。蓝灯应该会开始闪烁。
板上的机器学习模型经过训练,能够识别“yes”这个词以及检测是否存在语音。它通过点亮彩色 LED 来传达结果。下表显示了每种 LED 颜色的含义:
检测结果 | LED 灯颜色 |
“是” | 黄色 |
“否” | 红色 |
未知语音 | 绿色 |
未检测到语音 | 没有亮起的 LED 灯 |
来试试吧!
将白板对准您的嘴巴说“是”。您会看到黄色的 LED 指示灯。如果当您说“是”时没有反应,请尝试以下操作:
- 将背衬板放置在 10 英寸左右从口中
- 避免背景噪音太大
- 再说一遍“是”快速接连几次(试着说“是、是、是”)
3. 设置您的软件
现在,我们将自行在微控制器上下载、安装并运行语音模型。为此,我们首先下载此程序的源代码以及构建该程序所需的依赖项。该程序是使用 C++ 编写的,必须先编译成二进制文件,然后才能下载到开发板上。二进制文件是指包含可直接由 SparkFun Edge 硬件运行形式的程序的文件。
以下说明适用于 Linux 或 MacOS。
下载 TensorFlow 代码库
该代码可在 GitHub 上的 TensorFlow 代码库中的以下位置找到:
https://github.com/tensorflow/tensorflow/tree/master/tensorflow/lite/micro
在计算机上打开一个终端,切换到您通常用于存储编码项目的目录,下载 TensorFlow 代码库并输入创建的目录,如下所示:
cd ~ # change into your home (or any other) directory git clone --depth 1 https://github.com/tensorflow/tensorflow.git cd tensorflow
下载 Python 依赖项
我们将使用 Python 3 来准备二进制文件并将其刷入设备。Python 脚本依赖于某些可用的库。运行以下命令以安装这些依赖项:
pip3 install pycrypto pyserial --user
4. 构建并准备二进制文件
我们将构建二进制文件并运行相关命令,使其为下载到设备做好准备。
构建二进制文件
如需下载所有必需的依赖项并创建二进制文件,请运行以下命令:
make -f tensorflow/lite/micro/tools/make/Makefile TARGET=sparkfun_edge micro_speech_bin
如果构建成功,输出的最后一行应如下所示:
arm-none-eabi-objcopy tensorflow/lite/micro/tools/make/gen/sparkfun_edge_cortex-m4/bin/micro_speech tensorflow/lite/micro/tools/make/gen/sparkfun_edge_cortex-m4/bin/micro_speech.bin -O binary
如需确认二进制文件是否已成功创建,请运行以下命令:
test -f \ tensorflow/lite/micro/tools/make/gen/sparkfun_edge_cortex-m4/bin/micro_speech.bin && \ echo "Binary was successfully created" || echo "Binary is missing"
您应该会看到控制台中显示了 Binary was successfully created
!如果您看到 Binary is missing
,则表示构建流程存在问题,需要调试。
准备二进制文件
二进制文件必须使用加密密钥进行签名,才能部署到设备。我们现在将运行一些命令来为二进制文件签名,以便可以将其下载到 SparkFun Edge。
输入以下命令,设置一些可用于开发的虚拟加密密钥:
cp tensorflow/lite/micro/tools/make/downloads/AmbiqSuite-Rel2.2.0/tools/apollo3_scripts/keys_info0.py tensorflow/lite/micro/tools/make/downloads/AmbiqSuite-Rel2.2.0/tools/apollo3_scripts/keys_info.py
现在,运行以下命令以创建已签名的二进制文件:
python3 tensorflow/lite/micro/tools/make/downloads/AmbiqSuite-Rel2.2.0/tools/apollo3_scripts/create_cust_image_blob.py \ --bin tensorflow/lite/micro/tools/make/gen/sparkfun_edge_cortex-m4/bin/micro_speech.bin \ --load-address 0xC000 \ --magic-num 0xCB \ -o main_nonsecure_ota \ --version 0x0
这将创建文件 main_nonsecure_ota.bin
。现在,我们将运行另一个命令,以创建该文件的最终版本,该版本可用于通过引导加载程序脚本来刷写设备,我们将在下一步中使用该脚本:
python3 tensorflow/lite/micro/tools/make/downloads/AmbiqSuite-Rel2.2.0/tools/apollo3_scripts/create_cust_wireupdate_blob.py \ --load-address 0x20000 \ --bin main_nonsecure_ota.bin \ -i 6 \ -o main_nonsecure_wire \ --options 0x1
现在,运行命令的目录中应该会有一个名为 main_nonsecure_wire.bin
的文件。这是我们将刷写到设备上的文件。
5. 准备刷写二进制文件
什么是闪烁?
SparkFun Edge 将其当前运行的程序存储在其 512 千字节的闪存中。如果我们希望开发板运行一个新程序,就必须将其发送到开发板,它会将其存储在闪存中,覆盖之前保存的所有程序。
此过程称为“刷写”,我们将使用它来将程序发送到开发板。
将编程器安装到开发板上
为了将新程序下载到板上,我们将使用 SparkFun USB-C Serial Basic 串行编程器。此设备可让你的计算机通过 USB 与微控制器通信。
如需将此设备连接到开发板,请执行以下步骤:
- 找到 SparkFun Edge 侧面的六针针脚。
- 将 SparkFun USB-C Serial Basic 端口插入这些引脚,确保标有“BLK”的引脚和“GRN”正确排列。
将编程器连接到计算机
我们将通过 USB 将开发板连接到您的计算机。为了给开发板编程,我们需要知道您的计算机为该设备指定的名称。最好的方法是列出连接计算机之前和之后的所有设备,并查看是哪台设备是新设备。
在通过 USB 连接设备之前,请运行以下命令:
If you are using Linux: ls /dev/tty* If you are using MacOS: ls /dev/cu*
这应该会输出如下所示的已连接设备列表:
/dev/cu.Bluetooth-Incoming-Port /dev/cu.MALS /dev/cu.SOC
现在,将编程器连接到计算机的 USB 端口。再次输入以下命令:
If you are using Linux: ls /dev/tty* If you are using MacOS: ls /dev/cu*
您应该会在输出中看到额外的项,如以下示例所示。新商品可能会有不同的名称。这个新项是设备的名称。
/dev/cu.Bluetooth-Incoming-Port /dev/cu.MALS /dev/cu.SOC /dev/cu.wchusbserial-1450
首先,我们创建一个环境变量来标识设备名称:
export DEVICENAME=put your device name here
接下来,我们将创建一个环境变量,以指定波特率,即数据发送到设备的速度:
export BAUD_RATE=921600
6. 刷写二进制文件
运行脚本以刷写开发板
要刷写开发板,我们必须将其放入特殊的“引导加载程序”中,其状态,使其准备好接收新的二进制文件。然后,我们将运行一个脚本,将二进制文件发送到开发板。
让我们熟悉一下板上的以下按钮:
请按照以下步骤重置并刷写开发板:
- 确保开发板已连接到编程器,并且整个设置都通过 USB 连接到计算机。
- Start 按住开发板上标有
14
的按钮。请按住手机按钮,直到第 6 步。 - 仍然按住标有
14
的按钮,如需将开发板重置为引导加载程序状态,请点击标有RST
的按钮以重置开发板。 - 仍然按住标有
14
的按钮,将以下命令粘贴到你的终端,然后按 Enter 键即可运行该命令(为方便起见,你可以先将此命令粘贴到终端中,然后再开始按住该按钮,但在到达这一步之前请勿按 Enter 键)
python3 tensorflow/lite/micro/tools/make/downloads/AmbiqSuite-Rel2.2.0/tools/apollo3_scripts/uart_wired_update.py -b ${BAUD_RATE} ${DEVICENAME} -r 1 -f main_nonsecure_wire.bin -i 6
- 继续按住标记为
14
的按钮,您现在应该会在屏幕上看到如下所示的内容:
Connecting with Corvette over serial port /dev/cu.usbserial-1440... Sending Hello. Received response for Hello Received Status length = 0x58 version = 0x3 Max Storage = 0x4ffa0 Status = 0x2 State = 0x7 AMInfo = 0x1 0xff2da3ff 0x55fff 0x1 0x49f40003 0xffffffff [...lots more 0xffffffff...] Sending OTA Descriptor = 0xfe000 Sending Update Command. number of updates needed = 1 Sending block of size 0x158b0 from 0x0 to 0x158b0 Sending Data Packet of length 8180 Sending Data Packet of length 8180 [...lots more Sending Data Packet of length 8180...]
- 看到
Sending Data Packet of length 8180
后,请停止按住 Jamboard 上标有14
的按钮(但继续按住也没关系)。程序将继续在终端上输出行。最终将如下所示:
[...lots more Sending Data Packet of length 8180...] Sending Data Packet of length 8180 Sending Data Packet of length 6440 Sending Reset Command. Done.
如果您看到 Done
,则表示刷写成功。如果程序输出以错误结尾,请检查是否输出了 Sending Reset Command
。如果是,则尽管出现错误,刷写操作仍可能成功。
在 Linux 计算机上,您可能会遇到 NoResponse Error
。这是因为 ch34x 串行驱动程序已经与现有串行驱动程序一起安装,可按如下方式解析:
第 1 步:重新安装正确版本的 ch34x 库。确保在安装过程中将设备从计算机中拔下。
git clone https://github.com/juliagoda/CH341SER.git cd CH341SER/ make sudo insmod ch34x.ko sudo rmmod ch341
第 2 步:插入开发板 USB 并运行:
dmesg | grep "ch34x"
您应该会看到类似如下的消息:
[ 1299.444724] ch34x_attach+0x1af/0x280 [ch34x] [ 1299.445386] usb 2-13.1: ch34x converter now attached to ttyUSB0
如果使用的驱动程序不是“ch34x”(例如:ch341),请尝试运行以下命令停用其他驱动程序:
rmmod <non-ch34x driver name>
拔下并重新插入设备,确保所使用的驱动程序为“ch34x”。
7. 演示
试用计划
成功刷写开发板后,点击标记为
RST
,用于重启开发板并启动程序。如果蓝色 LED 开始闪烁,则表示闪烁成功。如果没看到,请向下滚动到下方的“如果账号无法正常使用该怎么办?”部分。
板上的机器学习模型经过训练,能够识别“yes”这个词以及检测是否存在语音。它通过点亮彩色 LED 来传达结果。下表显示了每种 LED 颜色的含义:
检测结果 | LED 灯颜色 |
“是” | 黄色 |
“否” | 红色 |
未知语音 | 绿色 |
未检测到语音 | 没有亮起的 LED 灯 |
来试试吧!
将白板对准您的嘴巴说“是”。您会看到黄色的 LED 指示灯。如果当您说“是”时没有反应,请尝试以下操作:
- 将背衬板放置在 10 英寸左右从口中
- 避免背景噪音太大
- 再说一遍“是”快速接连几次(试着说“是、是、是”)
如果账号无法正常使用,该怎么办?
以下是一些可能出现的问题及其调试方法:
问题:闪烁后,所有 LED 指示灯均未亮起。
解决方案:尝试按 RST
按钮,或者断开开发板与编程器的连接并重新连接。如果这些方法均不奏效,请尝试再次刷写开发板。
问题:蓝色 LED 指示灯亮起,但非常暗。
解决方法:在电池电量不足时更换电池。或者,该开发板也可通过计算机使用编程器和数据线为其供电。
8. 读取调试输出(可选)
如果您遇到问题且需要详细调试代码,请参阅本部分内容。为了了解代码运行时微控制器中发生的情况,您可以通过板的串行连接输出调试信息。您使用计算机连接到开发板,并显示该开发板发送的数据。
打开串行连接
默认情况下,我们的 SparkFun Edge 示例代码会记录所有语音命令及其置信度。如需查看开发板的输出,您可以运行以下命令:
screen ${DEVICENAME} 115200
您最初可能会看到类似如下的输出:(只有当开发板在连接后重置时,才会显示,否则您可能开始看到调试信息)
Apollo3 Burst Mode is Available Apollo3 operating in Burst Mode (96MHz)
可以说“是”试试发出一些命令或“否”。您应该会看到每个命令在板上输出的调试信息:
Heard yes (202) @65536ms
在上述日志中,yes
是指该命令。数字 202
表示系统能够听到命令的置信度(200 为最小值)。最后,65536ms
表示自微控制器上次重置后经过的时间。
如需停止查看调试输出,请先按 Ctrl+A
,紧接着按 K
键,然后按 Y
键。
写入调试日志
您可以在您刚刚使用的 Command_responder.cc 文件中看到记录此信息的代码:
tensorflow/lite/micro/examples/micro_speech/sparkfun_edge/command_responder.cc
如需记录数据,您可以调用 error_reporter->Report()
方法。它支持用于字符串插值的标准 printf
令牌,您可以使用这些令牌在日志中包含重要信息:
error_reporter->Report("Heard %s (%d) @%dms", found_command, score, current_time);
当您自行更改下一节中的代码时,此方法会派上用场。
9. 扩展代码(可选)
现在,您已了解如何构建和刷写 SparkFun Edge,您可以开始使用代码并将其部署到设备,以查看结果。
阅读代码
建议从以下文件开始阅读代码:command_responder.cc.
tensorflow/lite/micro/examples/micro_speech/sparkfun_edge/command_responder.cc
您可以点击此处在 GitHub 上查看该文件。
当检测到语音指令时,系统会调用此文件中的方法 RespondToCommand
。现有代码会根据听到“是”“否”还是听到未知指令来打开不同的 LED 指示灯。以下代码段展示了具体工作原理:
if (found_command[0] == 'y') {
am_hal_gpio_output_set(AM_BSP_GPIO_LED_YELLOW);
}
if (found_command[0] == 'n') {
am_hal_gpio_output_set(AM_BSP_GPIO_LED_RED);
}
if (found_command[0] == 'u') {
am_hal_gpio_output_set(AM_BSP_GPIO_LED_GREEN);
}
found_command
参数包含检测到的命令的名称。通过检查第一个字符,这组 if
语句可以确定要点亮哪个 LED。
系统会使用多个参数调用 RespondToCommand 方法:
void RespondToCommand(tflite::ErrorReporter* error_reporter,
int32_t current_time, const char* found_command,
uint8_t score, bool is_new_command) {
error_reporter
用于记录调试信息(稍后会详细介绍)。current_time
表示检测到命令的时间。found_command
告诉我们检测到了哪个命令。score
表示我们对检测到命令的置信度。is_new_command
会让我们知道这是否是第一次听到该命令。
score
是一个 0 到 255 之间的整数,表示检测到命令的概率。示例代码仅在得分大于 200 时才会将命令视为有效。根据我们的测试,大多数有效命令的范围为 200-210。
修改代码
SparkFun Edge 板有四个 LED。目前,我们会让蓝色 LED 指示灯闪烁,以表示正在进行识别。您可以在 command_responder.cc
文件中看到此代码:
static int count = 0;
// Toggle the blue LED every time an inference is performed.
++count;
if (count & 1) {
am_hal_gpio_output_set(AM_BSP_GPIO_LED_BLUE);
} else {
am_hal_gpio_output_clear(AM_BSP_GPIO_LED_BLUE);
}
由于我们有一个包含四个 LED 的库,因此我们修改了该程序,以将其用作给定命令的 score
的可见指示符。低分值对应的是单个点亮的 LED,而高分值将导致有多个灯。
为了确保我们有办法了解程序正在运行,我们将持续让红色 LED 指示灯闪烁,而不是蓝色 LED 指示灯。相邻的蓝色、绿色和黄色 LED 将用于展示我们最新的 score
的强度。为简单起见,仅当用户选择“是”时,我们才会点亮这些 LED 灯。如果检测到其他字词,LED 指示灯将亮起。
如需进行此项更改,请将 command_responder.cc
文件中的所有代码替换为以下代码段:
#include "tensorflow/lite/micro/examples/micro_speech/command_responder.h"
#include "am_bsp.h"
// This implementation will light up the LEDs on the board in response to different commands.
void RespondToCommand(tflite::ErrorReporter* error_reporter,
int32_t current_time, const char* found_command,
uint8_t score, bool is_new_command) {
static bool is_initialized = false;
if (!is_initialized) {
// Setup LEDs as outputs
am_hal_gpio_pinconfig(AM_BSP_GPIO_LED_RED, g_AM_HAL_GPIO_OUTPUT_12);
am_hal_gpio_pinconfig(AM_BSP_GPIO_LED_BLUE, g_AM_HAL_GPIO_OUTPUT_12);
am_hal_gpio_pinconfig(AM_BSP_GPIO_LED_GREEN, g_AM_HAL_GPIO_OUTPUT_12);
am_hal_gpio_pinconfig(AM_BSP_GPIO_LED_YELLOW, g_AM_HAL_GPIO_OUTPUT_12);
// Ensure all pins are cleared
am_hal_gpio_output_clear(AM_BSP_GPIO_LED_RED);
am_hal_gpio_output_clear(AM_BSP_GPIO_LED_BLUE);
am_hal_gpio_output_clear(AM_BSP_GPIO_LED_GREEN);
am_hal_gpio_output_clear(AM_BSP_GPIO_LED_YELLOW);
is_initialized = true;
}
static int count = 0;
// Toggle the red LED every time an inference is performed.
++count;
if (count & 1) {
am_hal_gpio_output_set(AM_BSP_GPIO_LED_RED);
} else {
am_hal_gpio_output_clear(AM_BSP_GPIO_LED_RED);
}
if (is_new_command) {
// Clear the last three LEDs
am_hal_gpio_output_clear(AM_BSP_GPIO_LED_BLUE);
am_hal_gpio_output_clear(AM_BSP_GPIO_LED_GREEN);
am_hal_gpio_output_clear(AM_BSP_GPIO_LED_YELLOW);
error_reporter->Report("Heard %s (%d) @%dms", found_command, score,
current_time);
// Only indicate a 'yes'
if (found_command[0] == 'y') {
// Always light the blue LED
am_hal_gpio_output_set(AM_BSP_GPIO_LED_BLUE);
// Light the other LEDs depending on score
if (score >= 205) {
am_hal_gpio_output_set(AM_BSP_GPIO_LED_GREEN);
}
if(score >= 210) {
am_hal_gpio_output_set(AM_BSP_GPIO_LED_YELLOW);
}
}
}
}
如果检测到新命令,is_new_command
将为 true。我们将清空蓝色、绿色和黄色 LED 指示灯,然后根据 found_command
和 score
的值再次点亮它们。
重新构建并刷写
更改代码后,通过运行构建和准备二进制文件中的所有步骤对其进行测试。
10. 后续步骤
恭喜,您已在微控制器上成功构建了您的第一个语音检测器!
我们希望您喜欢这个有关使用适用于微控制器的 TensorFlow Lite 进行开发的简要介绍。微控制器深度学习是一个令人兴奋的新概念,我们鼓励你去外面进行尝试!
参考文档
- 现在,您已拥有使用基本程序的经验,请训练您自己的模型理解不同的命令。注意:训练将需要几个小时!
- 详细了解适用于微控制器的 TensorFlow Lite(网站、GitHub)。
- 尝试其他示例,并尝试在 SparkFun Edge 上运行它们(如果受支持)。
- 请参阅 O'Reilly 出版的 TinyML:在 Arduino 和超低功耗微控制器上使用 TensorFlow 进行机器学习,其中介绍了微型设备上的机器学习,并详细介绍了几个有趣的项目。此 Codelab 基于本书第 7 章和第 8 章。
谢谢,祝您构建愉快!