方案:定稿客户端/服务器音频边界,修正下一步顺序

音频边界(§5 新增章节,从"待定稿"改为"定稿")

四个问题的决定:
- 传什么:闲置关闭,会话中持续上传(含 0.5–1s 预卷)
  闲置时服务器完全听不到房间声音;会话中上传完整音频流,
  服务端才能做可靠的"说完判定"。唤醒控制的是"是否上传",
  不是"是否处理"。
- 唤醒:本地。延迟低、断网可响应;它是注意力的门,不是传输的门。
- 打断:客户端判定。三条理由——WebRTC 的 AEC 本来就在客户端跑
  (需要播放参考),客户端麦克风信号已是去回声的;客户端知道自己
  正在播什么,"用户在此刻开口"本地可判,不需额外协议;比服务端
  判定少一个来回,更容易满足 ≤300ms。
- 断线:WebRTC 内建 ICE restart;抖动缓冲 100–200ms 待实测。
  缓冲换稳定性但直接加首音延迟,不能凭感觉设。

同时写明接口约定(上下行各传什么)、三条约束(播放参考须本地可得、
预卷缓冲、上传不等于存储)和这条边界的已知代价。

修正下一步顺序

原顺序把"N5095 WebRTC 客户端"列为第一步,但核实后发现:
代码里 pipecat 一次都没被 import 过(只在 requirements.txt 里),
引擎是独立 asyncio 由测试脚本驱动,**服务端没有任何客户端能连的入口**。
所以第一块是服务端 WebRTC 入口,客户端排第二。

