lfilter
FIR,即有限脉冲响应(finite impulse response),特点是无反馈,其离散形式可表示为
y n = ∑ k = 0 M b k x n − k y_n=\sum^M_{k=0}b_kx_{n-k} yn=k=0∑Mbkxn−k
IIR,即无限脉冲响应(infinite impulse response),可以写为
a 0 y n = ∑ k = 0 M b k x n − k − ∑ k = 1 N a k y n − k a_0y_n=\sum^M_{k=0}b_kx_{n-k}-\sum^N_{k=1}a_ky_{n-k} a0yn=k=0∑Mbkxn−k−k=1∑Nakyn−k
滤波器设计,就是对 a k , b k a_k, b_k ak,bk具体形式的求解,scipy.signal
中内置了一些常用的FIR和IIR滤波器。
其中lfilter
是最基础的脉冲响应滤波器,其形式为
lfilter(b, a, x, axis=-1, zi=None)
其中,a, b
为滤波参数,x
为待滤波数组,axix
为作用的坐标轴,zi
为默认延时,其Z变换形式的滤波函数为
Y ( z ) = ∑ i = 0 b i z − i ∑ i = 0 a i z − i X ( z ) Y(z)=\frac{\sum_{i=0} b_iz^{-i}}{\sum_{i=0} a_iz^{-i}}X(z) Y(z)=∑i=0aiz−i∑i=0biz−iX(z)
下面通过butter
函数生成3阶巴特沃斯滤波器对应的a
和b
值,用以lfilter
进行滤波
import scipy.signal as ss
import matplotlib.pyplot as plt
rng = np.random.default_rng()
t = np.linspace(-1, 1, 201)
PI = 2*np.pi
x = (np.sin(PI*0.75*t*(1-t) + 2.1) +
0.1*np.sin(PI*1.25*t + 1) +
0.18*np.cos(PI*3.85*t))
# 原始数据添加噪声
xn = x + rng.standard_normal(len(t)) * 0.08
b, a = ss.butter(3, 0.05)
z = ss.lfilter(b, a, xn)
z2 = ss.lfilter(b, a, z)
plt.plot(t, z, 'r--', t, z2, 'r')
plt.scatter(t, xn, marker='.', alpha=0.75)
plt.legend(('lfilter, once', 'lfilter, twice', 'noisy signal'), loc='best')
plt.show()
效果如下
可见,lfilter
的确对原始信号的噪声进行了过滤,过滤之后的信号也的确较为平滑,而且显然进行二次滤波的效果比第一次更加平滑,但却产生了延时。
这就是lfilter
的问题所在,即其为因果关系实时过滤,对于除了对称FIR之外的其他情况,都会产生延时。
filtfilt
相比之下,另一个滤波函数filtfilt
对输入信号进行正反两个方向的滤波,从而消除了lfilter
所产生的相位差。
y = ss.filtfilt(b, a, xn)
plt.plot(t, y, 'r')
plt.scatter(t, xn, marker='.', alpha=0.75)
plt.legend(('filtfilt', 'noisy signal'), loc='best')
plt.show()
效果为
filtfilt函数的调用方法如下
filtfilt(b, a, x, axis=-1, padtype='odd', padlen=None, method='pad', irlen=None)
除了在lfilter
中已有的b, a, x, axis
之外,另外三个参数表示:
padtype
: 滤波器填充信号的扩展类型,为"odd"
,"even"
,"constant"
或者None
。padlen
:在应用滤波器之前在轴两端延伸X的元素数目。此值必须小于要滤波元素个数- 1。(int型或None)method
:边缘处理方法,为"pad"
时,填充信号,填充类型由padtype
和padlen
决定;为"gust"
时,使用古斯塔夫森方法,填充方式由irlen
决定。irlen
:指定滤波器的脉冲响应的长度。如果irlen
是None,则脉冲响应的任何部分都被忽略。对于长信号,指定irlen
可以显著改善滤波器的性能。