虽然在函数名和使用方法上极为相似,但如果仔细观察,二者的滤波结果可能存在一定的相位差异。
例子
我们以一个简单的序列为例,使用MATLAB中巴特沃斯滤波器带通滤波之后,绘制结果。以下是MATLAB版本:
clc,clear
dat = 1:1000;
dt = 0.01;
f0 = 1;
f1 = 20;
fs=1/dt/2;
[b,a]=butter(2,[f0,f1]/fs);
dat1=filtfilt(b,a,dat);
subplot(311)
plot((0:999)*fs, dat);
subplot(312)
plot((0:999)*fs, dat1);
subplot(313)
plot((0:999)*fs/1000, abs(fft(dat1)));
以及 Python 版本,使用 scipy.signal 实现:
import numpy as np
from scipy import signal
import matplotlib.pyplot as plt
import scipy
dat = np.arange(1,1001,1)
f0 = 1
f1 = 20
dt = 0.01
fs = 1/dt/2
b, a = signal.butter(2, [f0/fs,f1/fs], 'bandpass')
dat1 = signal.filtfilt(b, a, dat,padtype = 'odd')
plt.figure(figsize=[10,10])
plt.subplot(311)
plt.plot(np.arange(1000)*fs, dat)
plt.subplot(312)
plt.plot(np.arange(1000)*fs, dat1, label="Python")
plt.subplot(313)
plt.plot(np.arange(1000)*fs/1000, np.abs(scipy.fft(dat1)))
如果将MATLAB的滤波结果 dat1
拷贝至Python脚本中,并赋值给 aM
,可以计算二者结果的互相关:
plt.figure(figsize=[10,10])
cc = signal.correlate(dat1, aM)
plt.plot(cc, label="Cross-correlate")
二者的互相关显示,二者存在三个 $dt$ ,即 $0.03$ 的延迟/相位差。
原因与解决
在进行带通滤波时,需要在边缘进行填充(padding)为信号边界进行滤波。在MATLAB中默认padding的长度为3*(max(len(a), len(b)) - 1)
,而scipy.signal.filtfilt
中默认为 3*max(len(a), len(b))
。
可以通过在Python通过添加padlen
参数,指定padding长度。从而得到和MATLAB中一样的计算结果:
b, a = signal.butter(2, [f0/fs,f1/fs], 'bandpass')
dat1 = signal.filtfilt(b, a, dat,padlen=3*(max(len(b),len(a))-1))
MATLAB 中则无法进行指定(截止R2021a版本)。
Reference
致谢
本文感谢 ZhhSong 老哥的锲而不舍。 感谢Paco Wong 在参考链接中的回答