如何用scipy和lfilter实时过滤?

时间:2016-11-08 09:41:47

标签: python scipy filtering signal-processing real-time

免责声明:我可能不如我应该使用dsp那么好,因此有更多问题让这些代码能够运行我应该拥有的东西。

我需要能够在传入信号发生时对其进行过滤。我试图使这个代码工作,但不能让我的生活得到它的工作。 引用scipy.signal.lfilter doc

import numpy as np
import scipy.signal
import matplotlib.pyplot as plt
from lib import fnlib

samples = 100
x = np.linspace(0, 7, samples)
y = [] # Unfiltered output
y_filt1 = [] # Real-time filtered

nyq = 0.5 * samples
f1_norm = 0.1 / nyq
f2_norm = 2 / nyq
b, a = scipy.signal.butter(2, [f1_norm, f2_norm], 'band', analog=False)
zi = scipy.signal.lfilter_zi(b,a)
zi = zi*(np.sin(0) + 0.1*np.sin(15*0))

最初将zi设置为zi * y [0],在本例中为0.取自lfilter doc中的示例代码。不确定这是否正确。

然后它到了我不确定如何使用初始少数样本的地步。 这里a和b系数是len(a)= 5。 由于lfilter需要从现在到n-4的输入值,我是否用零填充它,或者我是否需要等到5个样本经过,将其作为一个块进行采样,然后继续对每个下一步进行采样?

for i in range(0, len(a)-1): # Append 0 as initial values, wrong?
    y.append(0)

step = 0
for i in xrange(0, samples): #x:
    tmp = np.sin(x[i]) + 0.1*np.sin(15*x[i])
    y.append(tmp)

    # What to do with the inital filterings until len(y) ==  len(a) ?

    if (step> len(a)):
        y_filt, zi = scipy.signal.lfilter(b, a, y[-len(a):], axis=-1, zi=zi)
        y_filt1.append(y_filt[4])

print(len(y))
y = y[4:]
print(len(y))
y_filt2 = scipy.signal.lfilter(b, a, y) # Offline filtered

plt.plot(x, y, x, y_filt1, x, y_filt2)
plt.show()

2 个答案:

答案 0 :(得分:1)

我认为我遇到了同样的问题,并在https://github.com/scipy/scipy/issues/5116找到了解决方案:

from scipy import zeros, signal, random

def filter_sbs():
    data = random.random(2000)
    b = signal.firwin(150, 0.004)
    z = signal.lfilter_zi(b, 1)
    result = zeros(data.size)
    for i, x in enumerate(data):
        result[i], z = signal.lfilter(b, 1, [x], zi=z)
    return result

if __name__ == '__main__':
    result = filter_sbs()

我们的想法是在每个后​​续调用中使用过滤状态z。在前几个样本中,过滤器可能会产生奇怪的结果,但过了一段时间(取决于过滤器长度),它应该表现正常。

答案 1 :(得分:0)

我不相信上面的答案是正确的。初始化过滤器状态 z = signal.lfilter_zi(b,1) 正是导致前几个样本“奇怪”的原因,如果我正确理解 OP,这就是问题所在。正如我在评论中所说的那样,期望的结果是逐个样本的实现应该完全等同于一次过滤整个数据集(数值精度除外)。这可以通过将状态初始化为 0 (z=zeros(b.size-1)) 来完成。这是正确的:

from scipy import signal, random
from numpy import zeros

def filter_sbs(data, b):
    z = zeros(b.size-1)
    result = zeros(data.size)
    for i, x in enumerate(data):
        result[i], z = signal.lfilter(b, 1, [x], zi=z)
    return result
    
def filter(data, b):
    result = signal.lfilter(b,1,data)
    return result

if __name__ == '__main__':
    data = random.random(20000)
    b = signal.firwin(150, 0.004)
    result1 = filter_sbs(data, b)
    result2 = filter(data, b)
    print(result1 - result2)

输出:

[ 0.00000000e+00  0.00000000e+00  0.00000000e+00 ... -5.55111512e-17
  0.00000000e+00  1.66533454e-16]

我承认我不太确定 z = signal.lfilter_zi(b,1) 在做什么(看起来像半余弦),但初始状态应该为零。