it-swarm.com.de

Tiefpassfilter in SciPy erstellen - Methoden und Einheiten verstehen

Ich versuche, ein lautes Herzfrequenzsignal mit Python zu filtern. Da die Herzfrequenz niemals bei 220 Schlägen pro Minute liegen sollte, möchte ich alle Geräusche über 220 Schlägen pro Minute herausfiltern. Ich habe 220/Minute in 3,666,666,66 Hertz konvertiert und dann diese Hertz in rad/s konvertiert, um 23,0383461 rad/sec zu erhalten.

Die Abtastfrequenz des Chips, der Daten aufnimmt, beträgt 30 Hz, also habe ich diese in rad/s konvertiert, um 188,495559 rad/s zu erhalten.

Nachdem ich ein paar Sachen online nachgeschlagen hatte, fand ich einige Vorschläge für einen Bandpassfilter, den ich in einen Tiefpass verwandeln wollte. Hier ist der Link zum Bandpasscode , also habe ich ihn folgendermaßen konvertiert:

from scipy.signal import butter, lfilter
from scipy.signal import freqs

def butter_lowpass(cutOff, fs, order=5):
    nyq = 0.5 * fs
    normalCutoff = cutOff / nyq
    b, a = butter(order, normalCutoff, btype='low', analog = True)
    return b, a

def butter_lowpass_filter(data, cutOff, fs, order=4):
    b, a = butter_lowpass(cutOff, fs, order=order)
    y = lfilter(b, a, data)
    return y

cutOff = 23.1 #cutoff frequency in rad/s
fs = 188.495559 #sampling frequency in rad/s
order = 20 #order of filter

#print sticker_data.ps1_dxdt2

y = butter_lowpass_filter(data, cutOff, fs, order)
plt.plot(y)

Ich bin jedoch sehr verwirrt, da ich ziemlich sicher bin, dass die Butterfunktion die Cutoff- und Sampling-Frequenz in rad/s erfasst, aber ich scheine eine seltsame Ausgabe zu bekommen. Ist es tatsächlich in Hz?

Zweitens, was ist der Zweck dieser beiden Zeilen:

    nyq = 0.5 * fs
    normalCutoff = cutOff / nyq

Ich weiß, es ist etwas über Normalisierung, aber ich dachte, der Nyquist war 2-mal die Abtastfrequenz, nicht die Hälfte. Und warum benutzt du den Nyquist als Normalisierer?

Kann man erklären, wie man mit diesen Funktionen Filter erstellt?

Ich habe den Filter mit eingezeichnet

w, h = signal.freqs(b, a)
plt.plot(w, 20 * np.log10(abs(h)))
plt.xscale('log')
plt.title('Butterworth filter frequency response')
plt.xlabel('Frequency [radians / second]')
plt.ylabel('Amplitude [dB]')
plt.margins(0, 0.1)
plt.grid(which='both', axis='both')
plt.axvline(100, color='green') # cutoff frequency
plt.show()

und hab das bekommen das bei 23 rad/s klar nicht abschneidet:

result

61
user3123955

Ein paar Kommentare:

  • Die Nyquist-Frequenz ist die halbe Abtastrate.
  • Sie arbeiten mit regelmäßig abgetasteten Daten. Sie möchten also einen digitalen Filter, keinen analogen Filter. Dies bedeutet, dass Sie beim Aufruf von butter nicht analog=True Verwenden sollten, und Sie sollten scipy.signal.freqz (Nicht freqs) verwenden, um den Frequenzgang zu generieren.
  • Ein Ziel dieser kurzen Utility-Funktionen ist es, Ihnen zu ermöglichen, alle Ihre Frequenzen in Hz ausgedrückt zu lassen. Sie sollten nicht in rad/sec konvertieren müssen. Solange Sie Ihre Frequenzen in einheitlichen Einheiten ausdrücken, übernimmt die Skalierung in den Utility-Funktionen die Normalisierung für Sie.

Hier ist meine modifizierte Version Ihres Skripts, gefolgt von der grafischen Darstellung, die es generiert.

import numpy as np
from scipy.signal import butter, lfilter, freqz
import matplotlib.pyplot as plt


def butter_lowpass(cutoff, fs, order=5):
    nyq = 0.5 * fs
    normal_cutoff = cutoff / nyq
    b, a = butter(order, normal_cutoff, btype='low', analog=False)
    return b, a

def butter_lowpass_filter(data, cutoff, fs, order=5):
    b, a = butter_lowpass(cutoff, fs, order=order)
    y = lfilter(b, a, data)
    return y


# Filter requirements.
order = 6
fs = 30.0       # sample rate, Hz
cutoff = 3.667  # desired cutoff frequency of the filter, Hz

# Get the filter coefficients so we can check its frequency response.
b, a = butter_lowpass(cutoff, fs, order)

# Plot the frequency response.
w, h = freqz(b, a, worN=8000)
plt.subplot(2, 1, 1)
plt.plot(0.5*fs*w/np.pi, np.abs(h), 'b')
plt.plot(cutoff, 0.5*np.sqrt(2), 'ko')
plt.axvline(cutoff, color='k')
plt.xlim(0, 0.5*fs)
plt.title("Lowpass Filter Frequency Response")
plt.xlabel('Frequency [Hz]')
plt.grid()


# Demonstrate the use of the filter.
# First make some data to be filtered.
T = 5.0         # seconds
n = int(T * fs) # total number of samples
t = np.linspace(0, T, n, endpoint=False)
# "Noisy" data.  We want to recover the 1.2 Hz signal from this.
data = np.sin(1.2*2*np.pi*t) + 1.5*np.cos(9*2*np.pi*t) + 0.5*np.sin(12.0*2*np.pi*t)

# Filter the data, and plot both the original and filtered signals.
y = butter_lowpass_filter(data, cutoff, fs, order)

plt.subplot(2, 1, 2)
plt.plot(t, data, 'b-', label='data')
plt.plot(t, y, 'g-', linewidth=2, label='filtered data')
plt.xlabel('Time [sec]')
plt.grid()
plt.legend()

plt.subplots_adjust(hspace=0.35)
plt.show()

lowpass example

125