filtfilt 给出意想不到的结果

信息处理 过滤器 带通 脑电图
2022-02-11 21:42:15

我正在尝试使用巴特沃斯滤波器和 filtfilt 过滤 EEG 信号。我浏览了很多文档,这两个命令似乎足以过滤。然而,结果很奇怪。

from scipy.signal import butter, filtfilt
import sys, pickle
from numpy import *
import matplotlib.pyplot as plt

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 = filtfilt(b, a, data)
    return y

with open(sys.argv[1]) as eeg:
    eeg_data = pickle.load(eeg)
    eeg_data = eeg_data[:,3]

fs = 128
lowcut = 1
highcut = 40.0

y = butter_bandpass_filter(eeg_data, lowcut, highcut, fs, order = 9)
plt.plot(y,'r')
plt.show()

在此处输入图像描述

这是脑电图数据 在此处输入图像描述

另外,不是时域的输出吗?无论域如何,它似乎都不正确。顺序太高,即前 1000 个点为 1e28。我检查了其他点,顺序是 1e17,即使是 4000 之后的点。这是我应该期待的吗?

1个回答

所以问题是你的过滤顺序太高了。这有两个问题:

  1. SciPy 有一个错误,它会在高阶生成不准确的过滤器。
  2. 在任何平台上,高阶过滤器都不能在一个阶段完成。

SciPy 错误:

SciPy 之前将原型滤波器生成为极点和零点列表,然后将它们转换为传递函数,然后将它们转换为新的频率和波段类型,然后将它们转换回极点和零点。这很糟糕,因为传递函数表示存在数值问题并且会丢失精度。所以你的过滤器目前有这些极点和零点:

Z 平面零极点图1+0j的特写

但它应该看起来像这样:

Z 平面零极点图1+0j的特写

这可能不是您的问题,因为这些极点和零点仍然大约是您要求的带通滤波器。在更高的阶数上,极点可能非常错误,它们会跳出单位圆并导致滤波器爆炸。只要使用output='zpk'. _ _

二阶部分:

当您实际进行过滤时,您必须使用传递函数,而不是极点和零点列表,因此如果您尝试在单个阶段执行整个过滤器,您的高阶过滤器将遭受相同的数值损坏。我们所做的是(从极点和零点列表开始,而不是从不准确的传递函数开始)将滤波器分解为二阶部分,每个部分都有最小的数值问题,并将这些部分级联在一起以获得最终的等效输出.

这适用于任何平台、硬件或软件,但 Matlab 和 Octave 具有sosfilt为您完成工作的功能。SciPy 不会,但将来会

现在,您可以手动拆分这些部分并lfilter重复使用每个部分以获得所需的输出或从该拉取请求中复制代码。

更新

SciPy 现在有 SOS。因此,最好的通用过滤方法是以 SOS 格式生成过滤器,例如:

sos = butter(order, [low, high], btype='band', output='sos')

然后使用过滤sosfiltfilt

y = sosfiltfilt(sos, data)