学习梳理一下用于AEC的各种LMS频域实现方法以及基础知识-未完待续
- 起源
- 换个维度,到频域
- 线性卷积&循环卷积
- 频域FIR的循环卷积等价于时域补0后的线性卷积
- 重叠保留&重叠相加---长数据序列滤波的考量
- 重叠保留
- 重叠相加
- block分块自适应探讨
- 参考
起源
least-mean-square方法是时域的,参考ANC 与 adaptive filter推导,能够得出一个迭代更新公式:
W
k
+
1
=
W
k
+
2
μ
e
(
k
)
X
(
k
)
\bold W_{k+1}=\bold W_k +2 \mu e(k)X(k)
Wk+1=Wk+2μe(k)X(k)
回声消除最终关注的是
e
(
n
)
=
d
(
n
)
−
y
(
n
)
=
d
(
n
)
−
W
T
X
(
n
)
=
d
(
n
)
−
X
T
(
n
)
W
e(n)=d(n)-y(n)=d(n)-\bold W^T\bold X(n)=d(n)-\bold X^T(n)\bold W
e(n)=d(n)−y(n)=d(n)−WTX(n)=d(n)−XT(n)W
而
y
(
n
)
=
W
T
X
(
n
)
=
X
T
(
n
)
W
y(n)=\bold W^T\bold X(n)=\bold X^T(n)\bold W
y(n)=WTX(n)=XT(n)W是这个滤波器的输出,抛开数学推导,这个实现范式其实简洁明了。但由于时域跟踪,每个采样点更新一次的计算量是非常巨大的,所以在上个世纪七八十年代涌现了频域lms实现的方法,目的是为了降低计算复杂度,从此,旧时王谢堂前燕,飞入寻常百姓家。人们开始慢慢的享受了无回声通话的便宜,这种情况直到AI的出现和普及。
换个维度,到频域
似乎时频变换是现代信号处理绕不过去的一个梗,我能追溯的文章是这篇Adaptive filtering in the frequency domain,这篇文章本身比较精简,唯一引申了The complex LMS algorithm,很显然后者是从数学推导的方式给了一个复数域更新lms参数以及得出误差信号的结果,并没有谈到时频变换的问题,而前者想到了如果把时域信号分块的变换到频域,采用每块更新参数的方法,将会大大减少计算量,按照文中的计算方法,1024点FFT的块更新方法,能节省的计算量非常的可观。但此文仍专注于频域自适应的拓展,即没有考虑线性、圆周卷积这个DFT之后绕不过去的问题,也没有给出滤波器长度和分块的关系,如【论文笔记之 AFiFD】Adaptive Filtering in the Frequency Domain一文的总结,“论文为实现 LMS 自适应滤波器提供了一个新的思路”,应该属于开创性的著作。90年提出的并被speex实现的Multidelay Block Frequency Domain Adaptive Filter引用了这一时期的Block implementation of adaptive digital filters和Fast implementations of LMS adaptive filters两篇论文,这两篇又大概是20世纪80年代初期的论述。两年后,92年一篇On the implementation of a partitioned block frequency domain adaptive filter (PBFDAF) for long acoustic echo cancellation引用了它,而这篇文章为webrtc第一代AEC的PBFDAF实现提供的算法基础。在继续学习之前,有必要恶补一下相关的基础知识。
线性卷积&循环卷积
这可能是要先理清的一个细节,虽然大学学过dft,工作中不断用fft来做事情,但很多特性和数学关系在这里需要重点的关注起来,线性和循环卷积来自matlab网站的介绍,让线性卷积和循环卷积变得等效,而更加简明扼要的论述可以参考Lecture 23: Circular Convolution,当然经典的数字信号处理教科书都会讲述这部分内容,但有时不用到可能就忽略了。
频域FIR的循环卷积等价于时域补0后的线性卷积
线性和循环卷积是为了解决DFT的问题而提出来的,的FIR滤波器搬到频域实现属于基本的数字信号处理问题,这个问题首先要解决的是圆周卷积产生的混叠,最好的办法是找到与线性卷积等价的策略,这个策略就是补0。假设一个长为
L
L
L的有限长序列
x
(
n
)
x(n)
x(n),经过一个长度
M
M
M冲激响应为
h
(
n
)
h(n)
h(n)的线性系统
(
F
I
R
)
(FIR)
(FIR),输出
y
(
n
)
y(n)
y(n)的序列,表示如下:
y
(
n
)
=
∑
k
=
0
M
−
1
h
(
k
)
x
(
n
−
k
)
,
x
(
n
)
=
0
,
n
<
0
o
r
n
≥
L
;
h
(
n
)
=
0
,
n
<
0
o
r
n
≥
M
y(n) = \sum_{k=0}^{M-1} h(k)x(n-k), \quad x(n)=0,n<0 \ or\ n\geq L;\quad h(n)=0,n<0 \ or\ n\geq M
y(n)=k=0∑M−1h(k)x(n−k),x(n)=0,n<0 or n≥L;h(n)=0,n<0 or n≥M那么
y
(
n
)
y(n)
y(n)的长度是
L
+
M
−
1
L+M-1
L+M−1。时域卷积等价于频域相乘(这句话背了很多遍,推导嘛。。。)我们可得:
Y
(
ω
)
=
X
(
ω
)
H
(
ω
)
Y(\omega)= X(\omega)H(\omega)
Y(ω)=X(ω)H(ω)但凡事都要细想一下,三个序列的长度不同,用
D
F
T
DFT
DFT变换得用最大值那个,即
N
≥
L
+
M
−
1
N\ge L+M-1
N≥L+M−1,此时针对于
L
L
L的有限长序列
x
(
n
)
x(n)
x(n)和长度
M
M
M冲激响应为
h
(
n
)
h(n)
h(n),后面显然需要补0,否则数据就不对了。数字信号处理――原理、算法与应用(第四版),JohnG.Proakis,DimitrisG.Manolakis著7章都实例。其中第二个例子讲述了混叠是怎么发生对的,并给出了一个结论,前
M
−
1
M-1
M−1个结果发生混叠污染,而正是利用这一特性,引申出了
长数据分块处理
\bold{长数据分块处理}
长数据分块处理的两个重要方法。
重叠保留&重叠相加—长数据序列滤波的考量
这里需要强调的时候,对应于时频分析类的算法,此类范式是不能够加窗的。Lecture 16 Linear Filtering with the DFT里有简单的介绍John G. Proakis 数字信号处理的离散傅里叶变换和应用章节有更加详细的论述。这里按照John G. Proakis 数字信号处理讲述的来记录分解一下两种方法的差别。
重叠保留
假设一个长长序列
x
(
n
)
x(n)
x(n),经过一个长度
M
M
M冲激响应为
h
(
n
)
h(n)
h(n)的线性系统
(
F
I
R
)
(FIR)
(FIR),输出
y
(
n
)
y(n)
y(n)的序列,为了进行分块分析,把序列分为长度为
L
L
L的数据块,一般的
L
>
>
M
L>>M
L>>M,那么
N
=
L
+
M
−
1
N = L+M-1
N=L+M−1作为FFT长度,对应第m个数据块,冲激响应补
L
−
1
L-1
L−1个0来凑齐,但数据块,则在前面补充上一个数据块的最后
M
−
1
M-1
M−1个点,完成拼接。将两个N点的DFT变换
H
(
k
)
{H(k)}
H(k)和
X
m
(
k
)
{X_m(k)}
Xm(k)相乘,得出:
Y
^
m
(
k
)
=
H
(
k
)
X
m
(
k
)
;
k
=
0
,
1
,
.
.
.
.
.
,
N
−
1
\hat{Y}_m(k) = H(k)X_m(k);\quad k = 0,1,.....,N-1
Y^m(k)=H(k)Xm(k);k=0,1,.....,N−1于是,我们通过逆变换,得出输出序列:
y
^
m
(
n
)
=
{
y
^
m
(
0
)
y
^
m
(
1
)
y
^
m
(
2
)
y
^
m
(
3
)
.
.
.
y
^
m
(
M
−
1
)
y
^
m
(
M
)
.
.
.
y
^
m
(
N
−
1
)
}
\hat{y}_m(n) = \{\hat{y}_m(0)\ \hat{y}_m(1)\ \hat{y}_m(2)\ \hat{y}_m(3)\ ... \hat{y}_m(M-1)\ \hat{y}_m(M)\ ... \hat{y}_m(N-1)\ \}
y^m(n)={y^m(0) y^m(1) y^m(2) y^m(3) ...y^m(M−1) y^m(M) ...y^m(N−1) }按照拼接原则,实际有效数据的前
M
−
1
M-1
M−1个数据被混叠污染了,那么剩下的
L
L
L个数据则被完美还原。即
y
m
(
n
−
M
)
=
y
^
m
(
n
)
,
n
=
M
,
M
+
1
,
.
.
.
,
N
−
1
{y}_m(n-M) = \hat{y}_m(n) \quad , n = M, M+1, ..., N-1
ym(n−M)=y^m(n),n=M,M+1,...,N−1而为了满足这种拼接方法,每一块处理前的后M-1个点被保留下来,留给下一次用,第一块前面直接补0,得到这样一个送给DFT的数据:
x
1
(
n
)
=
{
0
,
0
,
⋯
,
0
,
⏟
M
−
1
个点
x
(
0
)
,
x
(
1
)
,
.
.
.
x
(
L
−
1
)
}
⏟
第一帧
L
个点
x
2
(
n
)
=
{
x
(
L
−
M
+
1
)
,
,
⋯
,
x
(
L
−
1
)
,
⏟
保留
x
1
最后
M
−
1
个点
x
(
L
)
,
x
(
L
+
1
)
,
.
.
.
x
(
2
L
−
1
)
}
⏟
第二帧
L
个点
x
3
(
n
)
=
{
x
(
2
L
−
M
+
1
)
,
,
⋯
,
x
(
2
L
−
1
)
,
⏟
保留
x
2
最后
M
−
1
个点
x
(
2
L
)
,
x
(
2
L
+
1
)
,
.
.
.
x
(
3
L
−
1
)
}
⏟
第三帧
L
个点
\begin{align} x_1(n) = \begin{matrix} \underbrace{ \{0,0,\cdots,0,} \\ {M-1个点} \end{matrix}\begin{matrix} \underbrace{ x(0),x(1),...x(L-1)\}} \\ {第一帧L个点} \end{matrix} \newline x_2(n) = \begin{matrix} \underbrace{ \{x(L-M+1),,\cdots,x(L-1),} \\ {保留x_1最后M-1个点} \end{matrix}\begin{matrix} \underbrace{ x(L),x(L+1),...x(2L-1)\}} \\ {第二帧L个点} \end{matrix}\newline x_3(n) = \begin{matrix} \underbrace{ \{x(2L-M+1),,\cdots,x(2L-1),} \\ {保留x_2最后M-1个点} \end{matrix}\begin{matrix} \underbrace{ x(2L),x(2L+1),...x(3L-1)\}} \\ {第三帧L个点} \end{matrix} \end{align}
x1(n)=
{0,0,⋯,0,M−1个点
x(0),x(1),...x(L−1)}第一帧L个点x2(n)=
{x(L−M+1),,⋯,x(L−1),保留x1最后M−1个点
x(L),x(L+1),...x(2L−1)}第二帧L个点x3(n)=
{x(2L−M+1),,⋯,x(2L−1),保留x2最后M−1个点
x(2L),x(2L+1),...x(3L−1)}第三帧L个点
实用的情况是一般取每帧N点数据通过N阶滤波器,而FFT长度是2N,上式则变为:
x
1
(
n
)
=
{
0
,
0
,
⋯
,
0
,
⏟
N
个点
x
(
0
)
,
x
(
1
)
,
.
.
.
x
(
N
−
1
)
}
⏟
第一帧
N
个点
x
2
(
n
)
=
{
x
(
0
)
,
,
⋯
,
x
(
N
−
1
)
,
⏟
保留
x
1
最后
N
个点
x
(
N
)
,
x
(
N
+
1
)
,
.
.
.
x
(
2
N
−
1
)
}
⏟
第二帧
N
个点
x
3
(
n
)
=
{
x
(
N
)
,
,
⋯
,
x
(
2
N
−
1
)
,
⏟
保留
x
2
最后
N
个点
x
(
2
N
)
,
x
(
2
N
+
1
)
,
.
.
.
x
(
3
N
−
1
)
}
⏟
第三帧
N
个点
\begin{aligned} x_1(n) = \begin{matrix} \underbrace{ \{0,0,\cdots,0,} \\ {N个点} \end{matrix}\begin{matrix} \underbrace{ x(0),x(1),...x(N-1)\}} \\ {第一帧N个点} \end{matrix} \newline x_2(n) = \begin{matrix} \underbrace{ \{x(0),,\cdots,x(N-1),} \\ {保留x_1最后N个点} \end{matrix}\begin{matrix} \underbrace{ x(N),x(N+1),...x(2N-1)\}} \\ {第二帧N个点} \end{matrix}\newline x_3(n) = \begin{matrix} \underbrace{ \{x(N),,\cdots,x(2N-1),} \\ {保留x_2最后N个点} \end{matrix}\begin{matrix} \underbrace{ x(2N),x(2N+1),...x(3N-1)\}} \\ {第三帧N个点} \end{matrix} \end{aligned}
x1(n)=
{0,0,⋯,0,N个点
x(0),x(1),...x(N−1)}第一帧N个点x2(n)=
{x(0),,⋯,x(N−1),保留x1最后N个点
x(N),x(N+1),...x(2N−1)}第二帧N个点x3(n)=
{x(N),,⋯,x(2N−1),保留x2最后N个点
x(2N),x(2N+1),...x(3N−1)}第三帧N个点输出则只保留最后的N个点。
重叠相加
所有假设与重叠保留一致,只是拼帧的时候,数据块补充M-1个0,并计算DFT,这样得到的结果是没有任何混叠的L+M-1个点。我们通过逆变换,得出输出序列:
y
^
m
(
n
)
=
{
y
^
m
(
0
)
y
^
m
(
1
)
y
^
m
(
2
)
y
^
m
(
3
)
.
.
.
y
^
m
(
L
−
1
)
y
^
m
(
L
)
.
.
.
y
^
m
(
N
−
1
)
}
\hat{y}_m(n) = \{\hat{y}_m(0)\ \hat{y}_m(1)\ \hat{y}_m(2)\ \hat{y}_m(3)\ ... \hat{y}_m(L-1)\ \hat{y}_m(L)\ ... \hat{y}_m(N-1)\ \}
y^m(n)={y^m(0) y^m(1) y^m(2) y^m(3) ...y^m(L−1) y^m(L) ...y^m(N−1) }
虽然没有混叠,但数据输出长度多出了M-1个,这M-1个信息需要与后面的数据对应相加,完成卷积的交叠拼接。最终得到:
y
(
n
)
=
{
y
1
(
0
)
,
y
1
(
1
)
,
.
.
.
y
1
(
L
−
1
)
,
y
1
(
L
)
+
y
2
(
0
)
,
y
1
(
L
+
1
)
+
y
2
(
1
)
,
.
.
.
y
1
(
N
−
1
)
+
y
2
(
M
−
1
)
,
y
2
(
M
)
.
.
.
}
{y}(n) = \{{y}_1(0),\ {y}_1(1),\ ...\ {y}_1(L-1),\ {y}_1(L)+y_2(0), \ {y}_1(L+1)+y_2(1), \ ...{y}_1(N-1)+y_2(M-1), y_2(M)\ ...\}
y(n)={y1(0), y1(1), ... y1(L−1), y1(L)+y2(0), y1(L+1)+y2(1), ...y1(N−1)+y2(M−1),y2(M) ...}这种情况的拼接关系比较简单:
x
1
(
n
)
=
{
x
(
0
)
,
x
(
1
)
,
.
.
.
x
(
L
−
1
)
⏟
第一帧
L
个点
0
,
0
,
⋯
,
0
,
}
⏟
M
−
1
个点
x
2
(
n
)
=
{
x
(
L
)
,
x
(
L
+
1
)
,
.
.
.
x
(
2
L
−
1
)
⏟
第二帧
L
个点
0
,
0
,
⋯
,
0
,
}
⏟
M
−
1
个点
x
3
(
n
)
=
{
x
(
2
L
)
,
x
(
2
L
+
1
)
,
.
.
.
x
(
3
L
−
1
)
⏟
第三帧
L
个点
0
,
0
,
⋯
,
0
,
}
⏟
M
−
1
个点
\begin{align} x_1(n) = \begin{matrix} \underbrace{ \{x(0),x(1),...x(L-1)} \\ {第一帧L个点} \end{matrix}\begin{matrix} \underbrace{ 0,0,\cdots,0,\}} \\ {M-1个点} \end{matrix} \newline x_2(n) = \begin{matrix} \underbrace{\{ x(L),x(L+1),...x(2L-1)} \\ {第二帧L个点} \end{matrix}\begin{matrix} \underbrace{ 0,0,\cdots,0,\}} \\ {M-1个点} \end{matrix}\newline x_3(n) = \begin{matrix} \underbrace{\{ x(2L),x(2L+1),...x(3L-1)} \\ {第三帧L个点} \end{matrix}\begin{matrix} \underbrace{ 0,0,\cdots,0,\}} \\ {M-1个点} \end{matrix} \end{align}
x1(n)=
{x(0),x(1),...x(L−1)第一帧L个点
0,0,⋯,0,}M−1个点x2(n)=
{x(L),x(L+1),...x(2L−1)第二帧L个点
0,0,⋯,0,}M−1个点x3(n)=
{x(2L),x(2L+1),...x(3L−1)第三帧L个点
0,0,⋯,0,}M−1个点
block分块自适应探讨
频域处理和分块一体不可区分的,分块处理大大减轻了自适应的算法负担,块内只算一次参数更新(输出一块的数据也只在块边界更新一次(相对于sample)),Block implementation of adaptive digital filters被引用的是比较多的一篇,A Unified Approach to Time- and Frequency-Domain Realization of FIR Adaptive Digital Filters也阐述了block lms的一些特性,但这篇文章有点长,而且时间久远,晦涩难懂。由于这个领域的诱人前景,美国OSTI在80年左右召开了一个Block adaptive filtering的会议。讲到这里,我们回顾一下,上一章节的重叠保留&重叠相加方法为块处理数字滤波器奠定了基础,但要实现块处理自适应滤波器,不仅仅要完成时-频-时变换,还有一个自适应参数更新的过程,这个如何更新?在时域还是频域做?不同策略会带来什么样的计算效率?在想实战之前,应该先撸一撸基础的理论和方法,但论文眼花缭乱,从何撸起呢?综合几篇大作,并拜读浩哥依然选三篇,或者说是2+1,1是Block implementation of adaptive digital filters,而2是这篇引用的Adaptive filtering in the frequency domain和Fast implementations of LMS adaptive filters,不妨对比着看看后两篇讲述了啥,尝试解决什么问题。从时间线上来说Adaptive filtering in the frequency domain发表于1978年,文章简短的描述了从利用The complex LMS algorithm算法从频域上计算的可行性和计算效率,但没有追究时频算法的等价性和相关性,以及误差收敛等关键问题,文中的权重值应该都是在频域存在的,计算量非常经济实惠,看着也似乎比较容易实现。1980年发表的Fast implementations of LMS adaptive filters引用了前者,这也说明了一种传承关系,后者推导的时候严格遵守了“重叠保留”方法,有了前文的铺垫,这里不难理解,文中的创新点应该是参数更新部分的时频等价变换这里了。Block implementation of adaptive digital filters侧重点在于论述了分块自适应的数学推导,给出了理论上分块方法的基础,并没有特意强调在频域还是时域。
参考
The Comparison of the Constrained and Unconstrained Frequency-Domain Block-LMS Adaptive Algorithms
线性和循环卷积
Lecture 23: Circular Convolution