Scipy.signal.butter के साथ बैंड-पास बटरवर्थ फ़िल्टर कैसे लागू करें


84

अपडेट करें:

मुझे इस सवाल पर आधारित एक स्कैपी रेसिपी मिली! तो, किसी के लिए भी, सीधे जाएं: सामग्री »सिग्नल प्रोसेसिंग» बटरवर्थ बैंडपास


मुझे यह हासिल करने के लिए एक कठिन समय मिल रहा है कि शुरू में 1-डी के लिए एक बटरवर्थ बैंड-पास फ़िल्टर को लागू करने का एक सरल काम लगता था (समय-श्रृंखला)।

जिन मापदंडों को मुझे शामिल करना है, वे हैं 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

यहां छवि विवरण दर्ज करें

डॉक्स और उदाहरण भ्रमित और अस्पष्ट हैं, लेकिन मैं "बैंडपास के लिए" के रूप में चिह्नित प्रशंसा में प्रस्तुत किए गए फॉर्म को लागू करना चाहूंगा। टिप्पणियों में प्रश्न चिह्न यह दिखाते हैं कि मैंने अभी कुछ उदाहरण को बिना समझे कॉपी-पेस्ट किया है कि क्या हो रहा है।

मैं कोई इलेक्ट्रिकल इंजीनियरिंग या वैज्ञानिक नहीं हूं, बस एक चिकित्सा उपकरण डिजाइनर को ईएमजी संकेतों पर फ़िल्टर करने के बजाय कुछ सीधे-सीधे बैंडपास प्रदर्शन करने की आवश्यकता है।


मैंने dsp.stackexchange में कुछ करने की कोशिश की है, लेकिन वे इंजीनियरिंग के वैचारिक मुद्दों में बहुत अधिक (मैं जितना संभाल सकता हूं) ध्यान केंद्रित करता हूं और न ही स्कैपी फ़ंक्शन का उपयोग करने में बहुत अधिक।
हेलटनबीकर

जवाबों:


119

आप बट्टर्ड का उपयोग छोड़ सकते हैं, और इसके बजाय केवल फ़िल्टर के लिए एक आदेश चुनें और देखें कि क्या यह आपके फ़िल्टरिंग मानदंड को पूरा करता है। एक बैंडपास फ़िल्टर के लिए फ़िल्टर गुणांक उत्पन्न करने के लिए, मक्खन () फ़िल्टर ऑर्डर, कटऑफ़ फ़्रीक्वेंसी Wn=[low, high](Nyquist आवृत्ति के अंश के रूप में व्यक्त किया जाता है, जो कि आधा नमूना आवृत्ति है) और बैंड प्रकार btype="band"

