← Back to all projects← 返回所有项目
Hangzhou Boomy Intelligent Technology — Robotics R&D Intern杭州景业智能科技 — 机器人研发实习

Full-Duplex Acoustic Echo Cancellation for a Robot Intercom机器人对讲系统的全双工声学回声消除

Internship · Real-Time Audio & DSP实习 · 实时音频与 DSP · August 20262026 年 8 月
Python (numpy / scipy)Acoustic Echo CancellationFrequency-Domain Kalman FilterHammerstein ModelingRK3588S / LubanCat-4Embedded LinuxALSA / PortAudioReal-Time AudioUDP Voice TransportMeasurement & Calibration
26.8 dBAverage ERLE, 15-scene suite15 场景平均 ERLE
21 msAlgorithmic latency算法延迟
9.9%Of one CPU core单个 CPU 核心占用
3,700Lines of Python, 16 modulesPython 代码行数,16 个模块

Overview项目概述

The robot-side audio stack for a robot-to-operator intercom: a multichannel frequency-domain Kalman adaptive filter with Hammerstein saturation-basis modeling of loudspeaker nonlinearity, decision-directed Wiener residual suppression, minimum-statistics noise reduction, and low-latency UDP transport — about 3,700 lines of Python on a Rockchip RK3588S board, plus the 15-scene simulator and calibration toolchain built to prove it works.机器人端的音频软件栈,服务于机器人与操作员之间的对讲:多通道频域卡尔曼自适应滤波器、用 Hammerstein 饱和基建模扬声器非线性、判决引导的维纳残余抑制、基于最小值统计的降噪,以及低延迟 UDP 传输 —— 约 3,700 行 Python,运行在瑞芯微 RK3588S 开发板上,外加为验证它而专门搭建的 15 场景仿真器与标定工具链。

The Constraint约束条件

A robot needs to talk and listen at the same time. Its speaker sits roughly 15 cm from its microphone, so without echo cancellation the operator hears their own voice coming back.机器人需要同时说话和聆听。它的扬声器距麦克风只有约 15 cm,因此如果不做回声消除,操作员会听到自己的声音被送回来。

The obvious fix — mute the microphone while the far end is talking — turns the link half-duplex, which is unusable for a remote operator. It also posts a beautiful score on the standard metric, and avoiding that trap is where most of this project’s measurement effort went.最直观的办法——远端讲话时把麦克风静音——会把链路变成半双工,这对远程操作员来说根本没法用。而它偏偏能在业界标准指标上拿到一个漂亮的分数;本项目在测量上投入的大部分精力,就是为了绕开这个陷阱。

I built the robot-side (B-end) audio stack: adaptive echo cancellation, residual suppression, steady-state noise reduction, comfort noise, and low-latency UDP transport — about 3,700 lines of Python across 16 modules on a LubanCat-4 (Rockchip RK3588S), with numpy and scipy only. The harder half of the work was not the filter. It was the measurement infrastructure that can tell real echo cancellation apart from a fancy noise gate, and the physical calibration that says when the answer is “move the speaker,” not “tune a parameter.”我负责机器人端(B 端)的整套音频栈:自适应回声消除、残余抑制、稳态降噪、舒适噪声,以及低延迟 UDP 传输 —— 在 LubanCat-4(瑞芯微 RK3588S)上共约 3,700 行 Python、16 个模块,只依赖 numpy 与 scipy。这项工作更难的那一半并不是滤波器本身,而是能把真正的回声消除与"花哨的噪声门"区分开的测量设施,以及能告诉你"答案是把扬声器挪开"而不是"再调一个参数"的物理标定。

The Physics Sets the Budget物理条件决定了预算上限

Before any algorithm work, a calibration pass measures what the hardware actually makes possible. The algorithm operates on top of a physical budget it cannot exceed:在动手做算法之前,先用一轮标定测清楚硬件到底允许你做到什么程度。算法只能在一个它无法逾越的物理预算之上工作:

MeasurementDecision it drives
Echo return loss (ERL)Below 5 dB: go fix the mechanical layout, do not tune algorithms
Echo delayWhether the partition count covers the reverberation tail
Effective distortion, speech-referredThe physical ceiling for linear AEC; selects the nonlinear basis
Coherence ceiling, lower boundCross-check on the above; both low means a hardware problem
Clock drift (ppm)Single-card should read ≈ 0; nonzero means something is resampling
xrun countNonzero must be fixed first; everything downstream is invalid
测量项它所决定的事
回声返回衰减(ERL)低于 5 dB:先去修机械布局,不要调算法参数
回声时延分区数是否能覆盖混响拖尾
语音口径的有效失真线性 AEC 的物理天花板;决定非线性基的选择
相干天花板(下界)对上一项的交叉校验;两者都低则是硬件问题
时钟漂移(ppm)单声卡应为 ≈ 0;非零说明某处在重采样
xrun 计数非零必须先修;否则下游所有结果都不作数

