ARTICLE DETAIL

资讯详情

深耕商务建站与企业官网运营的一线实战洞察。

基于Matlab的语音降噪低通滤波器设计:FIR与IIR对比实践

基于Matlab的语音降噪低通滤波器设计:FIR与IIR对比实践 做语音相关的小项目时最让人头疼的往往不是算法本身而是采集回来的语音里混进的各种噪声。风扇的低频轰鸣、空调压缩机的连续嘶叫、电路板上的电磁干扰这些噪声叠加在正常语音上轻则影响听感重则直接毁掉后续的语音识别和端点检测。为了压住这些噪声我最初的选择就是在Matlab里搭一个基于低通滤波器的语音降噪模型把FIR和IIR两类滤波器都设计一遍对比它们在降噪效果、资源开销和实时性上的差异。整套模型做下来代码量不大但里头涉及的概念和取舍绝对值得拿出来好好梳理一遍。这篇博文适合刚接触数字信号处理、想做语音预处理或者想在Matlab里快速验证滤波器设计思路的读者我会把设计思路、参数计算、仿真步骤和踩过的坑一次性讲清楚。1. 语音降噪场景与低通滤波器的用武之地1.1 语音信号的频谱特征语音信号从频域看并不是均匀分布的。元音部分能量集中在低频段尤其是几百赫兹到两三千赫兹这一段辅音和齿音才会延伸到更高频段。拿电话语音系统来说300Hz到3.4kHz这个频带已经能保证足够的可懂度宽带语音一般也就到7kHz左右。实际做语音采集时如果采样率是16kHz那语音的有效频段通常集中在3.5kHz以下更高频段里除了极少量的辅音清音成分大部分是环境噪声、采集设备底噪和量化噪声。这意味着如果噪声恰好落在语音有效频段之外低通滤波器就能在几乎不损伤语音内容的前提下把这些噪声压下去。我在实际测试里最常见的场景就是采集设备附近有高频源比如电脑电源的高频啸叫、LED驱动电路的开关噪声或是风机叶片的高频气流声这些噪声经常集中在4kHz以上和正常的语音频段有明显间隔恰恰是低通滤波最擅长的区域。但有一点必须说清楚低通滤波器并不是万能的降噪工具。如果噪声是高斯白噪声这类宽带信号和语音频谱完全重叠单纯用低通滤波会导致语音高频成分大量丢失听起来声音发闷、可懂度下降噪声却只被削弱了一部分。所以在动手写滤波器之前先对信号做一次频谱分析、确认噪声频段和语音频段是否可分这一步比任何滤波算法都重要。1.2 为什么先搭仿真模型做语音降噪不能上来就改硬件或者写嵌入式代码成本太高、变量太多。在Matlab里搭仿真模型核心目的是把算法验证和参数调试独立出来用纯数学的方式确认思路可行再落到工程实现。这个流程我用了很多次节省的时间相当可观。Matlab在这个场景下的优势很明显信号处理工具箱齐全滤波器设计函数直接封装好了底层算法画频谱、播放对比音频都极其方便。更重要的是Matlab里的滤波器系数和结构可以直接导出成定点数、C数组或者HDL描述后续迁移到FPGA或者DSP平台时能省掉大量手工转换工作。我见过不少项目先用Matlab验证再迁移到硬件而FIR和IIR滤波器因为结构和系数转换反推都很成熟实际迁移效率非常高。1.3 模型目标与验收标准设计这个语音降噪模型时我给自己定了几个验收指标噪声频段外的分量至少衰减20dB以上听感上噪声明显消失。语音有效频段内的信号尽量少失真波形和频谱与原始语音高度吻合。滤波器延迟可控尤其后续要做实时处理时相位失真和群延迟不能太大。计算量要可接受FIR阶数过万或者IIR阶数上千的方案在实际硬件上很难跑得动。这些指标直接决定了我后续在FIR和IIR之间的选型也决定了截止频率、阶数等参数怎么定。先想清楚验收标准再动手设计比边写代码边试要高效得多。2. FIR与IIR滤波器原理差异与选型逻辑2.1 FIR滤波器的核心特征FIR滤波器的全称是有限脉冲响应滤波器结构上是一段抽头延迟线当前输出只取决于当前输入和历史输入的卷积没有反馈回路。这个特性决定了FIR滤波器天生稳定不管算出来的系数是什么样都不会出现振荡或者发散的问题。更关键的是只要系数满足偶对称或奇对称条件FIR滤波器就能做到严格的线性相位。线性相位在语音处理里意义重大。语音信号的相位信息直接影响音质和可懂度如果相位被非线性地扭曲辅音和声调容易听不清。FIR滤波器对所有频率分量的延迟一致波形形态不会因为滤波而严重畸变。这是它相比IIR在语音场景里最大的优势。代价就是阶数高、计算量大。要压出很陡的过渡带FIR需要数百甚至上千阶。比如采样率16kHz、要求在3.4kHz到3.8kHz之间衰减60dB窗函数法算下来可能需要三四百阶每一帧语音都要做相应数量的乘加运算实时性压力不容小觑。2.2 IIR滤波器的核心特征IIR滤波器是全称无限脉冲响应滤波器结构上多了反馈路径当前输出既依赖历史输入也依赖历史输出。因为输出会回馈到输入端同样的性能指标下IIR滤波器可以用比FIR少得多的阶数实现计算效率高出一到两个数量级。经典的巴特沃斯滤波器在通带内非常平坦切比雪夫和椭圆滤波器能提供更陡的过渡带。但IIR不是没有代价。反馈结构意味着滤波器有可能不稳定设计不当或者系数量化后极点移出单位圆滤波器就全盘失效。更重要的是IIR滤波器的相位响应是非线性的。高频和低频分量的延迟不一致语音经过IIR低通滤波后波形包络会发生变形听感上会有点闷闷的回音感。很多对音质要求高的场合比如音乐制作、助听器算法会尽量避免用高Q值的IIR滤波器处理语音。2.3 语音降噪里怎么选型这个问题的答案不是绝对的得看具体约束。我把两种方案的关键差异整理成了表格对比维度FIRIIR线性相位可以严格做到无法保证存在相位失真稳定性天然稳定无反馈设计不当可能不稳定相同指标所需阶数高几百到上千阶低几阶到几十阶计算量大小过渡带陡峭程度需要高阶梯形逼近低阶即可做得很陡对语音听感的影响小可能造成听觉失真实时处理延迟有固定延迟N/2点相位失真明显延迟不均我个人的选型经验是如果做离线处理或者系统对音质要求高优先选FIR线性相位这个优势在语音上太宝贵了。如果做实时嵌入式系统DSP算力有限同时又对延迟敏感IIR会是更务实的选择尤其是在截止频率不高、通带比较平滑的场景下IIR的听感损失并没有想象中那么大。另外还有一个折中思路用FIR做高频段的噪声压制再用一个低阶IIR做低频段的抖动去除两者级联可以平衡听感和算力。我的模型里就用到了这种思路后面实操部分会详细拆解。2.4 理想低通滤波器为什么不存在这个话题看起来偏理论但如果理解不了后面很多参数选择容易犯迷糊。理想低通滤波器的频响是矩形窗——通带内增益为1、阻带内增益为0、过渡带宽度为零。但它的冲激响应是一个无限长的sinc函数非因果、不可实现。任何物理可实现的数字滤波器都必须在通带纹波、阻带衰减和过渡带宽度之间做取舍。这就是为什么我们在Matlab里设计滤波器时要反复确认过渡带宽、纹波和阻带衰减这几个参数。它们之间互相制约把过渡带压窄阶数就上去了把阻带衰减做深通带纹波就可能变大。理解了这层矛盾再看fir1、butter这些函数的参数就会明白每个输入都在影响哪块性能。3. 仿真环境搭建与测试信号准备3.1 版本与工具箱Matlab做滤波器设计核心依赖就是Signal Processing Toolbox也就是信号处理工具箱以及基础的DSP System Toolbox非必需。我用的是Matlab R2021b和R2023a这两个版本里fir1、firpm、butter、cheby1这些函数接口保持一致老项目里的脚本基本可以直接跑。如果你的Matlab是精简安装运行fir1时可能会提示函数未定义通常是因为信号处理工具箱没有安装。我建议通过ver命令检查一下已安装工具箱实在没有就去Add-On Explorer里补装几百兆的事必不可少。3.2 构造测试语音与加噪信号做滤波器仿真我习惯先准备好一段干净的纯净语音再加不同频段的噪声这样后续可以量化对比降噪前后的信噪比和失真。真实录音环境里很难拿到纯净版本仿真里这个条件就方便多了。我常用的脚本长这样% 读取纯语音信号 [x, fs] audioread(clean_speech.wav); x x(:, 1); % 取单声道 t (0:length(x)-1) / fs; % 时间轴 % 叠加高频单频噪声模拟开关电源啸叫 noise_1 0.3 * sin(2*pi*5000*t) ; % 叠加高斯白噪声经过高通后的结果模拟带外频谱泄漏 noise_raw randn(length(x), 1); noise_hp highpass(noise_raw, 3000, fs); noise_2 0.1 * noise_hp; % 混合噪声 noisy x noise_1 noise_2;这里我故意用了一个单频5kHz噪声和一个3kHz以上的高通噪声目的是让噪声主要落在语音有效频段之外。如果噪声全是宽带白噪声低通滤波器的效果会很难看这本身也是要避免的测试误区。3.3 时域和频域分析滤波前后必须做频域对比不然光听声音说不清楚效果。我一般会画三张图放在一起原始语音频谱、噪声语音频谱、滤波后语音频谱。画频谱的代码很简单NFFT 2048; f (0:NFFT/2-1) * fs / NFFT; spec_x abs(fft(x, NFFT)); spec_noisy abs(fft(noisy, NFFT)); figure; plot(f, 20*log10(spec_x(1:NFFT/2)eps)); hold on; plot(f, 20*log10(spec_noisy(1:NFFT/2)eps)); legend(Clean,Noisy); xlabel(Frequency (Hz)); ylabel(Magnitude (dB));从图上能直观看到噪声频段和语音频段的分界之后设截止频率时就有据可依。我习惯先看频谱再定参数而不是凭空猜测截止频率。3.4 评价指标SNR与语音失真降噪效果光靠眼睛看频谱不够还要量化计算。简单版本的信噪比用这个公式SNR_in 10 * log10(sum(x.^2) / sum((noisy - x).^2) eps); SNR_out 10 * log10(sum(x.^2) / sum((y - x).^2) eps);但这里有个坑滤波后的信号会损失一部分高频语音成分y - x里除了残余噪声还包含了语音本身的损失所以SNR_out可能不升反降。如果出现这种情况不代表降噪失败而是语音失真太严重了。实操中我会同时算两个指标残余噪声能量和语音失真能量分开评估。更完整的做法是计算分段信噪比把语音分成20ms左右的小帧分别算每一帧的信噪比再取平均。分段信噪比更贴近人耳的主观感受尤其对语音这种非平稳信号来说单帧指标远比全局均值有说服力。4. FIR低通滤波器设计实操4.1 确定阶数与截止频率FIR滤波器设计的起点是确定阶数和截止频率。采样率16kHz语音有效频段在3.4kHz以下噪声从3.6kHz开始明显增强那么截止频率选3.4kHz、过渡带选200Hz到400Hz是合理的。归一化频率是关键一步很多人第一次用fir1就栽在这里。Matlab里的归一化频率是相对奈奎斯特频率的也就是fs 16000; fc 3400; Wn fc / (fs/2); % 归一化截止频率这个Wn才是fir1想要的输入直接填3400会让滤波器的实际截止频率跑到8kHz附近信号直接全通噪声一点没压住。阶数的估算有一个实用公式基于凯泽窗的经验设计A 60; % 期望阻带衰减(dB) delta_f 400; % 过渡带带宽(Hz) N ceil((A - 7.95) / (2.285 * 2 * pi * delta_f / fs));算出来需要约70阶。实际上我用凯泽窗可以自动获得更精准的阶数方法是用kaiserorddev [0.001, 0.001]; % 通带和阻带的纹波幅度 fcuts [3400, 3600]; % 通带边缘和阻带边缘 mag [1, 0]; % 期望幅值 [n_kaiser, Wn_kaiser, beta, ftype] kaiserord(fcuts, mag, dev, fs); b fir1(n_kaiser, Wn_kaiser, ftype, kaiser(n_kaiser1, beta), scale);这里kaiserord算出来阶数比经验公式更可靠因为它同时考虑了通带和阻带纹波。如果对设计工具不熟悉直接拿kaiserord的输出丢给fir1是最稳的做法。4.2 窗函数法与等纹波设计的对比我实际做了两种FIR设计一是凯泽窗法二是等纹波法firpm并对比了效果。凯泽窗法的优势是设计过程简单、参数物理意义明确beta控制主瓣宽度和旁瓣衰减的折中。缺点是性能并非最优同样的阶数下过渡带不一定能做到最窄。等纹波法则把通带和阻带内的误差均匀分配相同阶数下过渡带通常更窄阻带衰减也更均匀。% 等纹波设计 dev [0.005, 0.001]; % 通带纹波稍大阻带衰减深 firpm_b firpm(110, [0, 3200/8000* 2, 3600/8000 * 2, 1] * fs/2, [1 1 0 0], [1, 10]);实际滤波后的对比结果显示等纹波设计在同样的阶数下阻带衰减更均衡不像窗函数法在某些频段会冒出更大的旁瓣。不过等纹波设计需要用firpm指定频带边缘和期望幅值参数写起来绕一些我建议新手先用窗函数法跑通流程再换等纹波法精调。4.3 滤波执行与结果评估设计完滤波器系数之后滤波本身可以一行代码搞定y_fir filter(b, 1, noisy);用filter做因果滤波会有固定延迟延迟量大约是(length(b)-1)/2个采样点。如果是离线分析可以把延迟补回来做对齐delay (length(b)-1)/2; y_fir_corrected [y_fir(delay1:end); zeros(delay, 1)];更好的做法是用零相位滤波函数filtfilt。它会让信号先正向经过滤波器再反向经过一次两次滤波的总效果是相位响应为零波形不会出现群延迟导致的偏移y_fir_zp filtfilt(b, 1, noisy);但要注意filtfilt会放大边界效应信号两端会出现小幅震荡所以它适合离线处理不适合实时系统。实时场景下单帧滤波必须用filter延迟和相位问题需要在系统层解决。5. IIR低通滤波器设计实操5.1 巴特沃斯滤波器的设计与阶数计算IIR低通滤波器的设计我首先试的是巴特沃斯滤波器。它的最大优点是通带内响应最平坦不会因为纹波造成听感上的粗糙感。设计巴特沃斯滤波器的标准流程是先定阶数再算系数fs 16000; fp 3400; % 通带截止频率 fst 3600; % 阻带起始频率 Rp 1; % 通带最大衰减(dB) Rs 40; % 阻带最小衰减(dB) % 归一化频率 Wp fp / (fs/2); Ws fst / (fs/2); % 求最小阶数 [n_butter, Wn_butter] buttord(Wp, Ws, Rp, Rs); % 设计滤波器 [b_butter, a_butter] butter(n_butter, Wn_butter);这里buttord会基于给定的通带衰减、阻带衰减和过渡带计算出满足要求的最小阶数。正常这种指标算下来也就十几阶对比FIR动辄上百阶计算量差距一目了然。但滤波器阶数低不代表没有坑。IIR滤波器阶数一旦上去数值稳定性就会变差尤其是高阶直接型结构系数对精度非常敏感。实际仿真时我通常会把滤波器结构转换成二阶节级联也就是用tf2sos函数转换成SOS矩阵再用sosfilt滤波[sos, g] tf2sos(b_butter, a_butter); y_iir sosfilt(sos, noisy) * g;这种结构在数值稳定性上好很多后期如果要把滤波器系数移植到嵌入式平台二阶节级联也是最容易定点化的表达形式。5.2 切比雪夫与椭圆滤波器的横向对比除了巴特沃斯我还对比了切比雪夫I型通带等纹波和椭圆滤波器通带阻带都有纹波。切比雪夫I型的特征是允许通带有纹波换来更陡的过渡带。在相同阶数下它比巴特沃斯有更陡峭的滚降特性代价是通带内频率响应不平坦语音可能会在某些频段出现细微的染色。如果通带纹波控制在0.5dB以内听感差异不算明显。椭圆滤波器的过渡带在三者里最陡相同性能下阶数最低但通带和阻带都有纹波设计参数也更繁琐。用代码实现时[b_cheb, a_cheb] cheby1(n, Rp, Wn); [b_ellip, a_ellip] ellip(n, Rp, Rs, Wn);对比下来我在最终模型里选了巴特沃斯因为语音降噪这个场景并不追求极致的过渡带陡峭度通带平坦、相位相对平滑反而更重要。椭圆滤波器虽然阶数低但相位失真和瞬态响应更剧烈处理语音容易出金属声。IIR滤波器的选型结论其实很直接算力紧张就上椭圆音质优先就用巴特沃斯切比雪夫是中间态但很少成为最优解。5.3 IIR滤波的相位问题IIR滤波器最大的软肋就是相位非线性。这里我不是说不能用filtfilt把离线信号的相位校正回来而是想强调一个工程现实很多实时嵌入式系统里没有办法做离线双程滤波。我做了个测试用同一个巴特沃斯低通滤波器分别用filter和filtfilt处理同一段带噪语音filter版本听感明显发闷波形上高频细节像是向后拖了一下filtfilt版本虽然听感通透很多但由于边界效应信号首尾各损失了约一个滤波器脉冲响应长度长语音没有影响短语音比如单字识别就很尴尬。所以用IIR做实时语音降噪时我建议引入频域补偿或者设计低相位的级联结构尽量不要拿高阶IIR直接做实时因果滤波。这算是我踩过比较多坑的一个点。5.4 与FIR的综合对比测试我把三种方案FIR凯泽窗、FIR等纹波、IIR巴特沃斯放到同一份加噪语音上跑了一遍记录滤波前后的分段信噪比方案滤波前SNR(dB)滤波后SNR(dB)语音失真情况过渡带宽度FIR凯泽窗88阶6.213.4几乎无损约420HzFIR等纹波110阶6.214.1几乎无损约330HzIIR巴特沃斯12阶6.212.9高频略受损约620Hz椭圆8阶6.213.8明显金属感约280Hz从表格能看出来FIR等纹波的信噪比提升最大语音失真也最小但计算量最大。IIR巴特沃斯的阶数最低、计算最省但信噪比提升有限高频略损。椭圆虽然SNR数值不差听感却打了折扣。这个结果也验证了最开始说的选型逻辑没有万能方案只有跟应用场景匹配的方案。6. 常见问题与排查技巧实录6.1 归一化频率写错导致滤波器失效这是出现频率最高的问题。直接用Hz数值传给fir1滤波器实际截止频率被放大到了奈奎斯特频率以外信号根本不过滤。排查方法也很简单设计完滤波器后立刻用freqz画幅频响应图看一眼在哪个频率开始衰减freqz(b, 1, 1024, fs);如果幅频曲线在几千赫兹附近还是0dB说明归一化频率算错了。我习惯把采样率、截止频率、归一化频率这三者用注释写清楚避免两个月后回来看代码一头雾水。6.2 滤波后信号延迟导致对比失真用filter做因果滤波后输出信号整体偏移了(N/2)个采样点。如果在计算SNR时拿滤波输出和原始纯净信号直接相减会得到一段叠加了巨大人为噪声的误差信号SNR数值被严重拉低。这不是滤波器效果差而是对齐问题。解决办法是先把延迟补偿掉再计算或者直接用filtfilt。我见过不少初学者把这个问题误判成滤波器设计失败白白调了一整天参数。6.3 截止频率选择不当造成语音发闷截止频率设太低比如1kHz过滤完的语音像隔着被子说话辅音全丢了设太高比如6kHz高频噪声又压不干净。比较稳妥的做法是先用pwelch或者periodogram做功率谱估计找出语音能量衰减到接近底噪的频率点把截止频率设定在这个点附近再根据听感和SNR微调。6.4 filtfilt边界效应造成的首尾失真零相位滤波很香但有边界效应。信号首尾一小段会出现轻微的反弹震荡在纯语音段可能不明显但前后有静音段时静音段会被抬高成噗噗声。解决办法是在滤波前对信号做适当延拓比如给首尾各补一段50ms的渐入渐出信号滤波后再裁掉。这个方法简单有效我一直在用。6.5 工具箱缺失或者函数版本不兼容旧代码在别人机器上跑出Undefined function fir1大概率是信号处理工具箱没装。用which fir1能快速确认函数所在路径是否属于Matlab自带工具箱。另外注意高版本Matlab已经移除wavread和wavwrite统一用audioread和audiowrite老脚本里如果是这两个旧函数直接替换即可。6.6 IIR滤波器数值不稳定的排查用butter等函数设计出的IIR滤波器通常是稳定的但在把系数转成定点或者用低精度格式存储后可能会出现极点跑出单位圆的情况。仿真阶段如果filter输出出现NaN或者Inf多半是滤波器系数的极点问题。我习惯在滤波前检查极点p roots(a_butter); if any(abs(p) 1) warning(存在单位圆外极点滤波器不稳定); end如果出现不稳定优先检查阶数是否过高或者改用二阶节级联结构来改善数值特性。6.7 工程落地时的延迟预算实时语音系统的端到端延迟预算通常只有几十毫秒。FIR滤波器几百阶意味着延迟在几十个采样点16k采样率下延迟大约几毫秒到十几毫秒可接受。但IIR的相位失真不可控且没有统一延迟帧处理再做分块可能引入更多不确定性。所以实时系统里我往往会倾向于用FIR配合窗函数截断或者用IIR在低阶、低截止频率的特定场景下使用。做滤波之前先想清楚你的系统延迟预算再决定用哪种结构和处理方式。7. 模型验证思路与个人经验总结做完一轮滤波对比我心里其实有个非常清晰的结论低通滤波器不是所有语音降噪问题的答案但它作为预处理环节的重要性被很多人低估了。在噪声频段和语音频段可分离的场景里一个设计得当的低通滤波器能省掉后面自适应算法的不少压力。比如在语音识别系统里先做一个30阶的FIR低通滤除高频环境噪声后面VAD的误触发率能明显下降。真正设计时我的习惯是先把测试信号做一遍频谱分析看清楚噪声集中在哪语音有效频段到哪再定截止频率和滤波器类型。然后同时用fir1、firpm、butter三种方案滤波用分段信噪比加听感双重评估最后把最优方案的系数导出成C数组或定点系数交给后续模块。整个过程写成一个Matlab脚本跑一遍大概两三分钟但省下的调试时间是以天计算的。这个模型后续还可以扩展的地方不少。比如把低通滤波和自适应滤波LMS、NLMS串联起来利用参考麦克风做噪声对消或者把单频低通改成多频带分段处理低频和高频各用一个滤波器再进一步还能用深度学习做端到端降噪但低通滤波器模型依然是理解问题时最简单有效的参照物。仿真模型的价值不只是给出一个结果更在于它逼着我把参数、约束和权衡考虑了一遍这种理解是直接调用现成降噪库得不到的。最后再分享一个小技巧。做滤波器对比时不要只用一组噪声最好准备白噪声、单频干扰、窄带噪声、实际环境录音噪声四种测试样本同一组参数分别跑。你会发现滤波器在一个样本上的表现和另一个样本上的表现可能差异很大这能帮你更早发现设计隐患。我就是在一次用实际环境录音测试时发现单纯低通滤波对宽带噪声几乎无效这才意识到必须给模型加上前置频谱分析环节也才有了前面那一整套确认噪声频段的流程。
返回列表
PREV
查看更多资讯
NEXT
返回资讯列表