108 lines
3.7 KiB
Markdown
108 lines
3.7 KiB
Markdown
# Day 15: 性能优化 - 解决导航模式帧率问题
|
||
|
||
## 问题描述
|
||
导航模式下 FPS 从 10.0 降到 0.5-1.5,画面严重卡顿。
|
||
|
||
---
|
||
|
||
## 已完成优化
|
||
|
||
### 1. YOLO 帧间隔修复 ✅
|
||
将 `yolo_process_interval` 从 1 帧改为 5 帧。
|
||
|
||
### 2. 性能诊断日志 ✅
|
||
添加了 `[NAVIGATION DEBUG]` 日志用于监控状态。
|
||
|
||
### 3. 线程池化帧处理 ✅
|
||
使用 `ThreadPoolExecutor` 将 CPU 密集型处理移至后台线程。
|
||
|
||
### 4. 修复模型重复加载 BUG ✅
|
||
**根因**:`audio_stream.py` 中的 `import app_main` 触发模块顶层代码再次执行。
|
||
**修复**:改为 `sys.modules['app_main']` 获取已加载的模块。
|
||
|
||
### 5. 跳帧机制 ✅
|
||
**根因**:虽然用了 `run_in_executor`,但 `await` 仍同步等待处理完成。
|
||
**修复**:实现非阻塞式帧处理:
|
||
- 后台任务处理帧,主循环不等待
|
||
- 使用最后一次成功的结果广播
|
||
- 新帧覆盖待处理队列(跳过中间帧)
|
||
|
||
### 6. 导航语音频率优化 ✅
|
||
**问题**:FPS 恢复后,导航指令触发过于频繁(每1秒一次),造成干扰。
|
||
**修复**:将 `audio_player.py` 中的 `_voice_cooldown` 从 1.0秒 调整为 3.0秒。
|
||
|
||
---
|
||
|
||
## 代码修改
|
||
|
||
### `audio_stream.py`
|
||
- `get_tts_websocket()`: 改用 `sys.modules` 避免触发模块重载
|
||
|
||
### `app_main.py`
|
||
- 添加跳帧机制全局变量:`_nav_processing_task`, `_nav_last_result_image`, `_nav_pending_frame`
|
||
- 修改 `ws_camera_esp` 中的帧处理为非阻塞式
|
||
|
||
### `audio_player.py`
|
||
- 调整 `_voice_cooldown` = 3.0 (原 1.0)
|
||
|
||
---
|
||
|
||
## 预期效果
|
||
|
||
| 场景 | 修改前 | 修改后 |
|
||
|------|--------|--------|
|
||
| CHAT 模式 | 10 FPS | 10 FPS |
|
||
| 导航模式 | 0.5-1.5 FPS | 8-10 FPS |
|
||
| Omni 对话 | 0.5 FPS | 8-10 FPS |
|
||
|
|
||
|
||
**真正的根因**:
|
||
1. **同步帧处理阻塞事件循环** - `orchestrator.process_frame()` 在主 async 循环中同步执行
|
||
2. **Omni 对话阻塞** - TTS 音频流发送与帧处理串行竞争时间片
|
||
3. **双重 JPEG 编解码** - 每帧都解码+重编码
|
||
|
||
### 4. 线程池化帧处理 ✅
|
||
|
||
**方案**:将 CPU 密集型的帧处理移至 `ThreadPoolExecutor` 后台线程执行。
|
||
|
||
**修改**:
|
||
- 添加 `concurrent.futures.ThreadPoolExecutor` 导入
|
||
- 创建全局 `frame_processing_executor`(2 个 worker 线程)
|
||
- 使用 `await loop.run_in_executor()` 执行 `orchestrator.process_frame()`
|
||
- 使用 `await loop.run_in_executor()` 执行 `trafficlight_detection.process_single_frame()`
|
||
- 在 lifespan 关闭时调用 `executor.shutdown(wait=False)`
|
||
|
||
---
|
||
|
||
## 📝 代码变更汇总
|
||
|
||
| 文件 | 变更 |
|
||
|------|------|
|
||
| `workflow_blindpath.py` | YOLO 帧间隔检查 + 性能诊断计时 |
|
||
| `app_main.py` | 添加线程池 + `run_in_executor` 异步帧处理 |
|
||
| `audio_player.py` | 增加语音冷却时间至 3秒 |
|
||
|
||
---
|
||
|
||
|
||
## ⏭️ 下一步 (Day 16 计划)
|
||
|
||
### 1. 户外测试全流程配置
|
||
- [ ] **公网连接支持**:修改代码支持命令行指定 IP (避免硬编码) 或配置公网服务器地址。
|
||
- [ ] **开机自启动**:配置 `/etc/rc.local` 实现通电即运行。
|
||
- [ ] **程序固化**:将 `avaota_client` 部署到 `/usr/bin` 等非易失存储。
|
||
|
||
### 3. 程序部署与自启动 (已验证)
|
||
- [x] **解决存储空间不足问题**:`/overlay` 空间不足 (316KB),改用 `/mnt/UDISK` (17MB)。
|
||
- [x] **部署命令**:`adb push avaota_client /mnt/UDISK/`
|
||
- [x] **自启动配置**:`/etc/rc.local` 添加启动脚本
|
||
```bash
|
||
sleep 15
|
||
/mnt/UDISK/avaota_client &
|
||
```
|
||
- [x] **连通性验证**:通过串口日志确认 WiFi 连接成功,程序成功收发数据。
|
||
|
||
### 2. 网络自动连接
|
||
- [ ] 配置 `wpa_supplicant.conf` 预存户外热点信息。
|
||
- [ ] 验证断电重启后的自动重连能力。
|