在机器视觉应用开发中,Cognex VisionPro 提供了强大的图形化编程环境,其中 ToolBlock 作为核心的可复用功能单元,能够将多个视觉工具组合成一个完整的处理流程。然而,当标准工具无法满足复杂业务逻辑时,就需要通过脚本编写来扩展 ToolBlock 的功能边界。
本文面向已经掌握 VisionPro 基础操作、需要实现定制化视觉算法的工程师,将深入讲解 ToolBlock 脚本的编写方法。通过实际案例演示如何从简单的参数传递到复杂的图像处理逻辑,帮助读者掌握脚本与工具块的无缝集成技术。
1. 理解 ToolBlock 脚本的基本架构
1.1 ToolBlock 脚本的作用域与执行时机
ToolBlock 脚本本质上是嵌入在工具块内部的代码片段,它们在不同执行阶段被触发。VisionPro 提供了多种脚本类型,每种都有特定的执行时机和访问权限。
主要脚本类型包括:
- 初始化脚本:在工具块加载时执行,用于设置初始参数
- 预执行脚本:在工具运行前执行,可修改输入参数或进行预处理
- 后执行脚本:在工具运行后执行,可处理输出结果或进行数据转换
- 终止脚本:在工具块卸载时执行,用于资源清理
' 示例:简单的初始化脚本 Public Sub Initialize() ' 设置默认参数值 MyToolBlock.InputImage = Nothing MyToolBlock.ThresholdValue = 128 MyToolBlock.EnableFilter = True End Sub1.2 脚本语言选择与环境配置
VisionPro 主要支持 VB.NET 和 C# 两种脚本语言。VB.NET 是默认选项,语法相对简单;C# 则更适合有.NET 开发经验的工程师。
环境配置要点:
- 确保安装对应版本的 .NET Framework
- 在 VisionPro 中通过"工具">"选项">"脚本"设置默认语言
- 脚本编辑器提供语法高亮和基本智能提示
注意:虽然 VisionPro 支持两种语言,但在同一个项目中建议保持一致性,避免混合使用带来的维护复杂度。
1.3 脚本与工具块的数据交换机制
脚本通过特定接口与 ToolBlock 进行数据交互。输入端子提供参数传入,输出端子返回处理结果,内部变量则用于临时存储中间数据。
数据流向示意图:
输入端子 → 预执行脚本 → 视觉工具 → 后执行脚本 → 输出端子2. 准备 ToolBlock 脚本开发环境
2.1 硬件与软件要求
开发 ToolBlock 脚本需要满足以下基础环境:
| 组件 | 最低要求 | 推荐配置 |
|---|---|---|
| 操作系统 | Windows 10 64位 | Windows 11 64位 |
| VisionPro 版本 | 9.0 | 9.7 或更新版本 |
| .NET Framework | 4.7.2 | 4.8 或 .NET Core 3.1+ |
| 内存 | 8GB | 16GB 或更多 |
| 处理器 | Intel i5 | Intel i7 或同等级 |
2.2 创建第一个脚本化 ToolBlock
通过具体步骤创建基础工具块:
新建 ToolBlock 项目
- 打开 VisionPro,选择"文件">"新建">"ToolBlock"
- 命名工具块为"ImageProcessor"
添加输入输出端子
- 右键点击端子面板,选择"添加输入"
- 创建
InputImage(CogImage8Grey 类型) - 创建
Threshold(Int32 类型) - 添加输出端子
ResultImage(CogImage8Grey 类型)
配置脚本编辑器
- 双击 ToolBlock 进入编辑模式
- 在属性窗口中找到"脚本"选项卡
- 选择编程语言(VB.NET 或 C#)
2.3 脚本调试环境搭建
有效的调试是脚本开发的关键环节:
' 在脚本中插入调试信息输出 Public Sub PreExecute() Try ' 检查输入图像是否有效 If InputImage Is Nothing Then CogToolBlock.Results.AddError("ERR001", "输入图像为空") Return End If ' 输出调试信息到 VisionPro 日志 CogToolBlock.Results.AddMessage("DBG001", $"图像尺寸: {InputImage.Width}x{InputImage.Height}") Catch ex As Exception CogToolBlock.Results.AddError("ERR999", $"脚本执行异常: {ex.Message}") End Try End Sub调试技巧:
- 使用
CogToolBlock.Results.AddMessage()输出中间状态 - 在关键位置设置断点(如果环境支持)
- 通过"立即窗口"查看变量值
3. 编写实用的 ToolBlock 脚本
3.1 图像处理脚本实战
以下示例演示如何实现自定义阈值处理:
' 后执行脚本:实现自适应阈值处理 Public Sub PostExecute() ' 获取二值化工具的执行结果 Dim binTool As CogBinarizeTool = DirectCast(Me.Owner, CogBinarizeTool) If binTool.Results.OutputImage Is Nothing Then CogToolBlock.Results.AddError("ERR002", "二值化处理失败") Return End If ' 自定义后处理:形态学开运算去噪 Dim morphedImage As CogImage8Grey = MorphologicalFilter( binTool.Results.OutputImage, CogMorphologyOperationConstants.Open, 2) ' 将结果赋值给输出端子 ResultImage = morphedImage ' 计算并输出质量指标 Dim whitePixels As Integer = CountWhitePixels(morphedImage) Dim totalPixels As Integer = morphedImage.Width * morphedImage.Height Dim fillRatio As Double = whitePixels / totalPixels CogToolBlock.Results.AddValue("FillRatio", fillRatio) End Sub ' 自定义形态学滤波函数 Private Function MorphologicalFilter( ByVal inputImage As CogImage8Grey, ByVal operation As CogMorphologyOperationConstants, ByVal kernelSize As Integer) As CogImage8Grey Dim morphologyTool As New CogMorphologyTool() morphologyTool.InputImage = inputImage ' 设置结构元素 morphologyTool.RunParams.StructuringElement = CogMorphologyStructuringElementConstants.Square morphologyTool.RunParams.Size = kernelSize morphologyTool.RunParams.MorphologyOperation = operation ' 执行处理 morphologyTool.Run() Return morphologyTool.OutputImage End Function3.2 复杂数据结构的处理技巧
VisionPro 的端子默认不支持数组传输,但可以通过序列化技巧实现复杂数据传递:
' 处理多个检测结果的脚本示例 Public Sub ProcessMultipleResults() ' 使用分隔符连接多个数据项 Dim results As New List(Of String) For i As Integer = 0 To DetectionTools.Count - 1 If DetectionTools(i).Results.Count > 0 Then Dim resultStr As String = $"{DetectionTools(i).Results(0).Score}:{DetectionTools(i).Results(0).PositionX}" results.Add(resultStr) End If Next ' 将列表序列化为字符串输出 If results.Count > 0 Then ResultsOutput = String.Join("|", results) Else ResultsOutput = "NoResults" End If End Sub ' 在接收端解析数据 Public Sub ParseResults() If ResultsOutput <> "NoResults" Then Dim resultArray As String() = ResultsOutput.Split("|"c) For Each resultStr As String In resultArray Dim parts As String() = resultStr.Split(":"c) If parts.Length = 2 Then Dim score As Double = Double.Parse(parts(0)) Dim positionX As Double = Double.Parse(parts(1)) ' 处理单个结果... End If Next End If End Sub3.3 与外部系统的集成脚本
ToolBlock 脚本可以调用外部库和系统功能:
Imports System.IO Imports System.Net Public Class ExternalIntegrationScript ' 调用 REST API 上传检测结果 Public Sub UploadResultsToServer() Try Dim resultData As New With { .Timestamp = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"), .ImageWidth = InputImage.Width, .ImageHeight = InputImage.Height, .DefectCount = DefectResults.Count, .OverallScore = QualityScore } Dim jsonData As String = Newtonsoft.Json.JsonConvert.SerializeObject(resultData) Using client As New WebClient() client.Headers.Add("Content-Type", "application/json") Dim response As String = client.UploadString( "http://api.example.com/inspection-results", "POST", jsonData) CogToolBlock.Results.AddMessage("UPLOAD", "数据上传成功") End Using Catch ex As Exception CogToolBlock.Results.AddError("UPLOAD_ERR", $"上传失败: {ex.Message}") End Try End Sub ' 保存图像到本地文件系统 Public Sub SaveResultImage() If ResultImage IsNot Nothing Then Dim filename As String = $"{DateTime.Now:yyyyMMdd_HHmmss}_{Guid.NewGuid().ToString("N").Substring(0, 8)}.bmp" Dim fullPath As String = Path.Combine("C:\VisionPro\Results", filename) ' 确保目录存在 Directory.CreateDirectory(Path.GetDirectoryName(fullPath)) ' 保存图像 ResultImage.Save(fullPath) CogToolBlock.Results.AddValue("SavedImagePath", fullPath) End If End Sub End Class4. 脚本调试与错误处理
4.1 系统化调试方法
建立完整的调试工作流能够显著提高脚本开发效率:
Public Class DebugHelper Private Shared DebugEnabled As Boolean = True ' 分级调试输出 Public Shared Sub LogDebug(message As String, Optional level As Integer = 1) If DebugEnabled AndAlso level <= CurrentDebugLevel Then CogToolBlock.Results.AddMessage($"DBG{level:00}", message) End If End Sub ' 变量值检查 Public Shared Sub CheckVariable(variableName As String, value As Object) If value Is Nothing Then CogToolBlock.Results.AddError("VAR_NULL", $"{variableName} 为空引用") ElseIf TypeOf value Is String AndAlso String.IsNullOrEmpty(value.ToString()) Then CogToolBlock.Results.AddWarning("VAR_EMPTY", $"{variableName} 为空字符串") Else LogDebug($"{variableName} = {value}") End If End Sub End Class ' 在脚本中使用调试助手 Public Sub MainProcessing() DebugHelper.CheckVariable("InputImage", InputImage) DebugHelper.CheckVariable("ThresholdValue", ThresholdValue) If InputImage Is Nothing Then Return Try ' 主要处理逻辑 DebugHelper.LogDebug("开始图像处理", 1) ProcessImage() DebugHelper.LogDebug("图像处理完成", 1) Catch ex As Exception DebugHelper.LogDebug($"处理异常: {ex.Message}", 3) CogToolBlock.Results.AddError("PROC_ERR", ex.ToString()) End Try End Sub4.2 常见脚本错误及解决方案
| 错误现象 | 可能原因 | 检查方法 | 解决方案 |
|---|---|---|---|
| 脚本编译错误 | 语法错误或类型不匹配 | 查看错误信息行号 | 修正语法,检查变量类型声明 |
| 运行时空引用异常 | 未初始化对象或端子未连接 | 添加空值检查 | 在访问前验证对象是否为空 |
| 性能问题或超时 | 循环处理大图像或复杂算法 | 添加执行时间监控 | 优化算法,考虑异步处理 |
| 内存泄漏 | 未及时释放大型对象 | 监控内存使用情况 | 使用 Using 语句确保资源释放 |
4.3 性能优化技巧
脚本性能直接影响整个视觉系统的响应速度:
Public Class PerformanceOptimizer Private Shared timer As Stopwatch = New Stopwatch() ' 性能监控装饰器 Public Shared Function MeasureTime(Of T)(operationName As String, operation As Func(Of T)) As T timer.Restart() Dim result As T = operation() timer.Stop() If timer.ElapsedMilliseconds > 100 Then ' 超过100ms记录警告 CogToolBlock.Results.AddWarning("PERF_WARN", $"{operationName} 耗时: {timer.ElapsedMilliseconds}ms") End If Return result End Function ' 图像处理优化:使用区域兴趣ROI Public Shared Function ProcessROI(image As CogImage8Grey, roi As CogRectangle) As CogImage8Grey If roi Is Nothing Then Return image ' 只处理感兴趣区域,提升性能 Dim roiImage As CogImage8Grey = image.Copy(roi) Return roiImage End Function End Class ' 优化后的处理脚本 Public Sub OptimizedProcessing() Dim result = PerformanceOptimizer.MeasureTime("图像二值化", Function() Return PerformanceOptimizer.ProcessROI(InputImage, ProcessingROI) End Function) End Sub5. 高级脚本开发技巧
5.1 动态工具配置脚本
通过脚本实现运行时的工具参数调整:
Public Class DynamicConfigurator ' 根据图像特性自动调整参数 Public Sub AutoAdjustParameters() If InputImage Is Nothing Then Return Dim stats As CogImageStatisticsTool = GetTool("ImageStats") stats.InputImage = InputImage stats.Run() ' 根据图像统计信息调整阈值 Dim meanIntensity As Double = stats.Results.Mean Dim stdDev As Double = stats.Results.StandardDeviation ' 自适应阈值算法 Dim adaptiveThreshold As Integer = CInt(meanIntensity - stdDev * 0.5) adaptiveThreshold = Math.Max(0, Math.Min(255, adaptiveThreshold)) ThresholdValue = adaptiveThreshold CogToolBlock.Results.AddValue("AutoThreshold", adaptiveThreshold) End Sub ' 根据条件启用/禁用特定工具 Public Sub DynamicToolManagement() Dim blurTool As CogGaussianFilterTool = GetTool("GaussianBlur") ' 只有在大图像且需要降噪时启用模糊处理 If InputImage.Width > 2000 AndAlso InputImage.Height > 2000 AndAlso EnableNoiseReduction Then blurTool.Enabled = True blurTool.RunParams.FilterSize = 3 Else blurTool.Enabled = False End If End Sub Private Function GetTool(toolName As String) As Object Return CogToolBlock.Tools(toolName) End Function End Class5.2 脚本模块化与复用
将常用功能封装为可复用模块:
' 图像质量评估模块 Public Class ImageQualityModule Public Shared Function CalculateSharpness(image As CogImage8Grey) As Double If image Is Nothing Then Return 0 Dim laplacianTool As New CogLaplacianTool() laplacianTool.InputImage = image laplacianTool.Run() Return laplacianTool.Results.OutputImage.Statistics.StandardDeviation End Function Public Shared Function CheckFocusQuality(image As CogImage8Grey, Optional threshold As Double = 50) As Boolean Dim sharpness As Double = CalculateSharpness(image) Return sharpness > threshold End Function End Class ' 几何变换模块 Public Class GeometryModule Public Shared Function RotateImage(image As CogImage8Grey, angle As Double) As CogImage8Grey Dim rotateTool As New CogRotateTool() rotateTool.InputImage = image rotateTool.RunParams.RotationAngle = angle rotateTool.Run() Return rotateTool.OutputImage End Function End Class ' 在主脚本中调用模块 Public Sub MainProcessing() ' 检查图像质量 If Not ImageQualityModule.CheckFocusQuality(InputImage) Then CogToolBlock.Results.AddError("FOCUS_ERR", "图像失焦,检测结果不可靠") Return End If ' 执行几何校正 If NeedsRotation Then InputImage = GeometryModule.RotateImage(InputImage, RotationAngle) End If End Sub5.3 与 C# 项目的混合编程
对于复杂算法,可以编译为 DLL 后在脚本中调用:
' 引用外部 C# 库 Imports MyAdvancedImageProcessingLib Public Class ExternalLibraryIntegration Private advancedProcessor As New AdvancedImageProcessor() Public Sub ProcessWithExternalLibrary() If InputImage Is Nothing Then Return Try ' 调用 C# 库中的复杂算法 Dim processedImage As CogImage8Grey = advancedProcessor.SegmentWithMachineLearning(InputImage) If processedImage IsNot Nothing Then ResultImage = processedImage ' 获取额外的分析结果 Dim analysisResults = advancedProcessor.GetAnalysisResults() CogToolBlock.Results.AddValue("Confidence", analysisResults.Confidence) CogToolBlock.Results.AddValue("DefectCount", analysisResults.DefectCount) End If Catch ex As Exception CogToolBlock.Results.AddError("EXT_LIB_ERR", $"外部库调用失败: {ex.Message}") End Try End Sub End Class6. 生产环境部署与维护
6.1 脚本版本管理策略
在生产环境中管理脚本变更需要系统化方法:
Public Class VersionManager Public Const SCRIPT_VERSION As String = "2.1.0" Public Sub LogVersionInfo() CogToolBlock.Results.AddValue("ScriptVersion", SCRIPT_VERSION) CogToolBlock.Results.AddValue("LastUpdated", "2024-01-20") CogToolBlock.Results.AddValue("Compatibility", "VisionPro 9.5+") End Sub ' 版本兼容性检查 Public Function CheckCompatibility() As Boolean Dim vproVersion As Version = GetVisionProVersion() Dim minVersion As New Version(9, 5) If vproVersion < minVersion Then CogToolBlock.Results.AddError("VER_ERR", $"需要 VisionPro {minVersion} 或更新版本") Return False End If Return True End Function End Class6.2 错误处理与日志记录标准化
建立生产级的错误处理机制:
Public Class ProductionErrorHandler Private Shared logPath As String = "C:\VisionPro\Logs\" Public Shared Sub LogError(errorCode As String, errorMessage As String, Optional severity As Integer = 2) Dim logEntry As String = $"{DateTime.Now:yyyy-MM-dd HH:mm:ss} | {errorCode} | {severity} | {errorMessage}" ' 输出到 VisionPro 结果窗口 Select Case severity Case 1 : CogToolBlock.Results.AddMessage(errorCode, errorMessage) Case 2 : CogToolBlock.Results.AddWarning(errorCode, errorMessage) Case 3 : CogToolBlock.Results.AddError(errorCode, errorMessage) End Select ' 同时记录到文件 WriteToLogFile(logEntry) End Sub Private Shared Sub WriteToLogFile(message As String) Try Directory.CreateDirectory(logPath) Dim filename As String = $"visionpro_{DateTime.Now:yyyyMMdd}.log" File.AppendAllText(Path.Combine(logPath, filename), message + Environment.NewLine) Catch ' 文件日志失败时不影响主流程 End Try End Sub End Class ' 统一错误处理包装器 Public Function SafeExecute(Of T)(operation As Func(Of T), operationName As String) As T Try Return operation() Catch ex As Exception ProductionErrorHandler.LogError("EXEC_ERR", $"{operationName} 执行失败: {ex.Message}", 3) Return Nothing End Try End Function6.3 性能监控与健康检查
持续监控脚本执行状态:
Public Class HealthMonitor Private Shared executionCount As Integer = 0 Private Shared errorCount As Integer = 0 Private Shared totalExecutionTime As Long = 0 Public Shared Sub RecordExecution(startTime As DateTime) executionCount += 1 Dim duration As Long = (DateTime.Now - startTime).TotalMilliseconds totalExecutionTime += duration ' 定期输出性能统计 If executionCount Mod 100 = 0 Then Dim avgTime As Double = totalExecutionTime / executionCount ProductionErrorHandler.LogError("PERF_STATS", $"执行统计: {executionCount} 次, 平均耗时: {avgTime:F2}ms, 错误率: {errorCount/executionCount:P2}", 1) End If End Sub Public Shared Sub RecordError() errorCount += 1 End Sub ' 资源使用检查 Public Shared Function CheckResourceUsage() As Boolean Dim process As Process = Process.GetCurrentProcess() Dim memoryMB As Double = process.WorkingSet64 / 1024 / 1024 If memoryMB > 500 Then ' 超过500MB警告 ProductionErrorHandler.LogError("MEM_WARN", $"内存使用过高: {memoryMB:F1}MB", 2) Return False End If Return True End Function End ClassToolBlock 脚本开发需要平衡功能实现与系统稳定性,在扩展视觉系统能力的同时确保生产环境的可靠运行。从简单的参数处理到复杂的图像算法,脚本为 VisionPro 提供了几乎无限的自定义可能性。实际项目中建议先在小范围验证脚本逻辑,再逐步应用到关键生产流程,同时建立完善的测试和监控机制。