DEBUG.WIKI
丽萨主机;为跨境业务选好 IP;原生 / 住宅 IP · 按套餐选择;查看 IP →
晚安云;香港 CN2 ¥15/月
V.PS:让项目靠近用户,亚太、欧洲、北美 KVM VPS

tf2sos 后信号不再延迟:保留数字滤波器的前导零

SciPy 1.18.1 将带纯延迟的传递函数转为 SOS 时可能丢失延迟。区分数字滤波系数与多项式表示,保留 lfilter 或显式延迟节。

把 b=[0, 0, 1]、a=[1, 0, 0] 交给 lfilter(),信号延迟两个采样点;先用 tf2sos() 转换再调用 sosfilt(),延迟却消失了。伴随的 BadCoefficients 警告不能随手忽略,因为这次转换已经改变了滤波器行为。

这一条针对 SciPy 1.18.1 的数字滤波转换路径。tf2sos 经过 tf2zpk 再配对成二阶节,分子归一化会去掉前导零;在按延迟算子表示的数字滤波器里,这些零可能恰好表示纯延迟。不能将它推广成“所有前导零都必须保留”,模拟传递函数与不同系数约定需要分别处理。

用脉冲响应判断丢了什么

对于本例,方程是 y[n]=x[n-2]。零初始状态下,脉冲应在第三个输出样本出现;若立即出现,说明时序已经变化。不要只比较频率响应的幅度:纯延迟主要改变相位,幅度相同仍可能对不上时间。

先保留原 b、a、analog 参数和采样含义。若原先的 lfilter 已符合项目需要,可以继续使用原来的系数和状态,避免进行这次表示转换。对高阶滤波器,直接型与 SOS 的数值稳定性不同,不能因此要求所有滤波器一律退回直接型。

已知只有两点延迟时,可直接写延迟节

一个 SOS 行按 [b0,b1,b2,a0,a1,a2] 排列。两点纯延迟可以明确写成:

import numpy as np
from scipy.signal import sosfilt

sos = np.array([[0.0, 0.0, 1.0, 1.0, 0.0, 0.0]])
x = np.arange(5.0)
y = sosfilt(sos, x)

这里的预期结果是 [0, 0, 0, 1, 2]。示例只表达这个已知的纯延迟,不是给任意 b,a 自动修补的转换器。一点延迟可用相应的一阶延迟节,更长延迟需要有意识地组合,不能不检查分子结构就追加任意节数。

若原滤波器还含有非延迟的动态部分,应先明确拆出的延迟长度和剩余传递函数,再验证整条级联与原定义等价。零分子、复数系数、模拟滤波器和非标准归一化,都不适合照抄上面的单节表示。

分块处理中同时迁移状态

lfilter 的 zi 与 sosfilt 的 zi 形状和意义不同。切换表示时,不能把原状态数组直接传给另一个接口;新增延迟节也需要携带其历史样本。只对每个块单独补零,会在每个块边界重新延迟并丢掉连续性。

验收至少比较脉冲位置、完整输出、相位及最后状态,再把同一信号按原块大小切开比较拼接结果。若输入可能为空,空块处理也要遵守原流式接口约定,与这里的转换丢延迟分开检查。

正式 1.18.1 仍采用上述转换链。后续补丁在讨论或开发分支中出现,不等于正式安装包已修好;恢复自动转换前,应核对具体发行版,并确认原来带前导零的样本仍保留时序。