
做机器视觉项目的兄弟应该都有过这种体验在VisionMaster里拖了一大堆模块结果发现流程是“一根筋”跑到底的想并行处理两个工位、想按产品型号切换不同检测流程、想让多个相机各干各的又互不干扰靠默认的单流程根本玩不转。我之前在笔记里零零散散提过Group这次专门开一篇把它讲透。Group模块在VM里其实一直存在但很多初学者压根没注意过它。你新建方案的时候左侧方案树里会默认生成一个“流程”节点下面挂着一个Group0大多数人的操作习惯就是在这个Group0里一路往下堆模块——图像源、定位、卡尺、字符识别、通信一气呵成。这个思路在小项目里没毛病但等你的项目涉及多个相机、多个检测工位或者需要按型号分支处理时单Group就会被卡死。Group说白了就是流程里的“分区”一个流程可以挂多个Group每个Group内部是一套完整的、可独立运行的模块链Group之间既可以并行跑也可以由外部信号分别触发。这篇笔记我打算按这个顺序聊先讲清楚Group到底解决什么问题再拆它的运行和触发机制然后用一个双相机协同检测的完整案例演示怎么搭多Group流程接着讲Group之间如何交换数据最后把我踩过的几个坑和排查思路一并倒出来。如果你已经在VM里搭过几个流程、但还没搞明白Group怎么用、什么时候该用这篇应该能帮你把逻辑理顺。1. 为什么要单独聊Group——VM流程组织的核心单元1.1 从“一根线串到底”到“多分支并行”VisionMaster默认的编排方式非常直观模块往画布上一拖一个输出连一个输入从图像源到结果输出数据一路流过去。这种单流水线模式的好处是思路清晰调试时从头到尾顺着看就行。但它有一个隐含前提整个方案的执行路径只有一条任何时刻只有一个“当前流程”在运行。实际产线根本不是这么回事。一个视觉工位经常要同时处理两个相机的图像一个拍定位、一个拍读码或者同一个相机要应对两种以上产品每种产品的检测步骤和参数都不一样。你用单流程做也不是不行但代价很大要么把检测逻辑写成密密麻麻的分支要么一个产品一套方案文件、来回切换加载。前者维护起来想死后者效率低到没法用。Group就是在“流程”这个层级之下、把模块链横向切分的机制。你可以把一条大流水线拆成多条独立的小流水线每条小流水线就是一个Group。每个Group内部照常拖模块、连逻辑但Group之间互不干扰可以独立启动、独立停止、独立触发。这样一台设备上同时跑两套视觉逻辑就变成了常态操作而不是靠硬凑。我习惯把Group类比成车间里的多条生产线厂房还是那个厂房方案但每条产线各干各的活有各自的进料口和出料口互不抢道。你甚至可以让一条产线24小时不停另一条按需启停。1.2 Group适合解决的典型场景结合我接触过的项目Group的高频使用场景基本集中在下面几类多相机并行最常见。一个工位两三个相机分别拍不同区域或不同角度每个相机的图像处理逻辑相对独立没必要硬串成一条链。给每个相机分配一个Group各自取流、各自处理、各自输出结果干净利落。多产品混线同一台设备隔几分钟换一种产品每种产品的检测项、ROI位置、算法参数都不一样。把每种产品的检测流程放进独立Group运行时根据PLC下发的产品型号激活对应的Group。工位分段处理有的流程前半段做预处理和粗定位后半段做精密测量两段由不同的触发信号控制。拆成两个Group后可以分别控制这两段的执行节奏。模板方案复用把公共的基础处理比如图像校正、标定转换放在一个Group里把和具体项目相关的逻辑放在另外的Group里换项目时只改业务Group公共部分不动。这些场景如果用单流程硬写最常见的做法是用条件分支模块把所有逻辑堆在一起。但分支一多画布上连线跟蜘蛛网一样调试时想单步跑某一分支还得反复设置条件极度折磨。Group的方案则是从结构上把问题拆开每个Group只管自己那一摊切来切去干净清爽。2. Group模块的运行机制与触发策略2.1 方案树里的Group层级第一次接触VM的时候可能很多人没留意过方案树的结构。标准的新建方案大概是这样的层次方案最顶层相当于一个工程文件。流程方案下面挂着一个“流程”节点流程就是模块集的载体。Group0流程下默认带一个Group实际项目里可以右键添加更多Group。模块图像源、定位、测量这些具体功能块拖到画布上后都归属到当前Group。这个“流程-分组-模块”的三层结构是理解VM编排逻辑的关键。模块之间在同一个Group内可以用连线直接传递数据不同Group之间则不能直接连线必须通过全局变量或跑结果汇总的模块来中转。这个限制我在后面专门讲现在先把层级关系记牢。每个Group的标签在界面下方点击标签就是在切换当前编辑和监控的Group。注意VM的“运行/停止”按钮默认控制的是当前激活的Group而不是整个方案。如果你开了多个Group只点运行按钮实际上只是运行当前这个标签页对应的Group。这个细节我见过不止一个人搞混——明明Group1跑得好好的点运行发现Group2没动以为程序挂了。2.2 全局触发、本地触发与外部信号Group的执行节奏由触发源决定。VM里Group的触发方式大致分三类触发方式说明典型使用场景全局触发所有设置为全局触发的Group由方案级的触发信号统一驱动执行步调一致多相机需要同时采集、同时处理的场合本地触发每个Group独立响应自己的触发信号互不等待不同工位按各自节拍运行连续触发Group内部流程一次跑完后立即自动重新开始不需要外部信号调试阶段、循环检测类应用实际调试时我建议先把所有Group都设成全局触发用一个软件触发信号统一控制。这样你在界面上手动触发一次所有Group就同步跑一轮方便观察每个Group的输出结果。等逻辑调通、上产线接真实信号时再根据工艺要求改成“主Group全局触发、从Group本地触发”的混合策略。这里有一个容易忽略的点全局触发不代表所有Group在同一时刻并行执行到同一进度。它只保证每个Group在同一拍触发信号下启动一轮执行。不同Group内部模块数量、单模块耗时不一样谁先跑完谁后跑完是正常的。如果你的后续逻辑要求“A Group定位完B Group才能开始测量”那就要在B Group的流程里加等信号机制或者用后面讲到的全局变量做握手。2.3 运行状态的判断多Group场景下判断“当前到底谁在跑”也很重要。VM在界面上会标出每个Group的运行状态但你要是做二次开发就得通过接口去轮询各Group的状态。我常用的做法是监控每个Group的“执行计数”或“完成标志”——在Group末尾放一个变量设置模块每执行一轮就把一个全局计数值加1上位机对比前后两次的计数就能判断这个Group是否正常在跑。这个方法比读状态位靠谱因为状态位在某些异常情况下刷新不及时而计数是每一次实际执行都会变化的。3. 一个完整案例双Group协同完成定位与读码3.1 案例需求与Group划分思路纸上谈兵没意思我拿一个实际做过的项目结构来讲。假设有个工位要同时完成两件事相机A俯拍产品做模板匹配定位把产品的XY坐标和角度算出来后续机器人要来抓取相机B侧拍产品侧面的二维码识别出条码内容用于绑定数据。两条链路处理的图像完全不同、算法完全不同、输出目标也不同。唯一的关系是它们服务于同一个产品、需要在同一拍内并行完成。这个需求用两个Group来搭是最自然的。我当时的分法是这样的Group0定位组图像源A → 模板匹配 → 坐标计算 → 写入全局变量Group1读码组图像源B → 条码识别 → 结果格式化 → 写入全局变量两个Group都设成全局触发这样一次触发两边同时开工。等两边都跑完上位机再去读各自的全局变量汇总成一条完整的产品数据。3.2 搭建步骤与关键参数先说图像源。调试阶段没有真实相机信号的时候建议在图像源模块里加载本地图片设置成“循环取图”保证每次触发都有图可用。等现场接线完成了再把图像源切换成相机取流模式。Group0和Group1各用一个图像源模块各对应各的相机通道。Group0的匹配部分我用的是“模板匹配”模块。这里有几个参数值得一提匹配分数阈值一般设80分以上。产品表面反光、运动模糊干扰大的场合阈值要适当下调到60~70但太低会带来误匹配。金字塔层数快速粗定位用3层追求精度可以降到1层。多Group并行时CPU资源是共享的定位组建议用稍高金字塔层数来省CPU因为读码组才是那个更吃资源的环节。角度搜索范围实际项目里产品旋转范围可能就正负5度没必要默认的360度全搜范围收窄能明显提升速度。坐标计算模块里我额外加了一个固定偏移量把像素坐标转换成机器人抓取点坐标。这个偏移量可以做成界面上的变量现场调试时直接修改不用每次都进模块内部翻找。Group1的条码识别重点是选择合适的条码类型。二维码用DataMatrix的场合很多但也要把QR码类型勾上防止客户临时换码制。条码模块的输出里有一个“识别结果字符串”这个后面要绑定到全局变量里去。两个Group都搭好后在模块末尾各放一个“变量设置”模块把要共享的结果写进去。Group0写定位坐标X、Y、AngleGroup1写条码字符串和识别状态。到这一步两个Group的流程本身已经闭环了。3.3 数据汇总与输出多Group跑完后数据怎么统一交给PLC或上位机我习惯的做法是再建一个Group2结果汇总组专门负责把各组的输出通过通信模块比如TCP客户端或S7通信发出去。这个Group可以由全局触发但更常见的做法是设成“由上位机命令触发”——上位机感知到定位组和读码组都跑完后主动发指令让汇总组发送数据。这样做有个好处发送逻辑和视觉逻辑解耦。视觉组专注于图像处理通信组只负责把变量整理成协议报文。哪个环节出了问题直接在对应Group里查不会因为模块太多而迷失方向。4. Group之间的数据交换全局变量与绑定4.1 为什么不能直接连线同一个Group内部模块A的输出引脚可以直接连到模块B的输入引脚这在VM里是最基本的连法。但跨Group时你会发现根本找不到另一个Group里模块的输出引脚——画布上压根不会显示别的Group的模块。这是VM刻意的设计Group之间保持数据隔离避免复杂的跨流程连线把逻辑搅成一锅粥。那跨Group的数据怎么传答案是通过全局变量中转。全局变量是挂在方案级别的一种数据容器任何Group都可以往里写、从里面读。它相当于一个公共白板定位组往白板上写坐标读码组往白板上写条码汇总组从白板上把这些数据取走。4.2 全局变量的读写配置创建全局变量的入口在VM的变量管理里可以定义成整型、浮点、字符串、布尔等基础类型也支持数组和结构体。我个人建议把要跨组共享的变量统一命名成“组名_用途_数据类型”的格式比如“Group0_PosX_Float”“Group1_Barcode_String”名字长了点但多Group协作时一眼就能看出这个变量是哪来的、给谁用的。写全局变量的方式有两种在Group流程里放一个“变量设置”模块把模块输出比如定位的X坐标值连到变量的输入上。这是一种显式写入流程执行到这一步时变量就会被更新。通过变量绑定功能直接把某个模块的输出引脚绑定到全局变量上。这种方式不用额外拖模块但绑定关系藏在属性面板里时间久了容易忘不太推荐用于关键数据。读全局变量就更方便了。任意Group里的任意模块只要它的某个输入参数没有强制要求连线就可以从变量管理器里选择全局变量填入。比如在Group1的条码识别模块前面可以把Region参数绑定到Group0写入的坐标值实现“Group0先定位Group1再用定位结果裁剪ROI”的联动效果。4.3 变量同步的竞争问题多Group并行执行时有一个隐蔽的坑如果两个Group同时往同一个全局变量写入可能会出现覆盖竞争。比如Group0和Group1都在跑Group0在写“ResultX”Group1恰好也在写“ResultX”最后留下来的是后执行完的那一次写入值。解决这个问题的办法是变量分区。每个Group只写自己独有的变量读别人的变量、不写别人的变量。还是拿前面那个案例说定位组只写“PosX”“PosY”“PosAngle”读码组只写“BarcodeStr”“ReadStatus”两组变量完全隔离谁也覆盖不到谁。如果确实需要多个Group都向同一个汇总变量累加数据那就要靠组间握手来串行化——比如用两个信号变量互锁保证同一时间只有一个Group在写汇总变量。4.4 二次开发时的跨Group操作做上位机集成时通过VM的二次开发接口也可以读写全局变量。常规的套路是拿到方案对象后按名称索引到GlobalVar集合里的变量然后直接赋值或取值。这里有一个版本相关的细节不同小版本里全局变量的接口命名略有差异但大体思路都是“按名取变量、按属性读写值”。建议在开发环境里先用脚本或调试工具把变量列表导出来确认名称拼写再写进正式代码。因为变量名大小写不对、多了个空格这类低级错误往往要等运行时才能暴露排起来很浪费时间。5. 踩坑实录Group使用中的常见问题与排查链路5.1 问题一两个Group的相机取流不同步现象定位组和读码组虽然用的是同一个全局触发但图像出来总是差那么零点几秒导致产品已经移动了两个相机拍到的不是同一位置。排查链路先确认两个Group的触发模式。如果一个是全局触发、一个是本地触发那肯定没法同步。确认触发模式无误后再去查图像源的“触发源”设置。VM里图像源模块有“硬触发”和“软触发”之分如果用软触发每次触发是排队进相机驱动的两个相机的响应延迟不同就会差几毫秒。在软件触发无法满足同步精度的情况下改用相机硬件外触发——用一个外部光源控制器或PLC的脉冲信号同时触发两个相机的Line0输入这样图像采集的物理时刻是严格一致的。Group这边只要设置为“图像到达后开始执行流程”即可。这个问题最大的坑在于差几毫秒在界面上一眼看不出来必须观察最终测量值的稳定性才会发现。排查时建议先用示波器或相机SDK自带的时间戳功能对比两路图像的实际采集时刻。5.2 问题二修改某个Group的参数后另一个Group的结果跟着变了现象调了Group0里匹配模块的最小分数后Group1的读码结果突然不稳定了。排查链路先检查Group1的ROI是不是绑定了Group0输出的坐标。如果绑定了那不是“Group1被影响”而是Group0的定位坐标变了导致Group1截取图像区域跟着变了。这是正常的数据联动不是故障。如果确认没有跨组数据引用再看是不是两个Group共用了同一个图像源模块实例。这种低级错误通常在复制粘贴Group时发生——复制出来的Group没换图像源两个Group读的是同一路图像信号一交织看起来就像“参数互相影响”。最后一种可能是两组的模块名称冲突。VM里部分模块在跨组引用时按名称索引如果两个组里有同名模块某些版本下会解析到错误的模块对象上。处理办法简单粗暴每个Group的模块名加前缀比如“G0_Match”“G1_ReadCode”彻底避开重名。5.3 问题三界面切换到某个Group后点运行没反应现象方案里有三个GroupGroup0和Group1都能正常跑切到Group2点运行按钮流程纹丝不动。排查链路查Group2的触发模式。如果设成了“本地触发”但还没有绑定任何触发信号源那么按钮运行指令根本不会驱动它。查Group2里有没有放在前面、且没有数据来源的模块。比如图像源模块的类型不对或者图像源设置成了硬触发但相机没触发整个流程就会卡在取流这一步。查Group2的“启用”状态。方案树的属性面板里有一个启用选项如果你在设计过程中误取消了启用这个Group在运行方案时会被直接跳过。这个问题的本质是对“运行”边界理解不清VM的运行按钮操作的是当前激活的Group不是所有Group的总开关。多Group场景下要么把总控逻辑放在上位机由上位机逐个启动各Group要么确保所有Group都用全局触发并且方案运行时自动跟随全局触发信号。5.4 问题四二次开发时按名称切换Group失败现象代码里写死了groupName运行时却抛“找不到Group”的异常。排查链路最直接的原因是名称拼写不一致。检查字符串里有没有不可见字符——我遇到过从配置文件里读出来的Group名带一个换行符排查了大半天。再看Group的索引方式。有的版本支持按索引访问有的版本只支持按名称。如果你的方案里Group顺序调整过按索引拿到的可能已经不是起初那个组了。辅助定位的办法运行前把方案里所有Group的名称循环打印出来和代码日志对照一遍。不要凭记忆写名称以打印结果为准。5.5 实测下来最值得养成的几个好习惯把上面的坑总结一下我会给正在用Group的人三个建议命名即注释。Group名、模块名、全局变量名全部用带语义的英文或拼音不要用Group1、Group2这种默认名。方案文件在项目交接时后面接手的人会感激你。每个Group的流程首尾都加一个状态标志模块。首部加“开始信号”末尾加“结束信号”用全局变量写出来。排查问题时看这几个信号变量的更新情况能快速锁定是哪个Group没跑完、卡在哪一步。定期备份方案文件。VM的方案文件是明文或可导出的格式Group多了以后一个不小心的误拖动、误删除模块恢复起来比单Group项目麻烦得多。6. Group模块的进阶使用与个人体会6.1 利用Group做方案模板化我现在的项目习惯是维护一套“壳子方案”里面预置好了通信模块、全局变量定义、通用状态逻辑按功能预先拆好了几个空Group——相机1取流组、相机2取流组、结果汇总组、异常处理组。新项目拿到手直接复制这套壳子往各个Group里填对应的算法模块。模板化以后项目的启动速度明显快了公共逻辑的Bug也被提前吃掉只关注新项目的业务差异。这个做法对团队协作尤其有用。不同工程师负责不同检测工位时各自在自己负责的Group里开发互相之间只要约定好全局变量的名称和类型就不存在代码合并冲突的问题——因为方案文件是同一份但每个人的操作范围天然隔离在各自Group内部。6.2 结合条件分支实现柔性生产Group和条件分支模块配合起来可以做一套非常灵活的生产逻辑。我曾经做过一个项目同一台设备处理三种不同规格的产品。方案里建了三个检测Group每个Group针对一种产品调好算法参数再建一个“调度”Group它的工作是从PLC变量里读取当前生产型号然后根据型号去激活对应的检测Group同时把另外两个Group停掉。运行时切换产品的过程在界面上看起来很顺滑但实现时要特别注意切换Group的瞬间要确保检测Group没有在处理中间帧否则可能带着旧参数跑新产品的第一张图。我的做法是在调度Group里先发停止指令等待当前检测Group执行到末尾状态标志后再发下一条激活指令相当于做一个软件握手。这套逻辑用单流程做会很痛苦用Group做却非常自然。6.3 关于软件版本和调试环境聊了这么多最后补一个版本相关的提醒。VisionMaster不同版本之间Group相关界面的细节有差异比如右键菜单位置、属性面板里触发方式的叫法都可能有变化。我写这篇笔记时参照的版本是V4.2左右的操作逻辑如果你用的是更新的版本界面上可能有一些改动但核心概念——流程拆分组、独立触发、全局变量互通——是稳定的。调试多Group方案时我的建议是不要一次性把所有Group都打开编辑。先逐个Group单独验证确认每个Group在手动触发下都能输出正确结果然后两个两个地组合观察并行运行时是否有数据异常最后再全部一起跑做稳定性测试。分步组合的方式能让你快速定位问题出在哪个Group内部还是组间协同省去很多瞎猜的时间。另外调试时善用VM的单步执行功能。在某个Group里单帧单帧地走观察每一步的中间图像和结果数据尤其是多Group并行时这个操作只暂停当前Group其他Group还在跑能帮你确认是不是存在数据竞争或时序问题。单步执行下如果发现某个模块输出和实时执行时不一致多半就是跨组变量被篡改或共用资源被占用了。Group这个东西说到底就是为了让你在一个方案里从容地组织多路视觉逻辑。理清它的工作机制之后你会发现方案的结构化程度提升了不止一个档次——该并行的并行该隔离的隔离该联动的用全局变量牵线。这也是为什么我每做一个新项目都会多花十分钟把Group划分想清楚再加模块。结构想清楚了后面的路就好走很多。