:SOP行为检测实战:动作都识别对了,为什么还是漏判?)
SOP行为检测实战动作都识别对了为什么还是漏判关于作者我接触视觉整整10 年。机器视觉、烟草、煤矿等行业都有深度开发经验。从硬件选型、算法开发、模型训练到上位机开发及部署都在一线磨过。之前是多家公司人工智能团队的技术负责人。现在自己创业了还在继续做视觉落地这件事。作者说第六篇聊SOP行为检测我从摄像头讲到人体检测、姿态估计再讲状态机和Web平台。那篇发出去以后很多朋友反馈讲得细能看明白也能拿去参考。这次我们往下走一步。假设你已经把模型跑起来了。画面有了检测框有了工人拿工具、放零件这些动作也能识别出来。接下来呢怎么知道密封圈真的装进去了怎么知道两颗螺丝都锁过而不是同一颗锁了两遍工人做到一半把东西拿走下一件进来前面的记录怎么办这些问题光看模型的检测准确率是看不出来的。这篇就拿一个装配工位从头把判断逻辑拆一遍。为了方便讲清楚下面用的是简化教学场景不是某家客户的项目复盘。代码也是示意重点看思路。01 一个看起来六步就能搞定的工位假设我们要给一个壳体装密封圈再盖上盖板锁两颗螺丝。操作流程是这样的放入壳体 → 安装密封圈 → 放盖板 → 锁第一颗螺丝 → 锁第二颗螺丝 → 取走工件。很简单对吧如果让我先做一个演示我可能很快就能画出方案相机对着工位识别壳体、密封圈、盖板和工具再用状态机把六个步骤串起来。识别到一个界面亮一个绿勾。六个都亮了显示OK。但先别急着庆祝。我们给这个方案出三道题。第一道工人把密封圈放进去又拿出来了。系统之前已经识别到密封圈第二步也打了勾。现在密封圈没了这个勾还算不算第二道两次锁付都锁在同一颗螺丝上。工具动作有两次电批甚至返回了两次OK。但另一颗根本没锁。第三道做到一半工件被拿走换了一件。如果状态没有清空新工件可能直接继承上一件的进度。前面什么都没做界面已经绿了一半。你看这三种情况未必是模型识别错了。密封圈确实出现过工具确实用了两次工位里也确实有壳体。单个检测结果都可能是对的串起来的结论却错了。这就是这篇要解决的问题。02 先别盯人的手看看你真正需要知道什么第六篇里我花了不少篇幅讲姿态估计。它能提供手腕、肘部等关键点帮助分析伸手、取料、操作工具这些动作。但到了这个装配工位我会先问一句每一步都需要靠人的动作来判断吗比如密封圈有没有装上我们真正关心的是槽里有没有密封圈。只要观察条件允许直接看零件状态比绕一圈分析“手伸进料盒、手收回来、手移到壳体上”更贴近目标。再比如螺丝有没有锁好。如果电批能提供锁付结果就应该评估读取设备数据。手转了几下不能替代扭矩结果。所以这个工位可以先这样拆要判断的事情优先找什么信号容易误用的替代信号壳体是否就位工件与夹具的位置关系或已有的到位信号画面里出现了一个壳体密封圈是否装入盖板覆盖前目标槽位中的可见状态手去过密封圈料盒盖板是否放好盖板与壳体的相对位置手在壳体上方停了一下两个位置是否锁付合格分别对应两个位置、当前工件的设备结果螺丝刀拿起来两次本件是否结束已确认的取件或换件信号操作员离开了画面这张表的用途很直接决定哪些地方用视觉哪些地方接设备哪些地方还缺信息。如果设备只有一根“完成”信号没有位置、没有结果明细就别在方案里默认它什么都能告诉你。缺的部分要继续找办法补补不了的就明确判断范围。比如只能检查工具有没有到过两个位置那界面就写“工具到位检查”。不要直接写成“锁付质量合格”。03 相机装好了最关键的半秒却没拍到3.1 全景看得清不代表零件看得清这个工位最麻烦的地方是密封圈装好以后马上就要被盖板挡住。相机看到最终成品没有用。我们需要看到的是密封圈装入以后、盖板盖下去以前那一小段过程。假设相机装在工位前上方整个桌面都能看到看起来很漂亮。结果工人一低头、一伸手恰好把密封圈挡住了。等手拿开盖板已经放好了。你说模型怎么判断所以我会先不接算法录几段原始视频逐帧看。看什么三个地方密封圈和槽位的边缘是否分得开。手离开、盖板还没遮住的时候有没有可用画面。换个人、换种允许的站位这段画面还在不在。如果这里过不了先改机位、光照或者允许调整的工位布局。别等模型训练完了才发现它一直在看手背。3.2 帧率不能拍脑袋定第六篇提到过抽帧处理用来降低多路视频的计算压力。这里要把适用条件讲得更细一点。假设密封圈只露出来0.2秒你的程序每0.5秒取一帧。那它可能在两次取帧之间出现了又消失了。模型一张都没收到调什么置信度都没用。因此处理帧率要根据关键状态持续多久来定还要看曝光、运动模糊、传输丢帧和实际推理速度。不能只看相机标称多少帧也不能把一个固定数字套在所有工位上。先在录像里找到最容易错过的那一下再验证系统能不能稳定抓住它。04 状态机怎么写先把“看见过”和“现在还在”分开有了画面接下来写判断逻辑。最直观的写法可能是这样# 反例只要出现过就永久记成完成ifring_detected:steps[install_ring]True这几行代码的问题就是开头那道题密封圈拿走以后这一步还是True。那改成每一帧更新呢# 另一个反例正常盖上盖板后也会因为看不见而变成Falsesteps[install_ring]ring_detected也不对。盖板放好以后本来就看不见密封圈你不能这时候又报漏装。正确的思路是把观察阶段分开。4.1 盖板放下前持续更新状态在能够检查密封圈的阶段记录最近一次有效观察。这里至少有三种情况看清了密封圈在要求的位置。看清了密封圈不在要求的位置。手挡着、画面糊了暂时判断不了。注意没有检测到不一定等于看清了它不存在。第三种情况不能直接当成第二种也不能一直借用很早以前的“在”。前面看到以后又发生过什么中间有没有失去观察都要考虑。4.2 盖板开始覆盖时检查前面的证据到了盖板覆盖这个节点再决定密封圈这一步能不能确认。下面是简化逻辑。它假设上游已经区分了遮挡和可观察状态也能给出盖板覆盖事件这些条件本身都需要验证。在密封圈可观察阶段 看清并确认在位 → 更新最近有效证据 看清并确认不在 → 撤销在位证据 关键过程不可见 → 标记存在未解决的观察缺口 盖板开始覆盖时 证据有效且没有未解决的观察缺口 → 确认本步 已确认缺件 → 提示漏装 其余情况 → 待复核 进入后续阶段 保存本步结果及对应片段 不再因为盖板遮住密封圈而重复报漏装有效证据能保留多久怎样算观察缺口已经解决这些不能随手写死要拿这个工位的正常、取出、重放、遮挡过程一起验证。4.3 返工拆开了就得重新确认还有一种情况盖板放好以后工人发现没放正又拿起来重装。这时候前面的密封圈状态可能受到影响。系统需要按约定的返工路径回到相应的观察阶段重新确认。不是一返工就把整件所有记录删光也不是之前打过勾就永远有效。改动了哪一部分就检查哪些完成条件已经失效。原来的记录仍然保留用来还原返工过程。05 两条OK不一定是两颗螺丝密封圈的问题解决了再来看锁付。假设我们能接到电批结果收到一次OK就把计数加一。等于二锁付完成。代码很短坑也很直接。同一个孔位锁两次会加到二同一条设备消息重发也可能加到二上一件延迟到达的结果还可能算到这一件头上。所以一条结果至少要回答三个问题属于哪一件属于哪个位置是不是已经处理过的同一条记录为了讲清楚假设上游已经能可靠地提供周期、位置和事件编号我们接收到的数据可以整理成这样# 内部统一后的示意数据不是某款电批的原始接口result{cycle_id:C018,position:A,event_id:E105,status:OK,}下面这个小例子只演示首次锁付过程中的关联和去重。返工解除NG、乱序消息等逻辑没有放进来。classTighteningCheck:def__init__(self,cycle_id):self.cycle_idcycle_id self.seen_eventsset()self.ok_positionsset()self.unresolved_ngset()defreceive(self,event):ifevent[cycle_id]!self.cycle_id:return周期不匹配转入待核查记录positionevent[position]ifpositionnotin{A,B}:return位置不明确转入待核查记录event_idevent[event_id]ifevent_idinself.seen_events:return重复记录不重复计数self.seen_events.add(event_id)ifevent[status]OK:self.ok_positions.add(position)elifevent[status]NG:self.unresolved_ng.add(position)else:return结果不明确需核查return已记录defcompleted(self):return(self.ok_positions{A,B}andnotself.unresolved_ng)这里用集合记录位置。同一个A位置来两次集合里仍然只有A不会冒充A和B都完成。发生过NG以后即使又来了OK这个示例也不会自动解除NG。实际系统要按确认过的返工规则处理并保留原结果、新结果及其对应关系。但我要强调代码里的字段好写现场的数据不一定有。如果设备不知道工件编号不知道锁的是哪个位置你在Python里加两个变量也不会自动得到这些信息。需要看能否利用扫码、设备任务、工装信号或经过验证的位置判断来补齐。补不齐就不能按上面这个例子的前提宣称“两颗螺丝都锁好了”。06 最容易漏写的逻辑这一件什么时候结束现在每一步都能判断了。还有一件事下一件什么时候开始很多演示程序的写法是全部完成后清空等待下一轮。正常走一遍当然没问题。但如果第三步就把工件拿走了呢由于没有走到“全部完成”程序还在等锁付。下一件一放上去前面几个步骤可能直接被跳过。我的建议是把“本件结束”和“本件合格”分开。等待工件 ↓ 确认新工件进入 创建周期 C018 ↓ 记录本件的视觉事件、设备结果、步骤变化 ↓ 确认工件取走 结束 C018保存结果 ├─ 条件满足本次检查通过 ├─ 已确认异常本次检查未通过 └─ 证据缺失待复核 ↓ 清理本周期工作状态等待新件工件没做完就被拿走这个周期也要结束只是不能判通过。操作员离开工件还在则不一定要结束。可以保留周期进入等待或暂停状态。没有工件码的情况下还要专门验证“换了一件”能不能可靠识别。如果有人把两个外观一样的壳体快速替换系统只知道画面里一直有壳体并不意味着它知道工件没换。这一点识别不稳后面记录再漂亮都可能记错对象。07 调试界面别只放一个红色NG做到这里软件界面也该跟上了。第六篇说客户需要看得见的系统。这次再往前一步现场调试的人需要知道系统为什么这么判。假设密封圈这一步没有通过我希望在界面上能顺着看到当前工件周期C018 当前阶段盖板放置 本步结果待复核 原因盖板覆盖前密封圈区域存在未解决的遮挡 操作查看对应片段 / 查看检测输出 / 查看步骤记录调试时按三层查就比较清楚。第一层原始画面有没有拍到如果关键过程一直在手底下先处理拍摄条件。第二层拍到了模型有没有认出来零件清晰可见但对应帧没有识别结果再去看样本、检测区域、模型和阈值。第三层认出来了规则为什么没收是不是事件早到了程序还在上一步是不是周期被错误重置是不是允许的返工顺序没有写进去这样查才不会所有问题都变成“把阈值调低一点试试”。视频和日志也要能按同一个周期、同一个时间段找到。否则一边翻录像一边猜那条日志对应哪个动作调试的人很快就没耐心了。08 验收时故意把流程做错最后说验收。正常操作跑十遍十遍都显示OK看起来很舒服。但系统要防的那些错可能一个都没试过。我会给这个示例工位准备下面这张表在受控条件下用单独管理的测试件验证。怎么测试想发现什么问题正常做完一件基本流程能不能走通不装密封圈直接盖盖板系统是不是只认伸手动作装好密封圈再拿出来是否把“曾经出现”一直当成完成同一个位置锁两次是否只数次数、不分位置一个位置NG后再返工是否按约定更新结果并保留过程做到一半拿走换新件下一件是否继承了上一件的进度人离开再回来工件不动是否错误重置周期关键步骤遮挡或断流是否把看不清的过程悄悄判成正常同一条设备消息重复到达是否重复计数换人、换允许的站位和速度系统是否只适应演示时那一套动作每一条都写清预期结果留实际结果和对应片段。调试用过的材料之外再留一部分没拿来调过的过程做验证。统计时也别只给一个总准确率。真正要分别看的是异常漏了多少、正常操作误报多少、多少次无法判断以及报警后现场要花多少时间处理。比如系统没有把漏装判成正常却总是报“待复核”也不能简单地说防漏效果很好。它可能把大量工作又交回给了人。测试数量和覆盖条件要结合风险、生产情况以及双方要求确定。这张表是起点不能用几次演示代替长期运行验证。写在最后这个工位拆下来模型要认的东西并没有特别多壳体、密封圈、盖板再加上需要的工具或位置。但把它们做成SOP检测要补上的细节不少。密封圈看见以后又被拿走怎么办盖板盖上以后看不见怎么办同一个位置锁了两遍怎么算做到一半换了件记录归谁。这些细节处理好了屏幕上的绿色勾才有意义。如果你也在做类似的项目可以先检查四件事每一步的完成依据是什么。是物体状态、动作特征还是设备结果原来的完成依据什么时候会失效。取出、返工、遮挡、换件有没有处理每条结果属于哪一件。能不能排除重复、延迟和串件那些最想防住的错误有没有真的试过。不要只拿正常流程证明系统有用。从一个工位开始把这些问题跑明白再谈复制到下一条线心里会有底得多。如果你手上也有具体的SOP检测需求欢迎带着工序和允许提供的现场素材交流。先看最怕漏的是哪一步、手头能拿到什么信号再往下拆方案。作者晟妙智能文中场景和代码用于解释实现思路不是可直接部署的完整系统。具体判定条件、异常处理和参数需要结合现场验证。