Init: 导入开发日志和项目文档

This commit is contained in:
Kevin Wong
2025-12-31 16:18:28 +08:00
commit bcebc7e316
32 changed files with 9208 additions and 0 deletions

347
DevLogs/Day11.md Normal file
View File

@@ -0,0 +1,347 @@
# 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 崩溃修复已完成
- ✅ 音频播放代码已实现
- ⚠️ 音频播放无声音(待调试)
- ⚠️ 需要服务器端配合验证