新顺序列出六步并标注每步的状态与阻塞条件,明确说明:
第 2 步用合成音频只能验证传输层,**验不了 AEC 与远场识别**——
那要等真实硬件。
This commit is contained in:
ylhan
2026-09-15 13:05:35 +08:00
parent 2b6093d392
commit bb4fa3028e
@@ -256,25 +256,65 @@ TTS按语义短句合成并播放,不把整段一次合成称为原生流式
- 网络失败给简短提示,本地按键、静音和屏幕继续工作,不反复播旧回复。 - 网络失败给简短提示,本地按键、静音和屏幕继续工作,不反复播旧回复。
- 麦克风、音箱、电机尽量机械隔离;软件静音和真实物理断麦需明确区分。 - 麦克风、音箱、电机尽量机械隔离;软件静音和真实物理断麦需明确区分。
### 客户端/服务器的音频边界(v2.4 新增,待定稿) ### 客户端/服务器的音频边界(v2.4 定稿)
**架构改了,但这条边界还没定义。** N5095 与服务器之间传什么、谁判定、 **决定日期:2026-09-15。这条边界决定 WebRTC 客户端的接口形态、
断了怎么办——这个接口不定下来,WebRTC、AEC、唤醒和首音延迟都没法正确实现。 首音延迟预算和隐私边界,先定下来再写代码。**
需要明确的四件事: | 问题 | 决定 | 理由 |
| 问题 | 选项 | 权衡 |
|---|---|---| |---|---|---|
| **传什么** | (a) 持续发送全部音频<br>(b) 只发 VAD 判定后的有效语音段(带 0.5–1s 预卷) | (a) 网络流量大、隐私面宽,但服务端可做更好的端点判定;<br>(b) 省带宽、隐私好,但 VAD 误判会切掉话头,且打断判定只能靠客户端 | | **传什么** | **闲置关闭,会话中持续上传**(含 0.5–1s 预卷) | 闲置时服务器**完全听不到**房间声音(隐私);会话中上传完整音频流,服务端才能做可靠的"说完判定"(方案 §5 要求)。折中方案见下 |
| **唤醒在哪** | (a) 本地(N5095)<br>(b) 服务器 | 本地唤醒延迟低、断网也能响应;但唤醒词模型要跑在 N5095 上(轻量,可行)。**倾向本地** | | **唤醒** | **本地**(N5095) | 延迟低、断网也能响应;唤醒词模型轻量,N5095 跑得动。且它是"**注意力**的门",不是"音频传输"的门 |
| **打断谁判定** | (a) 客户端本地判定后发信号<br>(b) 服务端从音频流判定 | (a) 快(不受网络抖动影响),但需要客户端知道"助手正在说话"这个状态;(b) 准,但受网络延迟影响。**方案要求 ≤300ms,倾向 (a)** | | **打断判定** | **客户端** | 三条理由:① WebRTC 的 AEC 本来就在客户端跑(需要播放参考),客户端的麦克风信号已是去过回声的;② 客户端**知道自己正在播什么**——"用户在助手说话时开口"本地就能判,不需要额外协议;③ 比服务端判定少一个网络来回,更容易满足 ≤300ms |
| **断线/抖动恢复** | 需要定义缓冲深度、重连策略、播放中的音频怎么办 | 缓冲换稳定性,但直接加首音延迟 | | **断线/抖动** | WebRTC 内建 ICE restart 重连;抖动缓冲初始 **100–200ms**,实测调整 | 缓冲换稳定性,但**直接加首音延迟**,不能凭感觉设,必须实测 |
**当前倾向**(待实测确认):**本地做唤醒与 VAD,只在检测到有效语音时发送 #### 状态机
带预卷的语音段;打断由客户端本地判定;播放缓冲初始 100–200ms,按实测抖动调整。**
这条边界会直接决定首音延迟预算,**必须在做 WebRTC 客户端之前定下来**, ```
否则接口要返工。 待机(不上传任何音频)
│
│ 本地唤醒词命中
▼
会话中(持续上传音频 + 控制信号)
│
│ 闲置超时 20s(可调)
▼
待机(停止上传)
```
**关键点:唤醒控制的是"是否上传",不是"是否处理"。**
闲置时麦克风数据只进本地唤醒引擎,不出网。
#### 接口约定
**N5095 → 服务器**
- 音频流:16kHz 单声道(WebRTC 内部用 Opus 编码)
- `wake` 信号:本地唤醒词命中,会话开始
- `barge_in` 信号:用户在助手播放期间开口,服务端据此取消当前生成
- `identity_hint`:本地人脸候选(**仅建议,不自动切身份**,见 §6)
**服务器 → N5095**
- 音频流:TTS 合成结果(Qwen3-TTS 输出 24kHz,需重采样到播放端)
- 状态事件:`listening` / `thinking` / `speaking` / `idle` —— 客户端据此渲染表情并判断"是否正在说话"
#### 三条必须遵守的约束
1. **播放参考信号必须在 N5095 本地可得**。AEC 需要它,而服务器与客户端的
回放时钟不同步,不能依赖服务器侧的时间戳。从阵列 USB 播放端点或
本地播放缓冲取。
2. **预卷缓冲 0.5–1s**。唤醒/打断判定完成到开始上传之间有延迟,
不留预卷会吞掉用户指令的开头。静音时清除缓冲。
3. **会话中持续上传 ≠ 持续存储。** 服务端处理完即丢弃,
默认不落盘(方案 §8:"默认不存原始音视频")。
#### 这条边界的已知代价
- 会话中上传的是**连续音频**,包括用户的停顿、思考声和周围环境音。
服务器处理这些数据,但不存储。
- 客户端 VAD/唤醒的**误判会切掉话头**——这是"闲置不上传"的固有代价,
靠预卷缓冲缓解,不能消除。
- 抖动缓冲直接计入首音延迟。**做 WebRTC 客户端时必须一并测**,
否则首音延迟数字不完整(见 §10)。
## 6. 朝向与机械 ## 6. 朝向与机械
@@ -361,15 +401,26 @@ P5通过Home Assistant统一调用。按实际设备比较官方Xiaomi Home和Xi
### 下一步顺序(按风险从高到低) ### 下一步顺序(按风险从高到低)
评审建议的顺序,认可并采纳——**先把风险最大的实时语音链路闭环**: 评审建议的思路——**先把风险最大的实时语音链路闭环**——认可并采纳。
但执行顺序要修正一处:**服务端目前没有任何客户端能连的入口**,
所以第一块不是客户端,是服务端的 WebRTC 入口。
1. **N5095 的 WebRTC 音频客户端**(尚未开始) > 核实过:代码里 `pipecat` 一次都没被 import 过(只在 `requirements.txt` 里),
含:音频采集/播放接入、与服务器的信令、VAD/唤醒的本地边界 > 引擎是独立 asyncio,由测试脚本用文件音频驱动。**没有进程在监听 WebRTC 连接。**
2. **接入真实麦克风与音箱**,验证 AEC、打断、唤醒
这是唯一能证伪 P2 的步骤,也是当前最大的未知 | # | 步骤 | 状态 | 能验证什么 / 不能验证什么 |
3. **验证 STT 中文识别正确率**(见下"最大功能风险") |---|---|---|---|
4. ESP32 表情串口(硬件已到货,可独立推进) | 0 | **音频边界定稿** | ✅ 2026-09-15 完成(见 §5) | 决定了客户端接口形态 |
5. 摄像头、多人跟踪、电机 | 1 | **服务端 WebRTC 入口**(Pipecat SmallWebRTC) | 未开始 | 让客户端有东西可连 |
| 2 | **N5095 客户端骨架** | 未开始 | 用**合成音频**即可跑通传输层:信令稳定性、帧格式、采样率边界、打断信号传递、**真实传输下的首音延迟** |
| 3 | **接入真实麦克风与音箱** | 阻塞:硬件未购 | 唯一能验证 AEC、打断、唤醒的一步,也是 P2 的全部内容 |
| 4 | **STT 中文识别正确率** | 阻塞:需录音 | 见下"最大功能风险" |
| 5 | ESP32 表情串口 | 硬件已到货,可独立推进 | 串口协议、表情渲染 |
| 6 | 摄像头、多人跟踪、电机 | 阻塞:硬件未购 | — |
**第 2 步的边界要说清楚**:用合成音频能验证传输层,**验不了 AEC 和远场识别**——
那些必须等真实麦克风和音箱。但传输层能提前跑通,硬件一到就能直接接,
不用同时调两件事。
**理由**:语音链路是全局瓶颈。它若走不通,后面的表情、转头做得再好也没用; **理由**:语音链路是全局瓶颈。它若走不通,后面的表情、转头做得再好也没用;
而它若走通,其余部分都是可分解的独立工作。 而它若走通,其余部分都是可分解的独立工作。