Python中的IIR响应

时间:2014-05-14 11:27:55

标签: python filter response

我有一个与this post直接相关的新问题 - 在Python中构建我有一个具有给定特征的二阶IIR带通滤波器[以下代码是故意惯用的]:

fs = 40e6           # 40 MHz f sample frequency
fc = 1e6/fs         # 1 MHz center cutoff
BW = 20e3/fs        # 20 kHz bandwidth 
fl = (fc - BW/2)/fs # 0.99 MHz f lower cutoff
fh = (fc + BW/2)/fs # 1.01 MHz f higher cutoff

给出系数:

R  = 1 - (3*BW)
K  = (1 - 2*R*np.cos(2*np.pi*fc) + (R*R)) / (2 - 2*np.cos(2*np.pi*fc))

a0 = 1 - K                       # a0 =  0.00140
a1 = 2*(K-R)*np.cos(2*np.pi*fc)  # a1 =  0.00018 
a2 = (R*R) - K                   # a2 = -0.00158

b1 = 2*R*np.cos(2*np.pi*fc)      # b1 =  1.97241
b2 = -(R*R)                      # b2 = -0.99700

正如ukrutt in the previous post所建议的,我使用过scipy.signal.freqz,但遗憾的是没有得到我想要的响应 - 我说相信过滤器按预期工作(代码如下)。这是freqz的结果:

enter image description here

我的问题是:如何生成更像预期回复的图表?

代码:

a = [0.0014086232031758072, 0.00018050359364826498, -0.001589126796824103]
b = [1.9724136161684902, -0.9970022500000001]

w,h  = signal.freqz(a, b)
h_dB = 20 * np.log10(np.abs(h))
plt.plot(w/np.max(w),h_dB)
plt.grid()

3 个答案:

答案 0 :(得分:3)

我认为问题不在于您对响应的绘制方式 - 它是您选择的过滤器。您正尝试仅使用低阶IIR滤波器创建非常窄的滤波器响应。我认为您需要更高阶的过滤器或放宽约束。

例如,以下使用作为IIR实现的butterworth过滤器,其响应的形状与您正在寻找的形状更相似。显然,需要做更多工作才能获得预期的滤波器特性。

    b, a = signal.butter(4, [1.0/4-1.0/2e2,1.0/4+1.0/2e2], 'bandpass', analog=False)
    w, h = signal.freqs(b, a)

    import matplotlib.pyplot as plt
    fig = plt.figure()
    plt.title('Digital filter frequency response')
    ax1 = fig.add_subplot(111)
    plt.semilogy(w, np.abs(h), 'b')

    plt.ylabel('Amplitude (dB)', color='b')
    plt.xlabel('Frequency (rad/sample)')
    ax2 = ax1.twinx()

    angles = np.unwrap(np.angle(h))
    plt.plot(w, angles, 'g')
    plt.ylabel('Angle (radians)', color='g')
    plt.grid()
    plt.axis('tight')
    plt.show()

给出:

Plot of Filter response

答案 1 :(得分:1)

你不会看到任何有线性x刻度的东西。我不知道numpy但我熟悉matlab并且有一些功能可以在日志中绘制。尝试使用x-log scale:

import matplotlib.pyplot  as pyplot
fig = pyplot.figure()
ax = fig.add_subplot(2,1,1)    
line, = ax.plot(w/np.max(w), h_dB, color='blue', lw=2)
ax.set_xscale('log')

show()

我没有测试过它,我没有安装python :(

修改

我试图在matlab中为一个IIR滤波器阶数4和一个IIR滤波器阶数20的模型化一个butterworth滤波器。

%!/usr/local/bin/matlab

%% Inputs
fs = 40e6;
fc = 1e6;
BW = 20e3;
fl = (fc - BW/2);
fh = (fc + BW/2);

%% Build bandpass filter IIR Butterworth order 4
N   = 4;        % Filter Order
h  = fdesign.bandpass('N,F3dB1,F3dB2', N, fl, fh, fs);
Hd1 = design(h, 'butter');

%% Build bandpass filter IIR Butterworth order 50
N   = 20;        % Filter Order
h  = fdesign.bandpass('N,F3dB1,F3dB2', N, fl, fh, fs);
Hd2 = design(h, 'butter');

%% Compare
fvtool(Hd1,Hd2);

The two filters

A little zoom

这里是第一个滤波器的系数A和B:

FilterStructure: 'Direct-Form II Transposed'                                               
A: [2.46193004641106e-06 0 -4.92386009282212e-06 0 2.46193004641106e-06]     
B: [1 -3.94637005453608 5.88902106889851 -3.93761314372475 0.995566972065978]

如果我有时间,我会尝试用numpy做同样的事情!

答案 2 :(得分:-1)

问题是,signal.freqz会返回半圈上的点...因此,除非您通过x执行此操作,否则无法扩展到signal.freqz的更大范围。我尝试了一点点,我看到你可以使用whole=True传递给signal.freqz,你将获得上面的内容,但镜像为负x。所以那不是它。但是,还有另一个关键字参数,允许您传递一个x点数组,您希望signal.freqz计算... ...所以我尝试使用np.arange(-5., 5., 0.1) ......并且它没有全部查看就像你期望在右边的情节 - 它看起来像原始情节的一堆反射。这让我思考......也许你右边的情节和左边的情节有不同的轴?具体来说,是一个角频率,另一个只是普通的旧频率?

进一步戳戳后,signal.freqz会返回w,h,其中w是弧度/样本的归一化角频率。因此,您不需要在代码中通过np.max(w)进行规范化来制作绘图。然而,这仍然无法解决问题。右边的图表以fc为单位,fc以MHz为单位(例如1 /样本)。

所以为了让左边的情节与右边的情节相匹配,我猜这意味着你需要“取消”x轴的“非正常化”,然后你需要转换远离单位的角频率用于MHz。

或者,更可能的是,使用与signal.freqz不同的功能。