feat: host-side TCP reconnection with non-blocking send and auto-restart
- communication.py: add ConnectionLostError, non-blocking send with select() for reliable cross-platform timeout (fixes Windows sendall hang), TCP keep-alive, connect_with_retry with exponential backoff, model_preloaded guard for DSP persistent model
- thread_pipeline.py: infer_worker detects ConnectionLostError and triggers coordinated pipeline shutdown via connection_lost Event + shutdown_event; push_frame gains timeout parameter for responsive UI during disconnect
- main_video.py: outer reconnection loop wraps frame processing; pre-push connection check + timeout polling prevents main thread blocking
- main.py: inference retry loop with reconnection on ConnectionLostError
- README: document reconnection mechanism, architecture, timing, and usage
Co-Authored-By: Claude Opus 4.7 noreply@anthropic.com
版权所有:中国计算机学会技术支持:开源发展技术委员会
京ICP备13000930号-9
京公网安备 11010802032778号
video_yolov4_tiny
基于 PNNA NPU 的 YOLOv4-tiny 视频目标检测系统,使用 3 线程流水线实现并行加速,支持 TCP 断线自动重连。
架构
队列设计
所有队列使用
queue.Queue(maxsize=N)限制内存:pre_qinfer_qpost_qdisplay_q线程职责
线程1 — 预处理 (
_preprocess_worker)pre_q取原始帧infer_q线程2 — 推理 (
_infer_worker)infer_q取预处理数据PNNAClient.infer()发送到 NPU 并阻塞等待结果线程3 — 后处理 + 画框 (
_postprocess_worker)post_q取 NPU 原始输出display_q主线程
push_frame()超时轮询 → 排空display_q取最新结果 → 显示q/ESC退出,+/-缩放,0重置缩放关键设计决策
push_frame使用阻塞put(),管线满时主线程等待,自动将读取速率与处理速率对齐,不丢帧PNNAClientsocket 引用,阻塞式推理天然保证同一时刻只有一个推理在飞display_q,只显示最新完成帧,避免积压cv2.imshow和cv2.waitKey必须在主线程调用(Windows 要求),已遵守model_preloaded=True)断线重连机制
对应 DSP 端固件的断线重连改造(模型常驻 + 循环 accept + TCP Keep-Alive + 链路监测),上位机实现了对应的自动重连功能。
DSP 端关键参数
上位机重连架构
1.
communication.py— 核心重连层ConnectionLostError异常类:区分”连接断开”和”超时”,前者需重连,后者可重试SO_KEEPALIVE(跨平台),在支持的平台设置TCP_KEEPIDLE=3s / TCP_KEEPINTVL=1s / TCP_KEEPCNT=3select()**:_send_request()将 socket 设为非阻塞,使用send()+select()实现 5 秒硬截止时间的可靠发送。解决了 Windows 上sendall()在 TCP 缓冲区满时不响应settimeout()的问题connect_with_retry()**:指数退避重连(1s → 2s → 4s → 8s → 15s 封顶),max_attempts=0表示无限重试reconnect()**:关闭旧 socket → 无限重试连接 → 成功后重建msgpack.Unpackerconnection_lost**:threading.Event,推理线程在检测到断连时置位,主线程轮询此标志is_connected属性:快速查询连接状态model_preloaded**:为True时add_model()/release_model()变为空操作2.
thread_pipeline.py— 推理线程感知断连_infer_worker捕获ConnectionLostError:connection_lost.set()通知主线程shutdown_event.set()协调关闭所有工作线程infer()返回None(推理超时)仅跳过当前帧,不触发关闭push_frame(timeout=0.5)**:队列满时 0.5 秒超时返回,主线程可轮询连接状态和键盘事件3.
main_video.py— 外层重连循环connection_lost,避免向已断开管线推帧cap和frame_idx保持不变,恢复后从断点继续4.
main.py— 单图检测重试ConnectionLostError→client.reconnect()→ 重试time.sleep(1)→ 重试完整重连时序
线程安全
client.sockclient._connectedclient.connection_lostthreading.Event内置线程安全client.unpackerqueue.Queue内置线程安全pipeline._shutdownthreading.Event内置线程安全项目结构
配置
在
main_video.py中修改:DSP 端固件需为新版(模型常驻 + 循环 accept),上位机使用
model_preloaded=True模式。运行
键盘快捷键
q/ESC+/=-0数据流
测试结果
日志如下所示: