资讯动态

海康VisionMaster全局变量与C#脚本:从数据共享到掉电恢复的工程实践

发布时间:2026/10/17 16:54:18 来源:尧图企业网站定制
简介面向海康VisionMaster二次开发者的项目源码包聚焦全局变量与全局脚本两项进阶功能。全局变量可在工程内跨流程共享数据、订阅模块参数或绑定结果全局脚本基于C#能实现流程控制、模块参数配置及全局通信。压缩包共3个文件以inscode工程文件、index.html说明文档和.gitignore配置为主整体仅7KB结构精简便于快速查看核心代码并迁移到本地工程。已有756人学习下载。代码中演示了如何添加引用、调试脚本并通过通讯场景用例展示如何用全局脚本设置全局变量值同时附带接口说明与注意事项可帮助开发者掌握全局变量在通信数据接收与流程间数据交换中的实际用法适合有一定C#基础、希望提升VisionMaster工程模块化与数据交互效率的开发者参考。1. 海康VisionMaster里的全局变量与脚本为什么“检测结果连线”解决不了现场逻辑刚接触海康VisionMaster的人大多是从拖流程开始的采图、定位、测量、显示一路把模块输出口拉到下一个输入口流程就能跑出结果。可等真正接手项目就会发现测量结果要发给PLC做分选要在两条独立流程之间共用同一个判定结论要按当前产品型号去切算法参数——这些需求靠模块之间的连线根本搭不出来。模块输出只能沿着流程线往前走走到头就断了它不会自己变成另一个流程能读到的数据。全局变量和C#脚本就是VisionMaster留给现场逻辑的口子。全局变量负责把数据从“流程内部”提升到“整个软件进程”层面C#脚本负责在这层数据上写判断、循环和业务规则。这篇文章从全局变量的作用域讲起把脚本读写全局变量的最小工程、触发方式、输入输出接法和掉电恢复一起讲完适合正在做视觉项目落地、被“结果只能连线不能写逻辑”卡住的工程师照着做。2. 全局变量的本质作用域、常用类型和脚本读写方式2.1 全局变量是“进程内共享”不等于跨设备存储先分清一个概念VisionMaster里的全局变量指的是在当前VM软件进程内、多个流程之间都可以访问的变量它服务的对象是“流程复用”和“数据解耦”。这和C里的全局变量思路一致——在一个进程里定义、在一个进程里共享进程退出数据就回到初值。很多从C转过来的工程师会下意识以为“全局变量持久化存储”其实不是它只是把变量的可见范围从单个模块扩大到了整个软件运行实例。在VM软件里每个流程模块的输出参数属于局部数据出了这个模块就没了。全局变量则挂在流程结构的顶层一个流程里写进去另一个流程里能读出来脚本模块也能读写。这个特性决定了它的典型用途跨流程传递产品型号、保存累计计数、存放PLC交互数据、缓存标定结果。它解决的是“数据在哪里等”的问题而不是“数据往哪里去”的问题——后者仍然要靠流程连线和通讯模块完成。2.2 五个常用变量类型用错类型是脚本报错的第一来源全局变量管理窗口里新建变量时第一件事就是选类型。类型选错后面脚本里全是隐式转换和空引用问题。下面是我在项目里常用的五类类型典型用途注意点Int32计数、工位号、循环次数自增操作要考虑多流程并行时的原子性Double测量值、标定结果、坐标偏移精度足够但和float混用时会有隐式截断String产品型号、条码、配方名判断时注意大小写和首尾空格BoolOK/NG结论、启用开关多个流程同时写同一个Bool时要约定写权限Object图像、区域、形状等算法结果脚本里取出后需要强转成具体类型再使用Object类型最容易被新手拿来“一招鲜”什么都能存但什么都要自己判空、自己转换。能用具体类型表达的数据不要用Object。例如存图像可以用Image类型存测量结果用Double只有像“不确定上游输出是什么只能先托管”的场景才值得用Object。变量的初值也建议设置成有意义的值比如计数从0开始、Bool从false开始不要留空否则脚本第一次读到null会直接抛异常。2.3 C#脚本里读写全局变量两种常见写法与兼容思路脚本模块读写全局变量不同版本的VisionMaster在API外壳上略有差别但思路是统一的先通过全局变量管理器拿到变量对象再读Value或写Value。我常用的写法是带泛型的版本代码意图最清楚using System; using VM.GlobalVariable; public class Script : IScript { public bool RunScript() { // 通过泛型方法拿到全局变量对象再读写Value GlobalVariablestring productNo GlobalVariable.GetVariablestring(g_ProductNo); string current productNo.Value; productNo.Value P12345; return true; } }这段代码做了三件事拿到名为g_ProductNo的全局变量对象读取当前值到current变量里把新值写回去。关键在第三步——VisualMaster的全局变量对象是引用型封装直接给Value赋值就能生效不需要额外的“提交”或“保存”动作。泛型的作用是在编译期就确定变量类型避免运行到一半才发现类型不匹配。国内很多VisionMaster 4.0示例程序里还能看到另一种写法通过变量名的索引直接取值比如GlobalVariable[g_ProductNo]。这种写法代码更短但拿到的对象是object类型每次使用都要强转一旦变量名写错编译期发现不了运行期才报“变量不存在”。我的建议是新项目一律用泛型写法变量名统一定义成常量后面避坑章节会单独展开。另外读写全局变量的脚本本身不要依赖流程线顺序因为全局变量的更新时机不一定和当前流程同步脚本里如果连续读两次两次之间可能有其他流程插进来改值。3. 从新建变量到跑通最小可复现的全局变量脚本工程3.1 第一步新建全局变量并设置合理初值打开VM流程编辑界面在菜单或工具栏找到“全局变量”管理窗口不同版本入口位置略有差异但通常都在流程结构的顶层工具栏里。新建三个变量覆盖脚本练手最常见的三种类型g_ProductNo类型String初值填Defaultg_LastResult类型Bool初值填falseg_OkCount类型Int32初值填0初值不是随便填的。String变量不填初值脚本第一次读取时拿到的是空字符串和null混在一起字符串函数会直接报空引用Bool变量不填初值默认false倒是能跑但逻辑上容易误导人计数类变量一定从0开始这是所有累计逻辑的基础。变量名列建议统一用“g_”前缀和局部变量、模块参数区分开。3.2 第二步添加C#脚本模块并熟悉脚本工具界面在流程工具箱的“脚本”分类下找到C#脚本模块有的版本里叫“VM脚本”或“脚本工具”拖到流程编辑区后双击进入脚本工具界面。界面分为三块区域左侧是变量引用和输入输出配置中间是代码编辑区下方是编译信息窗口。脚本工具界面第一次打开时代码编辑区会有一段模板代码保留它在RunScript方法体内写逻辑即可。配置输入类型时要克制如果脚本只做全局变量读写和逻辑判断把输入类型设为“无”或“None”不要选Image。输入类型一旦设成ImageVM运行时会强制把上游图像输出接到这个模块没有连线就传空图后续取图像宽高全为0这是新手最常踩的坑。脚本里需要引用第三方程序集时在脚本工具界面的“引用”区域添加dll路径添加完保存脚本编译信息窗口会显示编译是否通过。3.3 第三步写一个综合读写脚本using System; using VM.GlobalVariable; public class Script : IScript { // 变量名收敛成常量避免字符串手敲出错 private const string VAR_PRODUCT g_ProductNo; private const string VAR_RESULT g_LastResult; private const string VAR_COUNT g_OkCount; public bool RunScript() { try { // 读全局变量中的产品型号 string product GlobalVariable.GetVariablestring(VAR_PRODUCT).Value; // 模拟业务判断P开头的型号算作合格品 bool isOk product.StartsWith(P); // 写回判定结果 GlobalVariable.GetVariablebool(VAR_RESULT).Value isOk; // 合格品计数加一 if (isOk) { int count GlobalVariable.GetVariableint(VAR_COUNT).Value; GlobalVariable.GetVariableint(VAR_COUNT).Value count 1; } // 调试输出函数名以当前版本为准 OutputLog(product product , isOk isOk); return true; } catch (Exception ex) { OutputLog(run error: ex.Message); return false; } } private void OutputLog(string message) { Console.WriteLine([Script] message); } }这段脚本的逻辑链路是读型号 → 做判定 → 写结果 → 计数自增。注意计数那两行先读再写不是直接加一因为全局变量的Value是属性不能像普通局部变量那样直接自增。try/catch是脚本的保命结构任何一行抛异常脚本都会返回falseVM会把这个模块判定为执行失败日志里能看到具体原因。如果项目要求严格还可以在catch里把异常信息写进全局变量方便流程下游把错误信息一并发送出去。3.4 第四步用日志和变量监视验证读写结果脚本写完后在流程运行前先做一次“静态检查”保存脚本看编译信息窗口有没有报错。然后把g_ProductNo的初值改成P12345运行整个流程再回来看三个全局变量的当前值。g_LastResult应该变成trueg_OkCount应该变成1这就是一次完整的读写闭环。调试输出是这阶段最趁手的工具但我见过不少工程师把Console.WriteLine写到脚本里然后找不到输出在哪。Console.WriteLine实际输出到VM的日志窗口或调试输出窗口不同版本位置不同建议在脚本里直接调平台提供的日志接口如果没有就用“写全局变量→在变量监视窗口观察”这个方式代替。变量监视窗口能看到每个全局变量的实时值和最后修改时间比日志更直观也能用来发现问题值没变说明脚本没执行或没写进去值变了但不对说明业务逻辑有偏差。4. 让脚本在自动化流程里真正跑起来触发时机、连线设计与多流程协作4.1 触发时机脚本随流程执行不随线程单独运行C#脚本模块在VisionMaster里不是一个常驻后台线程它依附于所在流程的执行节奏。流程被触发一次脚本模块就在该次执行的对应位置运行一次流程没有被触发脚本代码不会自己醒来。这一点很重要因为很多从Web后端转过来的工程师会习惯性认为“脚本服务”想用它做持续监听结果发现PLC数据到了但脚本没反应。常规项目的做法是让脚本跟随流程每周期执行在脚本内部判断“这次有没有新需求”。比如PLC把请求写到全局变量g_Cmd脚本每次运行都检查g_Cmd的值发现是“拍照请求”就执行对应动作执行完把g_Cmd复位。这种轮询式写法简单可靠代价是脚本每次流程周期都会被调用代码里不要做重计算耗时超过一个流程周期会拖慢节拍。如果要做设备老化测试那种全自动跑批这种周期轮询反而是最容易观察状态的模式。4.2 输入输出连线的三种接法纯逻辑、图像处理、数值接收第一种是纯逻辑模式脚本模块不接任何输入数据全部从全局变量读结果全部写回全局变量。这种接法最解耦脚本不关心上游是谁上游也不关心脚本内部逻辑适合做判定、计数、组合逻辑。项目中大部分全局变量脚本建议都用这种模式后续调整流程顺序时脚本不用动。第二种是图像处理模式脚本输入类型选Image上游图像源拉到脚本输入口。这种模式要特别注意空图判断上游采集失败时脚本拿到的图像对象可能是Null或空图。代码里先判空再取宽高不要直接访问属性。第三种是数值接收模式上游测量模块的输出比如宽度、角度、坐标直接连到脚本模块的数值输入口脚本在RunScript里通过输入参数取用。这种模式适合做“测量结果后处理”但会引入流程顺序依赖连线断了或上游模块改输出名脚本就要跟着改。4.3 全局变量在“测量→判定→分选”链路里的位置一条典型的分选链路是这样的第一台相机做定位测量测量结果写进全局变量g_MeasX、g_MeasY、g_Angle第二道流程里脚本模块读取这三个变量结合产品型号g_ProductNo做角度补偿和OK/NG综合判定把结论写进g_Result最后通讯模块把g_Result和测量值一起发给PLC分选。这个链路里全局变量起到的作用是“跨流程解耦”。测量流程不需要知道后续有没有脚本脚本也不关心测量流程在流程线上的哪个位置两边的耦合点只是全局变量名。后期要增加测量项只需要在测量流程里补写全局变量脚本逻辑跟着扩展不用重新接线。如果两个流程实际是并行执行的或者分属不同流程结构这种解耦是唯一能稳定协作的方式。经验是给每个全局变量约定好写入方和读取方写入方只写、读取方只读避免两边同时写一个变量后面避坑章节会讲这个问题的具体后果。5. 全局变量与脚本的避坑清单五条血泪经验按现象→原因→解决5.1 脚本输入类型是Image获取到的Width和Height都是0现象脚本模块输入类型选了Image也在代码里取了图像宽高但运行结果里Width和Height全是0图像处理逻辑全部失效。原因这个坑多半不是代码写错而是输入配置和实际数据对不上。输入类型选了Image但流程运行时没有把上游图像输出接到脚本模块或者上游传过来的本身就是空图像对象另一种情况是输入类型里配的是Image上游实际输出的是Region或Shape类型不匹配时VM不会主动报错而是传个空对象进来。还有一种容易被忽略的情况脚本是在模块初始化的阶段被调用的此时图像还没有完成分配取宽高自然为0。解决先确认输入配置里Image输入口有真实的连线然后在脚本里做空判if (inputImage null || inputImage.Width 0) { return false; }如果脚本的主要目的是逻辑判定而不是图像处理干脆把输入类型改成“无”图像通过全局变量Object或用文件路径读取避免被图像输入绑定拖累。代码里做宽高判断时用“Width 0”作为异常条件不要只判断等于0因为某些异常图像宽度可能是负数或极大值。5.2 变量写得很勤另一个流程拿到的却是旧值现象流程A的脚本里给g_Result写了true切到流程B去读g_Result读到的还是上一次的false有时多跑几个周期又变对了看起来像“慢半拍”。原因最常见的原因是两个流程不在同一个运行实例里。全局变量属于当前软件进程但如果你开了多个流程文件或者用调试模式单独运行了某个流程这个流程里的全局变量集合和主流程不是同一套读的自然不是同一次运行写入的值。另外流程A写变量和流程B读变量如果发生在同一个流程周期的不同阶段读到的可能是写入前的快照值。解决确认两个流程挂在同一个流程文件下运行的是同一个主流程。需要读写同一个变量的关键模块建议加日志把“写入时刻的值”和“读取时刻的值”都打出来对比时间线。如果流程B必须立刻读到最新值就把读操作放在它自己的脚本模块里、通过全局变量读取而不是在界面绑定里做一次性映射。变量名复制粘贴不要手打这类问题一半以上出在变量名差一个字符上。5.3 脚本编译通过运行时报“Variable not found”现象脚本保存时编译正常一到流程运行就抛异常提示找不到某个变量检查全局变量管理窗口变量明明还在。原因变量名不匹配通常是脚本里写的变量名和全局变量窗口里的名称存在不可见差异比如前后空格、中文全角括号、大小写不一致。另一个隐蔽原因是你在全局变量窗口删掉过同名变量又新建了一个脚本里的变量名没跟着变等于引用了一个已经不存在的对象。VM的全局变量名对空格和大小写敏感程度因版本而异不要赌它不敏感。解决把变量名定义成常量集中放在脚本头部然后从全局变量窗口复制名称粘贴到常量里。private const string VAR_PRODUCT g_ProductNo; // 从全局变量窗口复制过来同时养成一个习惯变量名里只用字母、数字和下划线不要用中文、空格和点号。中文变量名在界面里看着没问题但某些版本在脚本编译或序列化时会产生编码差异。运行期一旦报变量找不到先去全局变量窗口导出变量清单和脚本里的常量逐条对比。5.4 多流程并行写同一个计数变量数量时对时错现象两个流程都在给g_OkCount做加一操作单独跑任何一个流程计数都正常两个流程并行跑一段时间后计数比实际合格品数量少而且每次少的数量不一样。原因这是典型的并发写问题。全局变量的“读→加→写”不是原子操作两个流程同时读到count100各自加一再写回结果都是101其中一次自增被覆盖了。单个流程内不会有这个问题因为流程是串行执行的多流程并行时脚本代码里的自增操作完全裸奔。解决用一个进程内锁把计数操作保护起来VisionMaster脚本里常见做法是定义一个静态锁对象private static readonly object countLock new object(); lock (countLock) { int count GlobalVariable.GetVariableint(VAR_COUNT).Value; GlobalVariable.GetVariableint(VAR_COUNT).Value count 1; }加锁会让多个流程的自增操作排队执行牺牲一点实时性换计数准确在正常节拍下影响可忽略。如果这种计数逻辑分散在多个脚本模块里每个模块都要用同一个锁对象不要各自new一个。计数类全局变量尽量约定为“单一写入方”其他流程如果需要计数结果只读不写从设计上避免并发。5.5 重启VM后全局变量回到初值上一班的数据全丢了现象白班跑了八个小时计数、良率、当前产品型号都在全局变量里夜班重启VM软件所有值回到初值报表数据对不上。原因VisionMaster的全局变量是内存变量软件关闭即清空设计上就不做自动持久化。初值只在流程启动或变量创建时写入一次之后的运行值都在内存里。这不是故障是机制但很多项目上线前没人跟甲方说清楚这一点导致现场数据丢失后引发客诉。解决关键变量要做“主动保存”。常见做法是在脚本里定期将需要持久化的变量序列化成文本文件或写入本地数据库流程启动时先读取文件恢复变量值。保存时机可以放在流程结束时、换型开始时或者定时每N秒存一次。最简单的文本格式是每行一个“变量名值”脚本启动时逐行解析。注意保存文件和读取文件要用固定路径不要用相对路径否则VM启动目录变了就找不到文件。这个机制不需要做得很重能保证断电重启后产品型号和累计计数不丢就够用测量值这类过程数据按需决定要不要保存。6. 进阶用全局变量做产品切换与断电恢复一个可复用的脚本框架项目跑过一段时间后你会发现全局变量脚本的真正价值不在单点功能而在“把整个现场逻辑串起来”。我现在的做法是每个项目开始先建一张全局变量清单表列出变量名、类型、初值、写入方、读取方、是否需要掉电保存。这张表在开发阶段用来对代码在验收阶段用来跟甲方确认行为在维护阶段用来排查问题。它比代码注释可靠得多因为代码会改表格一旦和现场核对过就变成了事实标准。产品切换是这套框架最典型的应用。PLC换型时把新型号写到g_ProductNo脚本从全局变量读到型号后在一张预先配置好的型号参数表里查对应的判据阈值和算法参数再通过VM的模块参数接口批量下发给测量模块。标定结果也可以存在全局变量里换型时直接取用不需要重新走一遍标定流程。这样一来换型逻辑全部集中在脚本和变量里流程图保持干净现场调试时只需要改参数表不用动流程结构。断电恢复我习惯做得很轻在脚本里定义一个SaveState和RestoreState分别负责把关键变量写进本地文件和启动时读回。保存文件用JSON格式字段名和变量名保持一致读回时逐项检查类型缺失的字段用初值兜底。有些工程师会在这个环节里堆太多代码想把所有变量都做持久化结果保存和恢复逻辑比业务逻辑还复杂。我会克制地把“必须持久化”的变量控制在五到八个其余变量各流程自己决定初值就够了。要验证这套框架靠不靠谱最直接的办法是拿设备老化测试那种全自动跑批来压让机器连续跑一个班次中途人为断电重启两次重点看三点计数有没有漂移、产品型号恢复后对应的参数有没有切换正确、日志里有没有变量读写异常。跑完一个班次不出问题这套变量框架就立住了。我现在每做一个VisionMaster项目都先建变量清单、再写脚本、最后连线顺序颠倒往往会返工希望帮到你。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