自适应滤波器更新算法-EP1
自适应滤波器是回声消除系统中非常重要的一个功能模块,而对于自适应滤波器来说,如果更新滤波器系数则是关键所在。本文将介绍几种现有的滤波器更新算法,并附上Matlab测试代码。
1、LMS算法
1.1算法原理
LMS算法即最小均方算法(Least Mean Square),它是由Widrow和Hoff在1960年提出的。将LMS算法应用在滤波器上的结构中,其包含两个主要的部分,第一个部分是LMS算法的部分,另一个部分是常用的滤波器结构即横向有限冲击响应结构,假设n时刻的输入信号向量表达式为
X
(
n
)
=
[
x
(
n
)
,
x
(
n
−
1
)
,
.
.
.
,
x
(
n
−
M
+
1
)
]
X(n) = [x(n),x(n-1),...,x(n-M+1)]
X(n)=[x(n),x(n−1),...,x(n−M+1)]
其中,M表示滤波器的阶数,是一个固定值,其根据实际需求而定。滤波器的权向量的表达式为
其中,i是滤波器权向量的下标,即表示 w i ( n ) w_i(n) wi(n) 为第几个滤波器权向量,其取值范围为0~M-1,如 w 0 ( n ) w_0(n) w0(n) 表示的是 x ( n ) x(n) x(n) 所对应的权向量。滤波器输出信号表达式为:
n时刻期望信号 d ( n ) d(n) d(n) 与算法实际输出信号之间的差值表示为:
其中误差信号 e ( n ) e(n) e(n) 进入LMS算法的函数模型中,通过该函数模型控制着LMS算法的步长参数,随着每一次迭代系数向量 W ( n ) W(n) W(n) 发生改变,最终 W ( n ) W(n) W(n) 趋近于其最有解,从而使实际输出信号 y ( n ) y(n) y(n) 与期望信号 d ( n ) d(n) d(n) 的差值无限接近与0。
传统LMS算法的具体步骤如下:
-
初始化参数。将自适应滤波器权系数的初始值 W ( 0 ) W(0) W(0) 设置为0,将自适应滤波器的阶数设置为M,选择合适的变化的步长值 μ \mu μ ,为了使算法收敛,步长 μ \mu μ 的取值应为:
0 < μ < 1 / λ m a x 0<\mu<1/\lambda_{max} 0<μ<1/λmax
其中 λ m a x \lambda_{max} λmax 表示输入信号 X ( n ) X(n) X(n) 的自相关矩阵的最大特征值。 -
计算滤波器的实际输出信号为 : y ( n ) = W T ( n ) X ( n ) y(n) = W^T(n) X(n) y(n)=WT(n)X(n)
-
计算自适应滤波器的误差为 : e ( n ) = d ( n ) − y ( n ) = d ( n ) − W T ( n ) X ( n ) e(n) = d(n) - y(n) = d(n) - W^T(n) X(n) e(n)=d(n)−y(n)=d(n)−WT(n)X(n)
-
更新滤波器权系数的值为: W ( n + 1 ) = W ( n ) + μ X ( n ) e ( n ) W(n+1) = W(n) + \mu X(n)e(n) W(n+1)=W(n)+μX(n)e(n)
重复上述步骤2~4,直到滤波器权系数逼近最佳滤波为止。
1.2 算法代码
function [e, y, w] = LMS(d, x, mu, M)
% Inputs:
% d - 麦克风语音
% x - 远端语音
% mu - 步长,0.05
% M - 滤波器阶数,也称为抽头数
%
% Outputs:
% e - 输出误差,近端语音估计
% y - 输出系数,远端回声估计
% w - 滤波器参数
d_length = length(d);
if (d_length <= M)
print('error: 信号长度小于滤波器阶数!');
return;
end
if (d_length ~= length(x))
print('error: 输入信号和参考信号长度不同!');
return;
end
xx = zeros(M,1);
w1 = zeros(M,1); % 滤波器权重
y = zeros(d_length,1); % 远端回声估计
e = zeros(d_length,1); % 近端语音估计
for n = M:d_length
xx = x(n:-1:n-M+1); % 纵向拼接 (40~1)-->(41~2)-->(42~3)....
y(n) = w1' * xx; % 远端回声估计 (40,1)'*(40,1)=1; (73113,1)
e(n) = d(n) - y(n); % 近端语音估计
w1 = w1 + mu * e(n) * xx; % (40,1)
w(:,n) = w1; % (40, 73113)
end
end
1.3 算法分析
LMS算法是一种随机梯度下降算法,随着迭代次数的增加,更新出的权重值无法精确收敛,但是其算法简单,易于实现,算法复杂度也较低。但是算法中的 μ \mu μ 为步长因子,其对于算法的收敛速度、时变系统的跟踪速度和稳态误差这些特征起到关键的影响作用,较小的步长因子 μ \mu μ 会获得较小的稳态误差从而提高算法的精度,同时也会获得较小的收敛速度,使算法收敛的很慢,对于时变系统来说较小的步长因子也会导致跟踪速度的减小。反之,较大的步长因子 μ \mu μ 会带来较大的收敛速度使得算法收敛的很快,同时对于时变系统来说,会提高其跟踪速度,但是会带来较大的稳态误差,降低算法的精度。因此 μ \mu μ 的选取很关键。
2、NLMS算法
2.1算法原理
归一化最小均方(Normalized Least Mean Squares,NLMS)算法是改进的变步长的LMS算法,对于较大的输入
X
(
n
)
X(n)
X(n) ,会导致梯度噪声的放大,因此需要用输入向量的平方范数进行归一化。根据原LMS算法中误差信号与远端输入信号的乘积,对远端输入信号的平方(功率)进行归一化处理,将固定步长因子的LMS算法变为根据输入信号时变的变步长LMS算法,算法具体针对
μ
\mu
μ 改进为函数
μ
(
n
)
\mu(n)
μ(n) 如下:
μ
(
n
)
=
μ
X
T
(
n
)
X
(
n
)
+
δ
\mu(n) = \frac{\mu}{X^T(n)X(n)+\delta}
μ(n)=XT(n)X(n)+δμ
其中,
μ
\mu
μ 为自适应常量,取值为0~2,
δ
\delta
δ 为一个较小的正常数,是为了防止输入信号
X
(
n
)
X(n)
X(n) 过小导致分母趋近于0进而带来的算法不稳定性。则滤波器权系数更新公式为:
W
(
n
+
1
)
=
W
(
n
)
+
μ
(
n
)
X
(
n
)
e
(
n
)
W(n+1) = W(n) + \mu(n) X(n)e(n)
W(n+1)=W(n)+μ(n)X(n)e(n)
具体的实现步骤,除了
μ
(
n
)
\mu(n)
μ(n) 的计算外,其他同LMS算法步骤相同。
2.2 算法代码
function[e,y,w] = NLMS(d,x,M)
% 输入:
% d - 麦克风语音
% x - 远端语音
% M - 滤波器阶数
%
% 输出:
% e - 近端语音估计
% y - 远端回声估计
% w - 滤波器参数
d_length = length(d);
if (d_length <= M)
print('error: 信号长度小于滤波器阶数!');
return;
end
if (d_length ~= length(x))
print('error: 输入信号和参考信号长度不同!');
return;
end
xx = zeros(M,1);
w1 = zeros(M,1); %滤波器权重
y = zeros(d_length,1); %近端语音
e = zeros(d_length,1); %误差
for n = 1:d_length
%在输入信号x前补上M-1个0,使输出y与输入具有相同长度
xx = [xx(2:M);x(n)]; %(39,1) +(1,1) = (40,1)
y(n) = w1' *xx;
mu = 1/(xx'*xx); %步长,这里简化了mu的计算,没有防止x(n)过小的情况
e(n) = d(n) - y(n); %误差
w1 = w1 + mu * e(n) * xx;
w(:,n) = w1;
end
end
1.3 算法分析
NLMS算法相对与LMS算法有着更快的收敛速度,同时保留了算法简洁、计算量小、易于实现的优点;但是NLMS算法没有考虑到滤波器阶数增长的问题。随着采样率的提升,滤波器的阶数已经达到千阶,算法复杂度也成倍增长。因此NLMS 算法仅适用于对处理精度和能力要求不是很高的一般场合。
3、PBFDAF算法
3.1 算法原理
参考文献一中对该算法的介绍:
3.2 算法代码
function [en, yk, W] = myFDAF(d,x,mu,mu_unconst, M, select)
% 参数:
% d 输入信号(麦克风语音)
% x 远端语音
% mu 约束 FDAF的步长
% mu_unconst 不受约束的 FDAF的步长
% M 滤波器阶数
% select; 选择有约束或无约束FDAF算法
x_new = zeros(M,1); % 将新块的M个样本初始化为0
x_old = zeros(M,1); % 将旧块的M个样本初始化为0
AdaptStart = 2*M; % 在获得2M个样本块后开始自适应
W = zeros(2*M,1); % 将2M个滤波器权重初始化为0
d_block = zeros(M,1); % 将期望信号的M个样本初始化为0
power_alpha = 0.5; % 常数以更新每个frequency bin的功率
power = zeros(2*M,1); % 将每个bin的平均功率初始化为0
d_length = length(d); % 输入序列的长度
en = []; % 误差向量
window_save_first_M = [ones(1,M), zeros(1,M)]'; % 设置向量以提取前M个元素 (2M,1)
for k = 1:d_length
x_new = [x_new(2:end,:); x(k)]; % 开始的输入信号块 (2M,1)
d_block = [d_block(2:end,:); d(k)]; % 开始的期望信号快 (M,1)
if mod(k,M)==0 % If iteration == block length,
x_blocks = [x_old; x_new]; % 2M样本的输入信号样本块 (2M,1)
x_old = x_new;
if k >= AdaptStart % 频域自适应滤波器
% 将参考信号转换到频域
Xk = fft(x_blocks); % (2M,1)
% FFT[old block; new block]
% Old block 包含M个先前的输入样本 (u_old)
% New 包含M个新的输入样本 (u_new)
% 计算滤波器估计信号
Yk = Xk.*W; % 输入和权重向量的乘积(2M,1)*(2M,1)=(2M,1)
temp = real(ifft(Yk)); % IFFT 输出的实部 (2M,1)
yk = temp(M+1:2*M); % 抛弃前M个元素,保留后M个元素 (M,1)
% 计算误差信号
error = d_block-yk; % 误差信号块 (M,1)
Ek = fft([zeros(1,M),error']'); % 在FFT之前插入零块以形成2M块(2M,1)
% 更新信号功率估算
power = (power_alpha.*power) + (1 - power_alpha).*(abs(Xk).^2); % (2M,1)
% 计算频域中的梯度和权重更新
if select == 1
gradient = real(ifft((1./power).*conj(Xk).* Ek)); % (2M,1)
gradient = gradient.*window_save_first_M; % 去除后一个数据块,并且补零 (2M,1)
W = W + mu.*(fft(gradient)); % 权重是频域的 (2M,1)
else
gradient = conj(Xk).* Ek; % (2M,1)
W = W + mu_unconst.*gradient; % (2M,1)
end
en = [en; error]; % 更新误差块
end
end
end
% 参考:https://github.com/CharlesThaCat/acoustic-interference-cancellation
3.3 算法分析
该算法利用FFT可快速实现卷积运算的特点,在频域进行滤波器的更新,同时使用逐块更新来代替逐点更新,从而大大降低了算法的计算复杂度。但同时其步长因子 μ \mu μ 是由自适应常量和远端信号决定的,是固定的无法根据不同复杂的场景进行变换,该算法应用在WebRTC的AEC算法中。在AEC算法中,其用了延时估计模块来对齐语音信号,并采用了语音检测等模块来去除噪声和无用语音的影响,以提高滤波器更新算法的效果。
续:本文先介绍几种比较经典的自适应滤波器算法,后续将介绍读者阅读论文总结并复现的滤波器算法!!!
参考文献:
[1] 葛世超,吕强。《实时语音处理实践指南》。
[2] Soo J S , Pang K K . Multidelay block frequency domain adaptive filter[J]. Acoustics Speech & Signal Processing IEEE Transactions on, 1990, 38(2):373-376.