Scipy.signal.butter로 대역 통과 버터 워스 필터를 구현하는 방법


84

최신 정보:

이 질문에 근거한 Scipy Recipe를 찾았습니다! 따라서 관심있는 사람은 다음으로 바로 이동하십시오. 목차»신호 처리»버터 워스 대역 통과


처음에는 1D numpy 배열 (시계열)에 대해 버터 워스 대역 통과 필터를 구현하는 간단한 작업처럼 보였던 것을 달성하는 데 어려움을 겪고 있습니다.

내가 포함해야하는 매개 변수는 sample_rate, 컷오프 주파수 IN 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

여기에 이미지 설명 입력

문서와 예제는 혼란스럽고 모호하지만 "for bandpass"로 표시된 칭찬에 제시된 양식을 구현하고 싶습니다. 주석의 물음표는 무슨 일이 일어나고 있는지 이해하지 못하고 몇 가지 예제를 복사하여 붙여 넣은 위치를 보여줍니다.

저는 전기 공학이나 과학자가 아니며 EMG 신호에 대해 다소 간단한 대역 통과 필터링을 수행해야하는 의료 장비 설계자 일뿐입니다.


나는 dsp.stackexchange에서 무언가를 시도했지만 공학의 개념적 문제에 너무 많이 (내가 처리 할 수있는 것 이상으로) 집중하고 scipy 함수를 사용하는 데 그다지 집중하지 않습니다.
heltonbiker

답변:


119

buttord의 사용을 건너 뛰고 대신 필터 주문을 선택하고 필터링 기준을 충족하는지 확인할 수 있습니다. 대역 통과 필터에 대한 필터 계수를 생성하려면 butter ()에 필터 차수, 차단 주파수 Wn=[low, high](샘플링 주파수의 절반 인 나이 퀴 스트 주파수의 일부로 표현됨) 및 대역 유형을 제공하십시오 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()

다음은이 스크립트에 의해 생성 된 플롯입니다.

여러 필터 차수에 대한 주파수 응답

여기에 이미지 설명 입력


1
필터링 된 출력이 항상 값 0에서 시작하는 이유를 알고 있습니까? 실제 입력 값과 일치시킬 수 x[0]있습니까? Cheby1 저역 통과 필터로 비슷한 작업을 시도했는데 같은 문제가 발생했습니다.
LWZ 2013

2
@LWZ : 함수 scipy.signal.lfilter_zizi인수를 사용 lfilter합니다. 자세한 내용은 lfilter_zi. TL; DR? 로 변경 y = lfilter(b, a, data)하십시오 zi = lfilter_zi(b, a); y, zo = lfilter(b, a, data, zi=zi*data[0]). (하지만이 대역 통과 또는 하이 패스 필터 변화를하지 않을 수 있습니다.)
워렌 Weckesser

1
scipy.signal.lfiter()wrt 의 출력 에서 원래 신호와 출력으로 180도 위상 편이가 있음을 알았습니다 signal.filtfilt(). 왜 그런가요 ? filtfilt()타이밍이 중요한 경우 대신 사용해야합니까 ?
Jason

1
그것은 그 주파수에서 필터의 위상 지연입니다. 버터 워스 필터를 통한 정현파의 위상 지연은 주파수에 비선형 적으로 의존합니다. 제로 위상 지연의 경우 예, 사용할 수 있습니다 filtfilt(). 여기에 내 대답 에는 filtfilt()필터로 인한 지연을 피하기 위해 사용하는 예가 포함되어 있습니다.
Warren Weckesser

1
Jason, dsp.stackexchange.com 에서 신호 처리 이론에 대한 질문을하는 것이 좋습니다 . 예상대로 작동하지 않는 일부 코드에 대한 질문이있는 경우 여기에서 stackoverflow에서 새 질문을 시작할 수 있습니다.
Warren Weckesser

41

허용되는 답변 의 필터 설계 방법 은 정확하지만 결함이 있습니다. B로 디자인 SciPy 대역 통과 필터는은 불안정 하고 발생할 수 오 필터 에서 높은 필터 차수 .

대신 필터 설계의 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)

이것은 이제 많은 경우에 더 나은 방법이기 때문입니다. 수락 된 답변에 대한 의견에서와 같이 순방향 역방향 필터링을 사용하여 위상 지연을 제거 할 수도 있습니다. 그냥 교체 sosfilt와 함께 sosfiltfilt.
Mike

