使用适用于微控制器和 SparkFun Edge 的 TensorFlow Lite 进行 AI 语音识别

1. 简介

构建内容

在此 Codelab 中,我们将学习如何使用适用于微控制器的 TensorFlow LiteSparkFun Edge 开发板上运行深度学习模型。我们将使用开发板内置的语音检测模型,该模型使用卷积神经网络来检测单词“yes”和“no”通过 Jamboard 的两个麦克风讲话。

bf256d403a1821af.gif

微控制器上的机器学习

机器学习技术可用于打造智能工具,让生活更轻松,就像 Google 助理一样。但通常情况下,这些体验需要大量的计算或资源,其中包括强大的云服务器或桌面设备。不过,现在可以在微小的低功耗硬件(例如微控制器)上运行机器学习推断。

微控制器极为常见、价格低廉、需要的能源非常少,并且非常可靠。它们是各种家用设备的一部分:Think 电器、汽车和玩具。事实上,每年生产大约 300 亿台微控制器驱动的设备。

1360b61fbfa33657.jpeg

通过将机器学习技术引入小型微控制器,我们可以提高日常使用的数十亿台设备的智能,而无需依赖昂贵的硬件或可靠的互联网连接。比如,有可以适应日常生活的智能电器、能够辨别问题和正常使用区别的智能工业传感器,以及可以帮助孩子以有趣和愉悦的方式学习的神奇玩具。

适用于微控制器的 TensorFlow Lite(软件)

358ffdb9eb758b90

TensorFlow 是 Google 的开源机器学习框架,用于训练和运行模型。TensorFlow Lite 是一个软件框架,它是 TensorFlow 的优化版本,旨在在手机等小型低功耗设备上运行 TensorFlow 模型。

适用于微控制器的 TensorFlow Lite 是一种软件框架,它是 TensorFlow 的优化版本,旨在在微型低功耗硬件(例如微控制器)上运行 TensorFlow 模型。它符合这些嵌入式环境中要求的约束条件,例如,它具有较小的二进制文件,不需要操作系统支持、任何标准 C/C++ 库或动态内存分配等。

SparkFun Edge(硬件)

SparkFun Edge 是一个基于微控制器的平台,即单块电路板上的小型计算机。它具有处理器、内存和 I/O 硬件,可向其他设备发送和接收数字信号。它配备了四个可通过软件控制的 LED 指示灯,颜色是您喜爱的 Google 颜色。

aa4493835a2338c6.png

与计算机不同,微控制器不运行操作系统。相反,您编写的程序直接在硬件上运行。您需要在计算机上编写代码,然后通过一个名为程序员的设备将其下载到微控制器。

微控制器并不是功能强大的计算机。它们的处理器较小,内存又不多。但由于微控制器的设计尽可能简单,因此微控制器的能耗非常少。根据您的程序的功能,SparkFun Edge 可以使用一块纽扣电池运行数周!

学习内容

  • 在计算机上编译 SparkFun Edge 的示例程序
  • 将程序部署到您的设备
  • 对程序进行更改并重新部署

所需条件

您将需要以下硬件:

您将需要以下软件:

  • Git(通过在命令行中运行 git 来检查是否已安装该工具)
  • Python 3(通过在命令行中运行 python3python --version 来检查是否已安装 Python)
  • Python 3 版 Pip(实用的 StackOverflow 解答
  • Make 4.2.1 或更高版本(在命令行中运行 make --version 来检查是否已安装该版本)
  • SparkFun Serial Basic 驱动程序

2. 设置硬件

SparkFun Edge 微控制器预安装了一个可以运行语音模型的二进制文件。在我们自己的版本覆盖它之前,我们先运行这个模型。

可通过以下方式为您的开发板提供支持:

  1. 将一块纽扣电池插入板背上的电池接头中(电池“+”侧面朝上)。如果您的开发板原本就已插入电池,请拉出塑料片,推入电池,确保电池已完全插入)

25a6cc6b208e8a4e

  1. 如果您没有纽扣式电池,可以使用 SparkFun USB-C Serial Basic 编程器设备为开发板供电。如需将此设备连接到开发板,请执行以下步骤:
  • 找到 SparkFun Edge 侧面的六针针脚。
  • 将 SparkFun USB-C Serial Basic 端口插入这些引脚,确保标有“BLK”的引脚和“GRN”正确排列。
  • 在 SparkFun USB-C 串行基本版和您的计算机之间连接一根 USB-C 数据线。

b140822f0019f92a.png

通过插入电池或连接 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 与微控制器通信。

如需将此设备连接到开发板,请执行以下步骤:

  1. 找到 SparkFun Edge 侧面的六针针脚。
  2. 将 SparkFun USB-C Serial Basic 端口插入这些引脚,确保标有“BLK”的引脚和“GRN”正确排列。

b140822f0019f92a.png

将编程器连接到计算机

我们将通过 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. 刷写二进制文件

运行脚本以刷写开发板

要刷写开发板,我们必须将其放入特殊的“引导加载程序”中,其状态,使其准备好接收新的二进制文件。然后,我们将运行一个脚本,将二进制文件发送到开发板。

让我们熟悉一下板上的以下按钮:

64c620570b9d2f83

请按照以下步骤重置并刷写开发板:

  1. 确保开发板已连接到编程器,并且整个设置都通过 USB 连接到计算机。
  2. Start 按住开发板上标有 14 的按钮。请按住手机按钮,直到第 6 步。
  3. 仍然按住标有 14 的按钮,如需将开发板重置为引导加载程序状态,请点击标有 RST 的按钮以重置开发板。
  4. 仍然按住标有 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
  1. 继续按住标记为 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...]
  1. 看到 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 开始闪烁,则表示闪烁成功。如果没看到,请向下滚动到下方的“如果账号无法正常使用该怎么办?”部分。

bf256d403a1821af.gif

板上的机器学习模型经过训练,能够识别“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_commandscore 的值再次点亮它们。

重新构建并刷写

更改代码后,通过运行构建和准备二进制文件中的所有步骤对其进行测试。

10. 后续步骤

恭喜,您已在微控制器上成功构建了您的第一个语音检测器!

我们希望您喜欢这个有关使用适用于微控制器的 TensorFlow Lite 进行开发的简要介绍。微控制器深度学习是一个令人兴奋的新概念,我们鼓励你去外面进行尝试!

参考文档

26699b18f2b199f

谢谢,祝您构建愉快!