octave滤波器设计

在octave中设计滤波器,会提示软件包属于Octave Forge。

百度Octave Forge,进入。在Files->Octave Forge Packages,选择最新Individual Package Releases。里面有大量软件包。这里的部分软件包已经包含在ubuntu源中,其中的滤波器设计在octave-signal中,可以sudo apt-get install octave-signal安装,在ubuntu14.04中,signal版本是1.2.2,比这里要旧。可以下载signal-1.3.2.tar.gz,然后在octave中用命令pkg install 安装(一般安装到home),然后load,就可以用了。使用pkg install -auto 安装则自动load,更方便。如果 sudo octave 启动,则pkg安装到/usr/local/share/octave/packages中。

软件包中实际主要是众多的.m文件组成。例如fir1.m是用来设计滤波器的。从ubuntu源安装的话,位于/usr/share/octave/packages/signal-1.2.2中,浏览fir1.m可以看到用法说明及例子,或在octave中用help fir1查看使用说明。

1, FIR 滤波器设计

## usage: b = fir1(n, w [, type] [, window] [, noscale]) ......

## Examples:
##   freqz(fir1(40,0.3));
##   freqz(fir1(15,[0.2, 0.5], 'stop'));  # note the zero-crossing at 0.1
##   freqz(fir1(15,[0.2, 0.5], 'stop', 'noscale'));

可以直观看到滤波器响应。输入参数是归一化频率。

设计好后,使用Hn=fir1(40,0.3);得到滤波器系数。

firls可以用来设计最优化线性相位FIR滤波器,滤波器几乎可以是任意响应的。一个设计多带通滤波器的例子:

freqz(firls(100,[0 .1,.15 .25,.3 .4,.45 .55,.6 1],[0 0 1 1 0 0 1 1 0 0],[20 1 20 1 20]))

设计了两个通带的带通滤波器。


2, IIR 滤波器设计

    IIR 滤波器包括了 巴特沃思、切比雪夫I, II、椭圆等等。

例如 设计切比雪夫II型低通滤波器

(1) 确定参数   [n,wc]=cheb2ord(0.0002,0.001,1,80)

n =  5
wc =  0.0010000
(2)设计滤波器 [b,a]=cheby2(5,80,0.001)

b =
   7.8431e-07  -2.3529e-06   1.5686e-06   1.5686e-06  -2.3529e-06   7.8431e-07
a =
   1.00000  -4.99721   9.98886  -9.98330   4.98887  -0.99722

(3)查看响应 freqz(b,a)。

这是一个通带很窄的低通滤波器,只有频带的万分之二。freqz缺省显示512频点,可以指定点数如freqz(b,a,32768),显示32768频点。然后放大看,幅度特性在低频有很大波动。

增加滤波器阶数n不能消除通带波动。减少滤波器阶数n并降低衰减指标来设计。

[b,a]=cheby2(4,78.5,0.001)

用freqz(b,a,32768*8)来观察幅频特性,其通带波动明显减弱

查看-6db附近曲线,滤波器精确的阻带衰减应该在78到78.5之间。

其阻带衰减曲线为

(4)稳定性

如果冲激响应h(n)绝对可和,则系统就是稳定的。使用impz来得到所设计滤波器的冲激响应

[x,t]=impz(b,a)

plot(x)

可以看出,该滤波器是稳定的。

设计得到的滤波器系数是双精度浮点值,如果转换为单精度浮点或定点,也需要重新检查幅频特性图和冲激响应图,来确定转换的可靠性。

下面使用cast函数将b,a转换为单精度数

octave:115> bc=cast(b,"single")
bc =

 Columns 1 through 3:

   1.18767202366143e-04  -4.75059438031167e-04   7.12584471330047e-04

 Columns 4 and 5:

  -4.75059438031167e-04   1.18767202366143e-04

octave:116> b
b =

 Columns 1 through 3:

   1.18767204282999e-04  -4.75059439772821e-04   7.12584471072194e-04

 Columns 4 and 5:

  -4.75059439772821e-04   1.18767204282999e-04

octave:117> ac=cast(a,"single")
ac =

 Columns 1 through 3:

   1.00000000000000e+00  -3.99856305122375e+00   5.99568986892700e+00

 Columns 4 and 5:

  -3.99569106101990e+00   9.98564064502716e-01

octave:118> a
a =

 Columns 1 through 3:

   1.00000000000000e+00  -3.99856300710054e+00   5.99569005368819e+00

 Columns 4 and 5:

  -3.99569108563957e+00   9.98564039052005e-01

然后观察幅频特性

freqz(bc,ac,32768)

可以看到,滤波器响应变得很坏。

察看冲激响应

[x,t]=impz(bc,ac)

plot(x)

冲激响应不收敛,系统不稳定了。

可见,b,a参数对系数极其敏感。


(5)滤波器转换为sos级联形式