Architecture系统架构

Two endpoints over UDP port 47000 on a LAN. The A-end is a PC with a closed-back headset and runs no echo cancellation at all — the headset makes speaker-to-microphone coupling negligible, and that is a stated architectural decision rather than an omission. The B-end robot runs the entire chain.局域网上两个端点,走 UDP 47000 端口。A 端是一台配封闭式耳机的 PC,完全不跑回声消除 —— 耳机让扬声器到麦克风的耦合可以忽略,这是一个被明确写下来的架构决策,而不是遗漏。B 端机器人跑完整的处理链。

A-END · OPERATOR B-END · ROBOT Windows PC + closed-back headsetpeer_pc.py — runs no AECheadset makes coupling negligibleLubanCat-4 · RK3588S · Ubuntu 22.04full AEC chain, ES8388 over TRRSsingle-card duplex → zero clock drift UDP 48 kHz raw PCM
Endpoint topology. Echo cancellation runs only on the robot; the operator’s closed-back headset is a load-bearing assumption, and both the code and the README warn that open speakers will howl.端点拓扑。回声消除只在机器人端运行;操作员的封闭式耳机是一个承重假设,代码与 README 都明确警告:用开放式扬声器会啸叫。

The Single-Stream Decision单流设计:最关键的一个决定

Playback and capture happen in one callback of one duplex stream, on one sound card, and the reference signal is the exact array handed to the codec in that same callback. Three things then fall out for free:播放与采集发生在同一块声卡、同一个双工流的同一个回调里,而参考信号就是在那个回调中交给编解码芯片的那个数组本身。于是有三件事白拿:

one duplex callback, one sound card → mic[n] and ref[n] are hardware-aligned microphonenear end + echoreferenceexact playback arraydelay alignGCC-PHAT + envelopeHammerstein basisx, tanh(2z), tanh(5z)multichannel FD Kalman24 partitions, overlap-saveWOLAsqrt-Hann COLAresidual + NR + CNGdecision-directed Wienerto operatorUDP :47000residual e[n]
B-end signal chain. Reference alignment comes first so the filter can be shorter, and filter length drives both CPU and convergence speed — the savings paid for the Hammerstein branches.B 端信号链。先做参考对齐,滤波器才能更短,而滤波器长度同时决定 CPU 占用和收敛速度 —— 省下来的开销正好付给了 Hammerstein 分支。

Why a Kalman Filter为什么选卡尔曼滤波器

The filter is a partitioned block frequency-domain adaptive filter using overlap-save, with K partitions × P Hammerstein branches, driven by a state-space model in which process noise represents echo-path variation and observation noise represents near-end speech plus the noise floor.滤波器是采用 overlap-save 的分块频域自适应滤波器(PBFDAF),有 K 个分区 × P 个 Hammerstein 分支,由一个状态空间模型驱动:过程噪声代表回声路径的变化,观测噪声代表近端语音加底噪。

The Nonlinearity Story非线性问题的来龙去脉

Loudspeaker distortion is a memoryless nonlinearity cascaded with a linear room response. Expand the nonlinearity over fixed basis functions and the whole path becomes an equivalent multichannel linear system: each basis function is a virtual reference channel with its own linear filter, the expansion coefficients get absorbed into the filters, and harmonic distortion is cancelled at the linear filtering stage instead of being left for the residual suppressor to scrape off. That is what makes it possible to exceed the linear distortion ceiling at all.扬声器失真可以看成一个无记忆非线性级联一个线性房间响应。把这个非线性在一组固定基函数上展开,整条通路就等价成一个多通道线性系统:每个基函数都成为一路带有自己线性滤波器的虚拟参考通道,展开系数被吸收进滤波器里,而谐波失真在线性滤波这一级就被消掉了,不必留给残余抑制器去刮。这正是"能够突破线性失真天花板"的根本原因。

Block-RMS peak on speech, relative to x — log scale1e01e11e21e31e41e51×xlinear70,000×x³polynomial1×σ·tanh(βz)saturationSpeech already carries 13 dB peak-to-average, so cubing it amplifies one loud syllable by 1000×. Scalar normalization cannot hold that,and the polynomial orders are collinear enough on speech (condition number 1e9–1e11) that orthogonalization amplifies noise instead of fixing it.
Why the polynomial basis diverges on speech, and why the saturation basis does not. Measured on the shipped code; the self-check fails the build if basis crest factor exceeds 200.为什么多项式基在语音上发散,而饱和基不会。数据实测于交付代码;当基的峰值因数超过 200 时,自检会直接让构建失败。

