
在数字信号处理中,我们经常需要对数据进行滤波。scipy库提供了强大的工具,其中scipy.signal.lfilter是实现iir和fir滤波器的核心函数。当数据量较小或可以一次性获得时,lfilter可以直接应用于整个数据集。然而,在实时数据流处理的场景下,数据是逐点到达的,这就要求滤波器能够以迭代方式工作,即每次处理一个数据点并维护其内部状态。
考虑一个典型的贝塞尔低通滤波器应用。首先,我们定义滤波器参数并计算其系数b和a:
import scipy.signal import numpy as np # 滤波器参数 fc_bessel = 0.14 # 截止频率 [归一化频率,或Hz,取决于fs] ordre_bessel = 3 # 滤波器阶数 fs = 300 # 采样频率 [Hz] # 生成滤波器系数 b, a = scipy.signal.bessel(ordre_bessel, fc_bessel, 'low', analog=False, output='ba', fs=fs) # 示例输入数据 # 为了演示,我们创建一个简单的输入信号,例如一个从0开始的阶跃信号 input_data = np.concatenate((np.zeros(10), np.ones(90))) # 100个数据点,前10个为0,后90个为1
一次性滤波版本: 对于整个数据集,我们可以直接调用lfilter:
filter_once = scipy.signal.lfilter(b, a, input_data)
迭代滤波版本: 为了模拟实时处理,我们需要逐点处理数据,并维护滤波器的内部状态zi。lfilter函数接受一个可选参数zi,用于指定滤波器的初始状态,并返回更新后的状态。一个常见的误区是使用scipy.signal.lfilter_zi来初始化zi:
# 错误的初始化方式: # z_initial_wrong = scipy.signal.lfilter_zi(b, a) filter_iter_wrong = [] # for input_value in input_data: # # filtered_value, z_initial_wrong = scipy.signal.lfilter(b, a, [input_value], zi=z_initial_wrong) # # filter_iter_wrong.append(filtered_value[0])
当使用lfilter_zi初始化zi并进行迭代滤波时,我们会发现其输出与一次性滤波的结果存在显著差异,尤其是在信号的起始阶段。例如,如果input_data的第一个值为0,filter_once[0]通常为0,而filter_iter_wrong[0]可能会是一个非零值(如0.999...)。
这种差异的根源在于lfilter_zi函数的设计目的与我们通常期望的“初始静止”条件不符。
scipy.signal.lfilter_zi(b, a):此函数旨在构造用于lfilter的初始条件,以实现“阶跃响应稳态”。这意味着它假设在滤波开始之前,输入信号已经经历了一个大的阶跃变化,并且滤波器已经达到了稳态。因此,它会计算一个非零的初始状态,使得滤波器在接收到第一个输入时就处于一个“活跃”的状态。这对于分析滤波器的阶跃响应或在特定稳态条件下启动滤波非常有用,但对于从“静止”状态开始处理新数据则不适用。
scipy.signal.lfilter(b, a, x, zi=None):当zi参数为None或未提供时,lfilter默认假设“初始静止”(initial rest)条件。这意味着滤波器在处理第一个数据点之前,其所有内部存储单元(延迟单元)都被初始化为零。这是我们通常在从头开始滤波时所期望的行为。
要使迭代滤波与一次性滤波的结果保持一致,我们需要确保迭代滤波器的初始状态也符合“初始静止”的假设。有两种主要的方法可以实现这一点:
使用scipy.signal.lfiltic:lfiltic(b, a, y0, x0)函数可以根据指定的初始输出y0和初始输入x0来构造滤波器的初始状态。对于“初始静止”条件,我们可以简单地将y0和x0都设为零:
z_initial_correct = scipy.signal.lfiltic(b, a, 0)
这里,0表示在滤波开始前,所有历史输入和输出均为零。
直接使用零向量初始化: 对于大多数常见的数字滤波器(特别是那些由scipy.signal函数如bessel、butter等生成的滤波器),当滤波器处于“初始静止”状态时,其内部状态向量zi(表示延迟单元的值)就是全零向量。lfiltic(b, a, 0)实际上会返回一个全零的zi向量。因此,我们可以更简洁地直接创建一个全零向量作为初始状态:
# 滤波器的状态向量长度通常是max(len(b), len(a)) - 1 # 对于bessel函数,len(a) = ordre_bessel + 1 # 所以状态向量长度为 ordre_bessel z_initial_correct = np.zeros(ordre_bessel)
或者更通用的方式,根据系数长度确定:
z_initial_correct = np.zeros(max(len(b), len(a)) - 1)
将上述正确初始化的zi应用于迭代滤波,即可获得与一次性滤波完全一致的结果:
# 正确的初始化方式:
z_correct = np.zeros(ordre_bessel) # 或 scipy.signal.lfiltic(b, a, 0)
filter_iter_correct = []
current_z = z_correct # 使用正确的初始状态
for input_value in input_data:
    # lfilter处理单个数据点时,输入需要是列表或数组形式,如 [input_value]
    filtered_value, current_z = scipy.signal.lfilter(b, a, [input_value], zi=current_z)
    filter_iter_correct.append(filtered_value[0])
# 验证结果一致性
# print("一次性滤波结果前几项:", filter_once[:5])
# print("迭代滤波结果前几项:", np.array(filter_iter_correct)[:5])
# print("两者是否近似相等:", np.allclose(filter_once, filter_iter_correct))通过理解并正确设置lfilter的初始状态zi,我们可以确保在实时或迭代数据处理场景下,滤波器的行为与一次性处理整个数据集时保持一致,从而避免不必要的误差和混淆。
以上就是理解Scipy lfilter的迭代滤波与初始状态设置的详细内容,更多请关注php中文网其它相关文章!
                        
                        每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
                
                                
                                
                                
                                
                                
                                Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号