octave:155> [sos,g]=tf2sos(b,a)
sos =

 Columns 1 through 3:

   1.00000000000000e+00  -1.99993260713065e+00   1.00000000001269e+00
   1.00000000000000e+00  -1.99998843707316e+00   9.99999999987311e-01

 Columns 4 through 6:

   1.00000000000000e+00  -1.99898365433624e+00   9.98983955750905e-01
   1.00000000000000e+00  -1.99957935276430e+00   9.99579656213214e-01

g =    1.18767204282999e-04

然后将sos转为单精度浮点数

octave:159> sosc=cast(sos,"single")
sosc =

 Columns 1 through 3:

   1.00000000000000e+00  -1.99993264675140e+00   1.00000000000000e+00
   1.00000000000000e+00  -1.99998843669891e+00   1.00000000000000e+00

 Columns 4 through 6:

   1.00000000000000e+00  -1.99898362159729e+00   9.98983979225159e-01
   1.00000000000000e+00  -1.99957931041718e+00   9.99579668045044e-01

定义输入为单位冲激序列x(n)=[1 0 0 0 0 0 ... ],使用100000点序列。

x=linspace(0,0,100000)

x(1)=1

使用sosc对x滤波来得到冲激响应

y=sosfilt(sosc,x)

乘以增益g

yg=y*g

plot(yg)

对比前面的冲激响应曲线,响应幅度有所下降。直观来看冲激响应曲线是一致的。该系统也是稳定的。

对此冲激响应做FFT变换来获得幅频特性。这里简单认为该冲激响应为有限时宽的,即认为是FIR(有限冲激响应)系统,从而使用freqz(yg)即可得到其幅频特性。如图


通带特性。可以看出,通带衰减了约3db,因此其冲激响应幅度下降。如果使用该参数,需要据此对输出幅度作补偿。


阻带特性



可见,sos参数的系数敏感度较低,实现的滤波器更稳定可靠。

另外,数据长度要大于滤波器趋于稳态响应时间,其时间从单位阶跃响应曲线可以看出。

    对于级联形式,增益g是一个很小的值,可以想象各级级联子滤波器会有很大的增益,这对于双精度浮点运算没有问题,但是使用单精度浮点或定点运算可能会比较容易溢出或者损失精度。较好的办法是把增益g分散到各级子滤波器中,使得滤波器具有下面的形式。


    为了确定Ak的值,考虑存在一频点w,使得|H(z)|=1,显然这是滤波器通带中的一点(这里不考虑该点w是否存在,仅说明其含义),则Ak取各级联函数在w处的频率响应的倒数认为是合理的,该Ak取值使得各级联函数在频率w处的响应为1,从而使得各级子滤波器的幅度响应在w附近趋于合理。这里不认为这样的取值是最优的,但认为是可以接受的,可以较好解决溢出或精度损失问题。

    这里并不打算求解这个w的值,实际上由[h,f]=freqz(b,a),观察abs(h)的值很容易知道w的位置,或者使用[h,f]=freqz(b,a,32768*8)增加频点密度来观察,结合幅频特性曲线更容易找到该点。当然得到的只是近似位置,实际上也不需要精确值。使用同样频点密度的freqz来得到各级子滤波器在该频点的响应值,其绝对值的倒数即为各级的Ak值,注意最后要使用g除以各级的Ak得到末级输出增益(或者将末级输出增益合并到最后一级的Ak中)。

    上面阐述了在工程中确定Ak的办法,并没有做严格数学证明,而且通过讨论来说明尽可能用得正确。

    滤波器在通带可能包含多个|H(z)|=1的w频点,因此得到的Ak值可能各不相同,可以根据子滤波器的具体要求来决定用哪一个。如果没有特殊要求,可以认为遇到的第一个符合|H(z)|=1的w频点即为所求。

    下面结合上述设计的IIR滤波器,给出例子说明具体方法。

(1) 求sos

上述由[sos,g]=tf2sos(b,a) 已求得sos。

(2) 取得子滤波器S1,S2

S1_b(1)=sos(1,1);
S1_b(2)=sos(1,2);
S1_b(3)=sos(1,3);
S1_a(1)=sos(1,4);
S1_a(2)=sos(1,5);
S1_a(3)=sos(1,6);
S2_b(1)=sos(2,1);
S2_b(2)=sos(2,2);
S2_b(3)=sos(2,3);
S2_a(1)=sos(2,4);
S2_a(2)=sos(2,5);
S2_a(3)=sos(2,6);
(3) 计算频率响应及取模,使用32768频点

[H,F]=freqz(b,a,32768);

[Hs1,Fs1]=freqz(S1_b,S1_a,32768);

[Hs2,Fs2]=freqz(S2_b,S2_a,32768);

Hm=abs(H);

Hms1=abs(Hs1);

Hms2=abs(Hs2);

(4) 观察Hm的值,并求出Ak(k=1,2)

octave:27> Hm
Hm =

   9.99545383224670e-01
   9.96803033882977e-01
   9.94312183059719e-01
   9.98480051931816e-01
   9.70153278642685e-01
   8.65366392734935e-01
   6.29142895139925e-01
   3.99511684648812e-01
   2.43693130492970e-01
   1.52834995284539e-01
   9.90403010191981e-02
