使用python实现数据滤波

针对高采集率、含噪数据,滤波可能是不可避免的处理方法。

滤波可能会导致一定程度的失真、丧失了频域信息等,但优点是关注了核心内容,合理使用会产生不错的效果。

除 MATLAB 外,Python 函数所带的 seaborn、pandas、scipy 等包均有一些基础的滤波方法。本文使用 scipy 包实现低通和带通滤波器,并对数据进行滤波。

cof_graph_test_2

滤波器

scipy.signal 系列用于数据处理,提供了所需的滤波器方法。

滤波器的设计

可以使用 scipy中的巴特沃斯滤波器等,设计低通滤波器:

scipy.signal.butter()

  • 参数1:滤波器的阶数
  • 参数2:归一化截止频率 Wn
    • 比如采样频率是 fs
    • 奈奎斯特频率 nyq = fs/2 或者 fs*0.5
    • 截止频率是 cut_off
    • 那么归一化截止频率就应该是 Wn = cut_off / nyq = 2 * cut_off / fs
    • 如果是带通,那么 Wn = [Wn1, Wn2]
  • 参数3:dtype,是滤波器的类型
    • 可选:{‘lowpass’, ‘highpass’, ‘bandpass’, ‘bandstop’}
  • 参数4:analog,布尔值
    • True,返回模拟滤波器
    • False,返回数字滤波器
  • 参数5:output,输出类型,可选
    • “ba”- IIR滤波器的分子(b)和分母(a)多项式系数向量【默认】
    • “zpk” - IIR滤波器传递函数的零点、极点和系统增益
    • “sos” - IIR滤波器的二阶截面表示

滤波器频响曲线

scipy.signal.freqs(b, a)

  • Compute frequency response of analog filter.
  • 计算 模拟滤波器 的频响曲线
  • 需要角频率 rad/s

scipy.signal.freqz(b, a)

  • Compute the frequency response of a digital filter.
  • 计算 数字滤波器 的频响曲线

例如,我们使用了传感器,获得了具有采样率的数字信号,就应该按照数字滤波器进行展示。

滤波的执行

scipy.signal.filtfilt()

  • A forward-backward filter, to obtain a filter with linear phase.
  • 一个前向-后向滤波器,以获得具有线性相位的滤波器

线性相位,filtfilt 和 lfilter 之间的区别

  • 一般会看到滤波过后,数据会出现延迟和失真
  • 但是使用 filtfilt 对信号进行滤波,可以有效消除延迟和失真,保持信号的相位信息

参考:补偿滤波器引入的延迟和失真

scipy.signal.lfilter(b, a, x)

  • Filter data along one-dimension with an IIR or FIR filter.
  • 使用 IIR 或者 FIR 滤波器沿一维数据滤波
  • x - array_like 的参数

确实是有区别的:蓝线-原始,绿线-lfilter,红线-filtfilt

image-20211014162240847

此外,如果 filtfilt 之后,结果全部为 Nan,很可能存在的问题是:

  • 数据中有 Nan 值

此时,使用 lfilter 仍然可以出结果,但是对于 filtfilt 就无法产生想要的结果了

可以使用 df.isnull() 检查数据是不是 Nan 值

滤波器示例

在项目中,我使用巴特沃斯滤波器设计了低通和带通滤波器,代码如下。

低通滤波器

1
2
3
4
5
6
7
8
9
10
11
12
def butter_lowpass(cut_off, order=5):
''' 低通滤波器的设计 '''
self.nyq = 0.5 * self.sample_rate
normal_cut_off = cut_off / self.nyq # Wn 归一化的截止频率
b, a = butter(order, normal_cut_off,
btype="low", analog=False)
return b, a
def butter_lowpass_filtfilt(cut_off_frequency, order=5):
''' 低通滤波器的执行,消除延迟 '''
b, a = self.butter_lowpass(cut_off_frequency, order=order)
y = filtfilt(b, a, self.dataframe)
return y

使用效果如本文开头图片所示。

带通滤波器

1
2
3
4
5
6
7
8
9
10
11
12
13
def butter_bandpass(cut_off_frequency_list, order=5):
''' 带通滤波器,需要两个截止频率,以 list 形式输入 '''
self.nyq = 0.5 * self.sample_rate
normal_cut_off = np.array(cut_off_frequency_list) / self.nyq
b, a = butter(order, normal_cut_off,
btype="bandpass", analog=False)
return b, a

def butter_bandpass_filtfilt(cut_off_frequency_list, order=5):
''' 带通滤波器的执行,消除延迟 '''
b, a = self.butter_bandpass(cut_off_frequency_list, order=order)
y = filtfilt(b, a, self.dataframe)
return y

可以通过时频图谱观察带通滤波后的效果:

image-20211019151758521

可以发现,使用带通滤波器确实将完整时频图中的两条亮带分离了出来。

其他滤波方法

在处理数据过程中,还有一种非常常见的方法,简单滑动平均滤波(Simple Moving Average,SMA)。相关资料表明,均值滤波也是数字低通滤波的一种。

目前发现,可以使用 pandas.rolling().mean() 等函数实现滑动平均滤波。


参考:

  1. scipy.signal.butter 官方介绍
  2. [开发技巧]·Python实现信号滤波(基于scipy)
  3. 均值滤波
  • Copyright: Copyright is owned by the author. For commercial reprints, please contact the author for authorization. For non-commercial reprints, please indicate the source.
  • Copyrights © 2020-2022 Sun Yue

请我喝杯咖啡吧~

支付宝
微信