在SciPy中创建低通滤波器 - 了解方法和单位

use*_*955 64 python filtering signal-processing scipy

我试图用python过滤嘈杂的心率信号.因为心率不应该是每分钟约220次心跳,我想过滤掉220bpm以上的所有噪音.我将220 /分钟转换为3.66666666赫兹,然后将赫兹转换为rad/s得到23.0383461 rad/sec.

采集数据的芯片采样频率为30Hz,因此我将其转换为rad/s,得到188.495559 rad/s.

在线查找了一些东西之后,我发现了一些带通滤波器的功能,我想把它变成低通.这是带通代码的链接,所以我将其转换为:

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)
Run Code Online (Sandbox Code Playgroud)

我很困惑,因为我很确定黄油功能采用rad/s的截止和采样频率,但我似乎得到一个奇怪的输出.它实际上是赫兹吗?

其次这两条线的目的是什么:

    nyq = 0.5 * fs
    normalCutoff = cutOff / nyq
Run Code Online (Sandbox Code Playgroud)

我知道它的正常化,但我认为奈奎斯特是采样频率的2倍,而不是一半.为什么你使用nyquist作为规范化器?

可以解释一下如何使用这些函数创建过滤器吗?

我使用绘制了滤镜

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()
Run Code Online (Sandbox Code Playgroud)

并得到这显然不会在23 rad/s切断:

结果

War*_*ser 131

一些评论:

  • 奈奎斯特频率是采样率的一半.
  • 您正在使用定期采样数据,因此您需要数字滤波器,而不是模拟滤波器.这意味着您不应该analog=True在调用中使用butter,并且您应该使用scipy.signal.freqz(而不是freqs)生成频率响应.
  • 这些短效用函数的一个目标是允许您将所有频率保留为Hz.您不必转换为rad/sec.只要您使用一致的单位表示频率,实用程序功能中的缩放就会为您进行标准化处理.

这是我的脚本的修改版本,后面是它生成的图表.

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()
Run Code Online (Sandbox Code Playgroud)

低通的例子

  • 是,我确定.考虑一下"你需要以两倍的带宽进行采样"的措辞; 这表示*采样率*必须是信号带宽的两倍. (4认同)
  • 换句话说:您正以30 Hz的频率进行采样.这意味着信号的带宽不应超过15 Hz; 15 Hz是奈奎斯特频率. (3认同)
  • 复活这个:我在另一个[线程](http://stackoverflow.com/a/13740532/209882)中看到建议使用filtfilt来执行向后/向前过滤而不是lfilter。您对此有何看法? (2认同)