第一章:抗噪基础——噪声来源与性能评估
各位同行,欢迎来到《语音交互系统抗噪性能调优手册》的第一章。
做语音抗噪这么多年,我最大的感触是:很多人一上来就调算法,却连噪声从哪来的都没搞清楚。这就像医生看病,连病因都没查明白就开药方,能治好才怪。
所以这一章,我们先打好基础。我会带你梳理噪声的三大来源,讲清楚几个核心指标,最后给出一套基线测试方法。嗯,这些都是我踩过坑之后总结出来的经验。
1.1 噪声来源分类
语音交互系统里的噪声,说白了就三类:环境噪声、结构噪声、电噪声。我分别说一下。
1.1.1 环境噪声
这是最常见的,也是大家最先想到的。比如马路上汽车喇叭声、咖啡馆里的人声嘈杂、办公室的空调声、家里的电视声……这些都属于环境噪声。
我个人习惯把环境噪声分成两类:
- 稳态噪声:像空调、风扇这种,频谱相对稳定,处理起来其实不难。
- 非稳态噪声:像关门声、突然的说话声、狗叫声,这种突发性的噪声最难搞。我在项目中遇到过最头疼的就是会议室里突然有人咳嗽,算法直接懵了。
1.1.2 结构噪声
这个很多人会忽略。结构噪声是指设备本身或与设备接触的物体产生的振动噪声。
举个例子:你把手机放在桌子上,对方说话时,声音通过桌面传导到麦克风,这就是结构噪声。还有设备内部的马达振动、按键声、触摸屏的反馈声……
1.1.3 电噪声
电噪声来自电路本身。比如电源纹波、数字电路开关噪声、ADC量化噪声等。
你想想看,麦克风采集到的信号,经过放大器、ADC、数字信号处理……每一步都会引入电噪声。尤其是低端硬件,电噪声可能比环境噪声还大。
我记得有一次调试一个车载语音系统,发现信噪比始终上不去。后来用示波器一测,发现电源纹波高达50mV,直接耦合到了音频信号里。换了LDO之后,问题就解决了。
1.2 抗噪性能核心指标
搞清楚了噪声来源,接下来就要知道怎么衡量抗噪效果。我常用的指标有三个:SNR、STOI、PESQ。它们各有侧重。
| 指标 | 全称 | 衡量什么 | 我的使用场景 |
|---|---|---|---|
| SNR | 信噪比 | 信号与噪声的能量比 | 快速评估,调参时看趋势 |
| STOI | 短时客观可懂度 | 语音的可懂度 | 评估人耳能不能听懂 |
| PESQ | 感知语音质量评估 | 语音的听觉质量 | 最终验收,贴近主观感受 |
1.2.1 SNR(信噪比)
SNR是最基础的指标。公式很简单:SNR = 10 * log10(信号能量 / 噪声能量),单位是dB。
但这里有个坑:SNR高不代表语音质量好。为什么?因为SNR只算能量比,不关心噪声的频谱分布。比如一个算法把噪声能量压低了,但同时也把语音的高频细节削没了,SNR可能还是高的,但听起来就是闷闷的。
所以我一般把SNR当作快速筛选指标。调参时先看SNR有没有提升,有的话再往下看STOI和PESQ。
1.2.2 STOI(短时客观可懂度)
STOI衡量的是语音的可懂度——说白了就是你能不能听清对方在说什么。它的取值范围是0到1,越接近1越好。
STOI对语音的时频结构比较敏感。如果算法把语音的某些频段破坏了,STOI会明显下降。我在项目中遇到过一种情况:SNR提升了3dB,但STOI反而下降了0.05。后来发现是降噪算法把清辅音(比如/s/、/sh/)给误杀了,导致可懂度变差。
1.2.3 PESQ(感知语音质量评估)
PESQ是ITU-T P.862标准定义的指标,它模拟人耳的听觉感知,给出一个-0.5到4.5的分数。分数越高,语音质量越好。
PESQ是我做最终验收时必看的指标。因为它最贴近主观听感。但PESQ也有缺点:计算量大,而且对非线性处理(比如噪声抑制)的评估不太准确。
我记得有一次,一个降噪算法在PESQ上得了4.2分,但实际听感却很差,有严重的音乐噪声。后来查资料才知道,PESQ对音乐噪声不敏感。所以不要迷信单一指标,最好三个指标结合起来看。
1.3 调优前的基线测试方法
在开始调优之前,一定要先做基线测试。不然你都不知道自己调得是好是坏。我一般按以下步骤来:
1.3.1 准备测试数据
测试数据要覆盖各种噪声场景。我建议至少准备以下类型:
- 纯净语音:至少20条,男女各半,内容覆盖不同音素
- 噪声数据:至少5种噪声(白噪声、粉红噪声、咖啡馆噪声、马路噪声、办公室噪声)
- 混合数据:将纯净语音和噪声按不同SNR(0dB、5dB、10dB、15dB)混合
我个人习惯用NOISEX-92数据库里的噪声,再加上自己录的一些真实场景噪声。
1.3.2 搭建测试流程
测试流程要标准化,不然结果没法对比。我一般用Python写一个测试脚本:
import numpy as np
import soundfile as sf
from pystoi import stoi
from pesq import pesq
def baseline_test(clean_file, noisy_file, fs=16000):
# 读取音频
clean, _ = sf.read(clean_file)
noisy, _ = sf.read(noisy_file)
# 计算SNR
signal_power = np.mean(clean ** 2)
noise_power = np.mean((noisy - clean) ** 2)
snr = 10 * np.log10(signal_power / noise_power)
# 计算STOI
stoi_score = stoi(clean, noisy, fs, extended=False)
# 计算PESQ
pesq_score = pesq(fs, clean, noisy, 'nb')
return snr, stoi_score, pesq_score
# 示例:测试一条数据
snr, stoi_score, pesq_score = baseline_test('clean.wav', 'noisy.wav')
print(f'SNR: {snr:.2f} dB, STOI: {stoi_score:.3f}, PESQ: {pesq_score:.2f}')
1.3.3 记录基线数据
把所有测试结果记录到一个表格里,作为后续调优的参考。我一般用CSV格式:
| 噪声类型 | 输入SNR (dB) | 输入STOI | 输入PESQ |
|---|---|---|---|
| 白噪声 | 0 | 0.45 | 1.23 |
| 白噪声 | 5 | 0.62 | 1.89 |
| 咖啡馆噪声 | 0 | 0.38 | 1.05 |
| 咖啡馆噪声 | 5 | 0.55 | 1.67 |
1.3.4 分析基线结果
拿到基线数据后,不要急着调优。先分析一下:
- 哪个噪声场景最差?是白噪声还是非稳态噪声?
- 哪个指标最差?是SNR低,还是STOI低?
- 有没有明显的异常值?比如某条数据PESQ特别低,可能是录音有问题。
我曾经遇到过一个项目,基线测试发现所有数据在0dB SNR下STOI都低于0.3。这说明原始信号的可懂度已经非常差了,单纯靠降噪算法很难提升。后来我们改用了麦克风阵列,才解决了问题。所以基线测试也能帮你判断硬件是否需要升级。
好了,第一章的内容就到这里。总结一下:
- 噪声来源要搞清楚,环境、结构、电路,一个都不能漏
- 三个指标各有侧重,SNR看能量,STOI看可懂度,PESQ看质量
- 基线测试是调优的起点,一定要标准化、可复现
下一章,我会讲传统降噪算法,包括谱减法、维纳滤波、自适应滤波这些经典方法。到时候我会结合项目经验,告诉你哪些方法好用,哪些是坑。
咱们下章见。