Files
Docs/DevLogs/Day14.md
2025-12-31 16:18:28 +08:00

298 lines
9.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Day 14: WebSocket TTS 调试与项目清理
**日期**2025-12-12
**目标**:解决 TTS 断续问题,清理项目冗余文件
---
## 📅 工作摘要
### 1. 项目清理 ✅
**删除的文件**(共 19 个):
| 类型 | 文件 |
|------|------|
| HTTP TTS 相关 | `http_tts_stream.cpp`, `http_tts_stream.h`, `http_client.cpp`, `http_client.h` |
| 测试脚本 | `build_test.sh`, `build_test_camera.sh`, `build_test_imu.sh` |
| 调试脚本 | `debug_network_libs.sh`, `find_libs_v3.sh`, `setup_mic.sh`, `fix_speaker.sh` |
| 测试源码 | `main_test.cpp`, `test_audio.cpp`, `test_camera.cpp`, `test_gpio.cpp`, `test_imu.cpp`, `test_network.cpp`, `test_udp_only.cpp` |
| 其他 | `Makefile_test`, `build_custom.sh`, `build_phase2.sh`, `build_phase3.sh`, `test_mic.sh`, `test/` 目录 |
**清理后目录结构**
```
avaota_app_demo/
├── build_main.sh # 主编译脚本
├── README.md
├── MUSL_COMPILE.md
└── src/
├── Makefile # 主 Makefile
├── main.cpp # 主程序
├── audio/ # 音频模块
├── camera/ # 摄像头模块
├── imu/ # IMU 模块
├── network/ # 网络模块 (ws_client, udp_sender)
└── utils/ # 日志工具
```
### 2. 恢复 WebSocket TTS ✅
**原因**HTTP TTS (`/stream.wav`) 方案虽然存在,但之前 WebSocket TTS 已验证可以播放声音,只是存在断续问题。重新切换回 WebSocket TTS 进行优化。
**代码变更**
- `main.cpp`: 移除 `http_tts_player_thread()`,恢复 `audio_capture_thread()` 中的 WebSocket TTS 播放逻辑
- `Makefile`: 移除 `http_tts_stream.cpp``http_client.cpp`
### 3. 增大 TTS 预缓冲区 ✅
**问题**Omni API 响应慢,每 1-2 秒才返回一批音频数据导致播放缓冲区频繁欠载underrun
**修改** (`main.cpp`):
```cpp
// 原设置0.5 秒预缓冲)
const size_t PRE_BUFFER_FRAMES = 8000; // 0.5s
const size_t MIN_PLAY_FRAMES = 3200; // 0.2s
// 新设置2 秒预缓冲)
const size_t PRE_BUFFER_FRAMES = 32000; // 2s
const size_t MIN_PLAY_FRAMES = 4800; // 0.3s
```
### 4. 日志优化 ✅
更新日志中的设备名称:
- `ESP32``设备` (更通用)
### 5. 服务器端导航器预初始化 ✅
**问题**:客户端连接后才开始初始化导航器,导致首次连接时服务器卡顿一段时间。
**修改** (`app_main.py`):在服务器启动时预初始化所有导航组件:
```python
# 在服务器启动时预初始化(避免客户端连接后延迟)
print("[NAVIGATION] 预初始化导航器...")
blind_path_navigator = BlindPathNavigator(yolo_seg_model, obstacle_detector)
cross_street_navigator = CrossStreetNavigator(yolo_seg_model, obstacle_detector)
orchestrator = NavigationMaster(blind_path_navigator, cross_street_navigator)
print("[NAV MASTER] 统领状态机已预初始化")
```
**效果**:客户端连接时无需等待导航器加载,响应更快。
---
## 📊 测试结果
### 2 秒预缓冲测试
```
[AUD-PLAY] Pre-buffer full (33920 frames), starting playback
[WARN] [AudioPlayer] Underrun occurred, recovering...
[WARN] [AudioPlayer] Underrun occurred, recovering...
...(仍有断续)
```
**结论**2 秒预缓冲仍不足以解决问题Omni API 发送间隔过长(~2 秒/批),播放速度远快于接收速度。
---
## 🔴 遗留问题
### TTS 语音断断续续
**根本原因**
- Omni API 生成音频速度 << 播放速度
- 每 1-2 秒发送一批 10KB 音频
- 即使 2 秒预缓冲,也会在长句子后半段出现 underrun
**可能的解决方案**
| 方案 | 描述 | 工作量 | 效果 |
|------|------|--------|------|
| **继续增大预缓冲** | 3-4 秒预缓冲 | 5 分钟 | 可能有效 |
| **服务器预缓冲** | 服务端收集完整 TTS 后再发送 | 2 小时 | 最佳 |
| **静音填充** | 缓冲区空时插入静音,防止卡顿 | 1 小时 | 中等 |
| **变速播放** | 缓冲区低时降速播放0.95x | 3 小时 | 高级 |
**推荐下一步**
1. 先尝试增大预缓冲到 **3-4 秒**
2. 如仍不行,考虑服务器端预缓冲方案
### 5. 服务器端 TTS 发送优化 ✅
**发现问题**
通过代码分析发现,`audio_stream.py` 中的 `broadcast_pcm16_realtime` 函数存在设计问题:
```python
# 原来的代码HTTP 20ms 节拍循环会阻塞整个函数
async def broadcast_pcm16_realtime(pcm16: bytes):
# ... WebSocket 发送 ...
# HTTP 节拍广播(阻塞!)
while off < len(pcm16):
# 每 20ms 发送一小块
await asyncio.sleep(next_tick - now) # 这会阻塞整个函数
```
**问题**:虽然 WebSocket 发送在前面,但 HTTP 节拍循环会阻塞函数返回。每 1KB 数据需要约 62ms 才能完成,导致下一个 Omni 音频块被延迟处理。
**修复**:将 HTTP 节拍广播改为后台任务
```python
# 修改后WebSocket 立即发送HTTP 在后台执行
async def broadcast_pcm16_realtime(pcm16: bytes):
# ... WebSocket 发送 ...
# Day 14 优化HTTP 广播放到后台任务
if stream_clients:
asyncio.create_task(_http_pacing_broadcast(pcm16))
# 函数立即返回,不阻塞
async def _http_pacing_broadcast(pcm16: bytes):
"""独立后台任务处理 HTTP 节拍广播"""
# 原来的 20ms 节拍循环代码
```
**预期效果**
- WebSocket 发送后立即返回处理下一个 Omni 音频块
- TTS 传输间隔完全由 Omni API 生成速度决定
- 无额外延迟
---
## 📝 代码变更汇总
| 文件 | 变更 |
|------|------|
| `main.cpp` | 移除 HTTP TTS 代码,增大预缓冲到 2 秒 |
| `Makefile` | 移除 HTTP 相关源文件 |
| `network/http_*` | **删除** |
| 测试文件 | **删除** 19 个文件 |
| **`audio_stream.py` (服务器)** | **HTTP 节拍广播改为后台任务WebSocket 立即返回** |
---
## 🔧 技术研究:实时语音传输最佳方案
### 协议对比
| 协议 | 适用场景 | 延迟 |
|------|---------|------|
| **WebRTC** | P2P 实时通话 | <100ms |
| **WebSocket** | 服务器中转、AI 语音 | 100-500ms |
| **HTTP Chunked** | 简单流式传输 | >500ms |
### 关键技术Jitter Buffer
专业音频系统使用**动态抖动缓冲**
1. 预缓冲启动(等待足够数据)
2. 动态调整缓冲区大小
3. 静音填充(缓冲区空时)
---
---
## 🎉 下午进展TTS 流畅 + 导航卡顿问题
### 6. TTS 播放成功 ✅
**服务器端修复生效**重启服务器后测试TTS 播放已流畅!
**修复方法总结**
| 问题 | 原因 | 修复 |
|------|------|------|
| TTS 断续 | `broadcast_pcm16_realtime` 中 HTTP 20ms 节拍循环阻塞 WebSocket 发送 | 将 HTTP 广播改为后台任务 `asyncio.create_task(_http_pacing_broadcast())` |
**关键代码变更** (`audio_stream.py`):
```python
# 修改前HTTP 节拍循环阻塞函数
async def broadcast_pcm16_realtime(pcm16: bytes):
await ws.send_bytes(pcm16k) # WebSocket 发送
while off < len(pcm16): # HTTP 节拍循环(阻塞!)
await asyncio.sleep(...)
# 修改后WebSocket 发送后立即返回
async def broadcast_pcm16_realtime(pcm16: bytes):
await ws.send_bytes(pcm16k) # WebSocket 发送
if stream_clients:
asyncio.create_task(_http_pacing_broadcast(pcm16)) # 后台执行
# 函数立即返回,不阻塞
```
### 7. 导航模式卡顿问题 🔴 (新发现)
**现象**:发出"开始导航"指令后,服务器响应"盲道导航已启动"可视化界面立即卡住FPS 从 10.0 暴跌到 0.2。
**根因分析**
服务器端 `workflow_blindpath.py` 存在 **YOLO 检测间隔参数未使用** 的 bug
```python
# 第253-256行定义了间隔参数
self.BLINDPATH_DETECTION_INTERVAL = 8 # 每8帧检测一次
self.last_blindpath_detection_frame = 0 # 但这个变量从未被使用!
# 第424行实际上每帧都执行 YOLO 推理
blind_path_mask, crosswalk_mask = self._detect_path_and_crosswalk(image) # 无间隔!
```
**对比障碍物检测**(正确实现):
```python
# 第454行障碍物检测正确使用了间隔
if self.frame_counter % self.OBSTACLE_DETECTION_INTERVAL == 0:
detected_obstacles = self._detect_obstacles(image, blind_path_mask)
```
**推荐修复方案**
修改 `workflow_blindpath.py` 第 424 行,添加帧间隔检查:
```python
# 修改后:每 8 帧执行一次 YOLO 推理
if self.frame_counter % self.BLINDPATH_DETECTION_INTERVAL == 0:
blind_path_mask, crosswalk_mask = self._detect_path_and_crosswalk(image)
self.last_blindpath_mask = blind_path_mask
self.last_crosswalk_mask = crosswalk_mask
else:
blind_path_mask = self.last_blindpath_mask
crosswalk_mask = self.last_crosswalk_mask
```
**预期效果**YOLO 推理从每帧 1 次降为每 8 帧 1 次,处理负载降低 8 倍。
---
## 📝 Day 14 完整代码变更汇总
| 文件 | 变更 |
|------|------|
| `main.cpp` | 移除 HTTP TTS 代码,增大预缓冲到 2 秒 |
| `Makefile` | 移除 HTTP 相关源文件 |
| `network/http_*` | **删除** |
| 测试文件 | **删除** 19 个文件 |
| **`audio_stream.py` (服务器)** | **HTTP 节拍广播改为后台任务WebSocket 立即返回** |
---
## 📂 待下次会话处理
### 必须修复
1. **导航卡顿问题**
- 修改 `workflow_blindpath.py` 第 424 行
- 添加 `BLINDPATH_DETECTION_INTERVAL` 帧间隔检查
- 测试验证导航模式 FPS 恢复正常
### 已完成验证
- ✅ 摄像头 WebSocket
- ✅ IMU UDP
- ✅ 麦克风采集
- ✅ TTS 播放(已流畅)
- ⚠️ 导航模式(待修复 YOLO 间隔问题)