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

Python 在SciPy中创建低通滤波器-了解方法和单位,python,scipy,filtering,signal-processing,Python,Scipy,Filtering,Signal Processing,我试图用python过滤嘈杂的心率信号。因为心率不应该超过每分钟220次,所以我想过滤掉所有超过220次/分钟的噪音。我将220/分钟转换为3.6666赫兹,然后将该赫兹转换为拉德/秒,得到23.0383461拉德/秒 采集数据的芯片的采样频率是30Hz,所以我将其转换为rad/s,得到188.495559 rad/s 在网上查了一些资料后,我找到了一些我想制作成低通滤波器的带通滤波器的函数,因此我将其转换为: from scipy.signal import butter, lfilter f

我试图用python过滤嘈杂的心率信号。因为心率不应该超过每分钟220次,所以我想过滤掉所有超过220次/分钟的噪音。我将220/分钟转换为3.6666赫兹,然后将该赫兹转换为拉德/秒,得到23.0383461拉德/秒

采集数据的芯片的采样频率是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)
我对此感到非常困惑,因为我非常确定butter函数采用了rad/s的截止频率和采样频率,但我似乎得到了一个奇怪的输出。实际上是赫兹吗

第二,这两条线的目的是什么:

    nyq = 0.5 * fs
    normalCutoff = cutOff / nyq
我知道这是关于标准化的,但我认为奈奎斯特是采样频率的2倍,而不是一半。你为什么要用奈奎斯特作为标准化器

有人能解释一下如何用这些函数创建过滤器吗

我使用以下方法绘制了过滤器:

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 rad/s时切断:

一些评论:

  • 这是抽样率的一半
  • 您使用的是定期采样的数据,因此您需要的是数字滤波器,而不是模拟滤波器。这意味着您不应该在调用
    butter
    时使用
    analog=True
    ,而应该使用
    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()

是的,我肯定。考虑“你需要以两倍的带宽取样”的措辞;也就是说,采样率必须是信号带宽的两倍。换句话说,你的采样频率是30Hz。这意味着你的信号带宽不应该超过15赫兹;15赫兹是奈奎斯特频率。我在另一个例子中看到了一个建议,使用filtfilt,它执行向后/向前滤波,而不是lfilter。你对此有何看法?@Bar:这些评论不适合回答你的问题。您可以创建一个新的stackoverflow问题,但主持人可能会关闭它,因为它不是一个编程问题。最好的地方可能是@samjewell,这是一个随机数,我似乎总是使用
freqz
。我喜欢平滑的绘图,有足够多的分辨率,可以在不重新生成绘图的情况下放大一点,8000大到足以在大多数情况下实现这一点。