4. 语音关键词识别(KWS)

任务目标

在STM32嵌入式平台上实现一个轻量级关键词识别(KWS)系统,通过麦克风实时采集音频,识别“是”(Yes)和“否”(No)两个关键词,并在识别成功后通过串口输出结果。本案例聚焦于模型推理部分,使用STM32Cube.AI将训练好的KWS模型部署到MCU上。

环境与依赖

  • 硬件:STM32F746G-Discovery开发板(板载数字麦克风MP34DT01)
  • 软件:STM32CubeIDE 1.13+、STM32Cube.AI 8.1+、X-CUBE-AI扩展包
  • 模型:预训练KWS模型(.tflite格式),输入为40×49的Mel频谱特征,输出为3类(Yes/No/静音)
  • 音频前端:16kHz采样,25ms帧长,10ms帧移,40维Mel滤波器组

实现方案

系统分为音频采集、特征提取和模型推理三部分。音频采集使用STM32的SAI接口配合DMA读取PDM麦克风数据,经硬件抽取滤波后得到16kHz PCM数据。特征提取在MCU上完成:每帧数据加Hamming窗后做512点FFT,经40维Mel滤波器组得到能量谱,累积49帧形成40×49的二维特征图,作为模型输入。模型推理使用STM32Cube.AI生成的C代码库,输入为量化后的int8特征数据,输出为3个类别的置信度分数。

关键完整代码

/* main.c - KWS推理主逻辑 */
#include "ai_platform.h"
#include "network.h"
#include "kws_features.h"

/* 模型输入输出缓冲区 */
static ai_handle network = AI_HANDLE_NULL;
static ai_network_report network_info;
static ai_buffer ai_input[1];
static ai_buffer ai_output[1];

/* 音频特征累积缓冲区:40维×49帧 */
static int8_t feature_buffer[40 * 49];

/* 识别结果回调 */
static volatile uint8_t detection_result = 0;

/* 模型初始化 */
void KWS_ModelInit(void) {
    ai_error err;
    const ai_network_params params = {
        AI_NETWORK_DATA_WEIGHTS,
        AI_NETWORK_DATA_ACTIVATIONS
    };
    
    err = ai_network_create(&network, ¶ms);
    if (err.type != AI_ERROR_NONE) {
        Error_Handler();
    }
    
    ai_network_get_info(network, &network_info);
    
    /* 配置输入输出缓冲区 */
    ai_input[0].data = feature_buffer;
    ai_input[0].fmt = AI_BUFFER_FMT_Q7;
    ai_input[0].size = network_info.n_inputs[0].size;
    
    ai_output[0].data = (void*)&detection_result;
    ai_output[0].fmt = AI_BUFFER_FMT_Q7;
    ai_output[0].size = network_info.n_outputs[0].size;
}

/* 推理执行:输入40×49特征图,输出3类置信度 */
uint8_t KWS_RunInference(int8_t* features) {
    ai_i32 batch_size = 1;
    ai_error err;
    
    memcpy(feature_buffer, features, sizeof(feature_buffer));
    
    err = ai_network_run(network, ai_input, ai_output);
    if (err.type != AI_ERROR_NONE) {
        return 0;
    }
    
    /* 返回最高置信度类别:0=静音, 1=Yes, 2=No */
    return detection_result;
}

/* 主循环:每采集完49帧特征后调用 */
void KWS_ProcessFrame(int8_t* mel_features) {
    uint8_t result = KWS_RunInference(mel_features);
    
    if (result == 1) {
        printf("Detected: YES\r\n");
    } else if (result == 2) {
        printf("Detected: NO\r\n");
    }
    /* result==0 时静音,不输出 */
}

特征提取核心代码

/* kws_features.c - Mel频谱特征提取 */
#include "kws_features.h"
#include "arm_math.h"

#define FRAME_LEN 512    /* 25ms @16kHz */
#define NUM_MEL_BINS 40
#define NUM_FRAMES 49

static float32_t hamming_window[FRAME_LEN];
static float32_t mel_filterbank[NUM_MEL_BINS][FRAME_LEN/2+1];

/* 初始化Hamming窗和Mel滤波器组 */
void KWS_FeatureInit(void) {
    /* Hamming窗 */
    for (int i = 0; i < FRAME_LEN; i++) {
        hamming_window[i] = 0.54 - 0.46 * cosf(2*PI*i/(FRAME_LEN-1));
    }
    
    /* Mel滤波器组(预计算,实际使用查表) */
    GenerateMelFilterbank(mel_filterbank, 16000, FRAME_LEN, NUM_MEL_BINS);
}

/* 将PCM帧转换为40维Mel能量特征 */
void KWS_ExtractFeatures(int16_t* pcm_frame, float32_t* mel_out) {
    static float32_t fft_input[FRAME_LEN];
    static float32_t fft_output[FRAME_LEN];
    static arm_rfft_fast_instance_f32 fft_instance;
    
    /* 加窗 */
    for (int i = 0; i < FRAME_LEN; i++) {
        fft_input[i] = (float32_t)pcm_frame[i] * hamming_window[i];
    }
    
    /* 512点FFT */
    arm_rfft_fast_init_f32(&fft_instance, FRAME_LEN);
    arm_rfft_fast_f32(&fft_instance, fft_input, fft_output, 0);
    
    /* 计算功率谱并应用Mel滤波器组 */
    for (int m = 0; m < NUM_MEL_BINS; m++) {
        float32_t energy = 0;
        for (int k = 0; k < FRAME_LEN/2+1; k++) {
            float32_t mag = fft_output[k];
            energy += mag * mag * mel_filterbank[m][k];
        }
        mel_out[m] = logf(energy + 1e-10);
    }
}

运行与验证

  1. 在STM32CubeIDE中创建工程,添加X-CUBE-AI生成的network.c/h文件
  2. 编译并烧录到STM32F746G-Discovery开发板
  3. 打开串口终端(115200-8-N-1),将开发板靠近麦克风
  4. 清晰说出“Yes”或“No”,观察串口输出

验证方法与通过标准

  • 功能验证:连续说出“Yes”10次,至少8次输出“Detected: YES”;连续说出“No”10次,至少8次输出“Detected: NO”
  • 性能验证:单次推理时间不超过100ms(可通过GPIO翻转测量),满足实时性要求
  • 鲁棒性验证:在环境噪声约50dB的室内,识别准确率不低于80%

常见问题

  • 识别率低:检查麦克风增益设置,确保输入音频幅度在-6dBFS至-1dBFS之间
  • 推理时间过长:确认STM32Cube.AI配置中启用了CMSIS-NN优化,并设置CPU频率为216MHz
  • 特征数据不匹配:验证输入特征量化参数(scale=0.0235, zero_point=-128)与训练时一致
KWS系统数据流与处理流程 PDM麦克风 SAI+DMA采集 16kHz 预处理 抽取滤波/分帧 25ms帧 特征提取 FFT→Mel→log 40维×49帧 int8量化 Cube.AI推理 CNN模型 3类输出 串口输出 "YES"/"NO" 时序要求: • 每帧处理时间:10ms(实时采集) • 特征累积:49帧 × 10ms = 490ms • 推理时间:<100ms(Cube.AI优化后) • 总延迟:<600ms(满足交互需求) 注:特征提取与推理可流水线并行,降低端到端延迟