在康耐视实习,那就必须得掌握我们公司大大大厉害的产品Vision Pro啦。经过大半个月的学习训练,基本掌握了不少技能。在这里写个阶段性小结,记录一下子。
贴两个文档,一个中文的(版本较老,VB版),还有一个VP自带的英文文档。
传送门,提取码ny2r
下面进入正题
准备工具:
正版Vision Pro、Visual Studio 2012(笔者使用版本,也是公司默认版本)、
Cognex开发工具包
步骤0:添加引用如下
(前4个是必须的基本引用)
步骤1:使用QuickBuild创建自己的操作程序(.vpp)
这里我就不多说了,默认大家都会一些PM、Blob这种基本操作,如果有需要详细说的,评论打出1让我看见哈(啧啧,这个人有点过分哈哈)
步骤2:配置VS
这里由于公司给我们配置了电脑,给的时候就有Cognex的工具包了。我想大家如果购买的正版Vision Pro也是会有的,这里提两个注意点:
(1).net框架是需要4.6版本的,2012默认4.5,记得升级。
大部分人下载下来的4.6安装时都会提示电脑已安装更高版本。这个问题困扰了我好一会儿,同事都纷纷转战2015或2017,但最后我发现是因为下载的.net 4.6的版本问题,导致的无法安装。应该安装开发包(给个官方链接),如果实在搞不懂,那就全下下来试一遍吧~
(2)Vision Pro工具箱的安装。
傻瓜式点就完了,然后VS里面ToolBox应该就有了。
步骤3:根据pdf438页开始码
这里按照他说的来就行了,不过他给的是VB程序,我改成了C#,现在公司内部好像都用C#开发了。
过程比较多,我从我的难点一步步说,详细的大家自己看pdf。最后我会贴码。
(1)看的懂.net框架的构造
每个文件的作用是什么,得知道。
(2)JobManager在代码中就代表了你的整个vpp项目,JobManager.Job(i)代表你项目中得第i个Job。
// 加载vpp作为一个job manager
myJobManager = (CogJobManager)CogSerializer.LoadObjectFromFile("../../../Job722.vpp")
// 引用job manager中的第一个
myJob = myJobManager.Job(0);
(3)”委托“指A线程访问B线程的功能时使用的东西,其处理模式为:
首先为跨线程事务创建一个相同声明的委托
// 每个win窗体控件只能通过创建它的线程来访问
// 创建一个有预期签名的委托 用于指向下面的停止事
delegate void myJobManagerDelegate(Object ByVaklsender,CogJobManagerActionEventArgs _ByVale);
在该事务创建该委托实例,提供2中实例化方法如下
// 创建委托实例 并指向停止事件的函数指
myJobManagerDelegate mydelegate = myJobManager_Stopped
myJobManagerDelegate mydelegate = new myJobManagerDelegate(myJobManager_Stopped);
创建一个该事务参数的数组
// 定义一个参数数
Object[] myobj = new Object[2] { ByValsender, _ByVale };
使用控件Invoke方法调用委托,完成操作
// 使用控件“魔术”调用Invoke,在GUI线程上调用我们的委托(当前我们在vpp线程,但是需要使用GUI线程来使按钮恢复可选,所以需要委托给GUI线程
this.Invoke(mydelegate, myobj);
最后,程序是怎么发现线程之间的不通融的呢?这就是为什么Invoke厉害的地方了,在事务中添加下列代码
if (InvokeRequired)
{
# 创建一个指针指向该函数
# 在正确的线程上调用同一个函数
}
InvokeRequired是每一个控件的”魔术“属性,如果程序处在错误的线程上,它就会返回”true“。这就是Microsoft的一个用于校正线程乱问题的标准方法。请牢记!
(4)Vision Pro 内部控件的调用,参数获取
首先我们需要现实的代码状态,是一个ICogRecord类,所以我们需要实例化该对象,然后将JobManager.UserResult传递给他。这样我们就获取到我们Job的结果了。之后就是检索我们需要的result。
// 输出运行状
Cognex.VisionPro.ICogRecord myrecord = myJobManager.UserResult() as ICogRecord
RunStatusTextBox.Text = myrecord.SubRecords["UserResultTag"].Content.ToString() + ":" + myrecord.SubRecords["JobName"].Content.ToString() + "-->";
RunStatusTextBox.Text += myrecord.SubRecords["RunStatus"].Content.ToString();
如果是要显示Job工具里的运行结果或者运行参数等,需要先在QuickBuild里面将你想要的参数添加到发送项,这样在Code里面才能获取到,不然有的参数(如Item里面的参数)是没有权限获得的,而Count这样的public变量可以直接调用,见程序
// 输出参数
Cognex.VisionPro.ToolGroup.CogToolGroup myTG = myJob.VisionTool as Cognex.VisionPro.ToolGroup.CogToolGroup;
Cognex.VisionPro.ToolBlock.CogToolBlock myTB1 = myTG.Tools["Detect_Defects"] as Cognex.VisionPro.ToolBlock.CogToolBlock;
Cognex.VisionPro.PMAlign.CogPMAlignTool myPM = myTB1.Tools["CogPMAlignTool2"] as Cognex.VisionPro.PMAlign.CogPMAlignTool
try
{
textBox1.Text = myPM.Results.Count.ToString();
}
catch (Exception e5)
{
textBox1.Text = e5.ToString();
}
输出图像的话,Vision Pro提供四类图像显示控件,分别是:
1)CogToolDisplay
2)CogRecordsDisplay
3)CogRecordDisplay
4)CogDisplay
ToolD最复杂,它知道如何连接到Vision Pro工具以及如何调用工具所有的执行结果,它通过显示区上放一个下拉框允许用户选择浏览哪张图像(就像QB里面的显示结果控件一样)。
RecordsD看上去和TD一样,也允许下拉选择,但是他对Vision Pro工具一无所知,他不能从工具中获取Records树,必须用编程的方法自己来获取。
RecordD(注意上面一个是Recordsssssssssss)显示单个图像记录及其图形的子记录(也就是你在fixture生成的图上进行操作,那么这些操作都会显示在fixture图上,这就是子记录),不可以自己选择下拉咯,所以他也是一无所知的,而且不提供Record树。
Display是最低级的显示控件,由其他所有显示控件来使用,一无所知,只显示一个图像和一组图形,图形图像必须通过编程提供。
这里我们选择RecordD。
Cognex.VisionPro.ICogRecord graphic_record;
graphic_record = myrecord.SubRecords["ShowLastRunRecordForUserQueue"];
graphic_record = graphic_record.SubRecords["LastRun"];
graphic_record = graphic_record.SubRecords[1];
// graphic_record = graphic_record.SubRecords["CogFixtureTool1.OutputImage"]; 方法2
CogRecordDisplay1.Record = graphic_record;
CogRecordDisplay1.Fit(true);
差不多基本的功能就是这样了,这一套做完无Bug,大家基本上可以在此基础上进行二次开发了。下面贴一下
源码
设置的各控件名称
Form1.cs整体
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using Cognex.VisionPro;
using Cognex.VisionPro.QuickBuild;
namespace mywind
{
public partial class Form1 : Form
{
private CogJobManager myJobManager;
private CogJob myJob;
private CogJobIndependent myIndepenentJob;
public Form1()
{
InitializeComponent();
// 临时办法:线程间相互访问
//Control.CheckForIllegalCrossThreadCalls = false;
}
private void Form1_Load(object sender, EventArgs e)
{
// 加载vpp作为一个job manager
myJobManager = (CogJobManager)CogSerializer.LoadObjectFromFile("../../../Job722.vpp");
// 引用job manager中的第一个job
myJob = myJobManager.Job(0);
// 初始化一个对象,其参数引用包含更多关于job manager的信息
myIndepenentJob = myJob.OwnedIndependent;
// 清空所有job manager队列,刷新陈旧的图像和结果
myJobManager.UserQueueFlush();
myJobManager.FailureQueueFlush();
myJob.ImageQueueFlush();
myIndepenentJob.RealTimeQueueFlush();
// 为停止操作绑定事件
// CJM没有在GUI创建按钮,但是它调用了一个按钮停止事件!这个事件需要访问GUI线程的按钮!故需要“委托”
myJobManager.Stopped += new CogJobManager.CogJobManagerStoppedEventHandler(myJobManager_Stopped);
//myJobManager.Stopped += myJobManager_Stopped;
// 状态显示操作绑定事件
myJobManager.UserResultAvailable += myJobManager_UserResultAvailable;
// 连接bar状态栏
CogDisplaystatusBar1.Display = CogRecordDisplay1;
}
private void button1_Click(object sender, EventArgs e)
{
try
{
// 避免死锁
//ControlBox = false;
ShowTrainCheckBox.Enabled = false;
RunContCheckBox.Enabled = false;
// 防止在单词任务完成前再次调用任务 让按钮不可点击就行了
RunOnceButton.Enabled = false;
myJobManager.Run();
}
catch (Exception e1)
{
MessageBox.Show(e1.Message);
}
}
// 每个win窗体控件只能通过创建它的线程来访问
// 创建一个有预期签名的委托 用于指向下面的停止事件
delegate void myJobManagerDelegate(Object ByVaklsender,CogJobManagerActionEventArgs _ByVale);
// 所有工作完成 CJM运行一个停止事件 重启按钮
private void myJobManager_Stopped(Object ByValsender, CogJobManagerActionEventArgs _ByVale)
{
if (InvokeRequired)
{
// 创建委托实例 并指向停止事件的函数指针
//myJobManagerDelegate mydelegate = myJobManager_Stopped;
myJobManagerDelegate mydelegate = new myJobManagerDelegate(myJobManager_Stopped);
// 定义一个参数数组
Object[] myobj = new Object[2] { ByValsender, _ByVale };
// 使用控件“魔术”调用Invoke,在GUI线程上调用我们的委托(当前我们在vpp线程,但是需要使用GUI线程来使按钮恢复可选,所以需要委托给GUI线程)
this.Invoke(mydelegate, myobj);
}
//线程报错 暂时不管
//ControlBox = true;
RunOnceButton.Enabled = true;
RunContCheckBox.Enabled = true;
ShowTrainCheckBox.Enabled = true;
}
private void RunContCheckBox_CheckedChanged(object sender, EventArgs e)
{
if (RunContCheckBox.Checked)
{
try
{
//ControlBox = false;
ShowTrainCheckBox.Enabled = false;
RunOnceButton.Enabled = false;
myJobManager.RunContinuous();
}
catch (Exception e2)
{
MessageBox.Show(e2.Message);
}
}
else
{
try
{
RunContCheckBox.Enabled = false;
myJobManager.Stop();
}
catch(Exception e3)
{
MessageBox.Show(e3.Message);
}
}
}
private void myJobManager_UserResultAvailable(Object sender, CogJobManagerActionEventArgs e)
{
if (InvokeRequired)
{
myJobManagerDelegate mydel = new myJobManagerDelegate(myJobManager_UserResultAvailable);
Object[] args = new Object[2] { sender, e };
Invoke(mydel, args);
return;
}
// 输出运行状态
Cognex.VisionPro.ICogRecord myrecord = myJobManager.UserResult() as ICogRecord;
RunStatusTextBox.Text = myrecord.SubRecords["UserResultTag"].Content.ToString() + ":" + myrecord.SubRecords["JobName"].Content.ToString() + "-->";
RunStatusTextBox.Text += myrecord.SubRecords["RunStatus"].Content.ToString();
// 输出参数
Cognex.VisionPro.ToolGroup.CogToolGroup myTG = myJob.VisionTool as Cognex.VisionPro.ToolGroup.CogToolGroup;
Cognex.VisionPro.ToolBlock.CogToolBlock myTB1 = myTG.Tools["Detect_Defects"] as Cognex.VisionPro.ToolBlock.CogToolBlock;
Cognex.VisionPro.PMAlign.CogPMAlignTool myPM = myTB1.Tools["CogPMAlignTool2"] as Cognex.VisionPro.PMAlign.CogPMAlignTool;
Cognex.VisionPro.ICogRecord tmpRecord;
try
{
textBox1.Text = myPM.Results.Count.ToString();
}
catch (Exception e5)
{
textBox1.Text = e5.ToString();
}
// 输出图像
Cognex.VisionPro.ICogRecord graphic_record;
graphic_record = myrecord.SubRecords["ShowLastRunRecordForUserQueue"];
graphic_record = graphic_record.SubRecords["LastRun"];
graphic_record = graphic_record.SubRecords[1];
//graphic_record = graphic_record.SubRecords["CogFixtureTool1.OutputImage"];
CogRecordDisplay1.Record = graphic_record;
CogRecordDisplay1.Fit(true);
//RunStatusTextBox.Text = "";
}
private void RetrainButton_Click(object sender, EventArgs e)
{
// 调取工具
Cognex.VisionPro.ToolGroup.CogToolGroup myTG = myJob.VisionTool as Cognex.VisionPro.ToolGroup.CogToolGroup;
Cognex.VisionPro.ToolBlock.CogToolBlock myTB = myTG.Tools["Detect_Defects"] as Cognex.VisionPro.ToolBlock.CogToolBlock;
Cognex.VisionPro.PMAlign.CogPMAlignTool myPM = myTB.Tools["CogPMAlignTool1"] as Cognex.VisionPro.PMAlign.CogPMAlignTool;
// 重训练
myPM.Pattern.Train();
RunStatusTextBox.Text = myPM.RunStatus.ToString();
}
private void ShowTrainCheckBox_CheckedChanged(object sender, EventArgs e)
{
if (ShowTrainCheckBox.Checked)
{
RunOnceButton.Enabled = false;
RunContCheckBox.Enabled = false;
RetrainButton.Enabled = true;
Cognex.VisionPro .ToolGroup .CogToolGroup myTG = myJob.VisionTool as Cognex.VisionPro.ToolGroup.CogToolGroup;
Cognex.VisionPro.ToolBlock.CogToolBlock myTB1 = myTG.Tools["Detect_Defects"] as Cognex.VisionPro.ToolBlock.CogToolBlock;
Cognex.VisionPro.PMAlign.CogPMAlignTool myPM = myTB1.Tools["CogPMAlignTool1"] as Cognex.VisionPro.PMAlign.CogPMAlignTool;
Cognex.VisionPro.ICogRecord tmpRecord;
tmpRecord = myPM.CreateCurrentRecord();
tmpRecord = tmpRecord.SubRecords["TrainImage"];
CogRecordDisplay1.Record = tmpRecord;
CogRecordDisplay1.Fit(true);
}
else
{
RunOnceButton.Enabled = true;
RunContCheckBox.Enabled = true;
RetrainButton.Enabled = false;
CogRecordDisplay1.Record = null;
}
}
private void textBox1_TextChanged(object sender, EventArgs e)
{
}
}
}
最后把我用的Job的框架给大家看一眼,不然不好理解我里面的一些名字是啥意思
这四部分给出来,大家按照我给的名字命名你的控件,然后如果想先爽一下,直接把我给的Form1.cs覆盖掉你原本的就应该ok了哦。但是我建议大家,还是先按照pdf自己做,不懂了或者卡壳了,再来看,可以理解更深哦~
最后笔者想说,如果有不理解,或者读后有所感想,想说一说哪里写的不够清楚,或者思路或者语言构造,甚至一句感谢,都是我十分看重的财富。我写了也不少了,但真的没什么人有学术互动的。怎么说呢,有点悲伤。
还有一点,我写的所有东西,如果大家有需要,我可以放到Git里,哪怕有一个人,我也不嫌麻烦。所以,还是想大家跟我说的。。。
也不知道怎么结尾,就给大家拜个早年吧。