7.2 KiB
7.2 KiB
Day 19 - 通信传输优化
日期: 2025-12-23
主题: 服务器端数据处理逻辑优化,解决传输拥堵问题
问题分析
用户反馈室外测试时传输拥堵,经分析确认:
- 2.4GHz 热点带宽不是瓶颈(需求 ~1.6 Mbps,实测 5-10 Mbps)
- 服务器配置强劲(双 E5-2680 v4 + 双 RTX 3090)
- 真正问题:服务器端每帧都执行无意义的解码-编码循环
瓶颈识别
之前的代码逻辑
# 每帧都解码,无论是否需要处理
arr = np.frombuffer(data, dtype=np.uint8)
bgr = cv2.imdecode(arr, cv2.IMREAD_COLOR) # CPU 密集!
# 即使不需要处理,也重新编码再发送
ok, enc = cv2.imencode(".jpg", bgr, ...) # 又是 CPU 密集!
await _broadcast_to_viewers(enc.tobytes())
问题:客户端发送的 data 本身就是 JPEG,完全不需要解码再编码!
优化方案
1. 零拷贝直传
对于不需要处理的模式(CHAT/IDLE/ITEM_SEARCH),直接转发原始 JPEG:
- if bgr is not None:
- ok, enc = cv2.imencode(".jpg", bgr, ...)
- await _broadcast_to_viewers(enc.tobytes())
+ await _broadcast_to_viewers(data) # 直接转发原始 JPEG
2. 延迟解码
只在真正需要导航处理时才执行 cv2.imdecode:
+ needs_processing = (orchestrator and not yolomedia_running)
+ bgr = None # 延迟初始化
+ if needs_processing:
+ current_state = orchestrator.get_state()
+ if current_state in ("ITEM_SEARCH", "CHAT", "IDLE"):
+ await _broadcast_to_viewers(data) # 零拷贝
+ continue
+ # 只有导航模式才解码
+ bgr = turbo_decode(data)
3. TurboJPEG 加速编解码
使用 TurboJPEG(基于 libjpeg-turbo 的 SIMD 优化库)替换 cv2.imencode/cv2.imdecode:
# 安装
pip install PyTurboJPEG
# 使用(带回退逻辑)
from turbojpeg import TurboJPEG
_turbo_jpeg = TurboJPEG()
def turbo_decode(jpeg_bytes):
return _turbo_jpeg.decode(jpeg_bytes) # 2-3x faster
def turbo_encode(bgr_image, quality=80):
return _turbo_jpeg.encode(bgr_image, quality=quality) # 2-3x faster
速度对比:
| 操作 | cv2 | TurboJPEG | 提升 |
|---|---|---|---|
| 解码 640x480 | ~5ms | ~2ms | 2.5x |
| 编码 640x480 | ~8ms | ~3ms | 2.7x |
4. 导航结果 JPEG 缓存
导航处理后的标注图像只在有新结果时编码一次,后续帧复用缓存:
_nav_last_result_jpeg = None # 缓存编码后的 JPEG
# 新结果时编码并缓存
if res.annotated_image is not None:
_nav_last_result_jpeg = turbo_encode(res.annotated_image, quality=80)
# 广播时直接使用缓存
await _broadcast_to_viewers(_nav_last_result_jpeg)
修改文件
| 文件 | 修改内容 |
|---|---|
app_main.py |
TurboJPEG 导入、turbo_decode/encode 函数、零拷贝直传、延迟解码、JPEG 缓存 |
requirements.txt |
添加 PyTurboJPEG>=1.7.0 |
.env |
GPU 1 配置、性能参数优化 |
预期效果
| 场景 | 之前 | 之后 | 优化幅度 |
|---|---|---|---|
| CHAT/IDLE 模式 | imdecode + imencode | 直接转发 | -100% |
| ITEM_SEARCH 模式 | imdecode + imencode | 直接转发 | -100% |
| 导航模式(新结果) | imdecode + imencode | turbo_decode + turbo_encode(一次) | 2-3x |
| 导航模式(复用结果) | imdecode + imencode | 直接转发缓存 | -100% |
综合效果:
- 非导航场景:CPU 开销降低 90%+
- 导航场景(8fps,YOLO 约 2fps):编码次数从 8 次/秒 降至 2 次/秒
- 编解码速度:提升 2-3 倍
部署步骤
# 服务器端
cd OpenAIglasses_for_Navigation
pip install PyTurboJPEG
python app_main.py
# 启动后应看到日志:
# [INIT] TurboJPEG 加载成功,JPEG 编解码将使用加速版本
验证结果 (2025-12-23 下午)
- 服务器端部署后测试
- 导航模式功能正常
- 所有模式切换正常
- nvidia-smi 确认使用 GPU 1
- 室外 4G 热点实测传输流畅度(待测)
新问题诊断
问题现象
测试盲道导航时发现:
- GPU 利用率仅 7% - 远低于预期
- Python 进程占用 120% CPU - 但服务器总 CPU 仅 3%(56 核利用率极低)
- 帧处理 FPS 仅 3-4 帧 - 画面卡顿严重
- TTS 语音有时不播放 - 音频 WebSocket 断开导致缓冲
根本原因:Python GIL 瓶颈
┌─────────────────────────────────────────────────────────────┐
│ Python 进程 │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ GIL (全局解释器锁) │ │
│ │ 同一时刻只有一个线程执行 Python 代码 │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ Thread 1 (frame_proc-0) ──▶ 执行中 │
│ Thread 2 (frame_proc-1) ──▶ 等待 GIL │
│ Thread 3 (frame_proc-2) ──▶ 等待 GIL │
│ │
│ 结果:3 个线程实际只能用 1 个 CPU 核心 │
└─────────────────────────────────────────────────────────────┘
关键代码位置:
# app_main.py 第 237 行
frame_processing_executor = ThreadPoolExecutor(max_workers=3, ...)
ThreadPoolExecutor 受 GIL 限制,无法真正并行化 CPU 密集型任务。
遗留问题与解决方案
问题 1:CPU 单线程瓶颈
状态:❌ 未解决
解决方案:使用 PyNvJpeg 将 JPEG 编解码移到 GPU
pip install pynvjpeg
实施步骤:
- 创建
gpu_jpeg.py模块 - 修改
app_main.py替换 turbo_decode/turbo_encode
问题 2:TTS 语音不播放
状态:❌ 未解决
现象:[TTS->WS] Buffering TTS audio, will send when reconnected
原因:音频 WebSocket 断开
问题 3:画面卡顿/滞后
状态:⚠️ 待问题 1 解决后验证
明日开发计划
- 安装 PyNvJpeg:
pip install pynvjpeg - 创建 gpu_jpeg.py 模块
- 修改 app_main.py 使用 GPU JPEG
- 性能验证:
watch -n 1 nvidia-smi - 验证 TTS 播放问题
服务器配置
| 组件 | 配置 |
|---|---|
| CPU | 2× Intel Xeon E5-2680 v4 (56 线程) |
| 内存 | 192 GB DDR4 |
| GPU | 2× NVIDIA RTX 3090 (24 GB) |