为什么较高的采样频率会弄乱我的带通滤波器?

信息处理 过滤器 Python 带通 奈奎斯特 scipy
2022-02-17 06:04:18

我正在使用一些 scipy.signal 模块在 python 中设计一个带通滤波器。

我正在绘制滤波器的频率响应,以验证我想要的频率是否在通带内。但是,当我增大采样频率时,我的滤波器的频率响应会完全混乱。

我目前有一个采样 >300KSPS 的信号,并试图为一些相当低的频率(1-100hz)创建一个带通滤波器。有人可以解释为什么会这样吗?

例如,以下代码产生此频率响应:

import numpy as np
import matplotlib.pyplot as plt
import scipy.signal

N = 3
fs = 10000.0
low = 100.0
high = 150.0

nyq = fs * 0.5

Wn = [low/nyq, high/nyq]

b, a = scipy.signal.butter(N, Wn, btype='bandpass')
w, h = scipy.signal.freqz(b, a, worN=round(fs/2))

ax = plt.subplot(121)
ax.set(title='filter frequency response',
       xlabel='frequency [hz]',
       ylabel='gain',
       xlim=(low/2, high*2))

ax.plot((nyq / np.pi) * w, abs(h), label='filter freq response')
ax.axvline(x=125.0, linestyle='--', alpha=0.5, c='black', label='f=125.0')
ax.axhline(y=np.sqrt(0.5), linestyle='--', alpha=0.5, c='black', label='sqrt(0.5)')
ax.grid()
ax.legend()

带通滤波器-1

然后,当我将采样频率更改为 100000.0 时,响应结果是这样的:

fs = 100000.0

带通滤波器-2

编辑:将滤波器输出为二阶部分并使用scipy.signal.sosfreqz产生了正确的滤波器。请参阅下面的代码以获取修改后的行:

sos = scipy.signal.butter(N, Wn, btype='bandpass', output='sos')
w, h = scipy.signal.sosfreqz(sos, worN=round(fs/2))
2个回答

正如其他人在评论中所说,这看起来像数字错误。三阶滤波器通常不易出现这种情况,但采样频率越高,极点越接近 +1:

+1附近的零极点图

您可能会受益于将其拆分为二阶部分。最简单的方法是使用output='sos'and sosfreqz()sosfilt()它会自动处理拆分。

实际上,这些二阶部分函数应该是您尝试的第一件事。 它们直到最近才存在于 scipy 中,因此它们不是默认的,为了向后兼容,但应该总是比output='ba',lfilter等更好。

对于 IIR 滤波器,采样频率与带通滤波器频率的比率越高,极点越接近 Z 变换中的复单位圆。在计算滤波器响应时,靠近单位圆的极点会产生数值不稳定性(病态计算)。这是因为如果极点和单位圆之间的位置差异很小,即使极点“假定”在内部,任何数值量化或“舍入误差”都会在算术递归中被放大。

更好的解决方案是在滤波之前先对信号进行下采样,以使用可能具有更低阶且更稳定的 IIR 滤波器。如果需要,您可以在应用过滤器后进行上采样备份。