双讲检测(DTD):双讲场景下的滤波器发散问题
回声消除里最头疼的场景是什么?我个人觉得,就是双讲。两个人同时说话,麦克风信号里既有近端语音,又有远端回声。这时候自适应滤波器很容易跑偏,甚至直接发散。
为什么会这样?因为滤波器赖以收敛的误差信号被近端语音污染了。它以为那是回声残留,拼命调整系数,结果越调越乱。我在项目中遇到过好几次,双讲检测没做好,滤波器直接崩掉,用户体验极差。
双讲场景下的滤波器发散机制
先看一个典型的双讲场景:
- 远端信号 x(n) 播放到扬声器
- 近端语音 v(n) 同时产生
- 麦克风采集信号 d(n) = y(n) + v(n)
- 滤波器输出 ŷ(n) 试图逼近 y(n)
- 误差信号 e(n) = d(n) - ŷ(n) = v(n) + [y(n) - ŷ(n)]
问题就在这。当 v(n) 远大于 y(n) - ŷ(n) 时,误差信号几乎等于近端语音。NLMS 更新公式里,系数调整量正比于 e(n)。滤波器以为回声很大,拼命调整,实际上是在追踪近端语音。发散是必然的。
核心结论:双讲时如果不做 DTD,自适应滤波器会在近端语音上收敛,导致回声路径模型被破坏。一旦切回单讲,回声会突然冒出来,需要很长时间重新收敛。
Geigel 算法:最经典的 DTD 方法
Geigel 算法是 ITU-T G.168 推荐的方法,也是我最早接触的 DTD 方案。原理很简单:比较远端信号和麦克风信号的幅度。
判断逻辑:
if |d(n)| >= T * max(|x(n)|, |x(n-1)|, ..., |x(n-L+1)|)
双讲状态
else
单讲状态
其中 T 是阈值,通常取 0.5 或 0.707(对应 -6dB 或 -3dB)。L 是回声路径长度,一般取 8-16 个样本。
为什么这么设计?因为回声路径有衰减,麦克风收到的回声幅度一定小于远端信号。如果麦克风信号比远端信号还大,那肯定有近端语音。
我的经验:阈值 T 不要死磕 0.5。我在一个车载项目里,车内空间小,回声衰减只有 -3dB,T 设 0.5 导致频繁误判。后来改成 0.707,效果好了很多。建议根据实际回声衰减量动态调整 T。
Geigel 算法的优缺点
| 维度 | 说明 |
|---|---|
| 优点 | 计算量极小,只需比较幅度;实现简单,适合嵌入式 |
| 缺点 | 对噪声敏感;无法区分近端语音和突发噪声;阈值固定时适应性差 |
| 适用场景 | 信噪比高、回声路径稳定的环境,如免提电话 |
| 不适用场景 | 强噪声环境、回声路径快速变化、音乐信号 |
基于相关性的 DTD 方法
Geigel 只看幅度,信息量有限。更可靠的方法是利用相关性。核心思想:远端信号和回声是相关的,和近端语音不相关。
常用的相关性指标:
- 互相关系数:计算远端信号和麦克风信号的归一化互相关
- 相干函数:频域上的相关性度量
- 误差信号与远端信号的相关性:如果误差和远端相关,说明还有残留回声
我比较喜欢用归一化互相关(NCC):
ρ = E[x(n) * d(n)] / sqrt(E[x²(n)] * E[d²(n)])
当 ρ 接近 1 时,说明麦克风信号主要是回声,单讲状态。当 ρ 接近 0 时,说明近端语音占主导,双讲状态。
注意:相关性方法计算量大,嵌入式上要谨慎。我曾在 Cortex-M4 上跑 NCC,每帧 10ms 的数据,计算量占用了 15% 的 CPU 时间。后来改用分段计算,每 4 帧算一次,才压到 3%。
Geigel 与相关性方法的对比
| 对比项 | Geigel | 相关性方法 |
|---|---|---|
| 计算量 | 极低 | 较高 |
| 抗噪声能力 | 弱 | 强 |
| 检测延迟 | 低(逐样本判断) | 高(需要统计窗口) |
| 误判率 | 高(噪声易误判) | 低 |
| 嵌入式适配 | 非常适合 | 需要优化 |
双讲检测的核心逻辑
下面这张图展示了 DTD 在 AEC 中的位置和作用:
实际项目中的 DTD 策略
我在多个嵌入式项目里总结了一套 DTD 策略,分享给你:
- 先用 Geigel 做粗检:计算量小,快速判断是否可能双讲
- 再用相关性做精检:只在 Geigel 判为双讲时触发,节省算力
- 加入滞回比较:避免频繁切换状态,设置进入和退出双讲的不同阈值
- 冻结滤波器系数:双讲时不更新滤波器,但继续计算误差用于输出
- 缓慢恢复:双讲结束后,先小步长更新几帧,再恢复正常步长
避坑指南:我曾经在一个项目里只用了 Geigel,结果在嘈杂环境中频繁误判。近端安静时它判成双讲,滤波器不更新;等真正双讲时它又判成单讲,滤波器直接发散。后来加了相关性验证,问题才解决。
嵌入式上的优化建议
嵌入式资源有限,DTD 也要精打细算:
- 定点化:Geigel 用整数比较,避免浮点运算
- 查表法:相关性计算中的平方根用查表代替
- 帧间复用:上一帧的部分计算结果可以复用
- 降采样:相关性计算可以在降采样域进行,精度损失不大
嗯,双讲检测就讲到这里。记住一个原则:宁可漏检,不要误判。漏检最多导致回声残留,误判会让滤波器发散,恢复起来更麻烦。