如何使用 scipy 和 lfilter 进行实时过滤?
How to real-time filter with scipy and lfilter?
免责声明:我在 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
文档中的示例代码中获得了它,但我不确定是否这是完全正确的。
然后到了我不确定如何处理少数初始样本的地步。
这里的系数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()
我想我遇到了同样的问题,并在 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) * data[0]
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()
想法是在对 lfilter
的每个后续调用中传递过滤器状态 z
。对于前几个样本,过滤器可能会给出奇怪的结果,但稍后(取决于过滤器长度)它开始正常运行。
问题不在于您如何缓冲输入。问题是在 'offline' 版本中,过滤器的状态是使用 lfilter_zi
初始化的,它计算 LTI 的内部状态,以便当新样本到达时输出已经处于稳定状态输入。在 'real-time' 版本中,您跳过此步骤,以便过滤器的初始状态为 0。您可以将两个版本初始化为使用 lfilter_zi
或将两者都初始化为 0。然后,有多少并不重要您一次过滤的样本。
请注意,如果您初始化为 0,过滤器将 'ring' 在达到稳定状态之前持续一定时间。对于 FIR 滤波器,有一个解析解可以确定这个时间。对于许多 IIR 滤波器,没有。
以下是正确的。为了简单起见,我初始化为 0 并一次将输入提供给样本。但是,任何非零块大小都会产生等效的输出。
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]
免责声明:我在 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
文档中的示例代码中获得了它,但我不确定是否这是完全正确的。
然后到了我不确定如何处理少数初始样本的地步。
这里的系数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()
我想我遇到了同样的问题,并在 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) * data[0]
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()
想法是在对 lfilter
的每个后续调用中传递过滤器状态 z
。对于前几个样本,过滤器可能会给出奇怪的结果,但稍后(取决于过滤器长度)它开始正常运行。
问题不在于您如何缓冲输入。问题是在 'offline' 版本中,过滤器的状态是使用 lfilter_zi
初始化的,它计算 LTI 的内部状态,以便当新样本到达时输出已经处于稳定状态输入。在 'real-time' 版本中,您跳过此步骤,以便过滤器的初始状态为 0。您可以将两个版本初始化为使用 lfilter_zi
或将两者都初始化为 0。然后,有多少并不重要您一次过滤的样本。
请注意,如果您初始化为 0,过滤器将 'ring' 在达到稳定状态之前持续一定时间。对于 FIR 滤波器,有一个解析解可以确定这个时间。对于许多 IIR 滤波器,没有。
以下是正确的。为了简单起见,我初始化为 0 并一次将输入提供给样本。但是,任何非零块大小都会产生等效的输出。
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]