如何在Python中为采样信号编写低通滤波器?


16

我有一些信号每1 ns(1e-9 sec)采样一次,并且有1e4点。我需要从该信号中滤除高频。假设我需要过滤高于10 MHz的频率。我希望对于低于截止频率的频率,信号将保持不变。这意味着对于低于截止频率的频率,滤波器的增益将为1。我希望能够指定过滤器顺序。我的意思是,一阶滤波器在截止频率后具有20 db / dec的斜率(功率下降),二阶滤波器在截止频率后具有40 db / dec的斜率,依此类推。代码的高性能很重要。

Answers:


19

使用黄油 功能设计的滤波器的频率响应为:

巴特沃思滤波器响应

但是没有理由将滤波器限制为恒定的单调滤波器设计。如果您希望阻带更高的衰减和更陡峭的过渡带,则可以使用其他选项。有关使用iirdesing指定过滤器的更多信息,请参见this。如黄油设计的频率响应图所示,截止频率(-3dB点)离目标很远。可以通过在过滤之前进行下采样来缓解这种情况(使用如此狭窄的过滤器(带宽的2%),设计功能将面临一段艰难的时期)。让我们看一下用指定的截止频率过滤原始采样率。

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

from scipy.signal import fir_filter_design as ffd
from scipy.signal import filter_design as ifd

# setup some of the required parameters
Fs = 1e9           # sample-rate defined in the question, down-sampled

# remez (fir) design arguements
Fpass = 10e6       # passband edge
Fstop = 11.1e6     # stopband edge, transition band 100kHz
Wp = Fpass/(Fs)    # pass normalized frequency
Ws = Fstop/(Fs)    # stop normalized frequency

# iirdesign agruements
Wip = (Fpass)/(Fs/2)
Wis = (Fstop+1e6)/(Fs/2)
Rp = 1             # passband ripple
As = 42            # stopband attenuation

# Create a FIR filter, the remez function takes a list of 
# "bands" and the amplitude for each band.
taps = 4096
br = ffd.remez(taps, [0, Wp, Ws, .5], [1,0], maxiter=10000) 

# The iirdesign takes passband, stopband, passband ripple, 
# and stop attenuation.
bc, ac = ifd.iirdesign(Wip, Wis, Rp, As, ftype='ellip')  
bb, ab = ifd.iirdesign(Wip, Wis, Rp, As, ftype='cheby2') 

原始采样率过滤器

如前所述,由于我们试图过滤很小百分比的带宽,因此过滤器将不会出现急剧的截止。在这种情况下,通过低通滤波器,我们可以减少带宽以获得外观更好的滤波器。python / scipy.signal重采样功能可用于减少带宽。

请注意,重采样功能将执行过滤以防止混叠。也可以进行预过滤(以减少混叠),在这种情况下,我们可以简单地按100进行重采样并完成操作,但问题是创建过滤器。在此示例中,我们将采样降低25并创建一个新的过滤器

R = 25;            # how much to down sample by
Fsr = Fs/25.       # down-sampled sample rate
xs = signal.resample(x, len(x)/25.)

如果我们更新FIR滤波器的设计参数,则新的响应是。

# Down sampled version, create new filter and plot spectrum
R = 25.             # how much to down sample by
Fsr = Fs/R          # down-sampled sample rate
Fstop = 11.1e6      # modified stopband
Wp = Fpass/(Fsr)    # pass normalized frequency
Ws = Fstop/(Fsr)    # stop normalized frequency
taps = 256
br = ffd.remez(taps, [0, Wp, Ws, .5], [1,0], maxiter=10000) 

下采样滤波器响应

对降采样数据进行操作的滤波器具有更好的响应。使用FIR滤波器的另一个好处是您将具有线性相位响应。


1
谢谢。如何创建信号频谱图?
Alex

非常感谢您的出色回答!我想知道您是否可以解释如何基于使用Remez计算的系数来应用FIR滤波器?我无法理解filtfilta参数的要求。
ali_m

一旦你有来自过滤器的设计,(系数b为FIR b一个用于IIR)可以用两个不同的功能,以执行滤波:lfilter卷积filtfilt。通常,所有这些函数的工作方式相似:y = filtfilt(b,a,x)如果您具有FIR滤波器,则只需将a = 1设置x,则x为输入信号,b为FIR系数。这篇 文章可能也有帮助。
克里斯托弗·费尔顿,

5

这样行吗?

from __future__ import division
from scipy.signal import butter, lfilter

fs = 1E9 # 1 ns -> 1 GHz
cutoff = 10E6 # 10 MHz
B, A = butter(1, cutoff / (fs / 2), btype='low') # 1st order Butterworth low-pass
filtered_signal = lfilter(B, A, signal, axis=0)

您说得对,但是文档不是很完整。看起来像是butter的包装器iirfilter,对此有更好的记录

N:int过滤器的顺序。Wn:array_like一个给出临界频率的标量或长度为2的序列。

不过,其中大部分内容都是从matlab克隆的,因此您也可以查看其文档

归一化截止频率Wn必须为0到1之间的数字,其中1对应于奈奎斯特频率,每个样本π弧度。

更新:

我添加了这些功能的文档。:) Github使它变得容易。


1

不确定您的应用程序是什么,但是您可能想查看Gnuradio:http ://gnuradio.org/doc/doxygen/classgr__firdes.html

信号处理模块是用C ++编写的(尽管Gnuradio流程图是用Python编写的),但您确实说过高性能很重要。


1

这个FIR滤波器的效果很好。请注意,它两次应用了滤波器,分别进行“正向”和“反向”,以补偿信号偏移(filtfilt功能不起作用,不知道为什么):

def firfilt(interval, freq, sampling_rate):
    nfreq = freq/(0.5*sampling_rate)
    taps =  sampling_rate + 1
    a = 1
    b = scipy.signal.firwin(taps, cutoff=nfreq)
    firstpass = scipy.signal.lfilter(b, a, interval)
    secondpass = scipy.signal.lfilter(b, a, firstpass[::-1])[::-1]
    return secondpass

THIS是我进行此代码的地方以及可以通过带通和高通滤波器示例的过滤器设计和使用的绝佳资源。


我认为对FIR滤波器进行正向和反向滤波没有太大好处。IIR滤波器可以从正向/反向(filtfilt)中受益,因为您可以通过反向滤波从非线性相位滤波器中获得线性相位。
克里斯托弗·费尔顿,2012年

2
@ChristopherFelton我为了使RAW肌电信号与平滑后的信号同步而反转。我知道我可以移动信号,但是两次滤波可以减少麻烦。值得注意的是,第二遍几乎不会更改已过滤的第一遍...谢谢!
heltonbiker

嗯是的 要消除延迟(群延迟),要点。
克里斯托弗·费尔顿

1

我没有评论权...

@endolith:除了使用scipy.signal.filtfilt(B,A,x)之外,我使用的其他方法与之相同其中x是要过滤的输入向量-例如numpy.random.normal(size =(N))filtfilt对信号进行正向和反向传递。为了完整起见(大多数与@endolith相同):

import numpy as np
import scipy.signal as sps

input = np.random.normal(size=(N)) # Random signal as example
bz, az = sps.butter(FiltOrder, Bandwidth/(SamplingFreq/2)) # Gives you lowpass Butterworth as default
output = sps.filtfilt(bz, az, input) # Makes forward/reverse filtering (linear phase filter)

@heltonbiker也建议使用filtfilt,它需要系数数组。如果您需要在复杂的基带上执行带通滤波,则需要更复杂的配置,但这在这里似乎不是问题。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.