基于STM32的离线语音识别方案:SenseVoice-Small ONNX模型嵌入式部署教程

1. 引言:让设备“听懂”你的声音

你有没有想过,给家里的台灯、风扇或者自己做的小机器人加上语音控制,让它不用联网,就能听懂你说“开灯”、“关灯”?这听起来很酷,但一想到要在小小的单片机上跑复杂的语音识别模型,是不是觉得头大?

别担心,今天我们就来聊聊怎么把这件事变得简单。我们有一个叫SenseVoice-Small的轻量级语音识别模型,它身材小巧,能力却不弱。更重要的是,我们可以把它转换成一种叫ONNX的通用格式,然后塞进一块非常常见且便宜的STM32单片机里。

这篇文章,我就带你一步步走通这个流程。从怎么准备模型,到怎么在STM32上搭建运行环境,再到最后怎么让单片机真的“听”到声音并做出反应。整个过程,我会尽量用大白话讲清楚,即使你之前没怎么接触过嵌入式AI,也能跟着做下来。我们的目标很明确:用最低的成本和最简单的步骤,让你的嵌入式设备拥有离线语音交互的能力。

2. 方案核心:为什么是SenseVoice-Small + STM32?

在动手之前,我们先花几分钟搞清楚,为什么选这个组合。理解了“为什么”,后面的“怎么做”会更顺畅。

2.1 SenseVoice-Small模型:小而精的语音专家

SenseVoice-Small是一个专门为嵌入式设备优化的语音识别模型。你可以把它想象成一个经过特殊训练的“耳朵”和“大脑”。它的特点非常鲜明:

  • 身材极度苗条:经过量化等优化手段后,它的模型文件可以压缩到几百KB甚至更小。这对于内存通常只有几十KB到几百KB的STM32来说,是能装得下的前提。
  • 专注关键词识别:它不像手机上的语音助手那样试图听懂所有句子,而是专注于识别你预先设定好的几个或几十个“命令词”,比如“打开空调”、“调高亮度”。这种任务简化带来了性能上的巨大优势。
  • 计算需求低:模型结构经过精心设计,需要的乘加运算次数(MACs)大大减少,使得它能在主频几十MHz的STM32上跑起来,并且还能留出时间处理其他任务。

2.2 STM32平台:无处不在的微型大脑

STM32系列单片机是嵌入式领域的“明星产品”,尤其是我们这次会用到的STM32F103C8T6(常被称为“蓝色药丸”或“最小系统板”),它有几个关键优势:

  • 成本极低:一块核心板可能就十几块钱,非常适合做原型验证和低成本量产。
  • 资源够用:以F103C8T6为例,它有72MHz的主频,20KB的RAM和64KB的Flash。虽然看起来寒酸,但经过优化,跑一个轻量级语音模型是可行的。
  • 生态完善:有强大的HAL库和丰富的社区支持,获取音频、处理数据都很方便。

简单来说,这个组合的精髓就是“把合适的能力,放到合适的平台上”。我们用小巧的模型去解决明确的任务(识别几个命令词),然后把它放到成本最低、最普及的硬件上跑通。这比用一颗强大的、昂贵的AI芯片来实现同样功能,在成本和普及性上要有吸引力得多。

3. 动手之前:环境与工具准备

工欲善其事,必先利其器。我们先来把需要的软件工具和硬件设备准备好。

3.1 硬件清单

你需要准备以下东西:

  1. STM32开发板:一块STM32F103C8T6核心板(或类似资源型号)。这是我们的主控。
  2. 麦克风模块:一个I2S接口的数字麦克风模块(如INMP441)。这是设备的“耳朵”,负责把声音转换成数字信号。I2S是音频领域常用的标准接口,能保证音质。
  3. 杜邦线:若干,用于连接开发板和麦克风。
  4. USB转串口模块:用于程序下载和调试信息打印。
  5. 电脑:一台用于编程和模型处理的电脑。

3.2 软件工具

