MATLAB 设计 FIR 低通滤波器:窗函数法从指标到频响验证的完整流程
MATLAB 设计 FIR 低通滤波器:窗函数法从指标到频响验证的完整流程
一、为什么选 FIR,而不是 IIR
做信号处理最常遇到的需求,就是从一堆混在一起的频率成分里只留下想要的那一段。比如 STM32 采集到的 ADC 数据里混了 50 Hz 工频干扰,或者一段语音里有高频噪声,第一反应往往是加一个低通滤波器。
低通滤波器分两大类:IIR 和 FIR。IIR 用很少的阶数就能做到很陡的过渡带,但代价是相位不是线性的——不同频率的信号通过滤波器后延迟不一样,波形会变形。FIR 的代价是阶数高、计算量大,但它可以做到严格的线性相位,所有频率成分延迟相同,只整体平移不改变波形形状。对于要保留波形、做后续时域分析的场合(比如心电信号、振动信号、ADC 采样数据),FIR 是更稳妥的选择。
本文用 MATLAB 的窗函数法,从指标出发,一步步设计一个 FIR 低通滤波器,并用频响曲线验证它到底合不合格。
二、先把指标写清楚
设计滤波器之前,必须先把四个指标定下来,否则后面无从下手:
- 通带截止频率 fp:希望完整保留的最高频率;
- 阻带截止频率 fs:希望滤掉的最低频率;
- 通带最大纹波 Rp:通带内允许的幅度波动,单位 dB;
- 阻带最小衰减 As:阻带内至少要压下去多少分贝。
举个具体例子。假设采样率 fsamp = 8000 Hz,要保留 1000 Hz 以下的有用信号,1500 Hz 以上的干扰至少压掉 50 dB,通带纹波不超过 1 dB。于是 fp = 1000 Hz,fs = 1500 Hz,Rp = 1 dB,As = 50 dB。过渡带宽度就是 fs - fp = 500 Hz。
三、窗函数怎么选:衰减与过渡带的取舍
窗函数法的核心思想很简单:先写出理想低通滤波器的单位脉冲响应(一个无限长的 sinc 函数),再用一个有限长的窗函数把它截断,得到有限长的 FIR 系数。窗选得不同,结果的特性也不同:矩形窗阻带衰减约 21 dB、主瓣最窄;汉宁窗约 44 dB;海明窗约 53 dB;布莱克曼窗约 74 dB、主瓣最宽。规律很清楚:阻带衰减越好,主瓣越宽,过渡带就越宽,需要的阶数也越高。按上面的指标,As = 50 dB,矩形窗和汉宁窗都不够,海明窗 53 dB 刚好够用,是最经济的选择;如果要求 60 dB 以上,就得用布莱克曼窗,并接受更宽的过渡带或更高的阶数。
阶数也有经验估算公式。海明窗的过渡带宽度近似为 Δf ≈ 3.3 × fsamp / N,其中 N 是滤波器阶数。代入过渡带 500 Hz、采样率 8000 Hz,得到 N ≈ 3.3 × 8000 / 500 ≈ 53。取偶数阶 54,方便后面做线性相位处理。
四、MATLAB 代码:四行完成设计
MATLAB 的 fir1 函数把窗函数法封装好了,设计只需要几行代码:
fsamp = 8000; % 采样率 Hz
fp = 1000; fs = 1500; % 通带/阻带截止频率 Hz
N = 54; % 滤波器阶数
Wn = (fp + fs) / 2 / (fsamp / 2); % 归一化截止频率,取过渡带中点
b = fir1(N, Wn, ‘low’, hamming(N + 1)); % 海明窗 FIR 低通
注意两个细节。第一,fir1 的截止频率是归一化的:1 对应奈奎斯特频率 fsamp/2,也就是 4000 Hz,所以要除以 fsamp/2。初学者最常见的错误就是直接把 1000 当成 Wn 传进去,得到的滤波器完全不对。第二,截止频率取过渡带中点 (fp + fs)/2,这是一个工程上常用的折中:通带和阻带各让一点,实际频响最接近指标要求。
五、验证:频响曲线说了算
设计完不能只看系数,必须画频响曲线验证。用 freqz 可以直接得到幅频响应:
[H, f] = freqz(b, 1, 2048, fsamp);
plot(f, 20*log10(abs(H))); grid on;
xlabel(‘频率 (Hz)’); ylabel(‘幅度 (dB)’);
合格的曲线应该满足三点:1000 Hz 以内幅度基本平坦(波动小于 1 dB),1500 Hz 以外幅度压到 -50 dB 以下,过渡带从 1000 Hz 单调下降到 1500 Hz。如果通带纹波超标,说明窗函数选得不对或者阶数不够;如果阻带衰减不够,优先换衰减更好的窗,而不是盲目加阶数——窗不对,加阶数只会让过渡带变窄,阻带衰减几乎不变,这是窗函数法的固有特性。
除了幅频响应,还要看相位。FIR 线性相位的特征是群延迟为常数,等于 N/2 个采样点,本例就是 27 个采样点,对应 27/8000 = 3.375 ms 的固定延迟。可以用 grpdelay 函数验证:
[gd, f] = grpdelay(b, 1, 2048, fsamp);
plot(f, gd); % 通带内应为一条水平线
如果群延迟在通带内不是常数,说明系数或对称性出了问题,需要回头检查。
六、实测:给一段带噪信号滤波
理论验证通过后,用一段实际信号走一遍完整流程。构造一个 500 Hz 有用信号叠加 2000 Hz 干扰的测试信号:
t = 0:1/fsamp:1-1/fsamp;
x = sin(2pi500t) + 0.8sin(2pi2000*t); % 500 Hz 有用 + 2000 Hz 干扰
y = filter(b, 1, x); % 滤波
滤波后的 y 应该只剩下 500 Hz 的正弦,2000 Hz 的成分被压到原来的千分之三以下(-50 dB)。用 FFT 对比滤波前后的频谱,2000 Hz 处的谱峰应该明显消失,而 500 Hz 处的幅度基本不变。
这里有一个工程上容易踩的坑:filter 函数做的是因果滤波,输出前面 N/2 个点是滤波器的建立过程,波形会有一个约 27 点的延迟。如果你的应用对延迟敏感(比如实时控制),要把这个延迟算进时序里;如果只是离线分析,可以用 filtfilt 做零相位滤波,它前后各滤一次,延迟相互抵消,但代价是等效阶数翻倍、阻带衰减也会变化,设计时要重新验证。
七、常见问题排查
- 滤波后信号幅度整体变小:检查是不是把 Wn 归一化错了,或者窗函数长度 N+1 与阶数 N 不匹配;
- 阻带衰减怎么加阶数都不够:换窗,别加阶数。矩形窗的理论极限就是 21 dB;
- 通带边缘出现明显下垂:截止频率取的是过渡带中点,通带边缘本来就有约 -6 dB 的衰减(海明窗),指标要求严格时应把 Wn 往阻带方向挪一点;
- 滤波后波形开头一段失真:这是滤波器建立过程,丢掉前 N 个点再分析,或者用 filtfilt;
- 在 STM32 上跑不动:54 阶 FIR 每次输出要做 55 次乘加,8000 Hz 采样率下就是每秒 44 万次乘加,Cortex-M4 的 DSP 指令(SMLAD、arm_fir_f32)可以轻松应付,记得用 CMSIS-DSP 库而不是手写循环。
八、小结
窗函数法设计 FIR 低通滤波器的流程可以浓缩成四步:定指标(fp、fs、Rp、As)→ 按衰减要求选窗、按过渡带估阶数 → fir1 生成系数 → freqz 验证幅频与群延迟,通不过就换窗或加阶数。记住两个关键点:阻带衰减由窗函数决定,阶数只决定过渡带宽窄;归一化频率 1 对应的是 fsamp/2 而不是 fsamp。这套流程在 MATLAB 里几分钟就能跑通,同一组系数稍加修改就能下放到 STM32 的 CMSIS-DSP 里跑,是信号处理从仿真到落地的最短路径。
- 点赞
- 收藏
- 关注作者
评论(0)