简介:一份面向C#开发者的VisionPro集成源码示例,重点演示如何配置VisionPro SDK,并利用CogFindCircleTool在图像中查找圆形特征、显示检测结果。VisionPro是康耐视公司出品的机器视觉工具集,在制造业、物流等领域应用广泛;本示例通过一个完整的WinForms工程,展示了从图像读取、参数设置到圆查找与结果绘制的典型流程。资源包共30个文件、2.81MB,核心内容为可直接编译运行的工程,包含6个.cs源文件、解决方案/配置文件、编译生成的exe/dll/pdb,以及窗体资源、说明文本与示例位图,整体结构清晰,适合对照学习或二次开发。示例详细呈现了CogFindCircleTool的调用方法,读者可按需调整半径范围、阈值等参数,并结合其他VisionPro工具扩展更复杂的图像分析任务。已有4380人学习下载,非常适合刚接触机器视觉或用C#调用VisionPro的工程师作为入门模板。
1. C# 调用 VisionPro 源码示例:从「VPP 加载」到「结果回读」的完整链路
产线视觉工位里最常见的一幕:PLC 给了拍照信号,上位机需要把 QuickBuild 里已经调好的 VPP 作业跑一遍,拿到坐标和 OK/NG 结果,再回给 PLC。很多用 VisionPro 做视觉集成的朋友,在 QuickBuild 里拖工具很熟练,一到 C# 调用就翻车——VPP 加了报错、CogDisplay 不显图、作业线程把界面卡死。这份 C# 调用 VisionPro 源码示例解决的就是这一整条路径:从程序集引用、VPP 作业加载,到 CogDisplay 显示、运行触发、结果回读和退出清理。适合做上位机集成、需要把视觉能力封进工业软件的人,也适合第一次接触 VisionPro 二次开发、想照着代码把流程跑通的新手。
2. 环境与引用:程序集版本、运行时授权和位宽先对齐
2.1 必须引用的程序集和它们的分工
VisionPro 的程序集是按功能拆的,不是引一个 Cognex.VisionPro.dll 就万事大吉。新建一个 WinForms 工程后,首先要做的是把下面这些程序集加进引用:
| 程序集 | 作用 | 是否必须 |
|---|---|---|
| Cognex.VisionPro.dll | 核心类型:CogJobManager、ICogJob、CogImage 等 | 必须 |
| Cognex.VisionPro.Display.dll | CogDisplay 控件,负责图像显示和图形叠加 | 界面显示时必须 |
| Cognex.VisionPro.ToolBlock.dll | ToolBlock 工具组,VPP 里常封装的容器 | 看 VPP 内容 |
| Cognex.VisionPro.ImageProcessing.dll | 图像预处理算法集 | 看 VPP 内容 |
判断该引哪个,有一个笨办法但很管用:在 QuickBuild 里打开目标 VPP,看左侧工具树里出现了哪几类工具,就去 Bin 目录找对应的名字开头程序集。见过有同事图省事一次性把 Bin 下所有 Cognex.VisionPro.*.dll 全引进来,编译能过,运行也没问题,但部署包会大一圈,而且版本冲突时特别难定位。我的习惯是先放最小集,报缺失了再补。
2.2 开发环境和运行环境的区别
VisionPro 的二次开发有个容易忽视的前提:QuickBuild 是开发调试环境,而工控机上的运行环境需要单独装 VisionPro 运行时和对应授权。源码示例工程能在你的开发机上跑起来,不代表拷到现场机上就一定能跑。
常见做法是分别处理三样东西:开发机装完整 SDK,目标机装运行时 Runtime,授权单独激活。注意授权类型也要看:软授权依赖一个常驻服务,硬加密狗则要保证驱动和系统位数一致。如果现场机是 64 位系统但装了 32 位的运行库,C# 工程就得把平台目标改成 x86,否则一运行就抛 BadImageFormatException。这个坑在后面的避坑章会展开。
2.3 新工程引用的配置清单
按这个顺序检查,基本能避开九成环境问题:
- 平台目标:项目属性里把"平台目标"显式设成 x86 或 x64,不要用 Any CPU 赌运气。看现场机装的 VisionPro 是什么位宽,跟随它。
- 程序集复制本地:引用属性里的"复制本地"建议设为 False。VisionPro 运行时会按自己的路径加载程序集,强制复制反而容易出现版本不一致。
- .NET Framework 版本:VisionPro 对 .NET 版本有要求,老版本 SDK 常见支持 .NET Framework 4.6.1 或 4.7.2,先按 SDK 里自带的示例工程对齐,再决定要不要升级。
- 授权服务:检查 Windows 服务里有没有 VisionPro 授权相关的服务在运行,首次跑之前手动确认一次激活状态。
3. 从 VPP 到界面:CogJobManager 加载、CogDisplay 显示与触发流程
3.1 用 CogJobManager 加载 VPP 作业
VisionPro 里把作业(Job)交给 CogJobManager 管理,这是 C# 和 VPP 之间的标准接缝。下面的代码演示了最基础的加载与启动:
using Cognex.VisionPro; // 在窗体字段中声明,避免局部变量被回收 private CogJobManager _jobManager; void LoadVpp(string vppFilePath) { // 创建作业管理器 _jobManager = new CogJobManager(); // 向管理器注册一个作业,把 VPP 文件路径传进去 ICogJob job = _jobManager.Jobs.Add("InspectJob", vppFilePath); // 挂接运行完成事件,后续在这个回调里取结果 job.Complete += OnJobComplete; }这段代码有两个关键点。Jobs.Add的第一个参数是作业名,建议和 VPP 里的作业名保持一致,代码里将来按名字找作业时不容易搞混;第二个参数是 VPP 文件的完整路径,特别注意路径里不要带中文和空格,某现场机就因为在 "C:\视觉配置\job1.vpp" 这种路径上反复加载失败,改成纯英文路径后一次通过。
Complete事件是异步的,视觉处理跑完后在 VisionPro 内部线程触发,回调参数里携带当次运行状态。不要在事件处理里直接操作窗体控件,这一点到避坑章再说。
3.2 CogDisplay 挂载到窗体并显示图像
CogDisplay 是专门用来显示 VisionPro 图像和结果图形的控件,和 PictureBox 完全不是一个概念。它支持缩放、平移,还能把工具画出的坐标框、边缘线叠加显示。挂载代码很简单:
using Cognex.VisionPro.Display; // 创建并挂载 CogDisplay var display = new CogDisplay { Dock = DockStyle.Fill, AutoSize = true // 图像缩放填满控件 }; // 添加到窗体的容器中 this.Controls.Add(display); // 拿到某次运行的图像,赋给控件的 Image 属性 display.Image = GetImageFromLastRun();AutoSize设为 true 时图像会自动缩放适配控件尺寸,视觉调试时比较方便;如果要严格按 1:1 像素看边缘细节,就把它设为 false,然后配合DisplayTransform做平移缩放。我一般会把 AutoSize 做成一个可切换按钮,正常跑用自适应,怀疑定位精度时切 1:1 看像素级细节。
图像来源方面,很多 VPP 内部封装了采图工具,C# 里不需要单独采图,直接让作业跑完再从结果里取图像即可。这份源码示例里给了一个"模拟图像源"的 VPP,没有相机也能把显示链路完整跑通,调试界面开发时非常有用。
3.3 同步 Run 与异步回调的边界
作业运行有两种最常用的姿势。同步单次运行适合按钮触发、一次拍一张的场景:
ICogJob job = _jobManager.Jobs["InspectJob"]; // 同步运行:当前线程阻塞到本次作业结束 job.Run(); // 运行结束后获取结果状态 bool passed = job.RunStatus == CogJobRunStatus.Complete;上面这段在 UI 线程里直接调,跑得快没问题,一旦视觉处理耗时超过几百毫秒,界面就会假死。更稳妥的方式是把运行丢到后台线程,用事件回调接结果:
void OnTriggerButton_Click(object sender, EventArgs e) { // 后台运行,UI 不被阻塞 Task.Run(() => { ICogJob job = _jobManager.Jobs["InspectJob"]; job.Run(); }); } void OnJobComplete(object sender, EventArgs e) { // 注意:此回调在 VisionPro 内部线程执行 // 不要在这里直接改 UI 控件 bool passed = /* 从事件参数里取本次运行状态 */; // 把结果通过消息、队列或 BeginInvoke 送给 UI 线程 BeginInvoke(new Action(() => { lblResult.Text = passed ? "OK" : "NG"; })); }这里有个容易踩的认知差:Run()虽然写起来像同步方法,但只要你先挂好了Complete事件,事件仍然会在作业线程里异步触发。很多初学者以为调用 Run 后的事件和调用者同线程,结果在事件里操作控件,界面随机卡死,过了很久才发现是线程问题。魂。
4. 避坑与排查:加载失败、界面卡死、退出崩溃的五个真实案例
4.1 BadImageFormatException 或加载作业报"格式不正确"
现象:程序一运行到Jobs.Add就抛异常,提示程序集无法加载或者图像格式不正确;有时编译正常但一执行就崩。
原因:八成是位宽不匹配。开发机装了 64 位 VisionPro,工程平台目标却是 x86;或者反过来,现场机只有 32 位运行库,工程却是 x64。Any CPU 在 .NET Framework 下默认按 64 位跑,碰上只有 32 位组件的机器就会现场翻车。
解决:先在 VisionPro 安装目录下确认 dll 的位数,再用 反编译工具看一眼工程引用的程序集路径。然后把项目属性里的"平台目标"改成和运行环境一致的位数,重新生成,问题就消失了。这个坑十有七八是环境不一致,不是代码问题。
4.2 VPP 在 QuickBuild 正常,C# 里却提示缺少工具
现象:同一个 VPP,QuickBuild 打开运行都正常,拿到 C# 工程里加载时报错,提示某个工具类型未找到或组件未安装。
原因:开发机装的是完整 SDK,包含所有工具;但目标机器或某些精简版环境缺少特定工具组件。还有一种情况:VPP 保存的版本比当前运行库高,新工具集在老环境里不存在。
解决:在最终运行的机器上执行一次 VisionPro 运行时完整安装,别用"最小安装"选项;同时打开 QuickBuild,把 VPP 重新保存一次,让作业格式对齐当前版本。保存后注意观察右下角有没有出现工具缺失警告。
4.3 Complete 事件里操作界面控件,界面随机卡死
现象:视觉运行完,有时候界面正常,有时候按钮点不动,严重时整个窗体转圈,过几秒才恢复,甚至直接无响应。
原因:Complete事件跑在视觉作业线程上,VisionPro 内部线程直接触碰 WinForms 控件属于跨线程操作,轻则随机卡顿,重则抛线程安全问题。
解决:事件回调里只保存数据和状态,把 UI 更新通过BeginInvoke或自己封装的界面消息队列切回 UI 线程。我在实际项目里习惯把结果包成一个结果对象,通过队列交给界面线程消费,这样连并发触发时先后顺序都能控制住。
4.4 关闭窗体后进程不退,VS 显示"正在调试"
现象:点了窗体右上角关闭,界面没了,但进程还挂在后台,CPU 占用还在涨;或者调试器一直显示正在运行,要手动停止。
原因:视觉作业是后台线程,关窗体只销毁了界面,作业还在跑。没有主动停止就退出,资源泄漏在长周期运行工控机上积累几小时就能让系统卡成幻灯片。
解决:窗体的FormClosing事件里,先把视觉作业停下来,再释放管理器:
void OnFormClosing(object sender, FormClosingEventArgs e) { if (_jobManager != null) { ICogJob job = _jobManager.Jobs["InspectJob"]; job.StopAndWait(); // 先停止当前作业 job.Complete -= OnJobComplete; job.Dispose(); // 释放作业 _jobManager.Dispose(); // 最后释放管理器 } }顺序不能反:先停、再拆事件、最后释放。见过有人只调 Dispose 不调 Stop,释放到一半作业还在跑,反而抛出更奇怪的异常。释放顺序这个事,最好就按上面的顺序写成死规矩。
4.5 首次运行慢或弹授权窗口,现场机表现不一致
现象:开发机上秒开,现场工控机首次运行要卡好几秒,甚至弹出授权激活窗口;过一会儿又恢复正常,但重启后又来一次。
原因:软授权服务没有设置成开机自启,或者首次调用时才检查授权状态,检查过程被系统安全软件拦截拖慢。
解决:检查 Windows 服务管理器里 VisionPro 授权服务的启动类型是否为自动;把部署目录加入杀毒软件白名单,避免 dll 检查引入延迟。另外,现场机有条件的话,首次运行前用管理员身份手工激活一次,后面就少很多意外。
5. 触发策略与作业切换:从单拍按钮改造成产线可用形态
5.1 三种触发模式怎么选
| 触发模式 | 实现方式 | 适合场景 | 注意点 |
|---|---|---|---|
| 单次触发 | 按钮或 PLC 信号到来后Job.Run()一次 | 静态检测、节拍慢 | 确保上一次跑完再触发下一次 |
| 连续运行 | 视觉线程里循环 Run,配合独立结果队列 | 高速传送带、实时监控 | 防止重入,结果消费要跟上 |
| 外部硬触发 | 相机或采集卡硬信号触发采图,C# 只响应 | 高速线扫、精准同步 | VPP 内置采图工具,C# 只做事件处理 |
产线上最常见的需求是"PLC 给一个信号,跑一次"。这里真正要防的是重入:PLC 信号可能来得比视觉处理快,如果两次触发在作业层叠在一起,结果顺序就会错乱。
5.2 带防重入保护的触发代码
下面这段是我在项目里长期使用的一个结构,核心思想是上一次运行没结束之前,新的触发信号直接丢弃:
private readonly object _stateLock = new object(); private volatile bool _isRunning; void TriggerInspect() { lock (_stateLock) { if (_isRunning) { // 上一次还没跑完,本次触发丢弃 return; } _isRunning = true; } Task.Run(() => { try { ICogJob job = _jobManager.Jobs["InspectJob"]; job.Run(); } finally { lock (_stateLock) { _isRunning = false; } } }); }_isRunning加 volatile 保证多线程可见性,_stateLock保护检查与赋值的原子性。这里有一个容易被忽略的细节:把_isRunning设为 true 的动作必须在 Task 启动之前完成,否则两个相邻触发信号可能同时通过检查。上面这段把状态置位放在锁内、Task 之外,正是为了保证这一点。
5.3 多个 VPP 作业的切换与缓存
现场经常有多产品共线的情况,产品 A 和产品 B 各有一个 VPP。朴素做法是每次切换都重新Jobs.Add新作业,运行完再删掉。问题是 VPP 加载本身有开销,高频切换时每次都要等加载,节拍就乱了。
更好的做法是把已加载的作业缓存起来,按产品码做键:
private readonly Dictionary<string, ICogJob> _jobCache = new Dictionary<string, ICogJob>(); ICogJob GetCachedJob(string productCode, string vppPath) { if (_jobCache.TryGetValue(productCode, out var job)) { return job; } ICogJob newJob = _jobManager.Jobs.Add(productCode, vppPath); _jobCache[productCode] = newJob; return newJob; }切换时先停掉当前正忙的作业,再从缓存里取目标作业;缓存里的作业如果长时间不用,可以定期释放。注意两个作业如果在同一个 CogJobManager 下,同一时刻最好只跑一个,避免多作业争抢视觉处理线程,精度和节拍都不稳定。
6. 进阶技巧:显示刷新节流与一个我保留至今的检查习惯
6.1 显示节流:别让 CogDisplay 拖垮视觉循环
视觉处理跑完一帧通常只要几十毫秒,但 CogDisplay 刷新一次图像加图形叠加,在性能差的工控机上可能要花掉一百多毫秒。如果每次都把图像刷到界面,视觉节拍会被界面拖慢,严重时表现为"处理很快、界面一卡一卡,整体节拍上不去"。我常用的技巧是:视觉线程只计算,UI 线程按固定频率合并刷新。做法是维护一个帧号计数,Complete事件里不碰 CogDisplay,只递增帧号;界面上放一个 100ms 的定时器,检测到帧号变化才刷新一次 CogDisplay。这样即使视觉一秒跑十帧,界面最多每秒刷新十次,数据不丢,界面还跟手。测试时对比一下,开启和关闭节流后的视觉循环耗时差距,在低配工控机上能到两倍以上。
6.2 每个新环境都要强制过一遍的核对清单
换电脑、换版本、从开发机搬到现场机,这三类场景最容易出幺蛾子。从那以后我每次部署新环境都会强制走一遍这个流程:第一步,确认 VisionPro 运行时位宽和工程平台目标一致,用任务管理器看进程位数;第二步,装完运行时后先跑一次官方的自检示例,确认授权已激活再动自己的工程;第三步,用自己的 VPP 在 QuickBuild 里打开并重新保存一次,确保工具集版本对齐。这一套流程走下来十分钟不到,省掉的是反复加载失败、闪退、界面卡死这类问题几个小时的排查时间。如果你也想少在环境问题上消耗精力,就从现在开始把这个清单固化进你的部署习惯。希望帮到你。
本文还有配套的精品资源,点击获取