恒美微站
首页
关于我们
建站服务
主题模板
案例展示
资讯中心
联系我们
无人零售拿取/放回识别实战:深度相机+YOLO+状态机
首页
资讯中心
/
无人零售拿取/放回识别实战:深度相机+YOLO+状态机
无人零售拿取/放回识别实战:深度相机+YOLO+状态机
发布时间:2026/10/18 22:17:48
做无人零售最让我头疼的不是商品识别而是顾客从货架上拿下一瓶水看了一眼又放回去这个动作在摄像头里经常被 AI 当作什么都没发生。这个项目里硬件用的是 Astra 深度相机算法主框架是 YOLO 目标检测最后要解决的问题就是无人零售场景里的“拿取/放回识别”——听起来好像只是检测加动作判断但真正跑起来之后才发现坑几乎全藏在那些看起来不起眼的细节里。这篇记录是从实战中整理出来的适合准备做无人零售、智能货架、仓储行为事件识别的朋友尤其是那些已经被“拿而不付、放回误扣”折磨过的人。1. 为什么“拿取/放回”是无人零售绕不开的事件级难题1.1 事件级识别和静态库存识别是完全不同的思路很多无人零售项目最初只做商品识别逻辑很直观货架上有摄像头识别到某商品数量减少了就认为顾客拿了并扣款。这套逻辑在顾客单纯拿取时能用但只要顾客拿起又放回系统就会出问题——货架上一瓶水都没有少顾客也没带出任何东西但系统如果只按“货架少了东西”来扣款就会误扣。更麻烦的是如果你为了降低误扣率干脆只在出货口判断商品那顾客拿了又放回的行为就完全没有记录用户行为数据也拿不到。所以这里需要的是“事件级识别”把一次完整的“手靠近货架—接触或拿起—离开或放回”定义成一个动作事件再判断这个事件是拿取、放回还是仅仅摸摸看。这个判断不能只看一帧画面因为单帧里“手在货架前”这个状态对拿和放来说几乎一模一样必须靠连续多帧的时序变化来判别。1.2 Astra深度相机在这个场景里的定位我最早试过只用普通RGB相机加YOLO做这个事效果很一般。原因很简单RGB图像里面手在货架前 20 厘米和手紧贴商品表面从画面上看不出尺度差异尤其当手和背景颜色接近的时候。模型能告诉你有只手但无法直接告诉你手离货架平面还有多远、手里有没有突然多出一个三维体积。这也是我引入 Astra 深度相机的原因。Astra 属于结构光深度方案通过投射红外结构光图案并解算图案形变来得到每个像素的距离信息。它的深度图本质上是一张“距离照片”每个像素直接存毫米级深度值特别适合室内、固定货架、近距离交互这类场景。这套项目里我主要用它的三路数据彩色图RGB、深度图depth、红外图IR。需要说明的是结构光相机对环境光和物体表面材质敏感这一点在后面踩坑部分会重点展开。提示如果你准备在无人零售场景复现这个方案不必纠结必须用某个型号关键是选一台能同步输出彩色图和深度图、并且在 30 帧/秒下能稳定工作的深度相机。拿我们的使用经验来说深度图分辨率 640x480 完全够用再高的分辨率会明显增加计算压力。2. 输入侧并不美好Astra三路数据流的对齐与坏点清洗2.1 彩色图、深度图、IR图的像素对齐在开始动手写识别逻辑之前我花了不少时间做“对齐”。Astra 相机虽然有官方SDK但彩色摄像头和深度摄像头在物理位置上是错开的两个镜头的视场角也不完全一致。也就是说深度图里的像素 (u1, v1) 和彩色图里的像素 (u2, v2) 并不是同一个空间点。如果直接拿 YOLO 在彩色图上检测出来的手部框坐标去深度图里取深度值取到的会是“差了几十像素”的位置。处理方法有两种使用SDK提供的对齐模式直接把深度图对齐到彩色图像素坐标系。这样彩色图每一个像素都有对应的深度值是最省事的方式自己标定两摄像头外参用 3D 点转换把深度图重投影到彩色图坐标适合深度定制场景。我项目里用的是第一种但即便开了对齐仍有像素级偏差尤其是画面边缘偏两三个像素就会导致手部边缘和商品边缘错位。为了稳妥我在取深度数据时不会直接取单个像素而是取一个局部窗口里的有效深度中位数。import numpy as np def get_aligned_depth_at_bbox(depth_img, bbox): x1, y1, x2, y2 bbox x1, y1 max(0, x1), max(0, y1) x2, y2 min(depth_img.shape[1]-1, x2), min(depth_img.shape[0]-1, y2) region depth_img[y1:y2, x1:x2] valid region[region 0] # 过滤深度为0的无效像素 if len(valid) 0: return None return np.median(valid).item()这样取中位数而不是平均值是因为手部边缘经常混入背景像素平均值会被背景深度拉偏中位数更能代表手部主体所在深度。2.2 深度图坏点、飞点与货架反光的前置过滤结构光方案天生会有一些“坏点”反射率太低的黑色物体测不到、镜面反光区域测不准、透明塑料瓶内部会穿过去的深度值。在无人零售货架场景里这几种情况几乎全占。银色包装零食、黑色饮料罐、矿泉水瓶都是深度相机最难处理的物体。我在进入判定逻辑之前必须先做三层过滤IR强度过滤IR图反映的是红外结构光反射强度如果某个区域 IR 强度过高通常是镜面反射区域深度值不可信如果 IR 强度过低通常是吸光材质深度也会缺失。用 IR 图做一个质量掩码把这两种情况标记为“不可信像素”时空一致性过滤对深度图做 3x3 中值滤波消除孤立飞点再用相邻几帧的深度做时间一致性判断如果某个像素深度在 1 帧内跳变超过 300mm大概率是噪声货架前缘的深度背景建模在无人的时候采集一段货架背景深度作为静态参考。后续每一帧的深度图减去背景深度差值超过阈值的区域才认为是“有东西进入了货架前端”这样能剔除大量背景抖动。这一步做完之后后续的手部深度面积统计才有意义。否则一张满是孔洞的深度图会让拿取和放回的判断变成猜谜。3. 从YOLO的2D框到3D空间手部与商品的关联怎么做3.1 手部检测训练里的几个采样细节YOLO 在这个项目里不是用来做人类别检测的。最开始我用 COCO 预训练模型直接找“person”发现超大一个人框完全没法用于手-货架交互分析因为人框几乎覆盖了大半个货架。后来我重新训练了一个手部检测器类别只有手和交互相关的商品候选。训练手部检测器时有一个对后期特别重要的细节框选手腕以下不要把手腕以上、小臂部分框进来。理由很实际拿取动作发生时手会伸进货架小臂和货架上的商品经常重叠如果框太大背景货架特征会严重影响深度面积统计。另外数据集里要刻意加入大量“手里拿着东西”的样本并且混入各种姿势手背朝镜头、手心朝镜头、侧抓、倒抓。我开始时漏了手背样本导致识别器对背对镜头的手召回率特别低这直接影响事件触发。3.2 检测框中心点并不是映射深度的最优选择拿到 YOLO 的手部框后下一步是把 2D 框变成 3D 位置。很多初学者会直接取检测框中心点然后去深度图里面查这个点的深度。这是一个典型的坑手部框中心经常落在手腕或者指缝空隙深度值要么是背景要么是无效值。更稳的方式是取检测框内所有有效深度像素的中位数深度作为手部深度同时把手部框内像素按深度聚类分成“前景手部像素”和“背景货架像素”。这里需要一个简单的反投影把像素坐标变成相机坐标系下的三维坐标def back_project(u, v, depth_mm, fx, fy, cx, cy): z depth_mm / 1000.0 # mm - m x (u - cx) * z / fx y (v - cy) * z / fy return x, y, z比如手部框中心的像素坐标是 (320, 240)深度是 800mm相机内参 fx570, fy570, cx320, cy240则算出来的三维坐标是 x0, y0, z0.8。这个三维位置就是手部在深度相机坐标系里的位置后续用来判断手离货架前缘的距离变化。我在项目里不只保存一个点而是构建“手部深度掩码”把检测框内有效深度像素中深度值小于背景平面阈值、并且连续成片的像素保留下来形成一个只属于手部及其持有物的深度区域。这个深度掩码的面积、质心、沿Z轴的宽度是拿放判断的核心特征。3.3 用深度掩码把“手”和“手里拿的东西”变成可量化特征手部深度掩码有一个非常关键的性质当手里握着东西的时候掩码面积会比空手时明显变大。因为手掌为了抓取物体会张开或形成握持整个手部前景区域的立体体积变大在深度图上的有效像素数量也会上升。放回商品时手里东西没了掩码面积会回落。这条曲线是整个识别逻辑里最重要的输入。取和放不能只看面积还要结合三维位置变化。我定义了几个特征手部掩码面积 S(t)前背景分离后的有效像素数手部深度 Z(t)掩码深度中位数手部质心 XY 坐标变化手框与商品框的 IoU 重叠度商品检测框的置信度变化。拿取动作的典型模式是手部从货架外逐渐靠近Z(t) 变小手部掩码面积突然增加因为手腕带走了商品随后 Z(t) 变大手远离货架同时商品检测框要么消失被手握持遮挡要么和手框一起移动。放回动作相反手部掩码面积先增加到达货架区域后面积减小Z(t) 由小变大离开时商品框重新出现在货架上。这些特征不是靠某一个就能下结论的需要汇总成事件判定。4. 深度面积曲线 状态机拿与放的判定不能再靠拍脑袋4.1 判定管线从一帧结果到一段动作先说我走过的弯路。最早我直接用 LSTM 对几十帧的手部特征做分类试图让网络自己学到“拿”和“放”的时序模式。结果非常不稳主要原因有两个拿和放在帧序列上的前半段几乎一样网络容易混淆不同顾客的手型、速度、伸入深度差异太大训练集根本覆盖不完。后来我把思路改成了“规则状态机为主小网络评分辅助”。核心逻辑是先判断“手是否进入了货架交互区域”再用状态机的状态迁移来认定拿取或放回事件。状态机的好处是每一帧都对应一个明确状态出问题的时候可以逐帧回放定位到底是哪一步跳错了。整个管线分四步YOLO 检测当前帧的手部框和候选商品框从深度图提取手部掩码计算面积 S(t)、深度 Z(t)、质心判断手部是否进入货架前缘区域深度阈值触发进入状态机根据连续多帧的特征变化判断事件类型。4.2 拿取/放回状态机与关键参数我给状态机设计了这几个状态IDLE空闲、REACH伸手靠近、HOLD持有状态掩码面积上升、RETRACT手离开、以及事件输出。拿取的判定路径是 IDLE → REACH → HOLD → RETRACT并且在 RETRACT 过程中商品框与手框发生交互后移出货架区域放回则是 IDLE → REACH → RELEASE → RETRACT区别在于手部掩码面积从大变小并且商品框重新出现在货架区域。光有状态不够还要有可靠的触发和迟滞参数。以下是这套项目里实测调出来的参考参数表参数推荐值说明深度接近阈值Z 350mm相对货架前缘手部进入交互区才触发状态机触发连续帧3帧连续满足才确认避免单帧抖动面积上升比S(t) 1.35 × S(t-3)判定手部持有物增多面积下降比S(t) 0.65 × S(t-3)判定手部持有物减少动作超时2.5秒超过则重置状态机最小有效事件时长0.2秒短于此认为是挥手、路过手部有效深度像素占比≥40%低于说明深度质量不可信迟滞思路体现在面积上升和下降用了不同阈值进入 HOLD 状态需要面积比超过 1.35但退出 HOLD 状态时面积比低于 0.65 才算 RELEASE中间留了一段缓冲带。这样顾客稍微握一下手、手掌开合都不会造成假事件。参数不是拍脑袋定的我是录制了大约 200 次拿取和 150 次放回动作后统计了面积比的分布然后按两个分布的交叉点外扩得到的参考值。4.3 多目标交叠时如何保持ID稳定无人零售场景最大的现实问题不是一个顾客而是两个顾客同时在货架前。这时 YOLO 会给出多个手部框如果每个框都单独建立状态机会面临 ID 跳变手 A 和手 B 交叉时检测框发生重叠下一帧系统把 A 的框标成 B导致状态机误判。我用的是基于深度质心的跟踪关联方法。YOLO 检测出所有手部框后把每个框的特征深度质心 XY、面积、外观向量与上一帧的既有轨迹做匹配匹配依据是三维空间距离和面积相似度。深度相机在这里又体现出优势两只手即使彩色画面重叠深度值往往不同能有效区分。匹配后每个轨迹保持自己的状态机事件最终按轨迹维度输出。5. 实测中最折磨人的三个误判场景与完整排查链路5.1 银色包装反光导致的假拿取现象某一种银色包装的零食并没有被人拿走但系统频繁记录“拿取事件”。一开始我怀疑是手部检测框误检回看彩色视频发现手确实是空手伸过去摸了一下但系统判断成“拿取”。排查过程是这样的我先看这一帧的深度图发现银色包装所在的货架区域有大量深度值为 0 的黑洞。原因是银色包装对红外结构光的反射过于强烈镜面反射让深度传感器无法解算出正确距离。顾客空手伸过去时手掌遮挡住了反光区域原本的深度空洞被手掌填上了。从深度掩码面积的角度来看手部区域面积确实“增大”了面积上升比触发了 HOLD 条件。修复方案是在深度掩码统计之前过滤掉“背景轮廓突变”的补充区域。具体来说我先用无人场景下的背景深度图做一份“反光空洞掩码”后续只统计那些背景里原本就不是空洞的前景像素。同时增加一个质量门控手部区域有效深度像素占比低于 40% 时该帧不参与事件判定。从那次修改之后反光导致的假拿取基本消失。5.2 顾客自带保温杯被误判为“放回”现象更离谱顾客拿着自己的保温杯靠近货架正准备打开杯盖喝水系统直接记录“放回商品”随后扣款逻辑差点被触发。排查这个问题时我先拉出那段时间的商品检测类别发现保温杯被识别成了货架上某款同色商品。接着看深度面积曲线顾客手握保温杯时手部掩码面积确实偏大靠近货架后保温杯瓶子外观和颜色与目标商品相似商品框的置信度还很高。从检测、深度面积、商品关联三个角度单独看全都符合“放回”特征但货架库存快照里并没有新增这个商品。这个案例暴露了一个重要教训只靠视觉事件判断“放回”是不够的必须加库存一致性校验。我后来在放回事件确认之前增加了一个约束条件事件被判定为放回时货架区域的商品计数必须比事件出现前增加一个或者至少在 3 秒内的快照上有新增检测框。如果没有库存变化就只记录为“疑似放回”不触发扣款。这套双确认机制把自定义物品导致的误判压到了很低的水平。5.3 午后强光让整柜深度图周期性跳变现象下午 2 点到 4 点之间整个货架的拿取召回率从 88% 掉到 60% 以下很多真实拿取没有被记录。起初我以为是 YOLO 在强光下检测不到手但查了彩色图后检测框是正常的。问题出在深度图某段时长里货架右侧的大片区域深度值在几个距离段之间来回跳变像是被周期性干扰。我调出了同步的 IR 图发现午后斜射阳光提升了环境红外噪声结构光信号信噪比下降深度解算开始不稳定。修复包含两步。第一步为深度图增加“坏点比例”统计每个事件记录都打上质量标签坏点比例高于阈值的事件不输出第二步修改相机曝光参数让曝光时间随风向光照强度动态调整尽量避开饱和区间。调整后午后时段的坏点比例明显下降。这个场景提醒我深度相机的质量监控不能只在实验室做真实环境里的光照波动比想象中大得多。注意结构光相机对环境光干扰敏感室外光照强、物体镜面反射多的时候不要硬从小孔深度图里抠特征妥协方案是深度质量差时降级为 RGB 视觉判断但事件置信度要打折扣。6. 落地部署算力取舍、量化精度与错误样本回流6.1 检测模型的量化与帧率权衡算法在 PC 上跑通之后还要部署到现场的小算力设备。我用的方案是 YOLO 小模型加 640x640 输入单模型同时检测手和候选商品减少一次前向推理。再用 TensorRT 做 INT8 量化模型体积缩小了 70% 左右推理速度在小算力盒子上可以跑到 20 帧以上。这个帧率维护手部跟踪和状态机完全够用因为人的拿取动作通常持续 1 秒以上。量化带来的精度损失比想象中小前提是校准集要贴近现场数据。我最初随便抽了几百张日常图片做校准量化后手部检测的召回率掉了 5 个点后来从门店录像里按时间段均匀抽样 2000 张再做校准精度基本恢复到浮点模型的水平。这步值得多花时间。6.2 用混淆矩阵四象限做错误样本回流部署后的另一件事是建立错误样本回流闭环。光靠一次训练解决不了所有现场问题我把每天识别出的事件片段自动截取下来按“系统预测结果 人工复核结果”分成四类真正例、真负例、假阳性、假阴性。假阳性和假阴性片段会定期进入训练集重新训练手部检测模型和商品检测模型同时更新状态机阈值参数。实际做下来数据回流比调网络结构带来的收益更明显。第一周现场误判率在 5% 左右经过三轮样本回流后误判率降到了 2% 以下。而且有了事件片段库之后每次新场景接入都能快速验证旧模型能不能覆盖不再需要从零标数据。这个项目做完整套下来我最大的体会是拿取/放回识别这件事真正的难点不在模型结构而在“如何把单帧的检测结果组装成一个可信的空间时序事件”。Astra 深度相机给了空间尺度YOLO 给了语义信息但它们只是原料。把原料变成可用的动作事件需要的是对场景的细致观察和大量试错后的参数打磨。如果你也正在做类似的事情建议先把深度图的质量问题解决再去碰模型和状态机否则后面所有环节都会在不稳定的输入上翻车。