1. 背景
本轮安全监控模型已经完成新版 6 类训练和测试,类别包括:
person / helmet / vest / tractor / slipper / smoking
在笔记本本地摄像头上,使用最新版权重直接测试时,检测效果正常,框延迟也不明显。
但现场部署到盒子和前端系统后,出现了几个明显问题:
- 反馈检测框有延迟。
- 人或目标移动快一点时,可能看起来检测不到。
- 有时目标即使不动,现场页面也不稳定。
- 现场为了跑得动,将输入尺寸从我们训练/测试常用的 832 降到了 640。
- 现场存在 20 路摄像头并发处理压力。
这个问题是典型的 模型推理结果和视频显示链路没有对齐。
2结论
当前现象更像是部署链路问题,而不是模型本身失效。
本地笔记本摄像头直接调用模型时,模型能够正常检测,延迟也较低。
现场盒子上出现延迟、漂移或漏检,主要可能来自三类因素:
- 检测结果和前端画面不是同一帧。
- 20 路视频并发导致后端排队,检测结果返回时已经过了好几秒。
- 现场使用 640 输入,小目标如 smoking、slipper 的细节损失比 832 更明显。
模型检测的是过去某一帧,前端却把框画在当前实时画面上,所以看起来框延迟、框漂移,甚至像没检测到。
3. 错帧
现场做法是:
摄像头 RTSP 视频流 -> 后端取一帧图片 -> 送入模型检测 -> 模型输出框坐标 -> 后端把坐标返回给前端 -> 前端把框画到实时视频画面上
这个流程看起来没问题,但真正的问题在于:前端画框时,视频已经播放到后面的帧了。
举个例子:
第 100 帧:有人吸烟 后端取第 100 帧去检测 模型 1 秒后返回 smoking 坐标 此时前端已经播放到第 125 帧 前端把第 100 帧的框画到第 125 帧上
结果就是:
- 如果人站着不动,框可能只是慢一点。
- 如果人移动了,框就会偏。
- 如果人已经离开画面,框就像"乱画"。
- 如果动作很短,比如吸烟动作,可能看起来像完全没检测到。
这不是模型没识别,而是 检测帧和显示帧没有同步。
4. 为什么 20 路摄像头会把问题放大
单路摄像头时,即使模型推理有一点延迟,问题可能还不明显。
但 20 路摄像头并发时,问题会快速放大。
假设每路摄像头 25 FPS:
20 路 x 25 FPS = 500 帧/秒
如果系统试图接近逐帧处理,就会同时面对:
- RTSP 拉流
- 视频解码
- 图片预处理
- 模型推理
- NMS 后处理
- 坐标返回
- 前端画框
- 视频播放同步
只要某个环节处理不过来,就会开始排队。
排队一旦形成,后端检测的就不是"当前画面",而是积压队列里的旧画面。
这时看到的延迟,实际上可能不是单次模型推理慢,而是:
视频帧排队延迟 + 解码延迟 + 推理延迟 + 网络返回延迟 + 前端绘制延迟
这些延迟叠加后,几百毫秒很容易变成几秒。
5. 为什么 640 会影响吸烟这类小目标
我们的新版模型训练和正式测试主要按 832 输入尺寸进行。
现场如果为了性能把输入降到 640,对大目标如 person、tractor 影响可能不算特别大,但对小目标影响会明显增加。
典型小目标包括:
- smoking
- slipper
- 小尺寸 helmet
- 遮挡或远距离 vest
以吸烟为例,真正有区分度的区域可能只是:
- 手部
- 嘴边
- 烟头
- 小范围姿态动作
图像从 832 降到 640 后,小目标对应的像素减少,细节被压缩,模型可利用的信息也会减少。
所以现场 640 跑不动 832,并不是简单的"尺寸小一点也一样",而是小目标检测会实打实受影响。
6. 怎么验证
最关键的验证方式是绕开前端画框。
建议现场先做一个最小验证:
单路 RTSP -> 832 输入 -> 后端直接检测 -> 后端直接保存带框图片或带框视频 -> 不经过前端画框
如果保存下来的带框图片/视频能够正常检测,说明模型在现场视频源上是能工作的。
这时问题主要在:
- 多路并发
- 后端排队
- 前端绘制
- 检测帧和显示帧同步
如果后端直接保存的带框图片也检测不到,再回头看模型侧:
- 现场画面和训练数据差异是否过大
- 光照、角度、分辨率是否变化明显
- 置信度阈值是否过高
- 是否需要继续补现场数据
这个验证很重要,因为它能把"模型问题"和"部署问题"分开。
7. 两种正确的画框方式
7.1 后端画框后推流
这是更稳的方式。
流程:
后端拉 RTSP -> 抽帧检测 -> 在同一帧上画框 -> 后端推送带框视频流 -> 前端只负责播放
优点:
- 框一定画在对应帧上。
- 前端不需要处理 frame_id。
- 问题边界清晰,便于排查。
- 适合前端能力一般、现场要求稳定的项目。
缺点:
- 后端需要承担解码、推理、画框、编码、推流。
- 后端工程复杂度更高。
但对于现场系统来说,这通常是最不容易出错的方案。
7.2 frame_id / timestamp 对齐
如果坚持由前端画框,就必须做帧级对齐。
流程:
前端/后端给每一帧打 frame_id 或 timestamp -> 后端检测某一帧 -> 返回 {frame_id, boxes} -> 前端只把 boxes 画回对应 frame_id 的那一帧
这个方案要求前端有帧缓存。
如果前端没有缓存对应帧,只是把返回坐标画到当前实时视频上,那还是会错帧。
没有帧缓存和 frame_id 对齐,就不要让前端直接画延迟返回的框。
8. 20 路摄像头不建议逐帧检测
安全事件检测不是游戏画面渲染,不需要每一帧都检测。
更合理的是抽帧检测和事件聚合。
推荐策略:
每路摄像头正常播放视频 每路每秒抽 2-5 帧做检测 检测结果进入事件缓存 20 秒内 smoking 命中 3 次,生成一次吸烟事件 事件进入 60-120 秒冷却,避免重复报警 保存时间、摄像头编号、截图、置信度、框坐标
这样做有几个好处:
- 降低 20 路并发算力压力。
- 减少偶发误检。
- 避免逐帧处理带来的排队积压。
- 更符合安全事件留痕逻辑。
例如吸烟事件可以定义为:
smoking conf >= 0.35 记为一次命中 20 秒滑动窗口内命中 >= 3 次,落一条事件日志 同一摄像头进入 60 秒冷却期
比起"每一帧都必须有框",这种方式更适合真实部署。
9. 对现场问题的推荐排查顺序
建议按下面顺序排查,不要一上来就改模型:
第一步:单路验证模型
1 路 RTSP 832 输入 后端直接保存带框图片/视频 不经过前端
目标:确认模型在现场视频源上是否正常。
第二步:记录耗时
记录以下时间:
视频解码耗时 预处理耗时 模型推理耗时 NMS 后处理耗时 坐标返回耗时 前端绘制耗时 20 路并发时队列等待耗时
目标:确认延迟到底来自模型,还是来自视频链路和排队。
第三步:确认画框同步方式
确认前端是否具备:
frame_id timestamp 帧缓存 延迟播放控制
如果没有,优先改为后端同帧画框后推流。
第四步:做抽帧和事件聚合
将逐帧检测改为:
每路 2-5 FPS 抽帧检测 20 秒内多次命中才报警
目标:降低算力压力,提高报警稳定性。
11. 工作边界
模型侧已经完成:
- 新版模型训练。
- 正式测试指标整理。
- 笔记本摄像头复测。
- 权重和测试脚本交付。
- 事件聚合示例脚本交付。
- 部署问题分析支持。
现场系统侧仍需继续排查:
- RTSP 多路接入。
- 盒子算力调度。
- 多路视频解码和队列积压。
- 后端是否同帧画框。
- 前端是否做 frame_id/timestamp 对齐。
- 20 路抽帧策略和事件日志落库。
这部分属于部署集成链路,需要现场部署/前后端团队继续处理。模型侧可以继续配合提供参数、测试脚本和结果解释,但不应直接接手现场系统改造。
12. 小结
这次问题的关键经验是:
实时检测不是只看模型 mAP,还要看视频帧、推理结果和前端显示是否严格同步。
模型在离线测试和本地摄像头上表现正常,只能说明模型具备识别能力。
真正上线时,RTSP 拉流、并发调度、输入尺寸、推理队列、前端画框同步,都会影响最终观感。
对于 20 路摄像头、吸烟这类小目标、安全事件留痕场景,更合理的路线是:
抽帧检测 事件聚合 后端同帧画框或严格 frame_id 对齐 按事件保存截图和日志
这样既能降低算力压力,也能避免"模型明明检测到了,但前端画错帧导致看起来没检测到"的问题。