3、数字滤波器设计:FIR与IIR滤波器原理、在Python中的实现、滤波器设计指标
各位同学,咱们今天聊聊数字滤波器。说实话,滤波器这东西,在航空电台里太常见了。你想想看,飞机在天上飞,各种电磁波乱窜,没有滤波器,那信号根本没法听。我个人习惯把滤波器比作一个「筛子」——把有用的信号留下来,没用的统统筛掉。
3.1 FIR与IIR滤波器:两种不同的「筛子」
数字滤波器分两大类:FIR(有限冲激响应)和IIR(无限冲激响应)。名字听着挺唬人,其实核心区别就一句话:有没有反馈。
FIR滤波器,说白了就是没有反馈。输入信号进来,经过一串延时和加权,直接输出。它的冲激响应是有限的——给一个脉冲,它响一阵就停了,不会没完没了。我在项目中遇到过,FIR滤波器最大的好处是线性相位。什么意思?就是不同频率的信号经过它,延迟时间是一样的。这在航空语音通信里特别重要,不然你说一句话,低频和高频部分不同步到达,听起来就像「大舌头」。
IIR滤波器呢,有反馈。输出信号会绕回来跟输入一起算。所以它的冲激响应是无限的——给一个脉冲,理论上它会一直响下去。IIR的优点是效率高,用很少的阶数就能达到很陡的截止特性。但代价是什么?相位是非线性的,而且搞不好会不稳定。我记得刚入行时,有个同事设计IIR滤波器,系数算错了,结果滤波器直接自激振荡,输出全是噪声。嗯,那场面挺尴尬的。
核心对比:
- FIR:无反馈,线性相位,稳定,但阶数高、计算量大
- IIR:有反馈,非线性相位,可能不稳定,但阶数低、效率高
3.2 滤波器设计指标:你得知道要「筛」成什么样
设计滤波器之前,先得搞清楚指标。我见过不少新手,上来就写代码,结果做出来的东西根本不能用。为什么?因为指标没想清楚。
常用的指标有这几个:
| 指标 | 说明 | 航空电台中的典型值 |
|---|---|---|
| 通带截止频率 | 信号能通过的频率上限(低通)或下限(高通) | 300 Hz ~ 3400 Hz(语音) |
| 阻带截止频率 | 信号被抑制的频率起点 | 通常比通带高200~500 Hz |
| 通带波纹 | 通带内幅度的波动范围 | ≤ 0.5 dB |
| 阻带衰减 | 阻带内信号被压低的程度 | ≥ 40 dB |
| 过渡带宽 | 通带到阻带的过渡区域宽度 | 越窄越好,但阶数会变高 |
举个例子。我在做航空电台的语音通道时,要求通带300~3400 Hz,阻带从4000 Hz开始衰减,阻带衰减至少40 dB。你想想看,如果阻带衰减不够,高频噪声就会混进来,飞行员听到的就是「嘶嘶」声,严重影响通信质量。
避坑指南:我曾经设计过一个滤波器,通带波纹设得太严(0.1 dB),结果阶数高得离谱,DSP跑不动。后来放宽到0.5 dB,效果完全够用。所以指标不是越严越好,够用就行。
3.3 在Python中实现:从理论到代码
好了,理论讲完了,咱们动手。Python里做滤波器设计,最常用的库是scipy.signal。我个人习惯用firwin设计FIR,用iirdesign设计IIR。
3.3.1 FIR滤波器设计
先来个低通FIR的例子。假设采样率8000 Hz,通带截止频率1500 Hz,阻带截止频率2000 Hz,阻带衰减40 dB。
import numpy as np
import matplotlib.pyplot as plt
from scipy.signal import firwin, freqz
# 参数设置
fs = 8000 # 采样率
f_pass = 1500 # 通带截止频率
f_stop = 2000 # 阻带截止频率
atten = 40 # 阻带衰减 (dB)
# 估算阶数(经验公式)
# 对于汉明窗,阶数 ≈ 3.3 * fs / (f_stop - f_pass)
N = int(3.3 * fs / (f_stop - f_pass))
if N % 2 == 0:
N += 1 # 保证奇数阶,得到线性相位
# 设计滤波器
taps = firwin(N, f_pass, fs=fs, window='hamming')
# 查看频率响应
w, h = freqz(taps, worN=8000)
freq = w * fs / (2 * np.pi)
plt.plot(freq, 20 * np.log10(abs(h)))
plt.title('FIR 低通滤波器频率响应')
plt.xlabel('频率 (Hz)')
plt.ylabel('幅度 (dB)')
plt.grid()
plt.show()
这段代码跑完之后,你会看到一条曲线。通带内基本平坦,阻带内掉到-40 dB以下。嗯,这就是我们要的效果。
注意:FIR滤波器的阶数N必须是奇数,才能保证线性相位。如果N是偶数,相位会偏移半个采样点。我在项目中吃过这个亏,后来养成了检查N奇偶的习惯。
3.3.2 IIR滤波器设计
IIR设计更灵活,但也要更小心。还是同样的指标,用IIR实现:
from scipy.signal import iirdesign
# 设计IIR低通滤波器
# 注意:频率要归一化到奈奎斯特频率
wp = f_pass / (fs/2) # 通带归一化频率
ws = f_stop / (fs/2) # 阻带归一化频率
gpass = 0.5 # 通带波纹 (dB)
gstop = atten # 阻带衰减 (dB)
b, a = iirdesign(wp, ws, gpass, gstop, ftype='ellip')
# 查看频率响应
w, h = freqz(b, a, worN=8000)
freq = w * fs / (2 * np.pi)
plt.plot(freq, 20 * np.log10(abs(h)))
plt.title('IIR 低通滤波器频率响应')
plt.xlabel('频率 (Hz)')
plt.ylabel('幅度 (dB)')
plt.grid()
plt.show()
你会发现,IIR只用了几阶就达到了和FIR几十阶一样的效果。但注意看相位响应——它是弯弯曲曲的,不是直线。这就是非线性相位的代价。
我的建议:如果对相位有要求(比如语音通信、数据调制),优先用FIR。如果对计算资源有要求(比如嵌入式DSP),可以考虑IIR,但一定要检查稳定性。怎么检查?看极点是否都在单位圆内。用np.abs(np.roots(a))就能算出来。
3.4 实战中的选择:FIR还是IIR?
说了这么多,到底选哪个?我总结一下:
- 航空语音通信:用FIR。因为人耳对相位敏感,线性相位能保证语音清晰。我做的电台里,语音通道全是FIR。
- 导航信号处理:看情况。如果只是滤除带外噪声,IIR就够了,省资源。但如果要保留信号波形,还是FIR靠谱。
- 实时性要求极高:IIR更合适。阶数低,计算量小,延迟也小。
你想想看,飞机上的DSP芯片,算力就那么点,每毫秒都得精打细算。选错了滤波器类型,要么效果不好,要么跑不动。所以设计之前,一定要把指标和资源都摸清楚。
最后说一句:滤波器设计不是一锤子买卖。设计完了,一定要在真实信号上测试。我曾经设计了一个滤波器,仿真完美,结果一接实际信号就出问题——因为实际信号里有直流偏置,滤波器没处理好。所以,仿真只是第一步,实测才是王道。
好了,这一章就到这里。下一章咱们聊聊「多速率信号处理」,看看怎么在采样率不同的系统之间传递信号。到时候我会分享一个我在航空电台里做采样率转换的案例,挺有意思的。