我正在设计cookbook之后的scipy中的带通滤波器。但是,如果我过多地降低滤波频率,我会在高阶滤波器中使用垃圾。我做错了什么?
from scipy.signal import butter, lfilter
def butter_bandpass(lowcut, highcut, fs, order=5):
nyq = 0.5 * fs
low = lowcut / nyq
high = highcut / nyq
b, a = butter(order, [low, high], btype='band')
return b, a
if __name__ == "__main__":
import numpy as np
import matplotlib.pyplot as plt
from scipy.signal import freqz
# Sample rate and desired cutoff frequencies (in Hz).
fs = 25
# Plot the frequency response for a few different orders.
plt.figure(1)
plt.clf()
for order in [1, 3, 5, 6, 9]:
b, a = butter_bandpass(0.5, 4, fs, order=order)
w, h = freqz(b, a, worN=2000)#np.logspace(-4, 3, 2000))
plt.semilogx((fs * 0.5 / np.pi) * w, abs(h), label="order = %d" % order)
plt.xlabel('Frequency (Hz)')
plt.ylabel('Gain')
plt.grid(True)
plt.legend(loc='best')
plt.figure(2)
plt.clf()
for order in [1, 3, 5, 6, 9]:
b, a = butter_bandpass(0.05, 0.4, fs, order=order)
w, h = freqz(b, a, worN=2000)#np.logspace(-4, 3, 2000))
plt.semilogx((fs * 0.5 / np.pi) * w, abs(h), label="order = %d" % order)
plt.xlabel('Frequency (Hz)')
plt.ylabel('Gain')
plt.grid(True)
plt.legend(loc='best')
plt.show()
更新:问题已在Scipy 0.14上进行了讨论并显然得到了解决。然而,在Scipy更新后,情节看起来仍然很糟糕。怎么了?
答案 0 :(得分:3)
b, a = butter
用于高阶滤镜,无论是在Matlab,SciPy还是Octave中。传递函数格式has numerical stability problems,因为一些系数非常大而其他系数非常小。这就是我们更改过滤器设计函数to use zpk format internally的原因。要了解这一点,您需要使用z, p, k = butter(output='zpk')
,然后使用极点和零而不是分子和分母。答案 1 :(得分:1)
显然这个问题是一个已知的错误:
答案 2 :(得分:1)
这是数字滤波器中的常见问题。由于浮点数的精度有限,截止频率远低于奈奎斯特频率的高阶滤波器往往具有不稳定的系数。最后我检查过(不可否认的是几年前)Matlab在保存精度方面做得比scipy好得多,尽管它仍然会给出足够极端的过滤器带来问题。
如果你不能使用matlab,有几个选项。首先是将过滤器分解为级联的二阶段。基本上你计算你想要的极点和零点,将它们分解成复共轭对,并计算每对的传递函数。
第二种选择是重新采样到与滤波器频率更相似的采样率。例如,在您的第二个示例中,您的采样率为25,最高截止频率为.4。您可以使用低通抗混叠滤波器,然后将系数抽取10倍,采样率为2.5。采样率越低,带通滤波器系数对舍入误差的敏感度越低。如果这样做,您必须确保抗锯齿滤波器没有相同的问题。
答案 3 :(得分:0)
脚本中创建的带通(BP)过滤器的顺序实际上是图中显示的那些的两倍。回想一下,滤波器的阶数是传递函数分母中多项式的阶数。 Canonic 带通过滤器总是偶数。
显示的数字是低通(LP)原型的顺序(通常标准化为1 rad / s的截止频率),用于应用LP-to-BP转换,使转换器的顺序加倍。因此,例如,如果我们从订单1的LP开始,我们最终会得到二阶带通:
1 /(S + 1) => LP-2-BP transf。 => k.s /(s ^ 2 + a.s + b)
k , a 和 b 是常量。标准带通滤波器的分子是 k.s ^(N / 2),因此滤波器的 N 顺序必须是偶数。
SciPy documentation中未提及带通的订单问题(也适用于陷波或带阻滤波器)。事实上,如果你在print(len(a))
之前打印分母a的长度(使用plt.show()
),你会看到它有19个系数,对应于18阶多项式。