Створення фільтра низьких частот у SciPy - розуміння методів та одиниць


82

Я намагаюся фільтрувати шумний сигнал серцевого ритму за допомогою python. Оскільки частота серцевих скорочень ніколи не повинна бути вище приблизно 220 ударів на хвилину, я хочу відфільтрувати весь шум вище 220 ударів на хвилину. Я перетворив 220 / хвилину в 3.66666666 Герц, а потім перетворив цей Герц в рад / с, щоб отримати 23.0383461 рад / с.

Частота дискретизації мікросхеми, яка приймає дані, становить 30 Гц, тому я перетворив її на рад / с, щоб отримати 188,495559 рад / с.

Після пошуку деяких речей в Інтернеті я знайшов деякі функції смугового фільтра, які я хотів перетворити на низькі частоти. Ось посилання на смуговий код , тому я перетворив його таким:

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)

Мене це дуже бентежить, бо я майже впевнений, що функція масла приймає частоту відсікання та вибірки в рад / с, але, здається, я отримую дивний результат. Це насправді в Гц?

По-друге, яка мета цих двох рядків:

    nyq = 0.5 * fs
    normalCutoff = cutOff / nyq

Я знаю, що це стосується нормалізації, але я думав, що найквіст вдвічі перевищує частоту вибірки, а не вдвічі. І чому ви використовуєте найквіст як нормалізатор?

Хтось може пояснити більше про те, як створювати фільтри з цими функціями?

Я побудував фільтр за допомогою:

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()

і отримав це, що явно не обмежує швидкість 23 рад / с:

результат

Відповіді:


162

Кілька коментарів:

  • Частота Найквіста становить половину частоти дискретизації.
  • Ви працюєте з регулярно відбираними даними, тому вам потрібен цифровий, а не аналоговий фільтр. Це означає, що ви не повинні використовувати analog=Trueу виклику butter, і ви повинні використовувати scipy.signal.freqz(не freqs) для генерації частотної характеристики.
  • Однією з ціх коротких функцій утиліти є надання можливості залишити всі свої частоти, виражені в Гц. Вам не потрібно конвертувати в рад / сек. Поки ви виражаєте свої частоти послідовними одиницями, масштабування в функціях утиліти дбає про нормалізацію для вас.

Ось моя змінена версія вашого сценарію, за якою слідує сюжет, який він генерує.

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()

приклад низьких частот


4
Так, я впевнений. Розгляньте формулювання "потрібно робити вибірку з подвійною пропускною здатністю"; це означає, що частота дискретизації повинна бути вдвічі більшою за пропускну здатність сигналу.
Warren Weckesser

3
Іншими словами: Ви берете проби з частотою 30 Гц. Це означає, що смуга пропускання вашого сигналу не повинна бути більше 15 Гц; 15 Гц - частота Найквіста.
Warren Weckesser

1
Воскрешаючи це: Я бачив в іншому потоці пропозицію використовувати filtfilt, який виконує фільтрацію назад / вперед замість lfilter. Яка ваша думка з цього приводу?
Бар

1
@Bar: Ці коментарі не є правильним місцем для вирішення вашого питання. Ви можете створити нове запитання stackoverflow, але модератори можуть закрити його, оскільки це не питання програмування . Найкраще місце для нього, мабуть, dsp.stackexchange.com
Уоррен Векессер

1
@samjewell Це випадкове число, яке, здається, я завжди використовую freqz. Мені подобається плавний графік із достатньою надмірною роздільною здатністю, щоб я міг трохи збільшити масштаб, не потребуючи регенерації сюжету, і 8000 досить великий для досягнення цього у більшості випадків.
Warren Weckesser
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.