@Mike 및 user13107 동일한 버그가 고역 통과 및 저역 통과 버터 워스 필터에도 영향을 줍니까? 그리고 솔루션은 동일합니까?
dewarrn1 19

3
@ dewarrn1 "버그"라고 부르는 것은 실제로 옳지 않습니다. 알고리즘이 올바르게 구현되었지만 본질적으로 불안정하므로 잘못된 알고리즘 선택 일뿐입니다. 그러나 예, 그것은 영향을 미치는 어떤 단지 높은 여부 로우 패스뿐 아니라 버터 워스 (Butterworth) 필터뿐만 아니라 다른 사람 - 높은 차수의 필터 체비 쇼프 같은 등등을. 어쨌든 일반적으로 항상 sos출력을 선택하는 것이 가장 좋습니다 . 이는 항상 불안정성을 피할 수 있기 때문입니다. 실시간 처리가 필요하지 않은 경우 항상 sosfiltfilt.
Mike

오래 전에이 답변을 찾지 못해 죄송합니다! @ user13107, 예, 선형 필터의 전달 함수 (또는 'ba') 표현에는 필터 순서가 클 때 심각한 수치 문제가 있습니다. 상대적으로 낮은 차수 필터조차도 원하는 대역폭이 샘플링 주파수에 비해 작을 때 문제가 발생할 수 있습니다. 내 원래 대답은 SOS 표현이 SciPy에 추가되기 전과 fs인수가 .NET의 많은 함수에 추가 되기 전에 작성 되었습니다 scipy.signal. 업데이트에 대한 답변은 오래되었습니다.
Warren Weckesser

이것에 대한 도움이 있습니까? stackoverflow.com/q/60866193/5025009
seralouk

4

대역 통과 필터의 경우 ws는 하단 및 상단 코너 주파수를 포함하는 튜플입니다. 이는 필터 응답이 통과 대역보다 3dB 낮은 디지털 주파수를 나타냅니다.

wp는 정지 대역 디지털 주파수를 포함하는 튜플입니다. 최대 감쇠가 시작되는 위치를 나타냅니다.

gpass는 통과 대역의 최대 감쇠 (dB 단위)이고 gstop은 저지 대역의 감쇠입니다.

예를 들어 코너 주파수가 300Hz와 3100Hz 인 8000 샘플 / 초의 샘플링 속도에 대한 필터를 설계하려한다고 가정 해 보겠습니다. 나이 퀴 스트 주파수는 샘플 속도를 2로 나눈 값입니다.이 예에서는 4000Hz입니다. 동등한 디지털 주파수는 1.0입니다. 그러면 두 코너 주파수는 300/4000 및 3100/4000입니다.

이제 정지 대역이 코너 주파수에서 30dB +/- 100Hz 아래로 내려 가기를 원한다고 가정 해 보겠습니다. 따라서 정지 대역은 200 및 3200Hz에서 시작하여 디지털 주파수는 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)

결과 필터의 길이는 정지 대역의 깊이와 코너 주파수와 정지 대역 주파수 간의 차이에 의해 결정되는 응답 곡선의 가파른 정도에 따라 달라집니다.


구현하려고했지만 여전히 누락 된 것이 있습니다. 한 가지는 gpass=0.00으로 나누기 오류를 발생시켜 0.1로 변경하고 오류가 중지되었습니다. 그 외에도 다음과 같은 문서가 butter있습니다. Passband and stopband edge frequencies, normalized from 0 to 1 (1 corresponds to pi radians / sample).귀하의 답변이 계산이 올바른지 의심 스럽기 때문에 아직 작업 중이며 곧 피드백을 제공 할 것입니다.
heltonbiker

(내도되지만, wswp두 개의 요소 각각을 상기 필터는 저 통해 또는 하이 패스 (수행 btype) 인자가 아닌 밴드 패스)
heltonbiker

1
docs.scipy.org/doc/scipy/reference/generated/… 의 문서에 따르면 buttord는 로우, 하이 및 대역 통과 필터를 설계합니다. gpass에 관해서는 buttord가 통과 대역에서 0dB 감쇠를 허용하지 않는 것 같습니다. 그런 다음 0이 아닌 값으로 설정하십시오.
sizzzzlerz
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.