c++实现操作系统最坏适应算法_(探讨滤波器)3. 自适应滤波-LMS算法的FPGA实现

a4db24b8f6454b46b6317abb5ba83562.png

最近有网友问能不能写一下LMS滤波算法的FPGA实现,当然可以,因为去年我就已经做过LMS滤波算法的FPGA实现,只是一直没有讲。

91bb3a8ea9eeb47f3f49b86cf3a3a9fe.png
小朋友,你是否有很多问号

本文简单阐述LMS算法的原理,更细节的内容知乎上有人介绍,然后讲LMS算法的FPGA实现方法,将涉及部分现代信号处理,随机信号分析,矩阵,概率论等知识。

本文所用的FPGA代码为去年写,记忆可能有些偏差,实现方法可能也有不妥之处,可供学习使用。


其实所有的数字滤波器都逃不过延时加权求和的六字真谛,但我们之前讨论的FIR滤波器,其截止频率等参数都是事先已经定好;如果事先已知有用信号与噪声信号的频率相差较远,用这种滤波器是可以完成滤波的。

但如果噪声与有用信号频率相差不大或者噪声近似高斯白噪声呢,这个情况该如何面对?这意味着滤波器参数不能再保持固定不变,也就意味着加权系数不能是定值,而是应该根据信号的某些统计特性改变。

维纳滤波器就是这样一种自适应滤波器,根据维纳霍夫方程可以求得维纳滤波器的加权系数。但是,维纳滤波器有其本身的局限性,在求解加权系数时,需要知道期望信号与输入信号的互相关函数,输入信号的自相关函数。这导致计算过程设计到矩阵运算,并且滤波器不适合非平稳信号,非平稳信号的相关性不是仅仅跟时间有关。

为了简化计算量,LMS不通过求均方误差的数学期望最小去获得最佳权值,而是直接对均方误差求最小值获得权值,然后不断地重复这个过程,直到接近最优维纳解。

维纳的权值更新方程:

根据最陡梯度下降,对 J 求导,得到

所以,权值更新方程最终为:

u为学习率,控制权值更新的速度。

所以,LMS算法相比于前面讲过的FIR滤波器,不仅要对输入信号进行加权求和,还要求滤波器输出信号与期望信号的差值,然后根据差值更新权值信息。

LMS算法的FPGA实现结构里,我们依然使用FIR的基础模块,基础模块实现加权和权值更新,然后通过generate for调用基础模块,对所有结果求和;顶层求误差;这里对实现细节不做过多讲述,可以参考(探讨滤波器)2. 手把手用Verilog实现FIR滤波器,非IP核的实现细节,原理大致相同。

示例中使用的输入信号为带噪信号,期望信号是带噪的语音信号。

base_generator.v 基础模块实现过程,当时使用的是Altera的芯片,所以设置了如果FPGA_FAMILY为“ALTERA”,会调用Altera的乘法器IP核进行权值增量的计算和加权计算过程。

always块里面,首先保存输入信号和误差信号,然后进行加权结果输出,权值更新。

wire signed [X_W+E_W-1:0] coef_tmp  ; // xin * err
reg  signed [X_W+E_W:0]  coef_nxt  ; // wout(k+1)
reg  signed [X_W+E_W-1:0] coef   ; // wout(k)
reg  signed [X_W-1:0]  x_r    ; // Save xin value for later calculation
reg  signed [E_W-1:0]  e_r    ; // Save err value for later calculation
reg   [2:0]   state   ;

//因为调用了Altera的IP核,不使用Altera就用*代替
generate if (FPGA_FAMILY == "ALTERA")
begin
mult16 m1(
 .dataa (x_r),
 .datab (e_r),
 .result (coef_tmp)
);

mult16 m2(
 .dataa (x_r),
 .datab (wout),
 .result (yout)
);
end
else
begin
assign coef_tmp = x_r * e_r;
assign yout = x_r * wout;
end
endgenerate

always @ (posedge clk or negedge rst_n)
begin
 if (!rst_n)
 begin
  state  <= 'd0;
  coef_nxt <= 'd0;
  coef  <= 'd0;
  wout  <= 'd0;
  update  <= 'd0;
  x_r   <= 'd0;
  e_r   <= 'd0;
 end
 else 
 case (state)
 3'd0: 
 begin
  if (en)
  begin
   state <= 3'd1;
   x_r <= xin ;
   e_r <= err ;
  end
  update <= 1'b0;
 end
 3'd1:
 begin
  coef_nxt <= coef_tmp + coef;
  coef <= coef_nxt[X_W-1+E_W:0];
  wout <= coef_nxt[X_W-1+E_W-:W_W];
  state <= 3'd0;
  update <= 1'b1;
 end
 default: state <= 3'd0;
 endcase
end

lms16_order.v中间模块部分代码,首先使用generate for调用了基础模块,然后对计算输出结果进行了扩位;这里没有使用Shift-RAM代替输入信号的延迟数组,也没有在求和过程使用流水线;这两个地方其实是需要改进的。

genvar i;
generate for (i = 1; i < 16; i = i + 1) begin:order
base_generator 
#(
.FPGA_FAMILY(FPGA_FAMILY),
.X_W(X_W )   ,
.E_W(E_W )   ,
.W_W(W_W )   
)
bg_inst
(
 .clk   (clk),
 .rst_n   (rst_n),
 .en    (en),
 
 .xin   (xin_arr[i-1]),
 .err   (err),
 .yout   (yout_arr[i]),
 .wout   (wout[(((i+1)*W_W)-1)-:W_W]),
 .update   (b_update[i])
);