软件方面稍微多一些,但别怕,我们一步步来:

  1. STM32CubeIDE:这是ST官方推出的免费集成开发环境,集成了代码编辑、编译、调试和烧录功能。我们将在这里编写和部署代码。
  2. STM32CubeMX:一个图形化配置工具,可以直观地配置单片机的引脚、时钟、外设(如I2S、串口)等,并生成初始化代码。它通常和CubeIDE捆绑在一起。
  3. Python环境:我们需要用Python来完成模型转换和预处理。建议安装Anaconda来管理Python环境,避免包冲突。
  4. ONNX Runtime:这是微软推出的一个高性能推理引擎,支持在各种硬件上运行ONNX模型。我们需要它的C语言版本,以便移植到STM32。
  5. 模型文件:SenseVoice-Small的ONNX格式模型文件。你可能需要从模型提供方的仓库获取,并可能需要使用工具对其进行进一步的量化或优化,以适配STM32的内存。

把硬件连起来,软件装好,我们就成功了一半。连接时,主要确保麦克风的I2S数据线(SD)、时钟线(SCK)、左右声道选择线(WS)正确连接到STM32对应的引脚上,并在CubeMX里配置好。

4. 从模型到代码:部署流程详解

这是最核心的部分,我们一步步拆解,把模型“塞进”单片机里。

4.1 第一步:模型准备与轻量化

拿到原始的SenseVoice-Small模型(可能是PyTorch或TensorFlow格式)后,我们不能直接用它。

  1. 转换为ONNX:使用相应的框架工具(如torch.onnx.export)将模型导出为ONNX格式。ONNX就像一个“中间翻译”,让不同框架训练的模型能在统一的运行时上执行。
  2. 模型量化:这是关键一步。默认模型参数是32位浮点数(float32),非常占空间和算力。我们需要将其量化为8位整数(int8)。你可以想象成把一张高清图片压缩成小尺寸的预览图,虽然细节有损失,但主要信息还在,并且体积和查看速度大大提升。可以使用ONNX Runtime提供的量化工具来完成。
  3. 模型简化:使用onnx-simplifier等工具对模型图结构进行优化,移除不必要的操作,让模型更“干净”。

完成这三步后,你应该得到一个.onnx文件,它的体积会比原始模型小很多,并且是整数格式,更适合MCU。

4.2 第二步:在STM32CubeMX中配置工程

打开STM32CubeMX,新建一个针对你开发板型号的工程。

  1. 配置时钟树:将系统主频(HCLK)设置到最高(如72MHz),为模型推理提供尽可能快的计算速度。
  2. 配置外设
    • I2S:配置为接收模式(Receiver),以接收来自麦克风的音频数据。设置好音频采样率(如16kHz)、数据位宽(16位或32位)和主从模式(通常STM32作为从机)。
    • USART:配置一个串口,用于打印调试信息(比如识别出的命令词是什么),方便我们观察结果。
    • 定时器:可以配置一个定时器来触发定期采集音频,或者使用I2S的DMA(直接存储器访问)自动搬运数据,这样不占用CPU。
  3. 生成代码:配置完成后,点击“Generate Code”,选择STM32CubeIDE作为开发工具,它会生成一个包含所有外设初始化代码的完整工程。

4.3 第三步:集成ONNX Runtime与推理代码

这是最具挑战性但也最有成就感的一步。

  1. 移植ONNX Runtime C API:你需要获取ONNX Runtime针对嵌入式设备(尤其是未使用操作系统)的C语言接口库。这个库通常已经对内存和体积做了优化。将相关的头文件(.h)和源文件(.c)添加到你的CubeIDE工程中。
  2. 集成模型数据:将上一步准备好的量化后的.onnx模型文件,通过一个转换脚本,将其内容转换为一个C语言数组(一个巨大的const unsigned char数组)。这样,模型数据就直接被编译进程序的Flash里了,上电就有。
  3. 编写音频预处理代码:麦克风采集到的原始音频数据(PCM格式)不能直接喂给模型。你需要编写代码来实现:
    • 静音检测:判断当前是否有声音,避免一直处理噪音。
    • 预加重:提升高频分量,让语音特征更明显。
    • 分帧与加窗:将连续的音频流切成一小段一小段(比如25ms一帧)并加上汉明窗。
    • 计算MFCC特征:这是最关键的一步,将声音的时域信号转换为人耳听觉特性相关的频域特征(梅尔频率倒谱系数)。这是模型能“理解”的输入格式。你需要找一个轻量级的MFCC C语言实现库,或者自己实现一个简化版。
  4. 编写推理流水线:在主循环中,建立这样一个流程:
    • 通过I2S(配合DMA)持续采集音频数据到缓冲区。
    • 缓冲区满一帧后,触发中断或检查,进行上述的音频预处理,得到一帧MFCC特征。
    • 累积足够多帧的特征(比如对应1秒的语音),组成一个二维数组(时间帧 x MFCC维度)。
    • 调用ONNX Runtime的API,创建会话(Session),将特征数组作为输入,运行模型推理。
    • 获取模型输出。输出通常是一个概率向量,每个元素对应一个命令词(包括“静音”或“未知”)的得分。取概率最高的那个,就是识别结果。
    • 通过串口打印出识别到的命令词,或者直接控制一个GPIO引脚(比如点亮LED)来做出响应。

