4. 数字信号处理基础(下):滤波器设计基础(FIR/IIR)。窗函数的作用与选择。
好,咱们接着聊。上一节我们把傅里叶变换和Z变换这些“数学工具”捋了一遍。这一节,咱们要动真格的了——设计滤波器。
说实话,滤波器是语音降噪的“心脏”。你想想看,噪声和语音在频域上混在一起,怎么把它们分开?靠的就是滤波器。我个人习惯把滤波器分成两大类:FIR(有限冲激响应)和IIR(无限冲激响应)。这两兄弟性格完全不同,用好了是神器,用不好就是灾难。
4.1 滤波器设计:FIR 与 IIR 的“性格”差异
先说说它们最本质的区别。
- FIR 滤波器:它的输出只取决于当前和过去的输入。没有反馈回路。所以它天生就是稳定的。你给它一个脉冲,它“响”一阵子就停了,不会没完没了。
- IIR 滤波器:它的输出不仅取决于输入,还取决于过去的输出。有反馈回路。你给它一个脉冲,它可能会“响”很久很久,理论上无限长。
嗯,这里要注意:稳定性是IIR的一个大坑。我在项目中遇到过,用IIR做高通滤波器,参数没调好,结果低频自激了,输出直接爆表。那声音,刺耳得很。
核心对比:
| 特性 | FIR | IIR |
|---|---|---|
| 稳定性 | 绝对稳定 | 可能不稳定(需谨慎) |
| 相位特性 | 可实现严格线性相位 | 非线性相位(有群延迟失真) |
| 计算量 | 较大(阶数高) | 较小(阶数低) |
| 设计难度 | 相对简单(窗函数法) | 需要模拟滤波器原型知识 |
| 应用场景 | 语音、通信、需要保相位的场景 | 音频均衡、低功耗嵌入式设备 |
说白了,如果你对相位不敏感,比如只是做简单的频段均衡,IIR 性价比很高。但如果你做的是语音增强,要保留语音的波形形状,那 FIR 的线性相位特性就太重要了。
4.2 窗函数:为什么需要它?
好,假设我们想设计一个 FIR 低通滤波器。理想情况下,它的频率响应是一个矩形。但理想是丰满的,现实是骨感的。这个理想滤波器在时域上对应的是 sinc 函数,它是无限长的。
我们只能截取一段。怎么截?直接“咔嚓”一刀剪断?
这就是问题所在。直接截断,相当于在时域上加了一个矩形窗。矩形窗在频域上会带来严重的吉布斯现象——通带和阻带边缘会出现剧烈的振荡,也就是“振铃效应”。
我刚开始学的时候,觉得这没什么大不了的。直到有一次,我用矩形窗设计了一个带通滤波器,结果在通带边缘出现了好几个 dB 的起伏,把语音的共振峰都给扭曲了。嗯,从那以后,我再也不敢随便用矩形窗了。
所以,窗函数的作用就是:平滑截断,抑制旁瓣,减少频谱泄露。
4.3 常见窗函数的选择与对比
不同的窗函数,就像不同性格的人。有的“主瓣窄”(频率分辨率高),但“旁瓣高”(泄露严重)。有的“旁瓣低”(泄露少),但“主瓣宽”(分辨率下降)。
这是一个典型的“鱼与熊掌”问题。你需要根据实际需求来权衡。
| 窗函数 | 主瓣宽度 | 旁瓣衰减 | 特点与用途 |
|---|---|---|---|
| 矩形窗 | 最窄 | -13 dB | 频率分辨率最高,但泄露最严重。慎用。 |
| 汉宁窗 | 较宽 | -31 dB | 通用性好,旁瓣衰减不错。我常用它做语音分帧。 |
| 海明窗 | 较宽 | -41 dB | 旁瓣更低,适合需要抑制噪声的场景。 |
| 布莱克曼窗 | 最宽 | -57 dB | 旁瓣衰减极大,但主瓣很宽。适合对泄露极度敏感的场景。 |
| 凯泽窗 | 可调 | 可调 | 通过调节 β 参数,灵活控制主瓣和旁瓣。我最喜欢用这个。 |
我的个人经验:
在语音降噪中,如果你做的是短时傅里叶变换(STFT),我建议用汉宁窗或海明窗。它们能很好地平衡时间分辨率和频率分辨率。如果你做的是滤波器设计,凯泽窗是首选,因为它可以让你精确控制阻带衰减。
4.4 实战:用窗函数法设计一个 FIR 低通滤波器
光说不练假把式。咱们直接上代码。假设我们要设计一个采样率 16kHz,截止频率 4kHz 的低通滤波器,阶数 64。
import numpy as np
import matplotlib.pyplot as plt
from scipy.signal import firwin, freqz
# 参数设置
fs = 16000 # 采样率
fc = 4000 # 截止频率
numtaps = 65 # 滤波器阶数(通常为奇数,保证对称)
# 使用汉宁窗设计
b_hanning = firwin(numtaps, fc, window='hann', fs=fs)
# 使用凯泽窗设计(beta=8,阻带衰减约60dB)
b_kaiser = firwin(numtaps, fc, window=('kaiser', 8), fs=fs)
# 计算频率响应
w, h_hanning = freqz(b_hanning, worN=8000, fs=fs)
w, h_kaiser = freqz(b_kaiser, worN=8000, fs=fs)
# 绘图
plt.figure(figsize=(10, 6))
plt.plot(w, 20 * np.log10(np.abs(h_hanning)), label='汉宁窗')
plt.plot(w, 20 * np.log10(np.abs(h_kaiser)), label='凯泽窗 (β=8)')
plt.axvline(fc, color='red', linestyle='--', label='截止频率')
plt.xlabel('频率 (Hz)')
plt.ylabel('幅度 (dB)')
plt.title('FIR 低通滤波器频率响应对比')
plt.legend()
plt.grid()
plt.show()
你运行一下就会发现,汉宁窗的过渡带比较宽,但阻带衰减大概在 -40dB 左右。凯泽窗的过渡带更陡峭,阻带衰减可以轻松做到 -60dB 以下。
避坑指南:
我曾经犯过一个错误:为了追求极致的阻带衰减,把凯泽窗的 β 值调得很大。结果滤波器阶数不够,导致通带边缘出现了严重的“过冲”。记住,窗函数法设计的滤波器,阶数越高,效果越好,但计算量也越大。 你需要找到一个平衡点。
4.5 本章知识体系:一张图看懂
为了帮你理清思路,我画了一张图。它把这一节的核心逻辑串起来了。
你看,从 FIR 和 IIR 的选择,到具体的窗函数设计,再到最后的权衡取舍,这是一条清晰的路径。你想想看,掌握了这个体系,以后遇到任何滤波需求,你都能快速找到切入点。
好了,这一节的内容就到这儿。窗函数的选择,说白了就是一场“主瓣”和“旁瓣”的博弈。多动手试试,你会有自己的手感。
公众号:蓝海资料掘金营,微信deep3321