assign yout_exp[i] = {{4{yout_arr[i][X_W+W_W-1]}}, yout_arr[i]};
end
endgenerate

always @ (posedge clk or negedge rst_n) 
begin
 if (!rst_n)
 begin
  en  <= 1'b0;
  state <= 3'd0;
  yout_ori<= 'd0;
  update <= 1'b0;
 end
 else
 case (state)
 3'd0:
 begin
  if (en_i)
  begin
   en  <= 1'b1;
   state <= 3'd1;
  end
  update<= 1'b0; 
 end
 3'd1:
 begin
  en <= 1'b0;
  if (b_update == 16'hffff)
  begin
   yout_ori <=  yout_exp[0] + yout_exp[1] + yout_exp[2] + yout_exp[3] + yout_exp[4] + yout_exp[5] + yout_exp[6] + yout_exp[7] + 
    yout_exp[8] + yout_exp[9] + yout_exp[10] + yout_exp[11] + yout_exp[12] + yout_exp[13] + yout_exp[14] + yout_exp[15];
   state <= 3'd0;
   update<= 1'b1;
  end
 end
 default: state <= 3'd0;
 endcase
end

lms.v 顶层实现部分代码,对滤波器求和结果与期望信号求误差信号,这里,u值采取了截位填充符号位的做法,信号右移N位,同时最高位填入N位符号位,误差数据相当于除以2的N次方,具体细节看FPGA定点数截位基本准则

lms16_order 
#(
.FPGA_FAMILY(FPGA_FAMILY),
.X_W(X_W )   ,
.E_W(E_W )   ,
.W_W(W_W )   ,
.Y_W(D_W )   
)
l_inst
(
 .clk  (clk) ,
 .rst_n  (rst_n) ,
 .en_i  (en_i) ,
 .xin  (xin) ,
 .err  (err_tmp),
 .update  (lms_up),
 .xnxt  ()  , 
 .wout  (wout) ,
 .yout  (lms_yout)
);

always @ (posedge clk or negedge rst_n)
begin
 if (!rst_n)
 begin
  update  <=  1'b0;
  err   <=  'd0;
  d_r   <=  'd0;
 end
 else if (lms_up)
 begin
  update  <=  1'b1;
  err   <=  d_r - lms_yout;
  d_r   <=  din;
 end
 else
 begin
  update  <=  1'b0;
 end
end


always @ (*)
case (u)
8'd0: err_tmp <=  err;
8'd1: err_tmp <=  {err[E_W-1],err[E_W-1:1]};
8'd2: err_tmp <=  {{2{err[E_W-1]}},err[E_W-1:2]};
8'd3: err_tmp <=  {{3{err[E_W-1]}},err[E_W-1:3]};
8'd4: err_tmp <=  {{4{err[E_W-1]}},err[E_W-1:4]};
8'd5: err_tmp <=  {{5{err[E_W-1]}},err[E_W-1:5]};
8'd6: err_tmp <=  {{6{err[E_W-1]}},err[E_W-1:6]};
8'd7: err_tmp <=  {{7{err[E_W-1]}},err[E_W-1:7]};
8'd8: err_tmp <=  {{8{err[E_W-1]}},err[E_W-1:8]};
8'd9: err_tmp <=  {{9{err[E_W-1]}},err[E_W-1:9]};
8'd10: err_tmp <=  {{10{err[E_W-1]}},err[E_W-1:10]};
8'd11: err_tmp <=  {{11{err[E_W-1]}},err[E_W-1:11]};
8'd12: err_tmp <=  {{12{err[E_W-1]}},err[E_W-1:12]};
8'd13: err_tmp <=  {{13{err[E_W-1]}},err[E_W-1:13]};
8'd14: err_tmp <=  {{14{err[E_W-1]}},err[E_W-1:14]};
8'd15: err_tmp <=  {{15{err[E_W-1]}},err[E_W-1:15]};
default: err_tmp<=  err ;
endcase

最终的仿真结果图:

e0c9ce6d45e1f7e3b1e2b1af833f706c.png

第一个是加了噪声的期望信号,第二个为输出的误差信号,第三个为输入的噪声信号,第四个为原始信号(不带噪信号)。

这里没有给出滤波器输出的信号,看起来近似一堆无规律的噪声。这里滤波器的输出更通俗的说,是输出一个输入信号与期望信号高度相关的信号。

所以,并不是大家以为的必须有了干净的原始信号,我才能从实际信号里分离出原始信号,不知道看完文章,你是否对LMS有了更多的认识。

欢迎关注留言点赞收藏我,一同探讨FPGA/电子/硬件/软件。

项目开源链接:

maxs-well/LMS-sound-filtering-by-Verilog​github.com
9161f5e385f00d04e0983107fbc4c061.png

欢迎大家关注Bug记录专栏,用微信扫描下方二维码关注微信公众号"BugRec",回复111获取本文所用到的工程文件

9c69391bfa572d00a564faaf7f37e522.png

万物皆可卷积:使用VCS观察Verilog二维数组仿真值的方法​zhuanlan.zhihu.com
30735a36e7f4d38cea7531e0e9a968b8.png
万物皆可卷积:(探讨滤波器)2. 手把手用Verilog实现FIR滤波器,非IP核​zhuanlan.zhihu.com
ab61dd60333c7fade01689041edb009b.png
万物皆可卷积:(探讨滤波器)1. 从单片机,计算机实现数字滤波器学习滤波器结构​zhuanlan.zhihu.com
666fe33c1a5cd1338ce75f264f40bf6e.png
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值