STM32开发入门:SenseVoice-Small在嵌入式系统的语音唤醒实现

1. 引言

想给自己的STM32设备加上语音唤醒功能吗?比如让智能音箱听到"小爱同学"就亮灯,或者让智能家居听到特定指令就执行操作?今天我们就来聊聊如何在STM32上实现SenseVoice-Small语音唤醒功能。

SenseVoice-Small是一个轻量级的语音识别模型,专门为嵌入式设备设计。它支持多种语言,识别准确率高,而且最重要的是——它足够小,可以在STM32这样的资源受限环境中运行。不需要昂贵的专用语音芯片,用你手头的STM32开发板就能玩转语音唤醒。

2. 环境准备与硬件选型

2.1 硬件需求

要玩转语音唤醒,你需要准备以下硬件:

  • STM32开发板:推荐使用F4或F7系列,主频至少100MHz,RAM不少于128KB
  • 麦克风模块:普通的I2S数字麦克风就可以,比如INMP441
  • 音频编解码器(可选):如果需要更好的音质,可以加个VS1053之类的编解码芯片
  • 调试工具:ST-Link调试器,串口模块

2.2 软件工具

准备好这些开发工具:

# 开发环境
STM32CubeIDE      # 官方IDE,免费好用
STM32CubeMX       # 引脚配置和代码生成工具

# 库文件
CMSIS-DSP         # ARM的DSP库,用于音频处理
SenseVoice-Small  # 语音识别模型(需要从官网下载)

3. 硬件连接与驱动编写

3.1 麦克风连接

把I2S麦克风接到STM32上其实很简单:

INMP441麦克风      STM32F4
    VDD    ->    3.3V
    GND    ->    GND
    SCK    ->    PB13 (I2S2_CK)
    WS     ->    PB12 (I2S2_WS) 
    SD     ->    PB15 (I2S2_SD)
    L/R    ->    GND (选择左声道)

3.2 I2S驱动程序

用STM32CubeMX配置I2S外设,然后写个简单的采集程序:

// I2S初始化代码
void MX_I2S2_Init(void)
{
    hi2s2.Instance = SPI2;
    hi2s2.Init.Mode = I2S_MODE_MASTER_RX;
    hi2s2.Init.Standard = I2S_STANDARD_PHILIPS;
    hi2s2.Init.DataFormat = I2S_DATAFORMAT_16B;
    hi2s2.Init.MCLKOutput = I2S_MCLKOUTPUT_ENABLE;
    hi2s2.Init.AudioFreq = I2S_AUDIOFREQ_16K;
    hi2s2.Init.CPOL = I2S_CPOL_LOW;
    hi2s2.Init.ClockSource = I2S_CLOCK_PLL;
    HAL_I2S_Init(&hi2s2);
}

// 音频数据采集
void record_audio(int16_t* buffer, uint32_t length)
{
    HAL_I2S_Receive(&hi2s2, (uint16_t*)buffer, length, HAL_MAX_DELAY);
}

4. SenseVoice-Small模型集成

4.1 模型准备

SenseVoice-Small需要先转换成适合STM32的格式。通常我们会用ONNX格式,然后用量化工具减小模型大小:

# 模型转换示例(在PC上运行)
import onnx
from onnxruntime.quantization import quantize_dynamic

# 加载原始模型
model = onnx.load("sensevoice_small.onnx")

# 动态量化,减小模型大小
quantized_model = quantize_dynamic(model)
onnx.save(quantized_model, "sensevoice_small_quantized.onnx")

4.2 模型部署

把量化后的模型集成到STM32项目中:

// 模型数据定义
const uint8_t sensevoice_model[] = {
    // 这里是你量化后的模型数据
    // 可以用xxd工具把.onnx文件转换成C数组
    0x08, 0x00, 0x12, 0x04, 0x1a, 0x02, 0x08, 0x01, 
    0x12, 0x04, 0x1a, 0x02, 0x08, 0x01, 0x12, 0x04,
    // ... 更多模型数据
};

// 初始化推理引擎
void sensevoice_init(void)
{
    // 初始化TensorFlow Lite Micro或者类似推理引擎
    // 加载模型数据
    // 分配张量内存
}

5. 语音唤醒实现

5.1 音频预处理

原始音频数据需要先预处理才能喂给模型:

