那一帧 0.709:一个深夜排障记
这天晚上,本来应该结束了。
交付包已经打好,模型已经换好,板子已经测过,HTTP 接口也通了。仓库干净,交付目录里有 deb、manifest、使用说明。按理说,这个夜晚应该属于睡觉。
但现实不是按理说。
客户群又响了。
他们说:识别不到。
于是我又坐回电脑前,重新打开视频,重新拿电脑上的 YOLO 跑了一遍。视频里有火,模型能识别,而且识别得很准。不是边缘情况,不是玄学误差,不是模型能力不够。火就在那里,框也在那里,分数也在那里。
这时对方发来了日志。
我打开日志,看了几行,血压直接上来了。
推理启动正常。RTSP 拉流正常。解码正常。预处理正常。NPU 推理正常。后处理正常。服务状态写入正常。/api/status HTTP 返回正常。
然后,在 20:27:21,日志里出现了这一行:
1 | "detections":{"items":[{"score":0.7093907}],"per_frame":0} |
我看到这里,差点气笑。
这不是没识别。
这是已经识别了。
低频 1 秒轮询都能捞到一次 score=0.7093907 的结果,你告诉我识别不到?
rkyolo 已经把火识别出来,通过 /api/status 返回给业务侧了。
但是最后,业务侧总结:
1 | detected=false |
这句话就像在已经拿到快递的情况下,系统告诉你”快递未送达”。
更离谱的是,他们说:按照文档,做了防抖,连续 3 次才报警。
我查了文档。
没有。
没有”防抖”。
没有”连续 3 次”。
没有”报警策略”。
没有”debounce”。
rkyolo 文档写的是推理服务接口,告诉你怎么启动模型、怎么查状态、怎么看 detections.items。至于你业务侧要不要连续 3 次,要不要报警,那是你自己的策略。
结果现在,业务侧自己加了”连续 3 次才报警”,又用 1 秒轮询去采一个 20 FPS 的推理流。
20 FPS 是什么概念?大约 50ms 一帧。他们 1 秒查一次。也就是说,中间大约 20 帧都没看。
然后他们要求连续 3 次。
这就像拿一个每秒闪 20 次的灯,用每秒一次的相机去拍,还要求连续拍到 3 张亮灯,否则就说灯没亮。
灯亮了。
你没拍到。
但日志里甚至已经拍到了一次。
然后你说没亮。
我真的服了。
更有意思的是,日志里的 items 只有:
1 | {"score":0.7093907} |
而 rkyolo 正常返回的检测项应该包含 class、score、points。中间 Java DTO 把 class 和 points 丢了,只剩一个 score。然后业务侧还没有按 items 非空判断,不知道看的什么字段。日志里 items 明明有值,最后却 detected=false。
这已经不是模型问题。
这是接入逻辑问题。
这也不是技术问题。
这是责任边界问题。
模型训练是我干的。ONNX 导出是我干的。RKNN 量化是我干的。RXC 封装是我干的。deb 打包是我干的。板端验证是我干的。文档是我写的。
客户日志是我看的。
连 Java 侧应该怎么判 items 非空,我都写了。
然后对面说:识别不到。
日志里明明写着:
1 | score=0.7093907 |
这个夜晚,真正没被识别出来的不是火。
是责任边界。