5. 关键优化与调试技巧

直接把代码跑起来,你可能会发现内存不够或者速度太慢。别急,嵌入式开发就是不断优化的过程。

5.1 内存优化是生命线

STM32的RAM非常紧张,必须精打细算。

  • 使用内存池:避免频繁的动态内存分配(malloc),这容易导致内存碎片。预先分配好几个固定大小的内存块,用于音频缓冲区、特征数组和模型输入输出。
  • 模型输入输出复用内存:如果可能,让模型的输入和输出Tensor共享同一块内存,或者使用中间过程的缓冲区。
  • 启用Flash加速:如果Flash速度比RAM慢,记得在CubeMX中开启“ART Accelerator”,它能缓存Flash指令,提升执行速度。

5.2 加速推理过程

  • 利用CMSIS-NN库:这是ARM官方为Cortex-M系列处理器提供的神经网络计算库,高度优化了卷积、全连接等操作。确保你的ONNX Runtime在编译时启用了CMSIS-NN后端支持,这能带来显著的性能提升。
  • 降低采样率:如果16kHz采样率负担太重,可以尝试降到8kHz。这对近距离命令词识别通常够用,能减少一半的数据处理量。
  • 简化MFCC计算:检查MFCC计算中的每一步,看看哪些步骤可以简化或查表实现,比如计算Mel滤波器组、做离散余弦变换(DCT)。

5.3 调试:让问题看得见

  • 串口是你的好朋友:在代码的关键节点(如采集开始、预处理完成、推理开始、得到结果)打印日志,能帮你清晰了解程序运行到了哪一步,以及数据是否正常。
  • 分段测试:不要试图一次性集成所有功能。先测试I2S能否正确采集到音频数据(可以打印出原始数据看波形)。再单独测试MFCC计算模块,用一段已知的音频验证特征是否正确。最后再接入模型进行推理测试。
  • 使用逻辑分析仪:如果条件允许,用逻辑分析仪抓取I2S总线上的波形,可以最直观地确认麦克风数据是否被正确发送和接收。

6. 实际效果与下一步

当你完成所有步骤,将程序烧录进STM32,对着麦克风清晰地说出“打开灯光”,看到开发板上的LED应声点亮,或者串口助手上打印出正确的命令词时,那种感觉是非常棒的。

这套方案跑通后,识别几个到十几个命令词,在安静的室内环境下,准确率可以达到比较实用的水平。当然,它也有局限,比如在嘈杂环境下效果会下降,无法理解复杂的连续语句。

但这已经为我们打开了一扇门。你可以基于此,尝试更多有趣的事情:

  • 增加命令词:在模型能力范围内,增加更多的控制指令。
  • 结合唤醒词:可以先做一个简单的“小X小X”这样的唤醒词检测,被唤醒后再进入命令词识别模式,更省电。
  • 移植到其他MCU:既然在资源紧张的F103上都能跑,那么性能更强的F4、H7系列运行起来会更加游刃有余。
  • 应用到实际项目:把它集成到你的智能台灯、语音控制小车或者任何有趣的创意项目中。

整个过程就像在有限的画布上作画,充满了挑战,但也充满了创造的乐趣。希望这篇教程能帮你跨出第一步。剩下的,就交给你的想象力了。


获取更多AI镜像

想探索更多AI镜像和应用场景?访问 CSDN星图镜像广场,提供丰富的预置镜像,覆盖大模型推理、图像生成、视频生成、模型微调等多个领域,支持一键部署。

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