4. 语音特征提取:MFCC特征原理、提取步骤、参数配置与其他常用特征
好,咱们进入语音识别的核心环节——特征提取。你想想看,麦克风采集到的原始音频信号,说白了就是一长串随时间变化的电压值。直接拿这个去识别,就像把一整本《红楼梦》的原始墨迹交给AI去理解,它根本无从下手。我们需要把音频信号,转换成机器能听懂、能计算的“特征向量”。
在嵌入式语音识别领域,MFCC(梅尔频率倒谱系数) 是当之无愧的“老大哥”。我最早接触它是在做一款离线语音开关的项目,当时试了好几种特征,最后还是老老实实回到了MFCC上。为什么?因为它最接近人耳的听觉特性,鲁棒性也最好。
4.1 MFCC特征原理:为什么它这么“像人耳”?
MFCC的核心思想,是模拟人耳对不同频率声音的非线性感知。人耳对低频信号的分辨率很高,对高频信号则比较“迟钝”。举个例子,你很容易听出100Hz和200Hz的区别,但很难分辨1000Hz和1100Hz的差异。
梅尔刻度(Mel Scale)就是用来描述这种非线性关系的。它把实际的物理频率(Hz)映射到人耳感知的“音高”频率(Mel)。转换公式如下:
Mel(f) = 2595 * log10(1 + f / 700)
嗯,这里要注意,这个公式不是拍脑袋想出来的,而是通过大量心理声学实验拟合出来的。我个人习惯把这个公式记在笔记本上,因为每次配置滤波器组时都要用到它。
MFCC的另一个关键点是“倒谱分析”。它能把语音信号中的“声道响应”(决定音色)和“激励源”(决定音调)分离开。我们做语音识别,主要关心的是声道形状的变化,也就是音素的内容。倒谱分析正好帮我们干了这个活。
核心要点: MFCC = 梅尔刻度(模拟人耳) + 倒谱分析(分离声道与激励)。这两者结合,让MFCC在噪声环境下依然能保持不错的识别率。
4.2 MFCC提取步骤:一步步拆解
MFCC的提取流程,我在STM32上优化过很多次。每一步都有坑,我一个个说。
- 预加重:语音信号的高频分量通常能量较低,容易在传输中衰减。预加重就是用一个高通滤波器,把高频部分“抬”起来。公式很简单:
y[n] = x[n] - 0.97 * x[n-1]。这个0.97是经验值,我试过0.95到0.99,差别不大,但0.97最稳。 - 分帧:语音信号是时变的,但在极短时间(10-30ms)内可以认为是平稳的。所以我们要把长信号切成一段段“帧”。帧长一般取20-40ms,帧移取帧长的一半或三分之一。比如采样率16kHz,帧长512点(32ms),帧移256点(16ms)。
- 加窗:分帧后直接做FFT,会在帧边界产生频谱泄漏。加窗就是为了缓解这个问题。汉明窗(Hamming Window)是首选,它的旁瓣衰减比较大。代码实现如下:
// 汉明窗生成函数
void hamming_window(float *window, int len) {
for (int i = 0; i < len; i++) {
window[i] = 0.54 - 0.46 * cos(2 * M_PI * i / (len - 1));
}
}
- FFT(快速傅里叶变换):对每一帧加窗后的数据做FFT,得到频谱。STM32的DSP库里有现成的
arm_rfft_f32函数,效率很高。我建议直接用硬件FPU加速,别用软件模拟。 - 梅尔滤波器组:这是最核心的一步。在梅尔刻度上均匀分布一组三角滤波器(通常20-40个),然后与FFT得到的幅度谱相乘,得到每个滤波器输出的能量。滤波器组的中心频率在梅尔域上是等间距的,但在赫兹域上是越来越稀疏的。
- 取对数:对每个滤波器输出的能量取自然对数。这一步是为了模拟人耳对声音强度的对数感知,同时也能压缩动态范围。
- DCT(离散余弦变换):对对数能量做DCT,得到倒谱系数。通常只取前12-13个系数,因为高阶系数代表的是频谱的快速变化,对语音识别贡献不大,反而容易引入噪声。
- 动态特征:为了捕捉语音的时序变化,我们还会计算一阶差分(Δ)和二阶差分(ΔΔ)。比如,一帧的MFCC特征向量可能是:12个静态系数 + 12个Δ系数 + 12个ΔΔ系数 + 1个能量项 = 37维。
我的经验: 在STM32F4上,我一般取13维静态MFCC + 13维Δ + 13维ΔΔ = 39维。这个维度在识别率和计算量之间取得了很好的平衡。如果你用的是F0或G0系列,可以只取13维静态系数,省掉动态特征,能省不少RAM和算力。
4.3 MFCC参数配置:嵌入式环境下的“调参”艺术
参数配置没有绝对的标准,完全取决于你的应用场景。我列一个常用的配置表,你可以直接拿去用:
| 参数名称 | 推荐值 | 说明 |
|---|---|---|
| 采样率 | 16 kHz | 语音信号主要能量在8kHz以下,16kHz足够 |
| 帧长 | 512 点 (32ms) | 保证频率分辨率,又不至于帧内变化太大 |
| 帧移 | 256 点 (16ms) | 50%重叠,保证时序连续性 |
| FFT点数 | 512 | 与帧长一致,用DSP库的基4算法效率最高 |
| 梅尔滤波器个数 | 26 | 常用值,再多对嵌入式来说计算量太大 |
| MFCC系数个数 | 13 | 去掉第0个系数(直流分量),保留1-13 |
| 动态特征 | Δ + ΔΔ | 可选,根据算力决定 |
我曾经踩过的坑: 有一次我把帧长设成了1024点(64ms),想着频率分辨率更高。结果识别率反而下降了。后来分析发现,帧长太长,帧内的语音信号已经不满足“短时平稳”的假设了,导致特征失真。所以,别贪心,32ms是黄金帧长。
4.4 其他常用特征:MFCC不是唯一的选择
虽然MFCC是主流,但有些场景下,其他特征可能更合适。我简单介绍几个:
- PLP(感知线性预测):它比MFCC更深入地模拟了人耳的听觉特性,包括等响度曲线和强度-响度压缩。在噪声环境下,PLP的鲁棒性往往优于MFCC。但计算量也更大,STM32F4勉强能跑,F0就别想了。
- FBANK(滤波器组能量):其实就是MFCC提取过程中的“对数梅尔能量”,不做DCT。FBANK保留了更多的频谱细节,在深度神经网络(DNN)作为分类器时,效果往往比MFCC好。因为DNN自己会学习特征之间的相关性,不需要DCT去解相关。
- GFCC(伽马通频率倒谱系数):基于伽马通滤波器组,更精确地模拟了人耳基底膜的滤波特性。我在做远场语音识别时用过一次,效果不错,但计算量比MFCC大30%左右。
- 过零率(ZCR)和短时能量:这两个是最简单的特征,计算量几乎为零。我经常用它们来做语音端点检测(VAD),判断当前是静音还是有人说话。比如,短时能量低于某个阈值,就认为是静音,直接跳过MFCC提取,能省不少电。
嗯,总结一下。MFCC是嵌入式语音识别的“基本功”,你必须熟练掌握它的原理和提取步骤。参数配置要根据你的芯片算力和应用场景灵活调整。其他特征可以作为补充,但别贪多,先把MFCC玩透了再说。
下一章,我们会把这些特征送入神经网络,看看STM32是怎么跑推理的。到时候你会发现,特征提取这一步做得好,后面的识别就成功了一半。