共计 2187 个字符,预计需要花费 6 分钟才能阅读完成。
为什么要在 MCU 上做语音识别?
当我们在智能门锁上用语音开锁,或者对工业控制器喊一声 ” 加速 ” 就能调整设备参数时,背后都是 MCU 在默默处理这些语音指令。相比云端方案,本地化处理有三个明显优势:

- 实时性:省去了网络往返延迟,响应时间能控制在 200ms 以内
- 隐私性:声音数据不用上传云端
- 成本:省去了 Wi-Fi/BLE 模块和云端服务费用
但 MCU 的 SRAM 往往只有几十到几百 KB,CPU 主频也就几百 MHz,这给语音识别带来三大挑战:
- 模型必须足够小(通常 <100KB)
- 特征提取要高效(避免复杂的 FFT 运算)
- 推理过程不能卡死其他任务
框架选型:TinyML 还是 TF Lite Micro?
目前 MCU 端主要有两大派系:
- TensorFlow Lite for Microcontrollers
- 优势:官方维护,支持量化训练(int8/float16)
- 缺点:运行时需要约 20KB 内存(仅框架)
-
适用场景:需要迁移已有 TF 模型时
-
TinyML 框架(如 Edge Impulse)
- 优势:提供端到端工具链(数据标注 -> 模型部署)
- 缺点:自定义算子支持有限
- 适用场景:快速原型开发
我个人的选型建议:
- 如果团队有 TF 经验,优先考虑 TF Lite Micro
- 如果要 3 天内出 demo,用 Edge Impulse
- 极端资源受限(<64KB RAM)时,考虑纯手工编写 CNN 推理代码
STM32H7 实战:从声音到文字
下面以 STM32H743(480MHz Cortex-M7,1MB RAM)为例,展示典型实现流程:
1. 音频采集(PDM 麦克风)
// 使用 DFSDM 接口接收 PDM 数据
void DFSDM_Init() {
hdfsdm1.Instance = DFSDM1_Channel0;
hdfsdm1.Init.OutputClock.Activation = ENABLE;
hdfsdm1.Init.OutputClock.Selection = DFSDM_CHANNEL_OUTPUT_CLOCK_SYSTEM;
hdfsdm1.Init.OutputClock.Divider = 40; // 采样率 =48MHz/40=1.2MHz
HAL_DFSDM_ChannelInit(&hdfsdm1);
}
2. 特征提取(MFCC 简化版)
// 计算 MFCC 的 12 个系数(省去 DCT 步骤)void compute_mfcc(int16_t* audio, float* mfcc_out) {// 1. 预加重:y[n] = x[n] - 0.97*x[n-1]
// 2. 分帧(20ms 一帧,50% 重叠)// 3. 加汉明窗
// 4. 计算 FFT 幅值(使用 ARM CMSIS-DSP 库)arm_rfft_fast_instance_f32 fft_inst;
arm_rfft_fast_init_f32(&fft_inst, 256);
arm_rfft_fast_f32(&fft_inst, frame, fft_out, 0);
// 5. 梅尔滤波器组(预先计算好的 24 个三角滤波器)for(int i=0; i<12; i++) {mfcc_out[i] = logf(dot_product(fft_mag, mel_filterbank[i]));
}
}
3. 模型推理(量化 CNN)
# 训练时导出 TF Lite 量化模型
converter = tf.lite.TFLiteConverter.from_keras_model(model)
converter.optimizations = [tf.lite.Optimize.DEFAULT]
converter.target_spec.supported_ops = [tf.lite.OpsSet.TFLITE_BUILTINS_INT8]
quantized_model = converter.convert()
内存优化三板斧
在 STM32 上跑模型就像在自行车后备箱装货,得精打细算:
- 模型量化:
- float32 转 int8 后模型缩小 4 倍
-
实测精度损失 <3%(关键词唤醒场景)
-
内存池管理:
// 预分配关键缓冲区 __attribute__((section(".sram1"))) int8_t input_tensor[490]; __attribute__((section(".sram2"))) int8_t output_tensor[12]; -
动态加载:
- 将模型权重存放在外部 Flash
- 推理时按需加载到 RAM(类似虚拟内存)
实时性保障方案
语音识别最怕卡顿,我的实战经验:
- 使用 DMA 双缓冲接收音频,避免 CPU 介入
- 设置模型推理为最低优先级任务(FreeRTOS 优先级 1)
-
关键时间节点:
-
音频采集:<1% CPU
- MFCC 计算:~15ms(开启 FPU 和 Cache)
- CNN 推理:~35ms(int8 模型)
避坑指南
- 采样率不对:
- PDM 麦克风的时钟必须精确匹配(误差 <1%)
-
实测发现时钟偏差 2% 会导致识别率下降 40%
-
环境噪声:
- 增加 VAD(语音活动检测)模块
-
在 MFCC 前做频域滤波(滤除 50Hz 工频干扰)
-
内存溢出:
- 用
arm-none-eabi-size工具检查内存分段 - 确保
.bss段不超过 SRAM 的 70%
开放性问题
最后留几个值得讨论的问题:
- 当识别精度和功耗冲突时(比如需要持续监听),你会如何权衡?
- 对于中文四声调识别,MCU 上的模型需要做哪些特殊处理?
- 有没有可能在 Cortex-M0(48MHz)上实现 10 个词的语音识别?
欢迎在评论区分享你的实战经验!
正文完