可以看到Hm(1)最接近1,就取该频点为所求w。(不必求出具体的w值,因为Hms1(1)和Hms2(1)位于同一频点)

A1=1/Hms1(1);

A2=1/Hms2(1);

octave:30> A1
A1 =    4.47250001934901e-03
octave:31> A2
A2 =    2.62432901186802e-02

(5) 计算末级输出增益

由于所求频点w的响应不精确为1,所以A1*A2的值与g有偏差,这里使用末级增益Ae来修正此偏差。由 A1*A2*Ae=g,得出Ae=g/(A1*A2);

octave:32> Ae=g/(A1*A2)
Ae =    1.01187741087664e+00
一般地,可以将Ae合并到末级子滤波器中 令A2=A2*Ae;
这样最后得到的Ak为

A1 =    4.47250001934901e-03                  A2 =    2.65549924581747e-02

将A1、A2乘到各子滤波器的分子项,则全部的子滤波器在w频点幅度响应都接近1。

实际使用中,延迟单元还是要使用双精度来保存才行。目前还没有找到解决办法。

下面是滤波器结构和C语言算法示例,xm为延迟单元。

//
//                 -1     -2
//         b0+ b1*Z + b2*Z
//  H(z)=----------------------
//                 -1     -2
//         1 - a1*Z - a2*Z
//
//                            xm(n)  
//  x(n) ------->------O------>------O------>------O------>------ y(n)
//                     |             |  -1  b0     |
//                     ^             v Z           ^
//                     |      xm(n-1)|             |
//                     O------<------O------>------O
//                     |      a1     |  -1  b1     |
//                     ^             v Z           ^
//                     |      xm(n-2)|             |
//                     O------<------O------>------O
//                            a2            b2
//
//
//   xm(n)=    x(n)+a1*xm(n-1)+a2*xm(n-2)
//   y(n) =b0*xm(n)+b1*xm(n-1)+b2*xm(n-2)
//   xm(n-2)=xm(n-1)
//   xm(n-1)=xm(n)

    #define SOSNUM    12            // number of IIR sos
    float sos[SOSNUM];

    #define S1b0    sos[0]
    #define S1b1    sos[2]
    #define S1b2    sos[4]
    #define S1a0    sos[6]
    #define S1a1    sos[8]
    #define S1a2    sos[10]
    #define S2b0    sos[1]
    #define S2b1    sos[3]
    #define S2b2    sos[5]
    #define S2a0    sos[7]
    #define S2a1    sos[9]
    #define S2a2    sos[11]

    #define DATNUM    30000L            // number of IIR data

    short sampleX[DATNUM];

    float resultX[DATNUM];

    double S1xm[3]={0.0,0.0,0.0};
    double S2xm[3]={0.0,0.0,0.0};

    float S1y;
    float S2y;

    int n;

    for(n=0;n<DATNUM;n++)
    {
        S1xm[0]=sampleX[n]  -S1a1*S1xm[1]-S1a2*S1xm[2];
        S1y    =S1b0*S1xm[0]+S1b1*S1xm[1]+S1b2*S1xm[2];
        S1xm[2]=S1xm[1];
        S1xm[1]=S1xm[0];
        
        S2xm[0]=S1y         -S2a1*S2xm[1]-S2a2*S2xm[2];
        S2y    =S2b0*S2xm[0]+S2b1*S2xm[1]+S2b2*S2xm[2];
        S2xm[2]=S2xm[1];
        S2xm[1]=S2xm[0];

        resultX[n]=S2y;
    }
输入为sampleX,输出为resultX,数据长度DATNUM。sos及数据从octave输出的文件中读取。


3,卡尔曼滤波

网上一个卡尔曼滤波的例子:速度为1的物体的运动轨迹测量,测量结果加入方差为1的高斯噪声。

    Z=(1:100); %观测值  
    noise=randn(1,100); %方差为1的高斯噪声  
    Z=Z+noise;  
      
    X=[0; 0]; %状态  
    P=[1 0; 0 1]; %状态协方差矩阵  
    F=[1 1; 0 1]; %状态转移矩阵  
    Q=[0.0001, 0; 0 0.0001]; %状态转移协方差矩阵  
    H=[1 0]; %观测矩阵  
    R=1; %观测噪声方差  
      
    figure;  
    hold on;  
      
    for i=1:100  
      
      X_ = F*X;  
      P_ = F*P*F'+Q;  
      K = P_*H'/(H*P_*H'+R);  
      X = X_+K*(Z(i)-H*X_);  
      P = (eye(2)-K*H)*P_;  
        
      plot(X(1), X(2)); %画点,横轴表示位置,纵轴表示速度  
    end  
   

发布了26 篇原创文章 · 获赞 2 · 访问量 5万+

猜你喜欢

转载自blog.csdn.net/lang999888/article/details/49202105