在传统的数字滤波器设计中,最重要的就是滤波器的系数。不管是用matlab的fdatool工具实现,还是用别的滤波器系数生成工具产生该系数,过程都是比较麻烦的。而且生成滤波器系数以后,滤波器的幅频响应,相频响应都已经被固定,如果要修改滤波器的参数,必须要从重新对滤波器的系数进行计算和量化并生成coe文件,才可以更改滤波器。所以基于以上原因,可以设计一个自适应的数字滤波器 ,通过计算输出信号和期望信号的误差来进行对滤波器系数的更新。
下面简单介绍一下系数更新算法:
从上面的公式我们可以看出,下一时刻的滤波器系数是上一个时刻的滤波器系数加步长因子(u)与误差和输入信号的乘积。(步长因子决定了滤波器的收敛情况,通常情况下,步长因子为输入信号的自相关矩阵的特征值分之2)
自适应滤波器的matlab仿真:
下面是对于LMS函数的实现:
function [w,e,yn] = my_LMS(xn,dn)
% 输入:
% xn 输入信号
% dn 理想信号
% L 迭代次数
% k 滤波器的阶数
% 输出:
% w 滤波器的系数矩阵 大小为 k×L 每一列代表一次迭代后的系数
% e 误差信号 大小为 L×1 每一行代表一次迭代后产生的误差
% yn 滤波器的输出信号
%% 参数配置
k=128; %滤波器阶数
L=length(xn); %迭代次数=输入信号长度
%% 初始化
yn=zeros(1,L); %初始化滤波输出信号
yn(1:k)=xn(1:k); %初始化输出信号前k位数据,保证
w=zeros(1,k); %初始化权重
e=zeros(1,L); %初始化误差
%% 求收敛常数u
fe = max(eig(xn*xn.'));%求解输入xn的自相关矩阵的最大特征值fe,A = eig(B),意为将矩阵B的特征值组成向量A
u = 2*(1/fe);
%% 迭代更新滤波器的参数
for i=(k+1):L %要保证输入延时后的信号有效,所以实际的迭代次数只有(L-k)次,
XN=xn((i-k+1):(i)); %将输入信号延迟,使得滤波器的每个抽头都有输入
yn(i)=w*XN'; %计算出滤波器的输出
e(i)=dn(i)-yn(i); %得出误差信号
w=w+u*e(i)*XN; %迭代更新滤波器的系数
end
end
下面便调用上述函数:
要将所生成的正弦波和加噪后的正弦波量化,在后面的vivado中会用到该量化好的数据。
(要注意加噪后的量化信号,有的时候会出现错误(在vivado中无法读取),所以在生成的txt文件中自行修改错误的数据)
clear;
clc;
close all;
L=1024; %信号长度
a=1; %原始信号幅度
t=1:L;
dn=a*sin(0.05*pi*t);%原始正弦信号
subplot(411);plot(dn);axis([0,L,-a-1,a+1]);
%fidc= fopen('C:/Users/Lenovo/Desktop/LMS/matlab/sin_data.txt','wt'); //将正弦波信号量化
%for x = 1 : L
% fprintf(fidc,'%x\n',round((dn+2.12)*58));
%end
%fclose(fidc);
xn=awgn(dn,1); %添加信噪比5dB的白高斯噪声
subplot(412);plot(xn);axis([0,L,-a-1,a+1]);
title('信号加高斯白噪声后的时域波形');
fidc= fopen('C:/Users/Lenovo/Desktop/LMS/matlab/add_noise_data.txt','wt'); //将加噪正弦波量化
for x = 1 : L
fprintf(fidc,'%x\n',round((xn+2.12)*58));
end
fclose(fidc);
[w,e,yn] = my_LMS(xn,dn);%调用滤波算法
subplot(413);plot(yn);axis([0,L,-a-1,a+1]);
title('LMS算法自适应滤波后的输出时域波形');
subplot(414);plot(e);axis([0,L,-a-1,a+1]);
title('LMS算法自适应滤波后与原始信号误差');
matlab仿真波形:
从上面的图我们可以看出该自适应滤波器已经完成了滤波。下面开始滤波器的fpga实现:
顶层模块:
module lms_top(
input clk_i,
input rst_n_i,
input signed [15:0] data_in, //输入待滤波数据
input signed [15:0] data_ref, //参考数据
output signed [15:0] error_o, //误差
output signed [31:0] data_o //输出数据
);
wire signed [15:0] coef1;
wire signed [15:0] coef2;
wire signed [15:0] coef3;
wire signed [15:0] coef4;
wire signed [15:0] coef5;
wire signed [15:0] coef6;
wire signed [15:0] coef7;
wire signed [15:0] coef8;
wire signed [15:0] coef9;
fir fir_inst (
.clk_i (clk_i ),
.rst_n_i (rst_n_i ),
.data_in (data_in ),
.coef1 (coef1 ),
.coef2 (coef2 ),
.coef3 (coef3 ),
.coef4 (coef4 ),
.coef5 (coef5 ),
.coef6 (coef6 ),
.coef7 (coef7 ),
.coef8 (coef8 ),
.coef9 (coef9 ),
.data_o(data_o)
);
error_calcu error_calcu_inst (
.clk_i (clk_i ),
.rst_n_i (rst_n_i ),
.data_in (data_o ), //data_in data_o
.data_ref (data_ref ),
.error_o ( error_o)
);
coef_update coef_update_inst(
.clk_i (clk_i ),
.rst_n_i (rst_n_i ),
.error_o (error_o ),
.data_in (data_in ),
.coef1 (coef1 ),
.coef2 (coef2 ),
.coef3 (coef3 ),
.coef4 (coef4 ),
.coef5 (coef5 ),
.coef6 (coef6 ),
.coef7 (coef7 ),
.coef8 (coef8 ),
.coef9 (coef9 )
);
endmodule
FIR滤波器模块:
module fir(
input clk_i,
input rst_n_i,
input signed [15:0] data_in, //fir_i,
input signed [15:0] coef1, //权值
input signed [15:0] coef2,
input signed [15:0] coef3,
input signed [15:0] coef4,
input signed [15:0] coef5,
input signed [15:0] coef6,
input signed [15:0] coef7,
input signed [15:0] coef8,
input signed [15:0] coef9,
output reg [31:0] data_o //fir_o
);
reg[15:0] delay_pipeline1 ;//延时模块
reg[15:0] delay_pipeline2 ;
reg[15:0] delay_pipeline3 ;
reg[15:0] delay_pipeline4 ;
reg[15:0] delay_pipeline5 ;
reg[15:0] delay_pipeline6 ;
reg[15:0] delay_pipeline7 ;
reg[15:0] delay_pipeline8 ;
reg[15:0] delay_pipeline9 ;
/*第一级流水,将输入信号进行延时,每到来一个时钟信号,
便将输入信号保存到delay_pipelin1中,然后将剩下的依次移动一位。*/
always@(posedge clk_i or negedge rst_n_i)
if(rst_n_i == 1'b0)
begin
delay_pipeline1 <= 16'b0 ;
delay_pipeline2 <= 16'b0 ;
delay_pipeline3 <= 16'b0 ;
delay_pipeline4 <= 16'b0 ;
delay_pipeline5 <= 16'b0 ;
delay_pipeline6 <= 16'b0 ;
delay_pipeline7 <= 16'b0 ;
delay_pipeline8 <= 16'b0 ;
delay_pipeline9 <= 16'b0 ;
end
else begin
delay_pipeline1 <= data_in ;
delay_pipeline2 <= delay_pipeline1 ;
delay_pipeline3 <= delay_pipeline2 ;
delay_pipeline4 <= delay_pipeline3 ;
delay_pipeline5 <= delay_pipeline4 ;
delay_pipeline6 <= delay_pipeline5 ;
delay_pipeline7 <= delay_pipeline6 ;
delay_pipeline8 <=delay_pipeline7 ;
delay_pipeline9<= delay_pipeline8 ;
end
//乘积结果保存寄存器
reg signed [31:0] multi_data1 ;
reg signed [31:0] multi_data2 ;
reg signed [31:0] multi_data3 ;
reg signed [31:0] multi_data4 ;
reg signed [31:0] multi_data5 ;
reg signed [31:0] multi_data6 ;
reg signed [31:0] multi_data7 ;
reg signed [31:0] multi_data8 ;
reg signed [31:0] multi_data9 ;
//x(n) * h(n-k)
always@(posedge clk_i or negedge rst_n_i)
if(rst_n_i == 1'b0)
begin
multi_data1 <= 32'b0 ;
end
else begin
multi_data1 <= delay_pipeline1 * coef1 ;
end
//x(1) * h(1)
always@(posedge clk_i or negedge rst_n_i)
if(rst_n_i == 1'b0)
multi_data2 <= 32'b0 ;
else
multi_data2 <= delay_pipeline2 * coef2 ;
//x(2) * h(2)
always@(posedge clk_i or negedge rst_n_i)
if(rst_n_i == 1'b0)
multi_data3 <= 32'b0 ;
else
multi_data3 <= delay_pipeline3 * coef3 ;
//x(3) * h(3)
always@(posedge clk_i or negedge rst_n_i)
if(rst_n_i == 1'b0)
multi_data4 <= 32'b0 ;
else
multi_data4 <= delay_pipeline4 * coef4 ;
//x(4) * h(4)
always@(posedge clk_i or negedge rst_n_i)
if(rst_n_i == 1'b0)
multi_data5 <= 32'b0 ;
else
multi_data5 <= delay_pipeline5 * coef5 ;
//x(5) * h(5)
always@(posedge clk_i or negedge rst_n_i)
if(rst_n_i == 1'b0)
multi_data6 <= 32'b0 ;
else
multi_data6 <= delay_pipeline6 * coef6 ;
//x(6) * h(6)
always@(posedge clk_i or negedge rst_n_i)
if(rst_n_i == 1'b0)
multi_data7 <= 32'b0 ;
else
multi_data7 <= delay_pipeline7 * coef7;
//x(7) * h(7)
always@(posedge clk_i or negedge rst_n_i)
if(rst_n_i == 1'b0)
multi_data8 <= 32'b0 ;
else
multi_data8 <= delay_pipeline8 * coef8;
//x(8) * h(8)
always@(posedge clk_i or negedge rst_n_i)
if(rst_n_i == 1'b0)
multi_data9 <= 32'b0 ;
else
multi_data9 <= delay_pipeline9 * coef9 ;
//将乘积累加,累加的结果就是滤波后的信号
always@(posedge clk_i or negedge rst_n_i)
if(rst_n_i == 1'b0)
data_o <= 32'b0;
else
data_o <= multi_data1 + multi_data2 + multi_data3 +
multi_data4 +multi_data5 + multi_data6 + multi_data7 +
multi_data8 + multi_data9 ;
endmodule
误差计算模块:
module error_calcu(
input clk_i,
input rst_n_i,
input signed [31:0] data_in, //滤波完成数据32
input signed [15:0] data_ref, //参考数据
output signed [15:0] error_o //误差输出
);
wire signed [15:0] error;
//延时寄存参考数据
reg signed [15:0] data_shift1;
reg signed [15:0] data_shift2;
reg signed [15:0] data_shift3;
reg signed [15:0] data_shift4;
reg signed [15:0] data_shift5;
reg signed [15:0] data_shift6;
reg signed [15:0] data_shift7;
reg signed [15:0] data_shift8;
reg signed [15:0] data_shift9;
always @(posedge clk_i or negedge rst_n_i) begin
if(rst_n_i == 1'b0)
begin
data_shift1 <= 16'd0;
data_shift2 <= 16'd0;
data_shift3 <= 16'd0;
data_shift4 <= 16'd0;
data_shift5 <= 16'd0;
data_shift6 <= 16'd0;
data_shift7 <= 16'd0;
data_shift8 <= 16'd0;
data_shift9 <= 16'd0;
end
else begin
data_shift1 <= data_ref ;
data_shift2 <= data_shift1;
data_shift3 <= data_shift2;
data_shift4 <= data_shift3;
data_shift5 <= data_shift4;
data_shift6 <= data_shift5;
data_shift7 <= data_shift6;
data_shift8 <= data_shift7;
data_shift9 <= data_shift8;
end
end
assign error = data_in - data_shift9;
assign error_o = error;
endmodule
系数更新模块:
module coef_update( //系数更新模块
input clk_i,
input rst_n_i,
input signed [15:0] error_o, //误差
input signed [15:0] data_in, //待滤波数据
//权值更新
output reg [15:0] coef1,
output reg [15:0] coef2,
output reg [15:0] coef3,
output reg [15:0] coef4,
output reg [15:0] coef5,
output reg [15:0] coef6,
output reg [15:0] coef7,
output reg [15:0] coef8,
output reg [15:0] coef9
);
reg signed [15:0] mu; //遗忘因子(步长)
reg [3:0] cnt; //计数器(计算一组滤波系数)
reg [3:0] cnt_flag; //计数器标志信号
reg cnt_flag_en; //计算使能信号
//延时寄存输入数据
reg signed [15:0] data_shift1;
reg signed [15:0] data_shift2;
reg signed [15:0] data_shift3;
reg signed [15:0] data_shift4;
reg signed [15:0] data_shift5;
reg signed [15:0] data_shift6;
reg signed [15:0] data_shift7;
reg signed [15:0] data_shift8;
reg signed [15:0] data_shift9;
always @(posedge clk_i or negedge rst_n_i) begin
if(rst_n_i == 1'b0)begin
data_shift1 <= 16'd0;
data_shift2 <= 16'd0;
data_shift3 <= 16'd0;
data_shift4 <= 16'd0;
data_shift5 <= 16'd0;
data_shift6 <= 16'd0;
data_shift7 <= 16'd0;
data_shift8 <= 16'd0;
data_shift9 <= 16'd0;
mu <= 16'd3; //步长不知道是多少,随便写了一个;
end
else begin
data_shift1 <= data_in ;
data_shift2 <= data_shift1;
data_shift3 <= data_shift2;
data_shift4 <= data_shift3;
data_shift5 <= data_shift4;
data_shift6 <= data_shift5;
data_shift7 <= data_shift6;
data_shift8 <= data_shift7;
data_shift9 <= data_shift8;
end
end
//权值更新
//mu*error*data_in*2
reg signed [15:0] coef1_reg;
reg signed [15:0] coef2_reg;
reg signed [15:0] coef3_reg;
reg signed [15:0] coef4_reg;
reg signed [15:0] coef5_reg;
reg signed [15:0] coef6_reg;
reg signed [15:0] coef7_reg;
reg signed [15:0] coef8_reg;
reg signed [15:0] coef9_reg;
always @(posedge clk_i or negedge rst_n_i)
if(rst_n_i == 1'b0)
cnt <= 4'b0;
else if(cnt == 4'd13)
cnt <= 4'b0;
else
cnt <= cnt + 1'b1;
always @(posedge clk_i or negedge rst_n_i)
if(rst_n_i == 1'b0)
cnt_flag <= 1'b0;
else if(cnt == 4'd13)
cnt_flag <= 1'b1;
else
cnt_flag <= 1'b0;
always @(posedge clk_i or negedge rst_n_i)
if(rst_n_i == 1'b0)
cnt_flag_en <= 1'b0;
else if(cnt_flag == 1'b1)
cnt_flag_en <= 1'b1;
else
cnt_flag_en <= cnt_flag_en;
always@(posedge clk_i or negedge rst_n_i)
begin
if(rst_n_i == 1'b0)
begin
coef1_reg <= 16'd0;//2
coef2_reg <= 16'd0;
coef3_reg <= 16'd0;
coef4_reg <= 16'd0;
coef5_reg <= 16'd0;
coef6_reg <= 16'd0;
coef7_reg <= 16'd0;
coef8_reg <= 16'd0;
coef9_reg <= 16'd0;
end
else begin
coef1_reg <= (mu * error_o * data_shift1);
coef2_reg <= (mu * error_o * data_shift2);
coef3_reg <= (mu * error_o * data_shift3);
coef4_reg <= (mu * error_o * data_shift4);
coef5_reg <= (mu * error_o * data_shift5);
coef6_reg <= (mu * error_o * data_shift6);
coef7_reg <= (mu * error_o * data_shift7);
coef8_reg <= (mu * error_o * data_shift8);
coef9_reg <= (mu * error_o * data_shift9);
end
end
always @(posedge clk_i or negedge rst_n_i)
if(rst_n_i == 1'b0)
begin
coef1 <= 16'd0;//0
coef2 <= 16'd0;
coef3 <= 16'd0;
coef4 <= 16'd0;
coef5 <= 16'd0;
coef6 <= 16'd0;
coef7 <= 16'd0;
coef8 <= 16'd0;
coef9 <= 16'd0;
end
else if(cnt_flag_en == 1'b1)
begin
coef1 <= coef1 + coef1_reg;
coef2 <= coef2 + coef2_reg;
coef3 <= coef3 + coef3_reg;
coef4 <= coef4 + coef4_reg;
coef5 <= coef5 + coef5_reg;
coef6 <= coef6 + coef6_reg;
coef7 <= coef7 + coef7_reg;
coef8 <= coef8 + coef8_reg;
coef9 <= coef9 + coef9_reg;
end
endmodule
testbench:
module tb_lms(
);
reg clk_i;
reg rst_n_i;
reg [15:0] data_in;
reg [15:0] data_ref;
wire [15:0] error_o;
wire [31:0] data_o;
reg [15:0] mem1[1:4096]; //设计一个rom放读入的数据
reg [15:0] mem2[1:4096];
reg [12:0] i;
//例化FIR滤波器
lms_top lms_top_inst(
.clk_i(clk_i),
.rst_n_i(rst_n_i),
.data_in(data_in), //输入待滤波数据
.data_ref(data_ref), //参考数据
.error_o(error_o), //误差
.data_o(data_o) //输出数据
);
initial
begin
$readmemh("C:/Users/Lenovo/Desktop/LMS/matlab/add_noise_data.txt",mem1);//将待滤波信号读入mem
$readmemh("C:/Users/Lenovo/Desktop/LMS/matlab/sin_data.txt",mem2);
rst_n_i= 0;
clk_i= 0;
#50;rst_n_i= 1;
#50000;
$stop;
end
initial
forever
#50 clk_i = ~clk_i;//时钟生成
always@(posedge clk_i or negedge rst_n_i)
if(!rst_n_i)
data_in <= 16'b0 ;
else
data_in <= mem1[i]; //读入数据
always@(posedge clk_i or negedge rst_n_i)
if(!rst_n_i)
data_ref <= 16'b0 ;
else
data_ref <= mem2[i]; //读入数据
always@(posedge clk_i or negedge rst_n_i)
if(!rst_n_i)
i <= 12'd0;
else
i <= i + 1'd1;
endmodule
整体RTL视图:
仿真:
在这里我们可以看到data_in信号经过滤波器后,输出的data_o和期望信号data_ref的波形(已近很接近了),为什么输出不是一个完美的正弦波,其原因如下:1.步长因子。(在FPGA实现中我都步长因子设定为常数3)2.仿真时间(由于fpga的仿真时间一般都是以ns为单位的,滤波器的系数还没有迭代出一个完美系数)
(以上就是全部了,希望大佬们的指正。)