ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

海康VisionMaster全局变量与脚本实战:跨流程通信与视觉引导核心技巧

海康VisionMaster全局变量与脚本实战:跨流程通信与视觉引导核心技巧 简介面向海康机器视觉软件开发者的轻量级项目代码包聚焦全局变量与全局脚本两大扩展能力。全局变量用于工程内跨流程共享数据支持模块参数订阅与结果绑定变量更新后订阅模块可自动同步最新值便于通讯数据接收与流程间交换。全局脚本基于C#语言提供流程控制、模块参数调整、全局通信等接口函数适合在图形化配置基础上补充自定义逻辑。代码包以具体示例演示全局变量如何配置、脚本接口如何引用与调试并展示通过通讯修改全局变量值的完整思路同时提示了常见使用注意事项可为视觉项目的模块化设计提供参考。资源共有三个文件以inscode、html与gitignore为主要类型压缩包仅7KB内容紧凑便于快速查看。目前已有756人学习浏览推荐给具备一定C#基础、熟悉海康视觉平台基本操作并希望提升工程复用与数据交互效率的开发者。1. 海康VisionMaster全局变量与脚本为什么单流程跑不通就得靠它俩海康VisionMaster全局变量与脚本说白了就是VM里两把“跨模块通行证”。我拆过不少海康VM项目发现真正的分水岭不是算法工具怎么配而是当需求变成“流程A测完的结果要喂给流程B”“定位角度要动态传给下一个工具”“要和PLC做信号握手”的时候全局变量和脚本就成了绕不开的硬要求。这篇笔记把VM的全局变量机制和C#脚本模块从原理到踩坑完整过一遍代码直接抄项目照着搭。适合正在用VM4.0做视觉引导、缺陷检测、多流程联动的工程师也适合刚装上海康VM软件、不知道从哪下手的初学者。2. 全局变量的设计逻辑跨流程通信与四种典型用法2.1 全局变量面板里到底能放什么类型、作用域与生命周期VM界面右侧有一个“全局变量”面板位置在流程编辑区的下方或右侧停靠具体看版本布局。这里的变量是整个工程级别的不归属于任何一个流程。也就是说你在流程A里写了它流程B、流程C都能读得到。这个“工程级作用域”是它和模块内局部变量最大的区别。新建变量的操作很简单在全局变量面板里点新增输入名称选类型设默认值。VM支持的类型大致有这些类型典型用途读写方式Int计数、状态码、整数阈值界面绑定 / 脚本读写Float位置、角度、测量值界面绑定 / 脚本读写Double需要高精度的坐标、尺寸脚本读写为主String产品型号、配方名、日志信息界面绑定 / 脚本读写Bool状态位、允许标志、流程互锁脚本读写为主Image跨流程传图像脚本读写生命周期这块容易被忽略。全局变量在方案加载时按默认值初始化运行时被脚本或模块修改后会一直保持到整个方案停止。方案停止后变量的值保持上一次的运行结果——如果你下次运行前没做重置很可能带着旧数据跑新一帧这是很多“第一帧结果不对”的根源。我一般会在流程最开始放一个“变量初始化”逻辑把需要清零的变量统一处理掉。2.2 四种典型用法数据中转、参数联动、结果汇总、跨流程状态位数据中转是使用频率最高的场景。比如一个方案里拆了两个流程流程1负责相机1的定位流程2负责相机2的缺陷检测但流程2需要知道流程1测到的产品偏移量。这时候把偏移量写进全局变量流程2的脚本或模块参数直接引用比在模块间拉线要清晰得多。逻辑上其实就是“生产者-消费者”模型一方写一方读。参数联动是另一个容易上手的功能。模块的输入参数可以直接绑定到全局变量上。举例来说二值化模块的阈值参数你在参数窗口里右键单击选择“链接全局变量”选一个Int类型的变量运行时就再也不需要手动改阈值了脚本里改变量就等于改参数。这个操作比在脚本里遍历搜索模块参数高效得多因为它绕开了模块内部的结构差异。结果汇总用于多相机、多工位项目。每个工位的检测结果独立出来一个Bool或Int变量最后做主流程的汇总判断时脚本一次性读回所有变量做与或逻辑。好处是每个工位的流程可以独立调试互不阻塞汇总逻辑单独维护。跨流程状态位是流程间同步的轻量方案。流程A处理完一帧图像后把一个Bool变量置为True流程B这边用“条件停止”模块轮询这个变量为True才继续往下跑。虽然不如用VM的流程调度器来得精致但在多流程并行的场景下这种方式直白、好排查、好维护。我见过不少现场用这种写法做“双相机交替触发”的握手信号。2.3 全局变量读写实操界面绑定与脚本读写的分工界面绑定适合静态的、需要调试时手动调整的参数。比如阈值、检测区域宽高、判定上下限这些。做法是在模块参数窗口找到目标参数右键选择“链接全局变量”弹出列表里选中或新建变量。这步做完参数右上角会多一个小链标记运行时会实时取变量的值。脚本读写适合动态计算、判断逻辑和批量赋值。VM的C#脚本模板里会注入对象常见写法大致是public int Run(Module module) { // 读取全局变量名称要和全局变量面板里完全一致 int threshold (int)module.GetGlobalVarValue(Threshold); // 读出来是 object用来做计算前先转型 double angle (double)module.GetGlobalVarValue(AngleOffset); // 计算一个补偿量写回全局变量 double compensated angle * 0.01 threshold; module.SetGlobalVarValue(Compensated, compensated); // 返回 0 表示脚本执行成功 return 0; }这里两个关键点。第一GetGlobalVarValue和SetGlobalVarValue是这套写法的核心入口参数传的是全局变量的名称字符串名称对不上就取不到值。第二读回来的类型是object必须做显式转型转型类型和你建变量时选的类型不一致运行时会直接抛异常。我在不少现场见过把Float变量读出来转成int导致溢出的情况数据全歪了但那不是脚本问题是类型没对齐。分工建议是这么定的固定不变的参数走界面绑定方便调试时肉眼观察凡是经过计算、判断、条件变化的值一律走脚本读写。两条路同时能用但混用的时候务必保证变量名不冲突大小写也算。3. C#脚本模块实战从读变量到写结果的完整链路3.1 脚本工具的界面与内置对象先搞清楚能碰什么VM4.0里的脚本模块在工具面板的“脚本”分类下拖进流程后双击就能打开编辑界面。界面大致分三块左侧或上方的代码编辑区、下方的运行日志区、以及一个“输入/输出配置”区。你自己第一次新建脚本模块时VM会生成一个模板模板里有入口函数和几个已经注入的对象。搞懂这些对象能干什么比背API重要得多。对象名职责常用操作GlobalVar全局变量的读写入口GetValue / SetValueOutput模块输出项的赋值给下游模块提供结果Image脚本输入口传来的图像取宽高、取像素数据Log运行时日志输出Info / Warn / ErrorModule当前模块上下文取上游模块输出、模块名Run运行控制查询当前运行状态新手最容易犯的错是试图在脚本里寻找所有模块的API把逻辑全写在脚本里。实际项目里我一般只让脚本干三件事读全局变量、计算与判断、写全局变量和输出项。视觉算法本身还是交给VM原生模块脚本做“胶水层”。这样脚本量小出问题也好定位。这个工具界面里还有一个编译按钮写完代码先编译编译通过后再跑流程。编译错误会在日志区直接列出比如变量名拼错、类型不匹配这时候VM的报错是比较好理解的照着修就行。真正头疼的是编译能过、运行才炸的那种后文避坑部分专门聊。3.2 第一段能跑的脚本读一个变量算完再写回去直接从最简单的开始读两个全局变量做一个阈值判断把结果写回第三个全局变量。这段代码解决的是“当测量值超过阈值时置位”的常见需求比如尺寸超差报警。public int Run(Module module) { // 1. 读取测量结果和上限阈值 double measure (double)module.GetGlobalVarValue(MeasureValue); double upperLimit (double)module.GetGlobalVarValue(UpperLimit); // 2. 判断是否超差 bool isOutOfTolerance measure upperLimit; // 3. 写回判定结果供流程后面的报警模块或PLC使用 module.SetGlobalVarValue(IsOutOfTolerance, isOutOfTolerance); // 4. 可选往日志里写一条运行记录 module.LogInfo(Measure measure.ToString(F3) Upper upperLimit.ToString(F3) Result isOutOfTolerance.ToString()); return 0; }这段逻辑不复杂但有三个细节值得说。第一ToString(F3)固定保留三位小数是为了日志整齐、方便对数值。第二LogInfo输出到模块的日志区跑完流程之后回看这里定位问题比盯着一堆数值猜要快。第三返回0表示成功返回非0表示运行失败——很多人在脚本里判断条件不满足时不知道怎么办直接返回-1就可以让流程停下来这是一个很实用的控制手段。这段脚本放到流程里之后可以手动改一个变量的默认值来验证把UpperLimit默认值调小运行一帧看IsOutOfTolerance是否变化。这个验证习惯能帮你确认变量读写是否真的打通了。3.3 脚本和视觉工具联动把定位结果变成下游参数再上一个难度。模板匹配模块跑完会输出一组定位结果里面有X坐标、Y坐标、角度。下游的测量工具需要根据这个角度动态调整自己的检测区域。第一步从脚本模块的上游拿到定位结果第二步算一个新的检测区域参数第三步写回全局变量让下游模块绑定。public int Run(Module module) { // 1. 获取上游模板匹配模块的输出常见输出项是 PositionX / PositionY / Angle double posX (double)module.GetModuleOutput(模板匹配, PositionX); double posY (double)module.GetModuleOutput(模板匹配, PositionY); double angle (double)module.GetModuleOutput(模板匹配, Angle); // 2. 把角度从度转为弧度供坐标旋转计算使用 double rad angle * Math.PI / 180.0; // 3. 计算检测区域中心点相对基准点的旋转偏移 double refX 100.0; double refY 80.0; double offsetX posX - refX; double offsetY posY - refY; double rotatedX refX offsetX * Math.Cos(rad) - offsetY * Math.Sin(rad); double rotatedY refY offsetX * Math.Sin(rad) offsetY * Math.Cos(rad); // 4. 写回全局变量供下游模块绑定 module.SetGlobalVarValue(TargetCenterX, rotatedX); module.SetGlobalVarValue(TargetCenterY, rotatedY); return 0; }GetModuleOutput的第一个参数是上游模块的名称这个名称必须和你流程里实际起的名字完全一致标点符号和空格都不能差。第二个参数是输出项名称不同版本的VM输出项命名有细微差别建议先在官方示例程序里查一下具体名字。坐标旋转公式是标准的二维旋转先把点平移到基准点旋转再平移回来。这里的refX/refY必须来自标定不能随手写我在现场一般通过“静态图手动测量”的方式先确定这个基准点。这套模式是VisionMaster项目里用的最多的结构上游视觉模块负责“看得见”脚本负责“算得准”下游模块负责“干得稳”。脚本卡在中间把视觉结果翻译成下游能用的参数。4. 把全局变量和脚本拼进项目视觉引导定位的完整流程4.1 项目需求拆解来料角度随机固定ROI必然翻车以一个我实际拆过的项目为例。产线来料是一块PCB板相机视野内板的旋转角度不固定大概在±30°之间浮动。原来用固定检测区域的老办法板子一转检测区域就扫到板外误报率直接失控。需求的本质是先定位再测量测量区域必须跟随板的实际姿态走。拆解下来要做三件事一是模板匹配确定板的中心坐标和角度二是根据角度把测量区域的位置和姿态动态算出来三是把计算结果传给测量模块。第一件事和第三件事VM原生模块就能做第二件事必须靠脚本。而且考虑到现场可能会切产品型号所有基准值都必须放进全局变量不能写死在脚本里。4.2 流程搭法与变量规划表流程结构如下按顺序排图像源、模板匹配、脚本动态ROI计算、卡尺测量、结果显示。其中脚本模块夹在模板匹配和卡尺测量之间承担“翻译官”的角色。变量规划是项目开始前最重要的一步。我习惯先列一张表明确每个变量是谁写的、谁读的、类型是什么再动手搭流程。这张表就是项目的数据字典。变量名类型写入方读取方含义RefCenterXDouble方案加载时初始化脚本基准点X坐标RefCenterYDouble方案加载时初始化脚本基准点Y坐标PartCenterXDouble脚本来自模板匹配卡尺测量板中心XPartCenterYDouble脚本来自模板匹配卡尺测量板中心YPartAngleDouble脚本来自模板匹配脚本、卡尺测量板旋转角度MeasureROI_XDouble脚本卡尺测量动态ROI中心XMeasureROI_YDouble脚本卡尺测量动态ROI中心YMeasureROI_AngleDouble脚本卡尺测量动态ROI旋转角InspectOKBool卡尺测量后置位PLC接口最终结果这张表的价值在于运行出问题的时候能很快定位是哪个环节的数据断了。变量名按“用途含义”的格式来避免叫temp、data这种没法维护的名字。现场调试时打开全局变量面板盯着这些值刷新哪一步没写入马上就能看出来。4.3 脚本核心计算坐标旋转与角度补偿脚本里解决的是最后一个数学问题已知板中心的坐标和旋转角求测量区域在图像里的实际位置和角度。public int Run(Module module) { // 1. 取上游模板匹配的定位结果 double posX (double)module.GetModuleOutput(模板匹配, PositionX); double posY (double)module.GetModuleOutput(模板匹配, PositionY); double angle (double)module.GetModuleOutput(模板匹配, Angle); // 2. 取基准点这是标定得到的固定值 double refX (double)module.GetGlobalVarValue(RefCenterX); double refY (double)module.GetGlobalVarValue(RefCenterY); // 3. 角度转弧度 double rad angle * Math.PI / 180.0; // 4. 测量区域相对基准点的固定偏移来自产品图纸或标定 double dx 35.0; double dy 20.0; // 5. 旋转后的坐标 double rotX refX dx * Math.Cos(rad) - dy * Math.Sin(rad); double rotY refY dx * Math.Sin(rad) dy * Math.Cos(rad); // 6. 写回全局变量让卡尺测量模块绑定 module.SetGlobalVarValue(MeasureROI_X, rotX); module.SetGlobalVarValue(MeasureROI_Y, rotY); module.SetGlobalVarValue(MeasureROI_Angle, angle); // 7. 日志输出方便验证计算过程 module.LogInfo(ROI_X rotX.ToString(F3) ROI_Y rotY.ToString(F3) Angle angle.ToString(F3)); return 0; }这里的dx和dy是测量区域在“板坐标系”里的固定偏移。什么意思呢就是当板完全不旋转时测量区域相对板中心的位置。这个值用什么渠道都要注意一个原则一旦产品换型只需要改这两个常量或者把它们提升为全局变量脚本本身不用动。角度补偿的数学本身不难难在来源数据的准确性——模板匹配的角度输出如果有0.5°的偏差乘上几百像素的臂长末端误差能到几个像素所以标定环节不能省。4.4 跑通的标准状态监控与结果校验流程搭完后不要急着连PLC。先手动跑静态图按下面的顺序验证第一步确认模板匹配输出的坐标和角度与实际图像里的板姿态对得上这一步看模板匹配模块的输出即可第二步确认脚本模块日志里打印的ROI坐标符合预期打开全局变量面板对照第三步把卡尺测量区域的显示开关打开看实际画出来的ROI是否贴合板的特征边。这套验证顺序对应着三个不同层面的风险视觉模块的定位准不准、数据链路的传递通不通、下游模块的执行对不对。任何一步不对都能在对应模块的位置直接定位。全部通过后再挂相机跑动态测试最后才接PLC。我在现场养成的习惯是永远保留一份静态图测试方案方案里有十几个不同角度、不同位置的板图每次改完脚本先跑一遍这个集合比现场临时调快得多。另外流程跑起来后要盯住模块状态栏绿色表示正常运行黄色是警告红色直接停。脚本模块如果返回了非0值状态栏会标红点开日志区看具体原因十有八九是类型转换或者变量名对不上。5. 全局变量与脚本的五个常见坑现象、原因、解决5.1 坑一脚本输入类型选IMAGE取到的宽高一直是0现象C#脚本模块的输入类型配置成IMAGE在脚本里读取当前图像对象的Width和Height编译、运行都正常但拿到的值始终是0。这段代码怎么改都没用同样的图像在图像源模块里显示尺寸正常。原因VM脚本模块的IMAGE输入拿到的是一个图像引用对象但引用是否已经完成像素数据装载取决于上游图像源模块的执行节拍和你取属性的时机。在脚本入口处直接读图像包装对象的尺寸属性很多时候取到的还只是初始化的空壳。解决最稳妥的方案是不用脚本的IMAGE输入口改用GetModuleOutput从上游已经完整跑完的模块里取尺寸信息。或者把输入类型改成“全局变量”用全局变量对象传递图像脚本里从全局变量拿到图像后再调用图像数据加载接口。我一般推荐前者因为流程结构清晰不额外占用全局变量口。5.2 坑二全局变量改晚了下游读到的是上一帧的旧值现象流程运行结果总是慢一拍下游模块用的是上一帧的位置数据。比如产品已经移位了定位工具还在按照前一帧的角度执行。原因流程里模块的执行顺序是线性的。脚本写变量的动作发生在某一时刻下游模块读变量发生在另一个时刻。如果你的生产节拍很快或者模块之间有并行关系下游模块可能在同一次执行周期里读到的还是旧值。这类问题在带并行分支的流程里尤其明显。解决先在流程图上确认所有用到该变量的模块都在脚本模块之后。如果存在并行分支就要在分支前加同步机制或者直接让流程串行化。我常用一个笨但有效的办法写一个Bool类型的“数据就绪”标志位脚本写完数据后置True下游模块做一个“等待标志位”的延时判断再开始执行。这样数据链路的时序就明确可控了。5.3 坑三脚本返回0但下游模块拿到的是空结果现象脚本模块运行显示绿色没有报错日志也打了但绑定脚本输出的下游模块提示“结果为空”或者拿到默认值。原因脚本的返回值代表执行状态不代表输出项赋值成功。很多人在脚本里只写了module.SetGlobalVarValue忘了给脚本模块本身的Output输出项赋值。绑定到下游模块的是Output输出项如果你没有给它赋值下游拿到的自然是空。解决在脚本模板的Output对象上调用赋值方法把要传给下游的结果显式写进去。做法是先在下游模块的绑定列表里确认它实际绑的是哪个输出项然后在脚本里用Output对象对那个输出项赋值。写完这个没顺手在日志里把写出的值打一遍确保不是空。5.4 坑四全局变量改名后脚本不报编译错运行到一半才崩现象在全局变量面板里把一个变量从SizeTolerance改成SizeToleranceNew脚本里没同步改。编译顺利通过运行到脚本模块时直接抛异常日志提示“找不到变量”。原因VM对全局变量名的解析发生在运行时而不是编译时。脚本编译器不会去校验字符串里引用了一个根本不存在的变量名因为GetGlobalVarValue的参数本身就是一个字符串变量编译期无法判断内容。只有运行到这一行实际去全局变量池里查找时才会发现名字对不上。解决改变量名的同时必须全文搜索脚本代码里的引用点。更稳妥的做法是在脚本里定义一个字符串常量统一管理变量名比如private const string VAR_TOLERANCE SizeTolerance;用到的地方都引用这个常量。改名字只需要改一处。从那以后我每次新建脚本第一件事就是在顶层把用到的所有变量名定义成常量一是防改名遗漏二是看代码就知道这个脚本依赖哪些全局变量。5.5 坑五相机分辨率一变脚本里按固定像素算的ROI全偏了现象项目调试阶段换了台相机或者采集模式从全分辨率改成ROI采集模式原来调试好的检测区域全部偏移模板匹配没问题但后续测量结果乱七八糟。原因脚本里存在硬编码的像素坐标、固定偏移量。这些值在原来的分辨率下是正确的但分辨率改变之后图像的坐标体系变了所有像素位置都要等比换算固定值没有跟着变。解决全项目禁用魔法数字。所有像素坐标和偏移必须要么放进全局变量要么根据图像宽高实时计算。我一般在脚本入口处取一次当前图像的宽高所有固定坐标都按比例换算成相对坐标。这样换相机、换分辨率之后只需要重新标定一次基准点脚本本身不用改。以后凡是脚本里出现大于50的数字常量我都会停下来审一遍它是不是该写成变量。6. 进阶用脚本日志和全局变量把运行过程“看”清楚项目进入联调阶段后最头疼的不是功能不会做而是出了问题不知道现场到底发生了什么。VisionMaster的流程像是一个黑匣子——每个模块的输出值可以点开看但几十个模块、几十个变量逐个翻效率太低。我常用的做法是把调试能力直接做进脚本里。第一招是分级日志。脚本里用LogInfo记录关键节点用LogWarn记录异常但不中断的情况用LogError记录会导致流程失败的情况。日志输出带上变量名和值比如module.LogInfo(PartAngle angle.ToString(F3))。运行完后直接看脚本模块的日志区数据链路哪一步断了一目了然比盯着全局变量面板猜测快得多。第二招是用全局变量做运行标志位。在脚本里给一个Bool变量写值这个变量同时绑定到流程里一个“条件显示”模块的使能端。脚本运行到某个位置时这个标志位翻转界面上对应的模块就跟着高亮状态变化。这是一个不需要断点的“软断点”比临时加输出暂停模块要灵活。第三招是结果回传前做范围校验。脚本里对即将写回全局变量或输出到PLC的每个数值先判断是否在合理范围内超范围就把值钳位到边界值同时打一条Error日志。这样PLC那边永远不会因为一个异常大的数产生误动作同时日志里留下了足够排查的记录。调试手段适用场景开销LogInfo 节点日志常规数据链路检查低软断点标志位条件性运行验证低输出范围钳位与PLC联调时保护低临时输出暂停模块单步观察图像状态高我记得有一次现场半夜联调产线反馈定位偶尔偏一个像素时好时坏。当时我面对的是一堆没有日志的脚本只能一遍遍加输出模块、跑图、看结果折腾到天亮才发现是一个全局变量在另一个流程里被周期性重置了。数据链路没打通不可怕可怕的是没有把链路“看”清楚的手段。从那以后我每次搭VM方案都会强制把全局变量规划表先建好再逐条做日志输出范围校验等到最后再写正式的判断逻辑。希望帮到你。本文还有配套的精品资源点击获取
返回列表