
纯视觉售货柜如何解决「拿错商品」的 99% 误判YOLO 能认出架子上有什么但用户伸手拿了一瓶、又放回去一瓶——怎么判断他到底拿了什么纯视觉方案最大的难题不是识别是行为判断。这篇文章把我从 10% 误判率降到 0.1% 的方法全部分享。大家好我是黒漂技术佬。上一篇讲了怎么训练 YOLO 识别商品。但模型只能告诉你「这一帧画面里有什么」它不知道「用户刚刚做了什么」。举个例子用户伸手进柜子拿了一瓶可乐然后又拿起一瓶雪碧看了看放回去了最后拿了矿泉水。YOLO 看到的只是三张图片里的瓶子位置变了它不知道哪个是最终拿走的。这就是误判的根源。今天聊聊我是怎么解决的。一、误判从哪来三种典型场景场景描述误判率拿 A 看 B用户拿起可乐又摸了一下雪碧放回可乐拿走雪碧视觉判断拿了可乐错拿多放少拿了两瓶水放回一瓶视觉判断少了一瓶对但不知道少了哪瓶遮挡恢复手挡住了摄像头拿走了挡住的商品视觉判断什么都没变错核心问题单帧识别只能告诉你「有什么」不能告诉你「做了什么」。二、三层校验架构把误判率从 10% 降到 0.1%我的方案是三层校验第一层时序帧对比行为分析 → 判断拿取动作 第二层重力传感器校验 → 验证拿走了什么 第三层开门前后对比 → 兜底确认三层按顺序执行任何一层不通过就触发人工审核。三、第一层时序帧对比——行为分析的核心不是对比开门和关门两帧而是分析整个过程的连续帧序列。3.1 流程开门 → 开始录像15fps 连续采集 ↓ 对每一帧 1. ROI 提取只分析货架区域 2. YOLO 检测所有商品 3. 与上一帧对比哪个商品框消失了哪个新出现了 ↓ 生成「商品变化事件序列」 t2s: 可乐框消失被手遮挡或拿走 t3s: 可乐框重新出现放回去了还是另一瓶 t5s: 矿泉水框消失 t6s: 门关闭 ↓ 根据事件序列推断用户拿走了矿泉水3.2 关键代码思路prev_boxes[]events[]forframeinvideo_frames:boxesyolo_detect(frame,roiROI_REGION)# 对比前后帧disappeared[bforbinprev_boxesifnotmatch_in(b,boxes)]appeared[bforbinboxesifnotmatch_in(b,prev_boxes)]ifdisappeared:events.append({time:time,type:removed,boxes:disappeared})ifappeared:events.append({time:time,type:added,boxes:appeared})prev_boxesboxes resultanalyze_events(events)# 推断最终拿取3.3 关键技术点ROI 区域只分析货架区域忽略柜门和边框节省 70% 算力IoU 匹配前后帧同一位置的框才算同一个商品阈值 0.5去抖动连续 3 帧确认变化才记录过滤手部快速移动造成的误检时序窗口关注关门前后 2 秒的帧这段时间的行为权重最高四、第二层重力传感器校验——物理世界不说谎视觉可能被遮挡骗过去但重力传感器不会。4.1 方案每个货道底部装一个 HX711 称重模块实时读取重量。开门前货道 A 重量 1,500g6 瓶可乐每瓶 250g 关门后货道 A 重量 1,250g 变化-250g 推断拿走了一瓶可乐4.2 双校验逻辑视觉判断用户拿走了可乐 重力判断重量减少 250g≈ 一瓶可乐 → 两者吻合直接扣款 视觉判断用户拿走了可乐 重力判断重量减少 500g≈ 两瓶可乐 → 不吻合触发人工审核⚠️注意HX711 精度约 ±2g对轻量商品口香糖 10g误差较大需要更高精度的传感器或放宽容差。五、第三层开门前后对比——兜底如果前两层都失败了比如视觉被完全遮挡、重力传感器故障用最朴素的方法开门前拍一张关门后拍一张对比两张图里每个位置的商品数量。这个方法准确率高但无法判断复杂的「拿多放少」场景作为兜底足够。六、真实数据上线三个月的结果指标数值总交易笔数3,247误判笔数4误判率0.12%用户投诉3 次赔付金额47 元三个月赔了 47 块钱换来 0.12% 的误判率。这个数字足够让运营方接受了。七、还有哪些坑红外补光晚上柜内全黑摄像头拍不到东西。加了一颗红外灯20 元问题解决冷凝水冷柜会有冷凝水摄像头镜头起雾。解决方案是镜头前加个小型加热片5 元用户手速极快有人伸手拿东西不到 0.5 秒就抽出来时序帧里只有一帧有手。解决方案是提高采集帧率到 30fps下一篇预告算法和视觉都讲完了下一篇进入后端——《Spring Boot 后端1 万台设备的高并发架构设计》包括 MQTT 设备接入、订单状态机、支付回调幂等性。互动你做行为分析遇到过什么奇怪的 case评论区分享一下