यहां एक स्क्रिप्ट है जो बटरवर्थ बैंडपास फिल्टर के साथ काम करने के लिए कुछ सुविधा कार्यों को परिभाषित करता है। जब एक स्क्रिप्ट के रूप में चलाया जाता है, तो यह दो प्लॉट बनाता है। एक ही नमूना दर और कटऑफ आवृत्तियों के लिए कई फिल्टर आदेशों पर आवृत्ति प्रतिक्रिया दिखाती है। अन्य भूखंड नमूना समय श्रृंखला पर फ़िल्टर (आदेश = 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
क्या आप जानते हैं कि फ़िल्टर्ड आउटपुट हमेशा शून्य पर क्यों शुरू होता है? क्या वास्तविक इनपुट मूल्य के साथ इसका मिलान संभव है x[0]? मैं Cheby1 कम पास फिल्टर के साथ समान सामान की कोशिश की, और मैं एक ही समस्या है।
LWZ

2
@LWZ: फ़ंक्शन scipy.signal.lfilter_ziऔर ziतर्क का उपयोग करें lfilter। विवरण के लिए, के लिए docstring देखें lfilter_zi। टी एल; डॉ? बस y = lfilter(b, a, data)करने के लिए बदल जाते हैं zi = lfilter_zi(b, a); y, zo = lfilter(b, a, data, zi=zi*data[0])। (लेकिन यह एक बैंडपास या उच्च पास फिल्टर के साथ अंतर नहीं कर सकता है।)
वॉरेन वीकेसर

1
मैंने देखा कि scipy.signal.lfiter()मूल सिग्नल और आउटपुट के लिए wrt के आउटपुट में 180 डिग्री फेज शिफ्ट है signal.filtfilt(), ऐसा क्यों है? filtfilt()अगर समय मेरे लिए महत्वपूर्ण है तो क्या मुझे इसके बजाय उपयोग करना चाहिए ?
जेसन

1
उस आवृत्ति पर फ़िल्टर का चरण विलंब है। बटरवर्थ फ़िल्टर के माध्यम से एक साइनसॉइड में चरण में देरी आवृत्ति पर ग़ैर-निर्भरता से निर्भर करती है। शून्य चरण विलंब के लिए, हां, आप उपयोग कर सकते हैं filtfilt()यहाँ मेरे उत्तर filtfilt()में फ़िल्टर द्वारा प्रेरित अंतराल से बचने के लिए उपयोग करने का एक उदाहरण शामिल है ।
वारेन वीकेसर

1
हे जेसन, मैं dsp.stackexchange.com पर सिग्नल प्रोसेसिंग सिद्धांत के बारे में सवाल पूछने की सलाह देता हूं । यदि आपके पास कुछ कोड के बारे में कोई प्रश्न है जो आपने लिखा है जो अपेक्षित रूप से काम नहीं कर रहा है, तो आप स्टैकओवरफ़्लो पर एक नया प्रश्न शुरू कर सकते हैं।
वॉरेन वीकेसर

41

स्वीकृत उत्तर में फ़िल्टर डिज़ाइन विधि सही है, लेकिन इसमें एक दोष है। ख के साथ बनाया गया SciPy बैंडपास फिल्टर, एक कर रहे हैं अस्थिर और में हो सकता है गलत फिल्टर पर उच्च फिल्टर आदेश

इसके बजाय, फ़िल्टर डिज़ाइन के एसओएस (सेकंड-ऑर्डर सेक्शन) आउटपुट का उपयोग करें।

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)

+1 क्योंकि यह अब कई मामलों में जाने का बेहतर तरीका है। स्वीकार किए गए उत्तर पर टिप्पणियों के रूप में, आगे-पीछे की फ़िल्टरिंग का उपयोग करके चरण की देरी को समाप्त करना भी संभव है। बस के sosfiltसाथ बदलें sosfiltfilt
माइक

@ माइक और user13107 क्या एक ही बग हाई-पास और लो-पास बटरवर्थ फिल्टर को भी प्रभावित करता है? और क्या समाधान वही है?
दीवारेन 1

3
@ dewarrn1 इसे "बग" कहना वास्तव में सही नहीं है; एल्गोरिथ्म सही ढंग से लागू किया गया है, लेकिन यह स्वाभाविक रूप से अस्थिर है, इसलिए यह एल्गोरिथ्म का सिर्फ एक बुरा विकल्प है। लेकिन हाँ, यह उच्चतर क्रम में किसी भी फिल्टर को प्रभावित करता है - न केवल उच्च- या निम्न-पास और न केवल बटरवर्थ फिल्टर, बल्कि अन्य भी जैसे चेबिशेव और इतने पर। वैसे भी, सामान्य तौर पर, हमेशा sosआउटपुट का चयन करना सबसे अच्छा होता है, क्योंकि यह हमेशा अस्थिरता से बचा रहेगा। और जब तक आपको वास्तविक समय प्रसंस्करण की आवश्यकता नहीं होती है, तब तक आपको हमेशा उपयोग करना चाहिए sosfiltfilt
माइक

