时域特征提取:短时能量、短时过零率、短时自相关函数、基音周期估计
语音信号说到底就是个随时间变化的波形。但直接拿原始波形去分析,信息量太大,也不够直观。所以我们要从时域里提取一些“特征”,把关键信息浓缩出来。
这一章,我带你看看四个最基础的时域特征:短时能量、短时过零率、短时自相关函数,还有基音周期估计。嗯,这些概念听起来有点多,但别急,它们之间是有逻辑链条的。
核心思路:语音是非平稳信号,但在10-30ms的短时片段里可以看作平稳。所以我们加一个窗,把信号切成帧,再对每一帧提取特征。这就是“短时”二字的由来。
短时能量:语音的“音量表”
短时能量,说白了就是某一帧语音信号的“音量”大小。数学上就是把一帧内所有采样点的平方加起来。
公式很简单:
E_n = Σ_{m=0}^{N-1} [x_n(m)]²
其中 x_n(m) 是第 n 帧的第 m 个采样点,N 是帧长。
你可能会问:为什么不用绝对值?我刚开始也这么想。后来发现平方操作能放大高能量部分,让清音和浊音的区分更明显。浊音的能量通常比清音高一个数量级。
我的经验:做端点检测(VAD)时,短时能量是最常用的特征之一。但我建议别只用能量,容易把噪声误判成语音。我一般会结合过零率一起用,效果会好很多。
实际应用中,我们通常用对数能量,因为人耳对声音的感知是对数的:
E_log = 10 * log10(E_n + ε)
加个 ε 是为了防止静音帧能量为零导致 log 爆炸。这个 ε 我一般取 1e-10。
短时过零率:频率的“粗糙估计”
短时过零率,就是在一帧内,信号波形穿过零轴的次数。说白了,就是看信号正负交替有多频繁。
公式:
Z_n = 0.5 * Σ_{m=1}^{N-1} |sgn[x_n(m)] - sgn[x_n(m-1)]|
其中 sgn 是符号函数,正数返回1,负数返回-1。
过零率能反映信号的频率特性。高频信号过零快,低频信号过零慢。清音(如 /s/、/f/)的过零率很高,浊音(如 /a/、/i/)的过零率很低。
注意:过零率对噪声非常敏感。背景噪声稍微大一点,过零率就会飙升。我曾经在一个工厂环境里做语音活动检测,结果过零率完全失效——机器轰鸣声让所有帧的过零率都差不多。后来我加了预加重和带通滤波,才把问题解决。
过零率的典型取值范围:
| 语音类型 | 过零率(8kHz采样,帧长20ms) |
|---|---|
| 浊音(元音) | 10-40 |
| 清音(摩擦音) | 60-120 |
| 静音/噪声 | 20-80(取决于环境) |
短时自相关函数:寻找“重复模式”
自相关函数,就是看信号和自己错开一个时间延迟后,有多“像”。如果信号有周期性,那么在周期整数倍的位置上,自相关会出现峰值。
公式:
R_n(k) = Σ_{m=0}^{N-1-k} x_n(m) * x_n(m + k)
k 是延迟量,取值范围从 0 到 N-1。
自相关函数有几个重要性质:
- R(0) 就是短时能量,是最大值
- R(k) 是偶函数,R(k) = R(-k)
- 对于周期信号,R(k) 在 k = T, 2T, 3T... 处出现峰值
你想想看,如果一段语音是浊音,声带振动会产生周期性的脉冲。自相关函数就能把这个周期“抓”出来。
关键点:自相关函数是基音周期估计的基础。但直接计算自相关计算量很大——O(N²) 的复杂度。实际中我们常用快速傅里叶变换(FFT)来加速,利用维纳-辛钦定理:自相关函数等于功率谱的逆傅里叶变换。
基音周期估计:找到声带的“振动频率”
基音周期,就是声带振动的周期。基音频率就是它的倒数。成年男性的基频大概在 80-200 Hz,女性在 150-350 Hz,儿童更高。
基音周期估计的方法有很多,我重点讲最经典的——基于自相关函数的方法。
步骤很简单:
- 对语音帧计算短时自相关函数 R(k)
- 在合理的基音周期范围内(比如 2ms-20ms,对应 50-500 Hz)寻找 R(k) 的最大峰值
- 峰值对应的 k 就是基音周期(以采样点数为单位)
- 基音频率 F0 = 采样率 / k
但这里有个坑——R(0) 永远是最大值。所以我们要避开 k=0 附近的小延迟区域,从最小基音周期对应的延迟开始搜索。
def pitch_estimation(frame, fs, f0_min=50, f0_max=500):
N = len(frame)
# 计算自相关
R = np.correlate(frame, frame, mode='full')
R = R[N-1:] # 取正延迟部分
# 确定搜索范围
k_min = int(fs / f0_max)
k_max = int(fs / f0_min)
# 在范围内找最大峰值
k_peak = np.argmax(R[k_min:k_max]) + k_min
# 基音频率
f0 = fs / k_peak
return f0, k_peak
我曾经踩过的坑:自相关法对共振峰的干扰很敏感。有时候第一共振峰(F1)的周期会被误判为基音周期,导致结果翻倍或减半。这就是所谓的“倍频/半频错误”。
解决办法:可以用倒谱法或平均幅度差函数(AMDF)做辅助判断。我个人的习惯是同时算自相关和AMDF,取两者一致的结果。
四种特征的对比与选择
| 特征 | 计算复杂度 | 主要用途 | 抗噪能力 |
|---|---|---|---|
| 短时能量 | O(N) | 端点检测、清浊音判别 | 中等 |
| 短时过零率 | O(N) | 清浊音判别、语音/音乐区分 | 弱 |
| 短时自相关函数 | O(N²) 或 O(N log N) 用FFT | 基音周期估计、共振峰分析 | 较强 |
| 基音周期 | 取决于估计方法 | 语音合成、说话人识别、情感分析 | 中等 |
实际项目中,我很少只用单一特征。比如做语音唤醒,我会用短时能量做粗检,再用过零率排除噪声,最后用基音周期确认是不是人声。三个特征串起来,误唤醒率能降不少。
一个小技巧:计算短时特征前,记得做预加重。预加重能提升高频成分,让清音的过零率更稳定,也让自相关函数的峰值更清晰。预加重系数我一般取 0.97。