web-dev-qa-db-ja.com

Scipy.signal.butterでバンドパスバターワースフィルターを実装する方法

更新:

この質問に基づいたScipy Recipeを見つけました!したがって、興味のある方は、まっすぐに進んでください。 内容"信号処理"バターワースバンドパス


最初は1次元numpy配列(時系列)のバタワースバンドパスフィルターを実装する単純なタスクと思われたものを達成するのに苦労しています。

含める必要があるパラメーターは、sample_rate、HERTZのカットオフ周波数、および場合によっては次数です(減衰、固有振動数などの他のパラメーターはわかりにくいため、「デフォルト」値で十分です)。

私が今持っているのはこれです。これはハイパスフィルターとして機能しているようですが、正しくやっているかどうかはわかりません。

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

enter image description here

ドキュメントと例は混乱を招き、あいまいですが、「for bandpass」とマークされたコマンドに示されているフォームを実装したいと思います。コメント内の疑問符は、何が起こっているのか理解せずに、コピーしたいくつかの例を貼り付けた場所を示しています。

私は電気工学や科学者ではありません。EMG信号に対してかなり簡単なバンドパスフィルタリングを実行する必要がある医療機器設計者です。

68
heltonbiker

Buttordの使用をスキップし、代わりにフィルターの順序を選択して、フィルター条件を満たしているかどうかを確認できます。バンドパスフィルターのフィルター係数を生成するには、butter()にフィルター次数、カットオフ周波数Wn=[low, high](サンプリング周波数の半分であるナイキスト周波数の割合として表される)、およびバンドタイプbtype="band"

Butterworthバンドパスフィルターを操作するためのいくつかの便利な関数を定義するスクリプトを次に示します。スクリプトとして実行すると、2つのプロットが作成されます。同じサンプリングレートとカットオフ周波数に対するいくつかのフィルター次数での周波数応答を示します。もう1つのプロットは、サンプル時系列に対するフィルター(order = 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 __== "__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()

このスクリプトによって生成されるプロットは次のとおりです。

Frequency response for several filter orders

enter image description here

96

accepted answer のフィルター設計法は正しいですが、欠陥があります。 b、aで設計されたSciPyバンドパスフィルターは 不安定 であり、 誤ったフィルター atフィルター次数 になる可能性があります

代わりに、フィルター設計のsos(2次セクション)出力を使用します。

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)

sos = butter_bandpass(lowcut, highcut, fs, order=order)
w, h = sosfreqz(sos, worN=2000)
20
user13107

バンドパスフィルターの場合、wsは、下隅周波数と上隅周波数を含むタプルです。これらは、フィルター応答が通過帯域より3 dB低いデジタル周波数を表します。

wpは、ストップバンドのデジタル周波数を含むタプルです。最大減衰が始まる位置を表します。

gpassはdB単位の通過帯域内の最大減衰であり、gstopは遮断帯域内の減衰です。

たとえば、300および3100 Hzのコーナー周波数を持つ8000サンプル/秒のサンプリングレートのフィルターを設計したいとします。ナイキスト周波数は、サンプルレートを2で割った値、この例では4000 Hzです。同等のデジタル周波数は1.0です。 2つのコーナー周波数は、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)

結果のフィルターの長さは、ストップバンドの深さと、コーナー周波数とストップバンド周波数の差によって決まる応答曲線の急峻さに依存します。

4
sizzzzlerz