带通的butterworth过滤器频率scipy

时间:2014-02-18 18:51:07

标签: python numpy scipy filtering

我正在设计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()

fs = 25, low = 0.5, high = 4 fs = 25, low = 0.05, high = 0.4

更新:问题已在Scipy 0.14上进行了讨论并显然得到了解决。然而,在Scipy更新后,情节看起来仍然很糟糕。怎么了?

After Scipy 0.14, even worse

4 个答案:

答案 0 :(得分:3)

  1. 不要将b, a = butter用于高阶滤镜,无论是在Matlab,SciPy还是Octave中。传递函数格式has numerical stability problems,因为一些系数非常大而其他系数非常小。这就是我们更改过滤器设计函数to use zpk format internally的原因。要了解这一点,您需要使用z, p, k = butter(output='zpk'),然后使用极点和零而不是分子和分母。
  2. 不要在一个阶段中执行高阶数字滤波器。无论您使用哪种软件或硬件,这都是一个坏主意。通常最好将它们分解为second-order sections。在Matlab中,您可以使用zp2sos自动生成这些内容。 This has not been implemented in SciPy yet,虽然我在这里有GPL代码:https://gist.github.com/endolith/4525003(GPL许可证与scipy不兼容,因此需要从头开始重写。)

答案 1 :(得分:1)

显然这个问题是一个已知的错误:

Github

答案 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阶多项式。