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

348 lines
9.9 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 11 - 摄像头稳定性优化与音频诊断
**日期**: 2025-12-09
**目标**: 解决摄像头 Sensor 数据获取超时 + 对齐 ESP32S3 协议参数
**结果**: ✅ 代码修改完成,待板端测试
---
## 📋 问题背景
### 1. 摄像头问题 (Day 10 遗留)
```
vipp fds select timeout[2000]ms, setNum:0!
AW_MPI_VENC_GetStream failed
```
- VI 层无法从 Sensor 获取数据
- 初期成功传输几帧后失败
### 2. 音频 WebSocket 问题 (Day 10 遗留)
- WebSocket 连接成功
- 客户端显示发送正常
- **服务器端未接收到数据**
---
## ✅ 解决方案
### 1. 摄像头优化
#### 修改 1: 增大缓冲区 (`camera.cpp`)
```diff
-#define VI_BUFFER_NUM 3 // 驱动要求的最小值
-#define VBV_BUFFER_SIZE 256 // 256KB
+#define VI_BUFFER_NUM 5 // 增加缓冲区避免溢出
+#define VBV_BUFFER_SIZE 1024 // 1MB VBV缓冲区
```
**原因**256KB VBV 对于 1280x720 JPEG 可能不足(单帧 60-80KB
#### 修改 2: 添加 `deinit()` 方法 (`camera.h`, `camera.cpp`)
```cpp
void Camera::deinit() {
// 停止编码器
if (m_venc_chn != MM_INVALID_CHN) {
AW_MPI_VENC_StopRecvPic(m_venc_chn);
AW_MPI_VENC_DestroyChn(m_venc_chn);
m_venc_chn = MM_INVALID_CHN;
}
// ... 关闭 VI/ISP/系统
}
```
#### 修改 3: 自动恢复机制 (`main.cpp`)
```cpp
int consecutive_failures = 0;
const int MAX_FAILURES = 5;
if (!camera.capture_frame(...)) {
consecutive_failures++;
if (consecutive_failures >= MAX_FAILURES) {
camera.deinit();
usleep(1000000); // 1秒
camera.init(); // 重新初始化
consecutive_failures = 0;
}
}
```
---
### 2. 音频诊断增强
添加发送计数器和周期性日志:
```cpp
// 每 100 次发送输出诊断
if (audio_send_count % 100 == 0) {
LOG_INFO("[AUD-CAP] Sent %d packets, %d bytes total",
audio_send_count, audio_bytes_total);
}
```
**目的**:验证客户端确实在发送数据,问题可能在服务器端
---
## 📊 修改文件汇总 (最新)
| 文件 | 修改内容 |
|------|----------|
| `camera.cpp` | VBV **2MB**, 允许丢帧, JPEG 质量 50 (降低帧体积) |
| `main.cpp` | 音频 **16kHz** (与 ESP32 一致), 断连时继续消费帧 |
---
## 🔴 关键修复 (Day 11 Update)
### 问题根因
根据日志分析:
1. 服务器返回 **403 Forbidden** 后,客户端不断重连
2. 重连期间调用 `sleep(1)` + `continue` 跳过了帧采集
3. **VI 层持续产生帧但不消费**,导致缓冲区溢出
4. VBV 满后编码器完全阻塞
### 解决方案
```cpp
// 1. 断连时不再 continue继续采集帧
if (!connected) {
// 消费帧以清空 VBV然后等待 1 秒重试
camera.capture_frame(...);
camera.release_frame(...);
usleep(1000000);
}
// 2. 允许丢帧防止完全阻塞
AW_MPI_VENC_ForbidDiscardingFrame(m_venc_chn, FALSE);
```
---
## 🧪 验证步骤
### 1. 编译程序
```bash
cd /path/to/avaota_app_demo
./build_main.sh
```
### 2. 上传到开发板
```bash
scp avaota_client root@192.168.110.132:/tmp/
```
### 3. 运行测试
```bash
/tmp/avaota_client
```
### 验证标准
- [ ] 摄像头持续工作超过 1 分钟
- [ ]`vipp fds select timeout` 错误
- [ ] 即使出错也能自动恢复
- [ ] 音频日志显示发送计数增长
---
## 📝 音频问题排查方向
如果客户端确认发送正常(日志显示计数增长),问题可能在:
1. **服务器端 `/ws_audio` 路由** - 检查是否正确处理 BINARY 帧
2. **数据格式** - 服务器期望的格式(采样率/通道数/编码)
3. **消息边界** - 320 bytes 的小包是否被正确处理
---
## 🔧 下午工作WebSocket 崩溃修复 + 音频播放实现
### 问题 1: 程序崩溃 (`terminate called without an active exception`)
**现象**:当服务器返回 `403 Forbidden` 后程序崩溃
**根因分析**
1. `perform_handshake()` 失败时,`m_recv_thread` 未启动
2.`disconnect()` 仍尝试 `join()` 未启动的线程
3. 更严重的是,接收线程阻塞在 `recv()` 时,`disconnect()` 直接关闭 socket 导致未定义行为
**修复方案** ([ws_client.cpp](file:///d:/CodingProjects/Antigravity/NaviGlass/NaviGlassClient/avaota_app_demo/src/network/ws_client.cpp#L103-L129))
```cpp
void WSClient::disconnect() {
// 1. 设置标志位
m_running = false;
m_connected = false;
// 2. 发送关闭帧
if (m_sockfd >= 0) {
uint8_t close_frame[] = {0x88, 0x00};
send(m_sockfd, close_frame, sizeof(close_frame), 0);
// ⭐ 关键修复:先 shutdown 中断阻塞的 recv()
shutdown(m_sockfd, SHUT_RDWR);
}
// 3. 等待线程退出(只有实际运行的线程才 join
if (m_recv_thread.joinable()) {
m_recv_thread.join();
}
// 4. 关闭 socket
if (m_sockfd >= 0) {
close(m_sockfd);
m_sockfd = -1;
}
}
```
---
### 问题 2: 音频只有采集没有播放
**目标**:实现双向音频通信(采集 + 播放)
**实现步骤**
#### 步骤 1: 扩展 WSClient 支持二进制数据接收
**修改** [ws_client.h](file:///d:/CodingProjects/Antigravity/NaviGlass/NaviGlassClient/avaota_app_demo/src/network/ws_client.h#L73-L77):
```cpp
// 新增:二进制数据轮询方法
void poll_binary_messages(std::function<void(const uint8_t*, size_t)> callback);
private:
// 新增:二进制数据队列
std::queue<std::vector<uint8_t>> m_binary_queue;
std::mutex m_binary_mutex;
```
**修改** [ws_client.cpp](file:///d:/CodingProjects/Antigravity/NaviGlass/NaviGlassClient/avaota_app_demo/src/network/ws_client.cpp#L227-L232):
```cpp
// recv_loop 中保存二进制数据
} else if (opcode == OP_BINARY) {
// 保存到队列而非丢弃
std::lock_guard<std::mutex> lock(m_binary_mutex);
m_binary_queue.push(payload);
LOG_DEBUG("[WS] Received binary: %zu bytes", payload.size());
}
```
#### 步骤 2: 在主线程中集成音频播放
**修改** [main.cpp](file:///d:/CodingProjects/Antigravity/NaviGlass/NaviGlassClient/avaota_app_demo/src/main.cpp#L140-L151):
```cpp
// 初始化扬声器 (hw:1,0 - I2S 接口连接 MAX98357A)
AudioPlayer speaker("hw:1,0", 16000, 1); // 16kHz, mono
bool speaker_enabled = false;
if (speaker.init()) {
LOG_INFO("[AUD-PLAY] Speaker initialized on hw:1,0");
speaker_enabled = true;
} else {
LOG_WARN("[AUD-PLAY] Speaker init failed, audio playback disabled");
}
```
**修改** [main.cpp](file:///d:/CodingProjects/Antigravity/NaviGlass/NaviGlassClient/avaota_app_demo/src/main.cpp#L221-L237):
```cpp
// 接收并播放服务器返回的音频 (TTS)
if (speaker_enabled) {
ws_aud.poll_binary_messages([&](const uint8_t* data, size_t size) {
// 服务器返回的是 16kHz, mono, S16_LE PCM 数据
if (size % 2 == 0) { // 确保是偶数字节16-bit 样本)
size_t frames = size / 2;
snd_pcm_sframes_t written = speaker.write((const int16_t*)data, frames);
if (written > 0) {
LOG_DEBUG("[AUD-PLAY] Played %ld frames (%zu bytes)", written, size);
}
}
});
}
```
---
## 📊 测试结果分析2025-12-09 17:30
### ✅ 成功部分
- ✅ 扬声器初始化成功:`hw:1,0` (I2S 接口 MAX98357A)
- ✅ 音频采集正常:已发送 2000+ 音频包
- ✅ IMU 正常ICM42688 工作正常
- ✅ WebSocket 崩溃问题修复(需进一步验证)
### ❌ 当前存在的问题
#### 问题 1: **音频播放无声音** 🔴 高优先级
**现象**:日志中**没有** `[AUD-PLAY] Played X frames` 记录
**可能原因**
1. ⚠️ **服务器未发送 TTS 音频**(最可能)
- 需要检查服务器端 `/ws_audio` 是否正确处理音频并返回 TTS
- 服务器日志是否显示 TTS 生成和发送
2. 二进制数据接收逻辑有误
3. 音频格式不匹配(采样率/通道数/格式)
**下一步调试**
```cpp
// 在 ws_client.cpp recv_loop 中添加:
} else if (opcode == OP_BINARY) {
LOG_INFO("[WS] 🔔 Received binary frame: %zu bytes", payload.size()); // 调试日志
std::lock_guard<std::mutex> lock(m_binary_mutex);
m_binary_queue.push(payload);
}
```
#### 问题 2: **WebSocket 频繁断开** 🟡 中优先级
```
[WS] Server closed connection
```
- 服务器主动关闭连接(可能超时或协议错误)
- 影响音频数据的持续接收
#### 问题 3: **摄像头 VBV 缓冲区满** 🟡 中优先级
```
WARNING: BitStreamFreeBufferSize XXX is too small
fail BsFull
```
- 编码器缓冲区不足
- 导致帧率波动0.4 FPS ~ 15 FPS
- 影响可视化流畅度
#### 问题 4: **可视化卡顿** 🟡 中优先级
- 可能与帧率不稳定有关
- 可能与 WebSocket 频繁重连有关
- 可能与网络带宽不足有关
---
## 🔍 下一步行动计划
### 优先级 1: 音频播放调试
1. **添加详细日志**
- WebSocket 接收端:确认是否收到二进制帧
- 播放端:确认 `poll_binary_messages` 是否被调用
2. **检查服务器端**
- 验证服务器是否接收到客户端音频
- 验证服务器是否生成并发送 TTS 音频
- 检查返回的音频格式16kHz, mono, S16_LE
### 优先级 2: WebSocket 稳定性
1. 分析服务器为什么主动断开连接
2. 检查客户端是否发送了不符合协议的数据
3. 考虑添加心跳机制
### 优先级 3: 摄像头优化
1. 进一步增大 VBV 缓冲区到 4MB
2. 降低 JPEG 质量或分辨率
3. 调整帧率到固定值(如 10 FPS
---
**当前状态**:
- ✅ WebSocket 崩溃修复已完成
- ✅ 音频播放代码已实现
- ⚠️ 音频播放无声音(待调试)
- ⚠️ 需要服务器端配合验证