你有没有遇到过这样的场景:在VisionPro里拖拽了几个视觉工具,流程跑通了,结果也正确,但一到批量处理或者需要和其他系统对接时,就发现手动操作效率太低,错误率却居高不下?
上个月,我帮一个做精密零件检测的团队排查问题,他们的质检流程已经用VisionPro设计好了,每个工位的相机、光源、标定都到位,但产线老师傅还是得一个个点“运行”、看结果、记录数据。半天下来,眼睛累,手也酸,最关键的是——人为漏检、误判根本无法避免。
问题的核心不在于VisionPro工具本身不好用,而在于他们只用了图形化界面,没把工具块(ToolBlock)的脚本能力真正用起来。ToolBlock脚本,才是把单次成功变成可复用、可批量、可集成流程的关键。
但很多人对ToolBlock脚本的理解还停留在“写几行代码调用工具”的层面。实际上,它真正解决的是三类问题:
- 把固定流程变成可配置流程:通过参数化输入,同一个ToolBlock能适应不同尺寸、不同光照条件的零件。
- 把孤立工具连接成智能工作流:让前一个工具的输出,能作为后一个工具的输入或判断条件。
- 把界面操作变成自动化指令:实现批量处理、结果记录、异常报警、甚至控制外部设备。
下面,我们就从最基础的脚本结构开始,一步步拆解如何编写真正能用于生产环境的ToolBlock脚本。
1. 先搞清楚ToolBlock脚本的本质:它不是“写代码”,而是“搭积木”
很多人一听到“脚本”就想到要写大段C#或VB.NET代码,这是第一个误解。ToolBlock脚本的核心,是让你用类似编程的逻辑,把VisionPro内置的视觉工具(如CogPMAlignTool、CogCaliperTool等)组织起来,但它本身是基于VBScript语法,并且深度集成在VisionPro环境中的。
1.1 脚本编辑器的两个入口:全局脚本与工具块脚本
在VisionPro中,你有两个地方可以写脚本:
- 作业编辑器中的全局脚本:用于控制整个作业的流程,比如作业开始、停止时的初始化或清理操作。
- ToolBlock内部的脚本:这才是我们今天重点要讲的,它直接绑定到某个具体的ToolBlock,用于控制该ToolBlock内部工具的运行顺序、数据传递和条件判断。
进入方式很简单:双击打开一个ToolBlock,在界面下方你会看到“脚本”标签页。点击后,就能看到脚本编辑器。
1.2 脚本的基本结构:Inputs, Outputs, RunScript
一个标准的ToolBlock脚本包含三个部分:
' 输入端子定义 Public Sub SetInputs(ByVal pToolBlock As Object, ByVal Inputs As Object) ' 这里定义从外部传入ToolBlock的参数 End Sub ' 输出端子定义 Public Sub GetOutputs(ByVal pToolBlock As Object, ByVal Outputs As Object) ' 这里定义ToolBlock向外部输出的结果 End Sub ' 主运行逻辑 Public Sub RunScript(ByVal pToolBlock As Object) ' 这里是ToolBlock每次执行时的核心逻辑 End Sub- SetInputs:负责接收外部传入的数据,比如图像、参数设置、控制信号等。
- GetOutputs:负责将ToolBlock内部的计算结果传递出去,比如定位坐标、测量值、判断结果等。
- RunScript:这是脚本的核心,ToolBlock每次运行时都会执行这里的代码。你在这里调用各个视觉工具,并根据它们的输出决定下一步动作。
很多新手会直接跳过Inputs/Outputs,把所有逻辑都写在RunScript里,这会导致ToolBlock难以复用和调试。正确的做法是:把需要外部控制的变量定义为输入,把需要对外暴露的结果定义为输出。
1.3 脚本中如何调用ToolBlock内的工具
假设你的ToolBlock里放了一个名为“CogPMAlignTool1”的模板匹配工具,在脚本中调用它的格式是:
Public Sub RunScript(ByVal pToolBlock As Object) ' 调用模板匹配工具 pToolBlock.Tools("CogPMAlignTool1").Run() ' 检查工具是否运行成功 If pToolBlock.Tools("CogPMAlignTool1").Status = CogToolStatusConstants.Accepted Then ' 成功找到模板,可以继续后续处理 Else ' 匹配失败,可能需要记录日志或触发异常处理 End If End Sub这里的关键是pToolBlock.Tools("工具名")这个语法,它让你能在脚本中直接访问到ToolBlock内每个工具的属性和方法。
2. 为什么单次跑通不等于能稳定批量使用?脚本的健壮性设计
我见过太多案例:在理想光照、标准样品下,ToolBlock脚本运行完美;一旦放到产线上,光照波动、零件位置偏差、表面污渍等问题出现,脚本就直接卡死或输出乱码。
问题的根源在于——脚本没有考虑异常情况。一个只能处理“理想情况”的脚本,是没有生产价值的。
2.1 必须处理的三种异常状态
每个VisionPro工具运行后,都会返回一个Status状态值。在脚本中,你必须检查这个状态:
Public Sub RunScript(ByVal pToolBlock As Object) ' 运行模板匹配 pToolBlock.Tools("CogPMAlignTool1").Run() Select Case pToolBlock.Tools("CogPMAlignTool1").Status Case CogToolStatusConstants.Accepted ' 工具运行成功,结果可信 ' 继续后续处理... Case CogToolStatusConstants.Warning ' 工具运行有警告,结果可能不完全可靠 ' 记录警告信息,决定是否继续或报警 Case CogToolStatusConstants.Error, CogToolStatusConstants.Timeout ' 工具运行失败或超时 ' 必须处理错误,不能直接继续后续工具 End Select End Sub实际经验:在产线环境中,我通常会为每个关键工具设置独立的异常处理逻辑。比如模板匹配失败时,尝试调整搜索区域或使用备用的匹配方法,而不是直接报错停机。
2.2 输入参数的验证:不要让错误数据进入视觉工具
脚本的输入参数可能来自其他ToolBlock、外部系统,甚至人工输入。如果不对输入进行验证,很容易导致工具运行异常。
Public Sub SetInputs(ByVal pToolBlock As Object, ByVal Inputs As Object) ' 验证输入的图像是否有效 If Inputs.Image Is Nothing Then Err.Raise 1001, , "输入图像不能为空" End If ' 验证搜索区域坐标是否合理 If Inputs.SearchRegionX < 0 Or Inputs.SearchRegionY < 0 Then Err.Raise 1002, , "搜索区域坐标不能为负值" End If ' 将验证通过的参数赋值给工具 pToolBlock.Tools("CogPMAlignTool1").SearchRegion = Inputs.SearchRegion End Sub关键点:在SetInputs阶段就拦截明显错误的输入,比在RunScript中处理工具运行失败要高效得多。
2.3 超时控制:防止脚本卡死整个系统
在产线环境下,一个工具如果因为异常情况长时间运行,可能会阻塞整个检测流程。为关键工具设置超时是必要的:
Public Sub RunScript(ByVal pToolBlock As Object) ' 设置超时时间(毫秒) pToolBlock.Tools("CogPMAlignTool1").Timeout = 5000 ' 5秒超时 pToolBlock.Tools("CogPMAlignTool1").Run() If pToolBlock.Tools("CogPMAlignTool1").Status = CogToolStatusConstants.Timeout Then ' 记录超时日志 ' 可能尝试其他方法或直接报错 End If End Sub3. 数据传递的进阶用法:解决“VisionPro的输出端子不支持数组传输”问题
这是很多人遇到的真实痛点:VisionPro的单个输出端子确实不能直接传输数组,但实际检测中,我们经常需要处理多个匹配结果、多个测量值等数组数据。
3.1 用字符串拼接实现数组传输
虽然不能直接传输数组,但我们可以把数组数据转换成字符串格式传递:
Public Sub GetOutputs(ByVal pToolBlock As Object, ByVal Outputs As Object) ' 假设有多个匹配结果需要输出 Dim results As String results = "" If pToolBlock.Tools("CogPMAlignTool1").Status = CogToolStatusConstants.Accepted Then Dim matchResults As CogPMAlignResults Set matchResults = pToolBlock.Tools("CogPMAlignTool1").Results For i = 0 To matchResults.Count - 1 If results <> "" Then results = results & ";" ' 用分号分隔 results = results & matchResults(i).TranslationX & "," & matchResults(i).TranslationY Next i End If Outputs.MultipleResults = results ' 输出格式:"100,50;150,80;200,30" End Sub在接收端,再按相同的规则解析这个字符串。这种方法虽然不够优雅,但在很多实际场景中足够实用。
3.2 通过多个输出端子传递结构化数据
如果需要传递更复杂的数据结构,可以设计多个输出端子来分别传递不同部分的数据:
Public Sub GetOutputs(ByVal pToolBlock As Object, ByVal Outputs As Object) If pToolBlock.Tools("CogPMAlignTool1").Status = CogToolStatusConstants.Accepted Then Dim matchResults As CogPMAlignResults Set matchResults = pToolBlock.Tools("CogPMAlignTool1").Results ' 传递结果数量 Outputs.ResultCount = matchResults.Count ' 传递第一个结果的位置(最匹配的结果) If matchResults.Count > 0 Then Outputs.PrimaryX = matchResults(0).TranslationX Outputs.PrimaryY = matchResults(0).TranslationY Outputs.PrimaryScore = matchResults(0).Score End If ' 传递是否有多个结果 Outputs.HasMultipleResults = (matchResults.Count > 1) End If End Sub3.3 使用C#进行二次开发实现真正的数组传输
如果字符串拼接的方式无法满足需求,特别是需要与其他系统深度集成时,就需要通过C#进行VisionPro二次开发。在C#中,你可以直接使用.NET的数组和集合类型,实现更灵活的数据传递。
不过这就超出了基础ToolBlock脚本的范围,需要创建自定义的CogTool或使用VisionPro的.NET API。
4. 从单次检测到批量处理:脚本的工程化升级
单个零件检测正确只是第一步,真正的价值在于能稳定处理成百上千个零件,并且能记录每次检测的结果、统计良率、发现系统性偏差。
4.1 在脚本中实现结果记录
最简单的记录方式是将结果输出到文本文件:
Public Sub RunScript(ByVal pToolBlock As Object) ' ...运行检测工具... If pToolBlock.Tools("CogPMAlignTool1").Status = CogToolStatusConstants.Accepted Then ' 构造结果字符串 Dim resultStr As String resultStr = Now() & "," & pToolBlock.Tools("CogPMAlignTool1").Results(0).TranslationX & _ "," & pToolBlock.Tools("CogPMAlignTool1").Results(0).TranslationY & _ "," & pToolBlock.Tools("CogPMAlignTool1").Results(0).Score & _ ",PASS" Else resultStr = Now() & ",0,0,0,FAIL" End If ' 追加写入文件 Dim fso, file Set fso = CreateObject("Scripting.FileSystemObject") Set file = fso.OpenTextFile("C:\检测结果.csv", 8, True) ' 8=追加模式 file.WriteLine resultStr file.Close End Sub生产环境建议:在实际项目中,我更推荐通过数据库记录结果,或者使用VisionPro自带的CogDataQueue工具来实现更专业的数据记录和分析。
4.2 批量处理的流程控制
通过脚本控制整个批量处理流程:
Public Sub RunScript(ByVal pToolBlock As Object) ' 从输入端子获取批量处理参数 Dim totalCount As Integer Dim currentIndex As Integer totalCount = pToolBlock.Inputs.TotalBatchCount currentIndex = pToolBlock.Inputs.CurrentBatchIndex ' 检查是否完成所有处理 If currentIndex >= totalCount Then pToolBlock.Outputs.BatchComplete = True Exit Sub End If ' 执行当前项目的检测 ' ...运行视觉工具... ' 更新进度 pToolBlock.Outputs.CurrentBatchIndex = currentIndex + 1 pToolBlock.Outputs.BatchProgress = (currentIndex + 1) / totalCount * 100 End Sub4.3 与外部系统的集成
ToolBlock脚本可以通过输入输出端子与PLC、MES系统等其他设备或系统通信:
Public Sub SetInputs(ByVal pToolBlock As Object, ByVal Inputs As Object) ' 从PLC接收启动信号 If Inputs.StartSignal = True Then pToolBlock.Inputs.EnableProcessing = True Else pToolBlock.Inputs.EnableProcessing = False End If End Sub Public Sub GetOutputs(ByVal pToolBlock As Object, ByVal Outputs As Object) ' 向MES系统输出检测结果 If pToolBlock.Tools("CogPMAlignTool1").Status = CogToolStatusConstants.Accepted Then Outputs.QualityResult = "PASS" Outputs.QualityCode = 1 Else Outputs.QualityResult = "FAIL" Outputs.QualityCode = 0 End If ' 输出给机械手的坐标信息 Outputs.RobotX = pToolBlock.Tools("CogPMAlignTool1").Results(0).TranslationX Outputs.RobotY = pToolBlock.Tools("CogPMAlignTool1").Results(0).TranslationY End Sub5. 调试与优化:让脚本从“能用”到“好用”
写脚本不难,难的是写出稳定、高效、易维护的脚本。以下是多年实战总结的调试经验。
5.1 充分利用VisionPro的调试功能
- 设置断点:在脚本编辑器中点击行号左侧的灰色区域,可以设置断点。运行到该行时会暂停,方便查看变量状态。
- 立即窗口:在调试模式下,可以使用立即窗口查看和修改变量值。
- 输出窗口:使用
System.Diagnostics.Debug.WriteLine输出调试信息。
5.2 脚本性能优化建议
Public Sub RunScript(ByVal pToolBlock As Object) ' 1. 避免在循环中重复创建对象 Dim fso As Object Set fso = CreateObject("Scripting.FileSystemObject") ' 2. 合理设置工具参数,平衡精度和速度 pToolBlock.Tools("CogPMAlignTool1").RunParams.AcceptThreshold = 0.7 ' 适当降低阈值提高速度 ' 3. 只在需要时运行高耗时工具 If pToolBlock.Inputs.EnableHighPrecision = True Then pToolBlock.Tools("CogCaliperTool1").Run() ' 高精度测量,较耗时 End If End Sub5.3 脚本版本管理与维护
- 添加注释:特别是复杂的逻辑判断,要写明为什么这样设计。
- 模块化设计:将常用功能封装成可重用的子过程。
- 参数集中管理:将需要频繁调整的参数放在脚本开头的常量定义区域。
6. 真实案例:齿轮检测的完整ToolBlock脚本实现
让我们看一个具体的齿轮检测案例,综合运用前面讲到的各种技巧:
' 齿轮检测ToolBlock脚本 Public Sub SetInputs(ByVal pToolBlock As Object, ByVal Inputs As Object) ' 输入验证 If Inputs.GearImage Is Nothing Then Err.Raise 1001, , "齿轮图像不能为空" End If ' 设置图像到各个工具 pToolBlock.Tools("CogPMAlignTool1").InputImage = Inputs.GearImage pToolBlock.Tools("CogCaliperTool1").InputImage = Inputs.GearImage ' 设置检测参数 pToolBlock.Tools("CogPMAlignTool1").RunParams.AcceptThreshold = Inputs.MatchThreshold End Sub Public Sub GetOutputs(ByVal pToolBlock As Object, ByVal Outputs As Object) ' 初始化输出 Outputs.IsGearDetected = False Outputs.TeethCount = 0 Outputs.QualityStatus = "UNKNOWN" ' 模板匹配结果 If pToolBlock.Tools("CogPMAlignTool1").Status = CogToolStatusConstants.Accepted Then Outputs.IsGearDetected = True Outputs.GearCenterX = pToolBlock.Tools("CogPMAlignTool1").Results(0).TranslationX Outputs.GearCenterY = pToolBlock.Tools("CogPMAlignTool1").Results(0).TranslationY ' 齿数测量结果 If pToolBlock.Tools("CogCaliperTool1").Status = CogToolStatusConstants.Accepted Then Outputs.TeethCount = pToolBlock.Tools("CogCaliperTool1").Results.Count ' 质量判断 If Outputs.TeethCount >= 20 And Outputs.TeethCount <= 30 Then Outputs.QualityStatus = "PASS" Else Outputs.QualityStatus = "FAIL" End If End If End If End Sub Public Sub RunScript(ByVal pToolBlock As Object) ' 运行模板匹配定位齿轮 pToolBlock.Tools("CogPMAlignTool1").Run() If pToolBlock.Tools("CogPMAlignTool1").Status = CogToolStatusConstants.Accepted Then ' 根据定位结果设置卡尺工具的搜索区域 Dim centerX, centerY, radius As Double centerX = pToolBlock.Tools("CogPMAlignTool1").Results(0).TranslationX centerY = pToolBlock.Tools("CogPMAlignTool1").Results(0).TranslationY radius = 150 ' 假设齿轮半径150像素 ' 设置卡尺工具在齿轮边缘测量 pToolBlock.Tools("CogCaliperTool1").SearchDirection = 0 ' 径向搜索 pToolBlock.Tools("CogCaliperTool1").SearchRegion = GetCircleRegion(centerX, centerY, radius) ' 运行齿数测量 pToolBlock.Tools("CogCaliperTool1").Run() End If End Sub ' 辅助函数:创建圆形区域 Private Function GetCircleRegion(centerX As Double, centerY As Double, radius As Double) As Object Dim circle As New CogCircle circle.CenterX = centerX circle.CenterY = centerY circle.Radius = radius Dim region As New CogCircularAnnulusSection Set region.CircularArc = circle region.AngleStart = 0 region.AngleSpan = 360 Set GetCircleRegion = region End Function这个案例展示了如何将多个视觉工具有机结合起来,实现复杂的检测任务。关键是理解每个工具的作用,并通过脚本让它们协同工作。
ToolBlock脚本的真正价值,不在于让你写出多复杂的代码,而在于让你能够把视觉检测的专家经验固化下来,变成可重复、可批量、可集成的自动化流程。从单次成功到稳定量产,中间差的就是这一层脚本能力。
开始写脚本时,不要追求一步到位。先让基本流程跑通,再加入异常处理,然后优化性能,最后考虑工程化扩展。每一个阶段都要充分测试,特别是在接近真实生产的环境下测试。只有这样,才能写出真正经得起考验的ToolBlock脚本。