void preprocess_audio(int16_t* input, float* output, uint32_t length)
{
    // 1. 预加重(增强高频)
    for (uint32_t i = 1; i < length; i++) {
        input[i] = input[i] - 0.97 * input[i-1];
    }
    
    // 2. 分帧(25ms一帧,10ms重叠)
    const uint32_t frame_size = 400;  // 16kHz * 0.025s
    const uint32_t frame_shift = 160; // 16kHz * 0.010s
    
    // 3. 加窗(汉明窗)
    for (uint32_t i = 0; i < frame_size; i++) {
        float window = 0.54 - 0.46 * cosf(2 * M_PI * i / (frame_size - 1));
        output[i] = input[i] * window;
    }
    
    // 4. 计算MFCC特征(可以用CMSIS-DSP库加速)
    arm_rfft_fast_instance_f32 fft_instance;
    arm_rfft_fast_init_f32(&fft_instance, frame_size);
    
    // ... 更多处理步骤
}

5.2 唤醒词检测

核心的唤醒逻辑是这样的:

bool detect_wakeword(float* audio_features)
{
    // 1. 运行推理
    float* output = run_inference(audio_features);
    
    // 2. 后处理
    uint32_t wakeword_index = argmax(output);
    float confidence = output[wakeword_index];
    
    // 3. 阈值判断
    if (confidence > 0.85f) {  // 置信度阈值
        return true;
    }
    
    return false;
}

// 主循环中的调用
void main_loop(void)
{
    while (1) {
        // 采集一帧音频
        int16_t audio_data[400];
        record_audio(audio_data, 400);
        
        // 预处理
        float features[80];
        preprocess_audio(audio_data, features, 400);
        
        // 检测唤醒词
        if (detect_wakeword(features)) {
            wakeword_detected();
        }
        
        // 休眠一段时间,节省功耗
        HAL_Delay(10);
    }
}

6. 低功耗优化技巧

在嵌入式设备上,省电很重要。这里有几个实用技巧:

6.1 硬件级省电

void enter_low_power_mode(void)
{
    // 关闭不用的外设时钟
    __HAL_RCC_GPIOA_CLK_DISABLE();
    __HAL_RCC_GPIOB_CLK_DISABLE();
    // ... 保留必要的时钟
    
    // 降低主频
    SystemCoreClock = 16000000;  // 降到16MHz
    HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_1);
    
    // 进入停止模式,等待中断唤醒
    HAL_PWR_EnterSTOPMode(PWR_LOWPOWERREGULATOR_ON, PWR_STOPENTRY_WFI);
}

6.2 软件级优化

  • 间歇工作:每100ms唤醒一次处理音频,其他时间休眠
  • 提前终止:如果音频能量太低,直接跳过推理
  • 模型量化:使用8整数量化,减少计算量

7. 常见问题与调试

7.1 音频质量差

如果识别不准,先检查音频质量:

void check_audio_quality(void)
{
    // 检查DC偏移
    int32_t sum = 0;
    for (int i = 0; i < 1000; i++) {
        sum += audio_buffer[i];
    }
    float dc_offset = (float)sum / 1000.0f;
    
    // 检查信号幅度
    int16_t max_val = 0;
    for (int i = 0; i < 1000; i++) {
        if (abs(audio_buffer[i]) > max_val) {
            max_val = abs(audio_buffer[i]);
        }
    }
    
    printf("DC偏移: %.2f, 最大幅度: %d\r\n", dc_offset, max_val);
}

7.2 模型推理太慢

如果推理速度跟不上实时要求:

  1. 降低采样率:从16kHz降到8kHz
  2. 减小帧长:从25ms降到20ms
  3. 简化特征:用更少的MFCC系数
  4. 使用硬件加速:如果STM32有DSP指令集,一定要用上

8. 总结

实现在STM32上跑SenseVoice-Small语音唤醒其实没有想象中那么难。关键是要做好音频预处理、模型量化和低功耗设计。虽然STM32的计算能力有限,但通过合理的优化,完全能够实现实用的语音唤醒功能。

建议先从简单的唤醒词开始,比如单音节的"嗨"、"喂",等调通了再尝试更复杂的词语。记得多用示波器和逻辑分析仪观察音频信号,好的输入质量是成功的一半。


获取更多AI镜像

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

Logo

openvela 操作系统专为 AIoT 领域量身定制,以轻量化、标准兼容、安全性和高度可扩展性为核心特点。openvela 以其卓越的技术优势,已成为众多物联网设备和 AI 硬件的技术首选,涵盖了智能手表、运动手环、智能音箱、耳机、智能家居设备以及机器人等多个领域。

更多推荐