4. 频域自适应滤波:分块频域自适应滤波(PBFDAF)、延迟系数更新策略、频域实现的计算效率分析

好,咱们进入频域自适应滤波这一节。说实话,时域LMS在助听器里跑起来,很多时候是「心有余而力不足」——收敛慢、计算量大,尤其当滤波器阶数一高,DSP芯片直接冒烟。我当年第一次在Cortex-M4上跑512阶的NLMS,结果一帧音频没处理完,中断就堆成山了。

那怎么办?把计算搬到频域去。这就是我们今天要聊的——分块频域自适应滤波(PBFDAF)

4.1 为什么非要去频域?

时域LMS每次采样点都要做一次滤波和系数更新。假设滤波器长度是N,那每次迭代就是O(N)的复杂度。你想想看,助听器里采样率16kHz,N=256,每秒要算16k×256=4百万次乘加。这还不算系数更新。

频域处理就不一样了。我们用FFT把信号整块整块地处理,一次算一批。复杂度从O(N)降到O(logN)。说白了,就是用FFT的快速算法来「作弊」。

我个人的习惯是:只要滤波器长度超过64阶,我就优先考虑频域实现。低于64阶,时域反而更省事,因为FFT的overhead也不小。

4.2 分块频域自适应滤波(PBFDAF)

PBFDAF的核心思想很简单:把输入信号分成块,每块做FFT,在频域完成滤波和误差计算,再IFFT回去。系数更新也在频域做。

具体流程是这样的:

  1. 收集L个新采样点,和上一块的数据拼接成2L点
  2. 做2L点FFT,得到频域信号
  3. 频域相乘完成滤波(相当于时域卷积)
  4. IFFT回到时域,取后L个点作为输出
  5. 计算误差信号
  6. 误差信号补零到2L,做FFT
  7. 频域更新滤波器系数
  8. IFFT后取前L个点作为新系数

这里有个关键点:重叠保留法。为什么用2L点FFT?因为时域线性卷积的结果长度是2L-1,用循环卷积会混叠。重叠保留法通过丢弃前L个点来消除混叠。

核心公式(频域更新):

W(k+1) = W(k) + μ * conj(X(k)) * E(k)

其中W(k)是频域滤波器系数,X(k)是频域输入,E(k)是频域误差,μ是步长。

嗯,这里要注意:conj()是共轭操作。我见过不少新手直接拿X(k)乘E(k),结果收敛方向完全反了。你想想看,频域里没有「转置」的概念,但共轭是必须的。

4.3 延迟系数更新策略

PBFDAF有个天生的毛病——延迟。因为你要攒够L个点才能做一次FFT,这L个点的延迟在助听器里是致命的。助听器的延迟要求通常在10ms以内,L=256时,16kHz采样率下就是16ms,已经超标了。

怎么办?我分享几个实战策略:

  • 小分块策略:把L设小一点,比如L=64。代价是FFT的计算效率下降,但延迟能控制在4ms以内。
  • 多延迟自适应滤波(MDF):把长滤波器拆成多个短块,每个块独立做PBFDAF,然后合并输出。这样延迟只取决于子块长度。
  • 预测-更新分离:滤波用当前系数,更新用历史数据。说白了就是「边用边学」,不等待更新完成。

我曾经在一个项目里用过MDF,把256阶滤波器拆成4个64阶子块。延迟从16ms降到了4ms,收敛速度几乎没变。但代价是内存占用翻了一倍——每个子块都要存自己的频域系数。

避坑指南:延迟系数更新策略里,最容易踩的坑是「系数不一致」。滤波用的系数和更新用的系数不是同一时刻的,可能导致收敛不稳定。我建议在更新完成后做一个「原子切换」——用指针指向新系数,而不是原地覆盖。

4.4 频域实现的计算效率分析

我们来算一笔账。假设滤波器长度N=256,块大小L=256。

操作 时域LMS(每次采样) PBFDAF(每块)
滤波 256次乘加 2次FFT(2L点)+ 2L次复数乘
系数更新 256次乘加 1次FFT + 2L次复数乘 + 1次IFFT
总计算量(每L点) 256×256 = 65536次乘加 约 3×2L×log2(2L) + 4L ≈ 3×512×9 + 1024 ≈ 14848次操作

看到了吗?PBFDAF的计算量只有时域的1/4左右。而且N越大,优势越明显。当N=1024时,PBFDAF的效率优势能达到10倍以上。

但这里有个隐藏成本——FFT的精度。定点DSP上做FFT,位宽不够的话,噪声会很大。我建议用Q15格式做FFT,输出结果要小心溢出。我踩过这个坑:有一次用16位定点做1024点FFT,结果中间变量直接溢出,出来的波形全是削顶的。

警告:频域自适应滤波的步长μ需要重新标定。时域μ的范围是0~1,但频域里因为能量分布不同,μ的取值通常要小1-2个数量级。我一般从0.01开始试,观察误差收敛曲线再调整。

4.5 实战中的取舍

说了这么多,到底什么时候用PBFDAF?我个人的经验是:

  • 助听器反馈抑制:强烈推荐。反馈路径通常很长(几十到几百阶),频域处理效率高,而且延迟可以通过MDF控制。
  • 低延迟场景(<3ms):别用PBFDAF,老老实实用时域。或者用子带处理,每个子带用短滤波器。
  • 资源受限的MCU:如果芯片没有硬件FFT加速器,PBFDAF的优势会打折扣。这时候要权衡FFT的软件实现开销。

我记得有一次给一个低功耗助听器芯片做算法移植。芯片只有几十KB的RAM,连1024点FFT的缓冲区都放不下。最后我用了重叠保留法的变种——分块大小L=32,FFT长度64。虽然计算效率不如大块高,但至少跑起来了。

嗯,频域自适应滤波就聊到这里。下一节我们会深入子带自适应滤波,那是另一个实战利器。到时候我会分享一个我踩过的坑——子带间的混叠怎么处理,保证让你少走弯路。