框架搭建
一.UI层:完成页面UI的设计
1.完成UI设计
2.完成窗口初始化操作等
例如:页面的移动,关闭,最大化,最小化,定时器显示底部时间等功能。
封装添加日志的方法----ListView控件专门用来显示日志消息,参数如下:
1 增加列 Columns属性设置
2 把视图(View)属性改成Details
3 如果想隐藏 把HeadStyle改成None
4 FullRowSelect设置true,则可选中日志
5 增加一个imagelist控件 增加图(images属性)
6 绑定imagelist 设置SmallImageList为你的imagelist
7 写一个通用添加日志的代码
(lv_Log[控件对象].InvokeRequired) //判断是否需要调用同步委托的方法
在窗体加载事件的时候,我们可以进行一些初始化工作,例如板卡,相机,IO信号的检测等功能
1.初始化板卡功能
在初始化板卡的时候,我们可以定义一个字段,用来确认板卡的状态,作为板卡打开与否的标志位,同时我们可以设置属性用来保护该字段,并且进行一些拓展,例如把底部板卡状态文字,颜色改变,板卡标志位改变的一些操作。
通过实例化板卡类,添加引用后,即可使用板卡类的对象,并调用其方法
调用板卡类的初始化板卡功能,若返回值为真,则证明板卡打开成功,并添加日志,板卡打开状态赋值为真。
2.初始化相机功能
这里注意要添加对应的相机具体实现类的程序集
初始化相机,我们同样可以定义一个相机打开标志位,属性进行扩展其内容。
这里我们用接口+反射,可以实现返回不同类的对象,进行不同相机的操作。
我们可以进行一些校验:例如返回的对象不能为空,调用查找相机方法返回的相机个数大于0,在进行一些图像操作。
3.检测IO信号
这里我们考虑到IO信号需要一直实时刷新,所以我们考虑到使用线程的方式去开启一个线程,专门处理IO信号的变化。
调用板卡类的检测IO信号方法,会有返回值,查阅文档得知,进行位移运算后再求位与,则可以得到该点的信号为1还是其他。若为1则是有信号,则可以把对象IO状态改变,这里是不断访问主线程的空间,所以需要用到同步委托的方式来进行访问。
X Y Z轴信号都如上所示。
急停信号,手动输入的启动,停止信号,则调用对应板卡的不同方法,根据文档中查阅,进行位移后再求位与运算,得到其状态。因为这里不需要一致实时刷新,所以我们可以不使用同步委托的方法,直接访问即可。
封装设置速度,加速度等操作的方法
这里我们定义了枚举,用来显示填写参数的合法性。例如初速度不能大于最大速度,最大脉冲速度,脉冲必须是整数,速度必须是整数等。
我们利用int.TryParse来判断,所填写的是否是整数,因为int.TryParse第二个参数必须要有out修饰,所以我们可以使用out来作为初速度等的返回值。
最后调用板卡的设置速度参数的方法,完成速度的设置。
点位运动:
点位运动前,我们可以进行一些校验。校验板卡状态,调用自己封装的设置速度方法,用来校验速度参数填写是否合法,并添加日志。
调用板卡对应的点位运动即可。
获得脉冲方法
因为脉冲反馈必须是要实时状态,所以我们可以考虑使用定时器。这里我们使用子线程定时器。定时器: System.windows.Forms.Timer 占用主线程
System.Timers.Timer 占用子线程
System.Threading.Timer 占用子线程
我们查到到第二种子线程定时器,对应的事件,我们定义的方法签名与该事件签名必须一致。
定义获得脉冲方法(方法签名与子线程定时器事件签名一致),这里是实时对主窗体进行刷新,所以需要用到同步委托的方式。最后在窗体加载事件的时候,进行定时器与该方法的绑定,定时器的启动等操作。
回零点方法
回零点前,我们先进行一些校验。
1.板卡是否打开,初速度,调用设置速度等参数的方法,判断参数设置是否正常,不正常则跳出,并且添加日志
2.Z轴先往上抬起,防止撞击,调用板卡的位置运动方法,并且循环等待,知道Z轴运动状态为停止状态的时候,才跳出该循环。这里我们可以使用Application.DoEvents(); //让主线程可以执行其他操作,使得页面流畅
3.回零点
勾选哪个轴,就进行哪个轴的回原点方法。
因为回原点有高速,低速,正向,负向等方式。所以这里我们使用三元表达式的方式,调用回原点方法 若选择正向回零则为1 否则为2(负向) 勾选高速回零则为(1) 否则低速为0
motionLS.GoZero(0, (ushort)(rb_Direction.Checked ? 1 : 2), (ushort)(rb_Hight.Checked ? 1 : 0));
当所有轴都运动完成停止后,调用脉冲清零方法。Application.DoEvents(); //让主线程可以执行其他操作,使得页面流畅
九点标定方法
1.首先进行数据校验--对应的脉冲值未填写完成,则不能进行标定
遍历控件中的容器(控件.Controls),如果需要按顺序遍历的时候,我们可以更改前台代码使得各个控件按照顺序来排列,若有控件的值为空,则提示,并且跳出。日志可以显示出来
//正则表达式 bool ress = Regex.IsMatch(txt.Text, "^[0-9-]+$"); ^以什么开头 $以什么结尾
2.传递脉冲位置与像素位置
调用采集图像方法,图像处理类的九点标定方法(会将行列坐标存到实体类的对象),显示九点标定方法,显示图像方法。
我们可以获取实体类对象中的行列坐标,通过遍历控件,将行列坐标值显示在页面控件的像素位置显示。
再通过循环遍历实际脉冲控件的方式,将页面上的实际脉冲,保存到实体类的对象X,Y脉冲当中
3.调用九点标定,获得九点标定的矩阵,日志显
保存矩阵方法
SaveFileDialog类专门用于文件的操作
打开文件,文件标题,文件打开默认路径设置
调用保存矩阵方法,根据返回值确认保存状态。
二.BLL业务逻辑层
1.自动贴合功能
放料位因为可能存在多个,所以定义成集合
将每个物料看成一个对象,我们可以定义成集合类的类型,将每个产品的信息都存到集合类中
1.矩阵的读入
2.完成自动贴合业务
这里我们需要不断的去拍照,识别,抓取。所以我们专门开个线程去进行贴合业务。
我们可以通过取消任务对象CancellationTokenSource,来控制贴合进行与暂停。
线程中进行
1.采集图像
2.物料像素位置的提取
3.显示图像
这里判断下:如果像素位置提取的个数大于1,则进行后续操作
4.显示物料区域的轮廓
5.转实际脉冲位置
循环遍历工件产品个数,我们把每个产品看做实体类的对象(重新new),在初始化器中,将实体类对象的脉冲值赋值给每个实体类的对象,不在牌照位置,则增加补偿。
6.调用运动控制方法
封装运动控制方法==》根据业务逻辑去封装
1.运动到物料上方,Z轴下移,吸气,Z轴上移,运动到放料位上方,Z轴下移,放气,Z轴上移,回原点。
2.自动轨迹业务
我们这里用圆和线段进行分割,然后进行直线和圆弧的拟合,通过get_contour_global_attrib_xld
来获得轮廓的全局属性,返回的值>0则是圆,否则是其他
我们在图像处理类封装一个方法,提取目标物体的xld轮廓,通过集合类的方式,将xld轮廓的起点行列坐标,圆弧的圆心坐标等,传递给每个实体类的对象。因为这里有循环,所以我们在图像处理类的方法中,把每段xld的像素坐标信息传递给实体类的对象。将拟合后的xld也存到实体类的对象中。 这里注意out传参的问题
这里页面有三种选择,我们可以定义一个枚举类型,通过UI层控件的改变,传递到下层,来改变走轨迹的方式。
3.自动分拣
1.封装划区域的方法。
这里会有第一次画不上(控件.Focus)聚焦的效果
2.封装创建圆和矩形的模板 圆==》创建模板==》中心移动法(模板仿射变换转到中心位置)
这里把查到的轮廓,输出的句柄数组作为实体类的对象传递
3.封装显示圆和矩形的模板轮廓(实体类的对象,id【圆或者矩形】)
4.保存模板 分别保存圆和矩形模板
5.多模板查找 查找到的行列坐标,模型句柄索引,模板转向实例的角度,个数,实例轮廓
6.读入模板
7.显示中心点
图像处理:获得轮廓,行列坐标
先执行属性和字段=》再执行构造方法
UI层调用 调用划区域方法==》采集图像==》显示图像==》调用创建矩形和原的方法==调用显示方法
定义-模板匹配参数类--实体类
业务层:
1.读入九点矩阵
2.读入模板
3.采集图像
4.显示图像
5.查找模板
6.显示模板
7.转实际脉冲
循环遍历查找到的所有实例,将不同类型,存放到不同集合中,用集合来承接这些数据。
8.板卡运动
参数调整:先进行验证==》封装==》传递
数据验证:相机,板卡状态,圆和矩形区域不为空
数据封装:将页面的参数赋值给实体类的对象
数据传递:调用方法的时候,将实体类对象传入,我们想修改的参数。
数学运算类中,我们把自动轨迹方法获取到的像素值,通过矩阵变换转换成实际的脉冲值。
通过UI页面上的选择,页面上的图像和手动,都需要转实际脉冲,CAD不需要则乘以脉冲当量即可,我们通过一个状态,来确认是线还是圆。通过集合类来存储转换后的实际脉冲值。注意这里矩阵仿射变换输出的值是htuple类型,它的值存在了.D中,是double类型,所以我们需要强转一下,在赋值给实体类的对象实际脉冲。
在业务逻辑层
定义自动轨迹的方法
1.读入矩阵
2.采集图像
3.显示图像
4.查找物料,像素提取(判断个数)
5.转实际脉冲
6.调用运动控制方法
三.DAL数据层
四.Tool工具层
工具层的封装
1.图像处理类:
该类专门用来处理halcon图像的一些算法,需要时进行调用即可==>
1.打开窗口方法
2.显示图像方法
显示图像,则需要图像作为参数输入,所以方法封装该考虑图像作为参数进行传递
3.九点标定
九点标定这里我们需要对图像进行处理,所以考虑图像作为参数传递,而我们处理后的轮廓,输出的圆心行列坐标,我们可以用实体类的对象进行存储,所以在定义方法的时候考虑:图像作为形参传递,实体类的对象作为形参传递进来。
实体类对象存储,产生的XLD轮廓,行列坐标,个数
4.显示九点标定
显示九点标定结果前,需要对实体类存放的xld个数,对象是否为空等进行校验。
5.提取物料像素个数方法
提取像素,我们需要图像,所以将图像作为形参,提取目标物体中心行列坐标,将目标物体的行列坐标,保存至实体类的对象中,并且将目标物体的区域保存到实体类,新增一个实体类的目标区域字段。最后释放非托管资源。
6.显示目标物体区域
2.运动板卡类
该类用来封装板卡的一些操作作为方法使用,这样我们就可以在mian中进行调用该类,实现高内聚,低耦合的思想。
板卡:
1.添加板卡的dll库
2.添加引用(如果添加引用失败报错,则证明该文件是c++写的,我们可以找到示例里面的.cs文件放到与板卡类统一目录当中)
3.添加命名空间后即可使用
4.封装一些常用方法:
板卡初始化,板卡点位运动,板卡连续运动,板卡急停,脉冲计数,脉冲清零,读取个轴状态,减速停止,回零,各轴IO信号检测,外部输入信号检测,主动输出信号,设置运动参数等方法。具体需要什么操作,就来封装什么方法。
封装方法思路:将变化的作为参数传递,不变的作为方法体,想要得到的值作为返回值或者用out来修饰。
3.相机操作类
相机类我们这里考虑到相机的兼容性,我们可以使用接口+反射的方式来实现。
接口类:实现相机的一些通用功能,如:相机初始化,打开相机,采集图像,关闭相机等操作。
反射+工厂类:这里我们可以Reflection; //添加反射接口
通过用Assembly.Load(FullName[程序集名称]).CreateInstance(ClassName[具体的类名]); 来返回不同类的对象,实现只需要修改配置文件即可达到使用不同相机的功能。===》注意:若这里报错找不到对应的库文件,一般是 主程序中未添加引用相机具体实现类,导致找不到。解决方法:把生成的文件拷到exe统一目录下,或者直接添加引用。
接口的具体实现类,我们这里对具体方法来进行实现 这里可以用SDK来实现采集图像,也可以用halcon来实现。以halcon为例:
首先我们这里需要进行三部曲 1.找到对应的halcon的dll库,2.添加引用 3.添加halcon的命名空间
1.查找相机
枚举查找相机个数,若相机个数大于0,则证明查找到了相机(我们可以把相机个数作为返回值,方便后面多个相机的时候进行分别打开)
2.初始化打开相机
这里我们考虑到相机个数可能为多个,所以考虑使用for循环。
我们可以利用查找相机输出的个数--作为相机句柄数组的长度,可以进行循环打开相机采集等操作,并进行相机初始化
3.采集图像
采集图像,图像可以作为输出参数,给后续显示等操作。
4.关闭相机
关闭相机应该考虑到多个相机,所以考虑循环关闭。
4.数学计算类
该类用作一些运算
1.求九点标定的矩阵
因为求九点标定矩阵,需要用到像素坐标,实际距离,而这些参数都存到了实体类的对象。所以我们考虑到封装该方法,将实体类的对象作为参数传递进来,矩阵作为输出参数。在调用halcon算法前,需要进行校验,例如对象不为空,像素坐标个数校验等。运算成功,返回状态,方便后续程序利用该状态。
2.保存九点标定
查看算子得到,需要保存的矩阵,路径等作为封装方法的参数。保存成功,返回状态
3.读取九点标定矩阵
输入路径,输出矩阵
4.转实际脉冲位置
输入目标物体行列坐标,输出实际位置,将实际脉冲位置存到实体类的对象中
五.实体层
实体类封装所需要存放的字段等
该类主要用于存放一些运算过程产生的图像数据等。
这里我们存放了九点标定的行列坐标,九点标定的点数,九点标定过程产生的XLD轮廓或者区域等。
存放X轴的脉冲,Y轴的脉冲
模板匹配专门弄个实体类的对象。