Measuring Honestly诚实地做测量

ERLE, the standard echo-cancellation metric, rewards muting the microphone. A gate that mutes whenever the far end talks posts spectacular ERLE and is useless, and for a while I was unknowingly tuning toward exactly that. The harness therefore reports four numbers instead of one:ERLE 这个业界标准的回声消除指标,奖励的是"把麦克风静音"。一个只要远端说话就静音的门电路,能刷出极其漂亮的 ERLE,却完全不可用;而我有一段时间正是在不知不觉地朝那个方向调参。因此评测框架报告的是四个数字,而不是一个:

Scene F — short-time ERLE across an abrupt echo-path change -10 dB0 dB10 dB20 dB30 dB0 s3 s6 s9 s12 s path change dip −3.5 dB re-converged, 3.3 s
One plot that justifies the whole “averages hide transients” argument: a scene mean of 9.6 dB describes neither the 30 dB steady state nor the −3.5 dB transient.一张图就足以支撑"平均值会藏起瞬态"这个论点:9.6 dB 的场景均值,既描述不了 30 dB 的稳态,也描述不了 −3.5 dB 的瞬态。

The Scene Suite场景集的设计

Fifteen scenes (A–O), each targeting one specific failure mode rather than just being “harder.” Room impulse response, loudspeaker nonlinearity, near-end speech, and background noise are independently switchable. Three details that took actual domain judgement:共 15 个场景(A~O),每一个都针对一种特定的失效模式,而不只是"更难"。房间冲激响应、扬声器非线性、近端语音与背景噪声可以独立开关。有三个细节真正需要领域判断:

Results结果

Full 15-scene suite, 12 s per scene, delivered configuration (512-sample block, 24 partitions, linear basis). Reproduced by running the shipped code.完整 15 场景、每场景 12 秒、交付配置(512 采样分块、24 个分区、线性基)。以下数据由运行交付代码复现。

MetricValue
Average ERLE, far-end single-talk26.8 dB
Average near-end SDR, double-talk−3.6 dB
Average noise attenuation15.3 dB
Worst short-time dip−3.5 dB
Longest re-convergence3.50 s
CPU, median per block1.053 ms / 10.67 ms budget — 9.9% of one core
CPU, 99th percentile1.207 ms
Algorithmic latency21.3 ms
Filter tail coverage256 ms
Double-talk false triggers0
指标数值
平均 ERLE(远端单讲)26.8 dB
平均近端 SDR(双讲)−3.6 dB
平均噪声衰减15.3 dB
最差短时谷底−3.5 dB
最长重收敛时间3.50 s
CPU,每块中位数1.053 ms / 10.67 ms budget — 9.9% of one core
CPU,99 分位1.207 ms
算法延迟21.3 ms
滤波器拖尾覆盖256 ms
双讲误触发0

What the Residual Suppressor Costs残余抑制器的代价

Paired ablation, measured at a 256-sample block and otherwise identical settings, so these numbers belong to that configuration rather than the one above:成对消融实验,在 256 采样分块下测量、其余设置完全相同,因此下面这组数字属于那个配置,而不是上一节的配置:

ConfigurationAvg ERLEAvg near-end SDRCPU median
Linear Kalman only15.7 dB+0.3 dB0.477 ms
Full pipeline25.8 dB−2.4 dB0.837 ms
Delta+10.1 dB−2.7 dB+0.36 ms
配置平均 ERLE平均近端 SDRCPU 中位数
仅线性卡尔曼15.7 dB+0.3 dB0.477 ms
完整流水线25.8 dB−2.4 dB0.837 ms
差值+10.1 dB−2.7 dB+0.36 ms

What Broke, and What That Taught Me出过什么问题,以及它教会了我什么

What I’d Do Differently如果重来,我会做哪些改变

The largest single improvement in the whole project was not algorithmic. Connecting an independent power supply improved the noise floor by 16 dB, and moving the speaker took ERL from 5.8 dB to 20.6 dB. No parameter change I made came close to either. I started believing the interesting work was in the filter; I finished believing the filter is the last 30% and the acoustics and electronics are the first 70%.整个项目里最大的单项提升并不来自算法。接上独立供电让底噪改善了 16 dB,而挪动扬声器把 ERL 从 5.8 dB 提到了 20.6 dB。我做过的任何参数改动都远远比不上这两件事。开始时我以为有意思的工作都在滤波器里;结束时我相信滤波器是最后的 30%,声学与电子学才是最前面的 70%。

Scope, and What I Am Not Claiming范围,以及我不声称的部分

The honest boundaries of this work:这项工作诚实的边界:

×❮
❯