Python 如何用Scipy.signal.butter实现带通巴特沃斯滤波器

Python 如何用Scipy.signal.butter实现带通巴特沃斯滤波器,python,scipy,signal-processing,digital-filter,Python,Scipy,Signal Processing,Digital Filter,更新: 我在这个问题上找到了一个简单的食谱!因此,对于任何感兴趣的人,请直接转到: 我很难实现一个简单的任务,为1-D numpy阵列(时间序列)实现巴特沃斯带通滤波器 我必须包括的参数是采样率、以赫兹为单位的截止频率以及可能的顺序(其他参数,如衰减、自然频率等,对我来说比较模糊,所以任何“默认”值都可以) 我现在拥有的是这个,它看起来像一个高通滤波器,但我无法确定我是否做对了: def butter_highpass(interval, sampling_rate, cutoff, orde

更新:

我在这个问题上找到了一个简单的食谱!因此,对于任何感兴趣的人,请直接转到:


我很难实现一个简单的任务,为1-D numpy阵列(时间序列)实现巴特沃斯带通滤波器

我必须包括的参数是采样率、以赫兹为单位的截止频率以及可能的顺序(其他参数,如衰减、自然频率等,对我来说比较模糊,所以任何“默认”值都可以)

我现在拥有的是这个,它看起来像一个高通滤波器,但我无法确定我是否做对了:

def butter_highpass(interval, sampling_rate, cutoff, order=5):
    nyq = sampling_rate * 0.5

    stopfreq = float(cutoff)
    cornerfreq = 0.4 * stopfreq  # (?)

    ws = cornerfreq/nyq
    wp = stopfreq/nyq

    # for bandpass:
    # wp = [0.2, 0.5], ws = [0.1, 0.6]

    N, wn = scipy.signal.buttord(wp, ws, 3, 16)   # (?)

    # for hardcoded order:
    # N = order

    b, a = scipy.signal.butter(N, wn, btype='high')   # should 'high' be here for bandpass?
    sf = scipy.signal.lfilter(b, a, interval)
    return sf

这些文档和示例令人费解且晦涩难懂,但我想实现在表中给出的标记为“for bandpass”的表单。注释中的问号显示了我只是复制粘贴了一些示例,而不了解发生了什么


我不是电气工程师或科学家,只是一名医疗设备设计师,需要对肌电信号执行一些简单的带通滤波。

对于带通滤波器,ws是一个包含上下角频率的元组。这些表示滤波器响应比通带小3 dB的数字频率

wp是包含阻带数字频率的元组。它们表示最大衰减开始的位置

gpass是通带中的最大衰减量(dB),而gstop是阻带中的衰减量

例如,您想设计一个采样率为8000个采样/秒的滤波器,其转角频率为300和3100 Hz。奈奎斯特频率是采样率除以2,或在本例中为4000 Hz。等效数字频率为1.0。两个转角频率分别为300/4000和3100/4000

现在让我们假设您希望阻带从转角频率降低30 dB+/-100 Hz。因此,阻带的起始频率为200和3200 Hz,数字频率为200/4000和3200/4000

要创建过滤器,您可以调用buttord作为

fs = 8000.0
fso2 = fs/2
N,wn = scipy.signal.buttord(ws=[300/fso2,3100/fso2], wp=[200/fs02,3200/fs02],
   gpass=0.0, gstop=30.0)

结果滤波器的长度取决于阻带的深度和响应曲线的陡度,而响应曲线的陡度由拐角频率和阻带频率之间的差值决定。

您可以跳过buttord,而是为过滤器选择一个顺序,看看它是否符合您的过滤标准。要生成带通滤波器的滤波器系数,请给出滤波器顺序、截止频率
Wn=[低、高]
(表示为奈奎斯特频率的分数,即采样频率的一半)和频带类型
btype=“band”

下面是一个脚本,它定义了两个方便的函数,用于使用Butterworth带通滤波器。当作为脚本运行时,它会生成两个绘图。其中一个显示了在相同采样率和截止频率的几个滤波器阶数下的频率响应。另一个图显示了过滤器(顺序=6)对样本时间序列的影响

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


def butter_bandpass_filter(data, lowcut, highcut, fs, order=5):
    b, a = butter_bandpass(lowcut, highcut, fs, order=order)
    y = lfilter(b, a, data)
    return y


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 = 5000.0
    lowcut = 500.0
    highcut = 1250.0

    # Plot the frequency response for a few different orders.
    plt.figure(1)
    plt.clf()
    for order in [3, 6, 9]:
        b, a = butter_bandpass(lowcut, highcut, fs, order=order)
        w, h = freqz(b, a, worN=2000)
        plt.plot((fs * 0.5 / np.pi) * w, abs(h), label="order = %d" % order)

    plt.plot([0, 0.5 * fs], [np.sqrt(0.5), np.sqrt(0.5)],
             '--', label='sqrt(0.5)')
    plt.xlabel('Frequency (Hz)')
    plt.ylabel('Gain')
    plt.grid(True)
    plt.legend(loc='best')

    # Filter a noisy signal.
    T = 0.05
    nsamples = T * fs
    t = np.linspace(0, T, nsamples, endpoint=False)
    a = 0.02
    f0 = 600.0
    x = 0.1 * np.sin(2 * np.pi * 1.2 * np.sqrt(t))
    x += 0.01 * np.cos(2 * np.pi * 312 * t + 0.1)
    x += a * np.cos(2 * np.pi * f0 * t + .11)
    x += 0.03 * np.cos(2 * np.pi * 2000 * t)
    plt.figure(2)
    plt.clf()
    plt.plot(t, x, label='Noisy signal')

    y = butter_bandpass_filter(x, lowcut, highcut, fs, order=6)
    plt.plot(t, y, label='Filtered signal (%g Hz)' % f0)
    plt.xlabel('time (seconds)')
    plt.hlines([-a, a], 0, T, linestyles='--')
    plt.grid(True)
    plt.axis('tight')
    plt.legend(loc='upper left')

    plt.show()
以下是此脚本生成的绘图:


中的过滤器设计方法是正确的,但存在缺陷。用b、a设计的SciPy带通滤波器是,并且可能导致at

相反,使用滤波器设计的sos(二阶段)输出

from scipy.signal import butter, sosfilt, sosfreqz

def butter_bandpass(lowcut, highcut, fs, order=5):
        nyq = 0.5 * fs
        low = lowcut / nyq
        high = highcut / nyq
        sos = butter(order, [low, high], analog=False, btype='band', output='sos')
        return sos

def butter_bandpass_filter(data, lowcut, highcut, fs, order=5):
        sos = butter_bandpass(lowcut, highcut, fs, order=order)
        y = sosfilt(sos, data)
        return y
此外,您还可以通过更改

b, a = butter_bandpass(lowcut, highcut, fs, order=order)
w, h = freqz(b, a, worN=2000)


我在dsp.stackexchange上尝试过一些东西,但是他们太多(超出我的能力)关注工程的概念问题,而不是太多地使用scipy函数。我尝试过实现它,但仍然缺少一些东西。有一件事是,
gpass=0.0
引发了一个被零除的错误,所以我将它改为0.1,错误就停止了。除此之外,
butter
的文档说:
通带和阻带边缘频率,从0标准化为1(1对应于π弧度/样本)。
我怀疑你的答案计算是否正确,所以我仍在努力,很快会给出一些反馈。(另外,尽管my
ws
wp
各有两个元素,但过滤器仅执行低通或高通(通过
btype
参数),而不执行带通)根据at的文档,buttord设计了低、高和带通滤波器。就gpass而言,我猜buttord不允许在通带中衰减0 dB。然后将其设置为一些非零值。您知道为什么滤波输出总是从零值开始吗?是否可以将其与实际输入值匹配
x[0]“太长了,读不下去了?”,我用同样的方法尝试了类似的东西,用CuBy1低通滤波器,我得到了同样的问题。(b,a);y,zo=lfilter(b,a,data,zi=zi*data[0])
(但这可能与带通或高通滤波器没有区别。)我注意到
scipy.signal.lfiter()
wrt的输出与原始信号和
信号.filter()
输出之间存在180度的相移,这是为什么?我应该使用
filter())
相反,如果计时对我很重要?这是该频率下滤波器的相位延迟。通过巴特沃斯滤波器的正弦信号的相位延迟非线性地取决于频率。对于零相位延迟,可以使用
filter()
。我的回答包括使用
filter()的示例
以避免过滤器导致的延迟。嘿,Jason,我建议您在上问一些关于信号处理理论的问题。如果您对您编写的某些代码有疑问,但没有达到预期的效果,您可以在这里开始一个关于stackoverflow的新问题。+1因为这是现在在许多情况下更好的方法
sos = butter_bandpass(lowcut, highcut, fs, order=order)
w, h = sosfreqz(sos, worN=2000)