क्षमा करें, मैंने इस उत्तर पर बहुत पहले गौर नहीं किया था! @ user13107, हां, एक रैखिक फिल्टर के ट्रांसफर फ़ंक्शन (या 'बा') के प्रतिनिधित्व में कुछ गंभीर संख्यात्मक मुद्दे होते हैं जब फ़िल्टर का क्रम बड़ा होता है। यहां तक ​​कि अपेक्षाकृत कम क्रम वाले फिल्टर में समस्याएं हो सकती हैं जब नमूना आवृत्ति की तुलना में वांछित बैंडविड्थ छोटा होता है। SOS प्रतिनिधित्व को SciPy में जोड़ने से पहले मेरा मूल उत्तर लिखा गया था, और इससे पहले कि fsकई कार्यों में तर्क जोड़ा गया था scipy.signal। एक अद्यतन के लिए उत्तर लंबे समय से अतिदेय है।
वॉरेन वीकेसर

इसके लिए कोई मदद? stackoverflow.com/q/60866193/5025009
सेरलौक

4

एक बैंडपास फ़िल्टर के लिए, ws एक टपल है जिसमें निचले और ऊपरी कोने की आवृत्तियाँ होती हैं। ये डिजिटल फ्रिक्वेंसी का प्रतिनिधित्व करते हैं जहां फ़िल्टर प्रतिक्रिया पासबैंड की तुलना में 3 डीबी कम है।

wp एक tuple है जिसमें स्टॉप बैंड डिजिटल फ्रीक्वेंसी होती है। वे उस स्थान का प्रतिनिधित्व करते हैं जहां अधिकतम क्षीणन शुरू होता है।

डीएसबी में पासपास में गैस्ट अधिकतम अटेंशन है जबकि स्टॉपबैंड्स में जीएसटीओपी एटेंट्रेशन है।

उदाहरण के लिए, आप sec००० और ३१०० हर्ट्ज के कोने आवृत्तियों वाले sec००० नमूनों / सेक की नमूना दर के लिए एक फ़िल्टर डिजाइन करना चाहते थे। Nyquist आवृत्ति नमूना दर दो से विभाजित है, या इस उदाहरण में, 4000 हर्ट्ज। बराबर डिजिटल आवृत्ति 1.0 है। दो कोने की आवृत्तियाँ 300/4000 और 3100/4000 हैं।

अब आप कहते हैं कि आप चाहते थे कि स्टॉपबैंड कोने के आवृत्तियों से 30 डीबी +/- 100 हर्ट्ज नीचे हो। इस प्रकार, आपके स्टॉपबैंड 200 और 3200 हर्ट्ज पर शुरू होंगे, जिसके परिणामस्वरूप 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.0शून्य त्रुटि से एक विभाजन को बढ़ाता है, इसलिए मैंने इसे 0.1 में बदल दिया और त्रुटि बंद हो गई। इसके अलावा, butterकहने के लिए डॉक्स : Passband and stopband edge frequencies, normalized from 0 to 1 (1 corresponds to pi radians / sample).मुझे संदेह है कि यदि आपके उत्तर ने गणना सही की है, तो मैं अभी भी उस पर काम कर रहा हूं और जल्द ही अपनी प्रतिक्रिया दूंगा।
हेलटनबीकर

(यह भी, हालांकि मेरे wsऔर wpदो तत्व प्रत्येक हैं, फ़िल्टर केवल निम्न या उच्च पास ( btypeतर्क के माध्यम से ) करता है, लेकिन बैंड-पास नहीं)
हेल्टनबीकर

1
दस्तावेज़ में docs.scipy.org/doc/scipy/reference/generated/… के अनुसार , नितंब कम, उच्च और बैंड पास फ़िल्टर डिज़ाइन करता है। जहां तक ​​मैं सोचता हूं, मुझे लगता है कि बटवार्ड पासबैंड में 0 डीबी क्षीणन की अनुमति नहीं देता है। इसे कुछ गैर-शून्य मान पर सेट करें।
sizzzzlerz
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.