4、语音信号预处理:预加重、分帧、加窗、短时傅里叶变换(STFT)、梅尔频谱图生成
在STM32平台上部署Whisper模型进行AI语音识别时,原始音频数据无法直接送入神经网络。麦克风采集到的时域波形包含大量冗余信息、环境噪声和声道效应,且数据量庞大(16kHz采样率下,1秒音频即包含16000个采样点)。语音信号预处理的目标是将原始波形转换为模型能够高效处理的二维特征图——梅尔频谱图。本章将系统讲解从预加重到梅尔频谱图生成的完整信号处理链路,并给出在STM32嵌入式环境下的工程实现要点。
4.1 预加重:提升高频能量
核心原理:语音信号在传播过程中,由于声门激励和口鼻辐射的影响,高频分量(2kHz以上)的能量会以约6dB/倍频程的速率衰减。预加重通过一阶高通滤波器提升高频成分,使频谱变得平坦,便于后续特征提取。其传递函数为 H(z) = 1 - αz⁻¹,典型α值取0.97。
STM32实现要点:预加重在时域逐样本进行,计算量极小。在Cortex-M4/M7内核上,使用Q15定点数格式可避免浮点运算开销。对于16kHz采样率,每样本仅需一次乘法和一次减法,1秒音频的预加重处理耗时通常小于1ms。
// STM32定点预加重实现(Q15格式)
// α = 0.97 → Q15表示为 0.97 * 32768 = 31785
#define PRE_EMPH_ALPHA_Q15 31785
void pre_emphasis_q15(int16_t *data, uint32_t len) {
int32_t prev = 0;
for (uint32_t i = 0; i < len; i++) {
int32_t curr = data[i];
int32_t high_pass = curr - ((prev * PRE_EMPH_ALPHA_Q15) >> 15);
data[i] = (int16_t)high_pass;
prev = curr;
}
}
常见误区:预加重系数并非越大越好。α=0.97是经验最优值,过大会导致低频信息丢失,过小则高频提升不足。在STM32上还需注意定点运算的溢出问题——输入信号接近满幅时,预加重输出可能超过16位范围,需预留6dB余量。
4.2 分帧与加窗:短时平稳假设
核心原理:语音信号具有短时平稳性——在10-30ms的时间尺度内,信号的频谱特性可视为不变。分帧将连续信号切分为重叠的短段,帧移通常取帧长的40%-50%以保证帧间平滑过渡。加窗则用于减少帧边缘的频谱泄漏,汉明窗是最常用的窗函数。
| 窗函数 | 主瓣宽度 | 旁瓣衰减 | STM32适用性 |
|---|---|---|---|
| 矩形窗 | 最窄 | -13dB | 不推荐(频谱泄漏严重) |
| 汉明窗 | 较宽 | -43dB | 推荐(均衡性能) |
| 汉宁窗 | 较宽 | -31dB | 可选(旁瓣衰减略差) |
| 布莱克曼窗 | 最宽 | -58dB | 不推荐(计算量大) |
操作流程:在STM32上,分帧通常通过环形缓冲区实现。DMA持续采集音频数据,当缓冲区积累满一帧(400点)时触发中断,CPU从缓冲区中取出当前帧和上一帧的尾部数据拼接成完整帧。加窗操作可预先计算窗系数存入Flash,运行时逐点相乘。
工程风险:STM32的SRAM有限(如STM32F407仅有192KB),若直接存储全部音频数据再分帧会浪费内存。推荐使用双缓冲DMA方案:一个缓冲区采集,另一个缓冲区处理,交替进行。每帧400点×2字节=800字节,双缓冲仅需1.6KB。
4.3 短时傅里叶变换(STFT)
核心原理:STFT对每一帧加窗后的信号执行FFT,得到该帧的频域表示。对于400点的帧,通常零填充至512点进行FFT,输出257个有效频点(0-8kHz)。STM32上可使用CMSIS-DSP库的arm_rfft_fast_f32或arm_cfft_q15函数高效完成FFT计算。
// STM32 CMSIS-DSP FFT实现(512点实数FFT)
#include "arm_math.h"
#define FFT_SIZE 512
arm_rfft_fast_instance_f32 fft_instance;
float32_t input[FFT_SIZE];
float32_t output[FFT_SIZE];
// 初始化(仅需一次)
arm_rfft_fast_init_f32(&fft_instance, FFT_SIZE);
// 对每帧执行FFT
arm_rfft_fast_f32(&fft_instance, input, output, 0); // 0表示正变换
// 计算幅度谱(取前257个频点)
float32_t magnitude[257];
for (int i = 0; i < 257; i++) {
float32_t re = output[2*i];
float32_t im = output[2*i+1];
magnitude[i] = sqrtf(re*re + im*im);
}
性能评估:在STM32F407(168MHz)上,512点实数FFT耗时约50μs,加上幅度计算约80μs。每帧处理总耗时(含预加重、加窗、FFT)控制在200μs以内,远低于帧间隔10ms,满足实时性要求。
4.4 梅尔频谱图生成
核心原理:梅尔刻度模拟人耳对频率的非线性感知——低频分辨率高、高频分辨率低。将STFT得到的257个频点映射到40个梅尔滤波器组,每个滤波器为三角形状,中心频率在梅尔刻度上等间距分布。最后取对数压缩动态范围,得到梅尔频谱图。
梅尔频率转换公式:
mel = 2595 * log10(1 + f/700)
f = 700 * (10^(mel/2595) - 1)
STM32实现策略:梅尔滤波器组可预先计算并存储为40×257的稀疏矩阵(大部分元素为0),仅存储非零元素及其索引。对于40个滤波器,每个滤波器覆盖约6-7个频点,总非零元素约260个,Flash占用约1KB。
| 处理阶段 | 数据量 | 计算量 | 存储需求 |
|---|---|---|---|
| 原始音频(1秒) | 16000点×2B | - | 32KB |
| 分帧后(98帧) | 98×400点×2B | 低 | 78.4KB(可流式处理) |
| STFT幅度谱 | 98×257×4B | 中(FFT) | 100KB(可逐帧处理) |
| 梅尔频谱 | 98×40×4B | 低(矩阵乘) | 15.7KB |
4.5 实战案例:STM32F407实时语音特征提取
项目背景:某智能家居项目需要在STM32F407上实现关键词唤醒功能,要求从麦克风输入到梅尔频谱图输出的端到端延迟小于50ms,且SRAM占用不超过80KB。
初始方案与问题:最初设计将所有音频数据缓存后统一处理,导致SRAM占用高达120KB(98帧×400点×2B×2通道),超出预算。且FFT计算集中在处理阶段,造成CPU峰值负载过高。
优化方案:
- 流式处理架构:采用DMA双缓冲,每采集满一帧立即处理,处理完即释放内存。SRAM占用降至8KB(双缓冲×400点×2B)。
- 定点数优化:将浮点FFT替换为Q15定点FFT,计算速度提升40%,功耗降低25%。
- 梅尔滤波器稀疏存储:仅存储非零系数,Flash占用从40KB降至1.2KB。
- 增量特征拼接:Whisper需要80维特征(40梅尔+40增量),增量特征通过相邻帧差分计算,额外增加1帧延迟。
实测结果:
- 端到端延迟:32ms(满足50ms要求)
- SRAM峰值:42KB(含特征缓存)
- CPU负载:35%(168MHz主频)
- 识别准确率:与PC端浮点实现相比下降0.8%(在可接受范围内)
经验复盘:嵌入式语音处理的核心矛盾是内存与延迟。流式处理是解决之道——不要试图一次性处理整段音频。另外,定点数优化应优先于算法简化,因为STM32F4系列带有FPU,但定点FFT仍比浮点快30%以上。
4.6 常见误区与总结
误区一:忽略直流偏移。STM32的ADC通常有1.65V的直流偏置(3.3V供电时),需在预加重前去除直流分量,否则会严重影响低频特征。
误区二:FFT点数选择不当。FFT点数过小导致频率分辨率不足(如256点仅4Hz分辨率),过大则增加计算量。512点(频率分辨率31.25Hz)是语音识别的平衡选择。
误区三:梅尔滤波器组未归一化。不同滤波器带宽不同,需对每个滤波器进行能量归一化,否则高频滤波器输出会偏大。
误区四:忽略帧间连续性。Whisper模型对时间维度敏感,帧移过大(如20ms)会丢失语音动态信息,推荐10ms帧移。
本章核心要点:语音预处理的本质是将一维时域信号转换为二维时频特征。预加重补偿高频衰减,分帧加窗保证短时平稳性,STFT获取频域信息,梅尔滤波模拟人耳感知。在STM32上,需采用流式处理架构、定点数优化和稀疏存储策略,才能在有限资源下实现实时处理。完整的处理链路为:ADC采集→直流去除→预加重→分帧→加窗→FFT→幅度谱→梅尔滤波→对数压缩→增量特征拼接→送入Whisper模型。