2.1.1 定位引导:常规定位引导项目入门必看
本文主要介绍如何使用VM实现常规的定位引导项目。下图为定位引导项目整体流程图。
1 场景描述
适用场景:单相机拍物料的抓取场景。
标定方式:工具中心与旋转中心(一般为法兰中心)共轴的定位引导场景,可采用九点标定,即单相机与机构做平移标定(9点标定)。工具中心与旋转中心不共轴,可采用十二点标定,如图所示为不共轴示意图。
限制条件:不支持多相机联立坐标系、相机拍照位姿变化等场景。
2 机构及成像质量调整
2.1 硬件安装要求
2.1.1 机构应安装稳固,不能产生松动。
2.1.2 调整相机应保持镜头平面与物料平面平行。
2.1.3 网线接口要安装稳固应采用海康网线。
2.1.4 电源线应采用高柔电缆。
2.1.5 镜头光圈先拧到底再反拧半圈,保证既有一定的景深同时保持较好的图像锐利度。
2.2 系统安装要求
2.2.1 Win系统要求:Win10 64bit。
2.2.2 处理器要求:I5以上CPU。
2.3 成像质量要求
1)调整相机焦距保证成像清晰(以图像边缘过渡带不超过两个像素为准)。
2)保证待检测物料特征处于图像正中心(图像边缘容易产生径向畸变)。
2.4 机构精度要求
2.4.1 静态测试:相机在拍照位与物料保持静止,连续拍照,保证图像特征点提取稳定在一个像素以内,特征点提取不稳定则需选取更为稳定的特征点。
2.4.2 动态测试
1)平移精度:物料保持不动,机械手分别沿X/Y正负方向等步长移动4次得到三组像素差,保证像素差在一个像素以内。每次移动后回到原位,查看平移复位精度(即回原位查看物料图像特征点坐标,必须保证在在一个像素以内)。
2)旋转精度:机械手分别沿正反方向旋转一定角度(与标定时的角度相当),查看图像特征角度是否等角度变化(一般需保证在0.1度以内),同时回原位查看复位精度(即回原位查看物料图像特征点坐标必须在一个像素以内)。
2.4.3 点位移动测试:使用VM通讯发送绝对坐标给机械手,确保机械手能移动到指定点位。
3 搭建定位引导视觉方案
3.1 搭建标定方案
首先搭建标定方案:搭建好标定流程(以触发获取为例),应包含图像特征点提取、通讯触发及协议解析(用于触发流程及解析机械手物理坐标)、平移旋转标定模块(参数设置及说明参考平移旋转标定参数说明)以及发送数据(通过通讯回复本次标定状态)。
3.1.1 图像源模块:可根据实际情况选择相机或本地图像。一般情况配合机械手实时采图需选择相机并进行相机配置。
3.1.2 高精度模板匹配:提取图像特征点,需保证在同一位置特征点定位精度保持在一个像素以内,同一位置模板匹配角度需稳定保持在0.1度以内。
3.1.3 平移旋转标定模块
1)平移旋转标定基本参数设置如图所示:平移次数默认九次;平移旋转标定需要设置旋转次数,一般设为3。
2)平移旋转标定参数表及详细介绍,请按实际情况填写对应参数。
平移旋转标定基本参数 | |
---|---|
标定类型 | 平移标定和平移旋转标定两种;平移标定对应的是旋转中心在工具中心的场景,平移旋转标定对应的是旋转中心和工具中心不共轴的场景(增加了旋转中心的标定);两种标定类型分别对应的基本参数会有所不同 |
标定点获取 | 选择触发获取或手动输入,通常选择触发获取。当选择手动输入时支持“平移旋转标定”模块单独运行,即手动输入坐标点生成标定矩阵 |
相机模式 | 支持相机静止和相机运动两种模式 |
自由度 | 可根据具体需求选择,有缩放、旋转、纵横比、倾斜、平移及透射、缩放、旋转、纵横比、倾斜及平移和缩放、旋转及平移3种模式,三个参数分别对应“透视变换”、“仿射变换”和“相似性变换” |
图像点 | 平移旋转标定的图像特征点,可订阅其他模块的点 |
物理点 | 机械臂的坐标点,VM图像上的一个图像点对应一个机械臂的物理点,可订阅通讯输入 |
图像角度 | 图像特征对应的角度,可订阅其他模块的角度,可通过相关计算得到旋转一致性(平移旋转标定时显示) |
物理角度 | 当前位姿机械手的角度,一般从外部设备获取,可通过相关计算得到旋转一致性、左右手坐标系(平移旋转标定时显示) |
平移次数 | 平移获取标定点的次数,只针对x/y方向的平移,一般设置成9点 |
旋转次数 | 旋转轴与图像中心不共轴时需设置旋转次数,一般设置成3次;且旋转是在第5个点的位置进行(平移旋转标定时显示) |
组合标定 | 开启后支持平移后旋转并平移回视野的标定方式;针对小视野场景,机构在视野内只能小角度旋转导致旋转中心拟合不准,此时可以开启组合标定,控制机构旋转较大角度然后平移回视野的方式进行旋转中心标定(该方式对平移精度要求较高) |
更新文件 | 一轮标定完成后,如果开启了更新文件控件,新一轮标定会将标定结果更新到标定文件中 |
标定文件路径 | 标定文件的绝对路径,该路径下若存在文件就加载,若不存在则加载失败,运行时报错 |
示教 | 示教使能,开启后,可根据相关设置辨别外部输入的信号是否为示教信号,并存储示教坐标 |
外部输入字符 | 示教开启后显示,可订阅外部设备的信号 |
外部触发字符 | 判断外部输入信号是否为示教信号的依据。当外部输入字符和外部触发字符一致时,则该信号为示教信号 |
标定输出结果 | |
标定状态 | 1表示标定成功,0表示标定失败 |
平移估计真实误差 | 平移的真实误差值,供评判平移标定结果 |
旋转真实平均误差 | 旋转的真实误差,供评判旋转标定结果 |
3.2 创建通讯设备及全局触发
可在通信管理添加通信设备,全局触发建立触发事件,实现机械手通信触发视觉流程。
3.3 标定
3.3.1 机械臂带动相机按照参数设定的移动方向及步长移动,每次移动触发标定流程。九点标定移动九次,十二点标定需先按九点移动最后三点在第五点的基础上按参数设置的角度旋转三次。
3.3.2 标定结果查看(出现异常需重新标定)
1)标定轨迹:如图所示为标定轨迹,查看标定轨迹中的X轴和Y轴是否垂直,轨迹是否存在波动(X/Y三点是否共线)。
2)标定状态:标定状态1表示成功,0表示失败需从新标定。平移估计真实误差表示平移产生的平均物理误差。旋转真实平均误差表示旋转产生的平均物理误差。
3)像素精度:模块结果中可查看像素误差,平移像素精度表示平移产生的平均像素误差,一般要求保证在一个像素以内。旋转像素精度表示旋转产生的平均像素误差,一般保证在0.5像素以内。
4)旋转一致性:表示图像坐标系与机械手坐标系的旋转一致性,1表示旋转一致,-1表示旋转方向相反,需旋转机构查看旋转一致性是否判断准确,若判断错误需调整参数从新标定。
3.4 示教
示教(示教流程可以和生产流程共用,方案搭建示意如下图):
示教动作如下
1)放置生产用的物料在工作台中。
2)调整机构位置,使得机构的吸嘴或夹爪可以很好地对准物料,记录此时机构的物理坐标(示教物理点)。
3)机构移动到拍照位置(示教拍照物理点),拍摄物料,获得像素坐标。
创建基准(基于单点抓取模块,最终基准数据会保存在单点抓取模块底层):
- 填写示教抓取物理点,即示教时抓取物料的机械手物理坐标;
- 填写/绑定基准图像像素点,即此时拍照位拍照获取的图像像素点。
- 创建动作如下图所示,创建完成后可将像素点绑定为运行图像点,若采用同一分支下来的特征点则无需重新绑定。
3.5 生产
3.5.1 生产拍照位确认
1)生产拍照位坐标最好与标定第五点拍照位一致,可产生X/Y偏移,但拍照姿态(相机旋转角度)必须保持不变。
2)生产拍照位与标定时的工作距离必须保持不变。
3.5.2 生产方案搭建:生产流程同示教流程,生产时需保证提取的特征点与示教图像特征点一致。
3.5.3 单点抓取模块
1)输入方式:平移标定对应九点标定,平移旋转标定对应十二点标定。确定是按点输入还是按坐标输入,可以订阅模块也可以手动输入。
2)像素点: 在抓取场景中,需要输入两个像素点,分别是基准点和运行点。其中基准点的像素点的输入在示教过程中已经填入。生产时需重新绑定前序模块提取的运行图像点(需与示教基准点为同一特征点)。
3)示教抓取物理坐标,示教拍照物理坐标点必须正确输入,且需要加载正确的标定文件。
3.5.4 运行获取偏差结果:模块结果分为相对坐标及绝对坐标,绝对坐标则为机构抓取的位置。使用格式化模块订阅单点抓取模块输出的偏差值,然后使用发送数据模块将结果发送给机械手,引导机械手前往正确的抓取位置。
2.1.2 外观检测:利用VM实现缺陷检测及尺寸量测
描述
环境:VM4.0.0
现象:如何使用VM进行检测与测量?
解答
1 功能需求
某客户现场要对生产的PCB板进行多项检测,主要对三个区域进行检测分别为:区域1,检测锡片的有无。区域1,检测锡片的外观完整性。区域2,测量装配件的长度d1 (图像中为上下端的高度)、测量区域3圆心到区域2装配件上端的距离d2。每次拍照后,视觉软件将总检测状态(OK为1,NG为0)、以及两个测量值(物理值)发给上位机。如下图所示。
2 检测步骤
首先分析需求,该项目主要分为检测与测量两个部分。其中,检测分为有无检测以及外观完整性检测,可以通过检测区域1的亮度来判断有无,通过检测区域1较亮区域的面积判断外观是否完整;测量在这里分为两个部分,分别为线与线之间距离的测量,圆心点与线之间距离的测量。这个项目完整的流程编辑如下图所示。
第一步:首先增加图像源模块,双击图像源模块,按照实际情况配置图像源(本地图像、相机、SDK),实际生产中通常使用相机,本地调试一般选择本地图像,通过SDK接口给图则选择SDK。注意在使用相机之前需要在快捷工具栏中的全局相机配置相机。
第二步:增加高精度匹配模块,使用连线连接,双击高精度匹配模块,配置参数,首先设。置图像源,通常为图像源模块的输出图像。
之后,点击特征模板中的创建,使用掩膜工具,选择合适的特征区域,设定合适的配置,参数创建合适的模板。需要注意的是,配置参数通常使用默认参数的自动模式,如果无法满足需求,可以切换手动模式进行调节,其中速度尺度与特征尺度控制模板的精细程度,影响模板匹配的速度,特征尺度越大,精细程度越低,匹配速度越快;调节对比度阈值影响可以建立想要建立的模板。
建立好模板之后,需要设置运行参数,包括最小匹配分数,匹配个数,匹配极性,角度范围尺度范围等。最小匹配参数设置越高,匹配越严格;匹配个数代表允许查找的匹配,当有多个匹配时,默认匹配分数最高的项;匹配极性代表建立的模板与背景之间的过渡亮暗是否一致;角度范围指建立的模板与可能存在的匹配项之间可以允许的角度变化,改参数会影响匹配的速度,角度范围设置越大,匹配速度越慢。
如果调节上述参数无法满足使用的要求,可以调节运行参数中的高级参数,其中最大重叠率代表匹配多个目标时,可以允许的目标之间的重叠情况;延拓阈值一般在匹配目标位于图像边缘,匹配不全时使用。
第三步:增加位置修正模块,使用连线连接,双击模块,参数一般选择默认参数,点击一次执行,点击创建基准。
第四步:增加亮度检测模块,使用连线连接,双击模块,设置如图所示的ROI。
第五步:增加Blob分析模块,使用连线连接,双击模块,设置如图所示的ROI。
之后,根据实际项目情况设置运行参数。
第六步:增加条件检测模块,使用连线连接,双击模块,订阅希望进行判断的参数,并且设置有效值范围。
到这里,本次项目的检测部分已经全部完成,下面进行测量部分。
第七步:增加直线查找模块,使用连线连接,双击模块,根据要查找的直线设置ROI。
之后,设置运行参数,其中边缘类型表示想要查找的直线的特征;边缘极性表示按照搜寻方向灰度值的变化情况;边缘阈值表示梯度阈值;滤波尺寸表示想要的边缘的抗噪能力,该数值越大,则抗噪能力越强,但同时也可能导致真正的边缘被滤除;卡尺数量表示查找直线需要使用卡尺工具找出的点的数量;剔除点数表示拟合时被排除的最小点的数量,该参数需要结合剔除距离共同设置;
如果上述的运行参数无法满足项目需要,可以设置高级参数。剔除距离表示允许的离群点到查找的直线的最大像素距离;投影宽度表示扫描边缘点查找ROI的区域宽度;初始拟合表示进行直线拟合的方式,一般选择全局拟合;拟合方式表示进行直线拟合的算法选择。
第七步:增加直线查找模块与圆查找模块,使用连线连接,参照上述直线查找的过程查找圆。
第八步:增加格式化模块,使用连线连接,双击配置想要输出的内容。
格式化结果如下所示,注意这里输出的是像素距离,实际中需要乘以当像素代表的实际物理尺寸。
第九步:增加发送数据模块,使用连线连接,选择通信方式,这里以TCP通信,并且默认视觉软甲是服务端,选择通信设备。需要注意的是,TCP服务端需要在快捷栏中的通信管理中进行配置。
这样,我们就完成了这个本项目所有的检测与测量的需求,并且可以对下位机发送通信信号。之后,打开网络通信助手验证,是否可以正常通信。网络通信助手输出结果如下所示。
问题根因
不熟悉VM做项目的流程。
2.1.3 图像检索:使用VM深度学习功能实现模型训练与图像检索功能
描述
环境:VM4.0.0及以上
现象:如何使用VM深度学习工具自己训练模型实现图像检索功能。
解答
1.图像检索原理
图像检索主要分为图像的表示学习和分类器注册两部分。图像的表示学习就是通过构建网络模型实现图像的特征表示,分类器注册就是将表示学习网络输出的图像特征向量输入分类器进行分类器注册。使用VM实现图像检索前也需要进行模型训练和分类器注册。
图像检索和图像分类的区别:图像分类是直接训练一个固定类别数的分类器模型;图像检索是训练一个图像表示模型,然后针对不同的检索库注册不同的分类器模型,从而避免了针对不同检索库需要训练不同网络模型的麻烦,提升网络模型的泛化能力。
2.基于VM的图像检索方法
2.1打标签
可以使用VMTrain1.4.0来进行数据集打标签工作,添加图片是对单张图片进行打标,添加文件夹是对多张图片批量打标签。打标签的结果如图所显示。
2.2模型训练
模型训练有三种环境可以选择:本地训练(需要支持深度学习加速的NVIDIA显卡和CUDA环境)、云服务器训练和本地服务器训练。训练参数:训练迭代次数(可以根据数据量来调整)、基础学习率(可以控制模型的收敛速度)、版本、剪枝比例(模型压缩手段,可以根据模型性能和精度来权衡调整)、数据增强(数据集扩充手段,用于丰富数据量,防止过拟合)。参数设置完毕,点击开始训练。训练结束在指定为文件夹下会生成bin文件(模型权重文件)。
2.3注册图像
在VM中使用DL图像检索模块中的Gallery管理中的注册图像按钮进行图像注册,可以单张图像注册,也可以按文件夹注册(可以针对不同的检索库注册不同的Gallery,执行时按需要加载gall文件)。
2.4图像检索
图像检索方案如图所示,主要使用DL图像检索模块进行图像检索。
参数配置:需要加载训练好的模型文件(VisionTrain训练得到的bin文件)和注册好的Gallery文件(gall文件)。
2.5检索结果
问题根因
不熟悉VM深度学习训练工具的使用。
2.1.4 字符识别:使用VM进行字符识别
描述
环境:VM4.0.0
现象:如何使用VM进行字符识别?
解答
1 功能需求:对如下图所示的铭牌上的字符进行识别,并将识别出的字符发送给上位机。
2 检测步骤:首先分析需求,该项目的主要需求为字符识别,可以使用字符识别模块完成识别,为了便于字符识别过程中进行图像分割,从而训练字符,首先需要对该图像的检测区域进行预处理。这个项目完整的流程编辑如下图所示。
第一步:首先增加图像源模块,双击图像源模块,选择本地图像。
第二步:使用高精度匹配模块和位置修正模块进行粗定位。增加高精度匹配模块,使用连线连接,双击高精度匹配模块,配置参数,首先设置图像源,通常为图像源模块的输出图像。之后,点击特征模板中的创建,使用掩膜工具,选择合适的特征区域,设定合适的配置,参数创建合适的模板。本案例中,创建的模板如下所示:
建立好模板之后,根据具体项目情况设置合理的运行参数,在这里设置最小匹配分数为0.5,最大匹配个数为1,匹配极性设置为考虑极性,角度范围根据物料可能旋转角度的实际情况设置,这里设置为-20到20尺度范围,尺度范围设置为0.98到1.02,高级参数使用默认参数。
增加位置修正模块,使用连线连接,双击模块,参数选择默认参数,点击一次执行,再点击创建基准。
第三步:为了便于进行字符分割从而训练字符,需要对图像做一些预处理工作。首先添加图像二值化模块,选择图像源作为输入源,并绘制ROI,运行参数选择自动二值化。
二值化结果如下图所示:
第四步:可以看到二值化后的图像仍然存在着干扰,不利于字符的分割与训练,可以使用形态学处理模块来去除干扰。增加形态学处理模块,使用连线连接,双击模块,选择图像源作为输入源,并绘制ROI,设置运行参数,其中开运算与腐蚀用来断开图像边界之间的粘连,闭运算与膨胀用来闭合图像边界之间的间隙。形态学形状指结构元素的形状,运算结果图像轮廓会和形态学形状比较相似。
形态学处理的结果如下图所示:
第五步:可以看见图像的干扰被去除,下面使用字符识别模块进行训练与识别,增加字符识别模块,使用连线连接,双击模块,选择形态学处理的输出图像作为输入源,并绘制ROI。在运行参数界面点击字符训练,
框选待识别的字符区域,设置相关参数,其中,距离阈值指字符片段到文本基线的距离,大于该值则无法被提取;字符间隙指两个字符间的最小横向间距;点击提取字符,字符区域将被分割成一个个单独的被红色框框住的字符区域。
点击训练字符,将被分割字符区域代表的字符填入字符框,点击添加至字符库。这样就完成了字符库的训练。
在运行参数中,根据实际情况,决定是否需要开启字符过滤,这里以开启字符过滤为例,点击字符过滤,启用字符过滤,设置相关参数,其中,识别字符个数代表需要进行字符识别的字符数量,字符类型用来设置需要识别的每个字符的类型。
设置运行参数中的相关参数,其中,有白底黑字和黑底白字两种;字符宽度范围与字符高度范围的参数范围是[1,512],需要根据待识别的字符的宽度与高度进行设置;宽度类型有可变类型和等宽类型两种类型。当字符宽度一致时选择等宽类型,当字符宽度有差异选择可变类型;片段面积代表单个字符所能允许像素面积范围;合格阈值指能够被识别字符的最小得分。
根据实际情况,决定是否需要进行高级参数的设置,其中,距离阈值指字符片段到文本基线的距离,大于该值则会被删除;忽略边框指是否忽略与ROI粘连的字符;主方向范围代表文本行倾斜角度搜索范围;倾斜角范围指允许字符倾斜的最大范围;最大宽高比代表单个字符外接矩形的最大宽高比,取值范围是;字符滤波使能指是否开启字符间字符宽度的滤波使能;笔画宽度范围代表单个笔画的宽度范围,在打开宽度滤波使能后才能生效。
字符识别的结果如下图所示:
第九步:增加发送数据模块,使用连线连接,选择通信方式,这里以TCP通信,并且默认视觉软件是服务端,选择通信设备。需要注意的是,TCP服务端需要在快捷栏中的通信管理中进行配置。
这样,我们就完成了字符识别,并且可以对下位机发送识别字符。
问题根因
不熟悉VM字符识别模块的使用。
2.1.5 联合开发:VM脚本联合OpenCV开发
描述
环境:VM4.0.0 + VS2015及以上
现象:VM脚本联合OpenCV开发
解答
首先需要进行OpenCV环境的配置
第一步:双击打开脚本模块
第二步:点击“编辑程序集 ”按钮
第三步:点击添加按钮,将VM安装路径下三方库中的OpenCV动态库,全部选中并添加到引用中
点击打开后,会报出如下错误
这是因为OpenCV库中的OpenCvSharpExtern.dll引用失败,需要手动拷贝到脚本依赖目录下
第四步:将OpenCvSharpExtern.dll拷贝到VM脚本模块依赖目录下
第五步:在脚本中添加OpenCV的命名空间
上述操作即可完成OpenCV在VM脚本中配置,可以在脚本中直接调用OpenCVSharp中的相关函数API进行图像处理。
其次,需要在脚本中实现ImageData图像类型和OpenCVSharp中Mat类型的转换
第一步:设置输入变量与输出变量,类型均为IMAGE类型
第二步:引用System.Runtime.InteropServices命名空间
第三步:将输入的imagedata类型转换成Mat类型,设置相应的ROI并调用OpenCV库中的接口,进行图像渲染后,将Mat类型转换成imagedata类型,进行输出。
1. C#
2. //实例化ImageData类型图像
3. ImageData img = new ImageData();
4. GetImageValue("in0", ref img);
5. ImageData imgOut = new ImageData();
6. Mat srcImage = Mat.Zeros(img.Heigth, img.Width, MatType.CV_8UC1);
7.
8. Rect rect = new Rect(0, 0, 300, 300);
9.
10. if (img.PixelFormat == ImagePixelFormate.MONO8)
11. {
12. //开辟内存空间
13. IntPtr grayPtr = Marshal.AllocHGlobal(img.Width * img.Heigth);
14. //向内存空间中写入数据
15. Marshal.Copy(img.Buffer, 0, grayPtr, img.Buffer.Length);
16.
17. //imagedata转Mat
18. srcImage = new Mat(img.Heigth, img.Width, MatType.CV_8UC1, grayPtr);
19. //设置ROI
20. Mat imageROI = new Mat(srcImage, rect);
21. Mat dstImage = Mat.Zeros(imageROI.Height, imageROI.Width, MatType.CV_8UC1);
22. //调用OpenCV中的接口进行图像处理
23. Cv2.Threshold(imageROI,dstImage,10,120,ThresholdTypes.Otsu);
24. //将处理后的图像拷贝到原图ROI区域
25. dstImage.CopyTo(imageROI);
26. byte[] datab = new Byte[srcImage.Width * srcImage.Height];
27.
28. //mat转ImageData
29. srcImage.GetArray(0, 0, datab);
30. imgOut.Buffer = datab;
31. imgOut.Width = srcImage.Width;
32. imgOut.Heigth = srcImage.Height;
33. imgOut.PixelFormat = ImagePixelFormate.MONO8;
34.
35. //用完记得释放指针
36. Marshal.FreeHGlobal(grayPtr);
37. }
38. SetImageValue("imageOut", imgOut);
问题根因
不熟悉VM脚本联合OpenCV开发的方法。
提示
鼠标选中脚本模块,按Ctrl+M快捷键,可以直接跳转到脚本模块目录。
2.1.6 联合开发:VM脚本联合OpenCV实现轮廓查找
描述
环境:VM4.0.0 + VS2015及以上
现象:如何利用VM脚本联合OpenCV实现轮廓查找与绘制。
解答
-
采集灰度图,如下图所示
-
脚本中获取海康图像内存对象数据
1. ImageData img = new ImageData();
2. GetImageValue("in0", ref img);
- 将海康图像格式ImageData转换为OpenCV Mat图像格式
1. Mat srcImage = Mat.Zeros(img.Heigth, img.Width, MatType.CV_8UC1);
2. Mat dstImage = Mat.Zeros(img.Heigth, img.Width, MatType.CV_8UC1);
3. if (img.PixelFormat == ImagePixelFormate.MONO8)
4. {
5. //开辟内存空间
6. IntPtr grayPtr = Marshal.AllocHGlobal(img.Width * img.Heigth);
7. //向内存空间中写入数据
8. Marshal.Copy(img.Buffer, 0, grayPtr, img.Buffer.Length);
9. //ImageData 转 Mat
10. srcImage = new Mat(img.Heigth, img.Width, MatType.CV_8UC1, grayPtr);
11. }
- 依次进行Canny边缘检测、轮廓查找
1. //调用OpenCV中函数接口进行图像处理
2. Cv2.Canny(srcImage, dstImage, 90, 230);
3. // 创建一个序列来存放所找到的轮廓
4. Point[][] contours;
5. HierarchyIndex[] hierarchy;
6. Cv2.FindContours(dstImage, out contours, out hierarchy, RetrievalModes.Tree, ContourApproximationModes.ApproxSimple, new Point(0, 0));
- 绘制轮廓
1. Mat dst_Image = Mat.Zeros(dstImage.Size(), srcImage.Type());
2. Random rnd = new Random();
3. for (int i = 0; i < contours.Length; i++)
4. {
5. Scalar color = new Scalar(rnd.Next(0, 255), rnd.Next(0, 255), rnd.Next(0, 255));
6. Cv2.DrawContours(dst_Image, contours, i, color, 2, LineTypes.Link8, hierarchy);
7. }
- 将OpenCV Mat图像格式转换为海康图像格式,输出图像像素格式为MONO8
1. IntPtr intPtr = dst_Image.Data;
2. byte[] data = new Byte[dst_Image.Width * dst_Image.Height];
3. Marshal.Copy(intPtr, data, 0, data.Length);
4. imgOut.Buffer = data;
5. imgOut.Width = dst_Image.Width;
6. imgOut.Heigth = dst_Image.Height;
7. imgOut.PixelFormat = ImagePixelFormate.MONO8;
- 释放内存,防止内存泄漏
Marshal.FreeHGlobal(grayPtr);
- 输出处理后的图像
SetImageValue("imageOut", imgOut);
-
运行脚本模块,输出图像如下图所示
-
如想呈现彩色渲染效果,首先将待绘制的灰度图转换为RGB图像
1. Cv2.CvtColor(dst_Image, dstImageRGB, ColorConversionCodes.GRAY2BGR)
2. Random rnd = new Random();
3. for (int i = 0; i < contours.Length; i++)
4. {
5. Scalar color = new Scalar(rnd.Next(0, 255), rnd.Next(0, 255), rnd.Next(0, 255));
6. Cv2.DrawContours(dstImageRGB, contours, i, color, 2, LineTypes.Link8, hierarchy);
7. }
- 然后在图像格式转换时,申请图像数据数组大小为图像宽高3,输出图像像素格式为RGB24
1. IntPtr intPtr = dstImageRGB.Data;
2. byte[] data = new Byte[dstImageRGB.Width * dstImageRGB.Height * 3];
3. Marshal.Copy(intPtr, data, 0, data.Length);
4. imgOut.Buffer = data;
5. imgOut.Width = dstImageRGB.Width;
6. imgOut.Heigth = dstImageRGB.Height;
7. imgOut.PixelFormat = ImagePixelFormate.RGB24;
-
运行脚本模块,输出图像如下图所示
-
完整脚本代码如下
1. using System;
2. using System.Text;
3. using System.Windows.Forms;
4. using Script.Methods;
5. using OpenCvSharp;
6. using System.Runtime.InteropServices;
7.
8. class UserScript : ScriptMethods, IProcessMethods
9. {
10. //the count of process
11. //执行次数计数
12. int processCount;
13.
14. /// <summary>
15. /// Initialize the field's value when compiling
16. /// 预编译时变量初始化
17. /// </summary>
18. public void Init()
19. {
20. //You can add other global fields here
21. //变量初始化,其余变量可在该函数中添加
22. processCount = 0;
23.
24. }
25.
26. /// <summary>
27. /// Enter the process function when running code once
28. /// 流程执行一次进入Process函数
29. /// </summary>
30. /// <returns></returns>
31. public bool Process()
32. {
33. //You can add your codes here, for realizing your desired function
34. //每次执行将进入该函数,此处添加所需的逻辑流程处理
35. //MessageBox.Show("Process Success");
36. ImageData img = new ImageData();
37. ImageData imgOut = new ImageData();
38. GetImageValue("in0", ref img);
39. Mat srcImage = Mat.Zeros(img.Heigth, img.Width, MatType.CV_8UC1);
40. Mat dstImage = Mat.Zeros(img.Heigth, img.Width, MatType.CV_8UC1);
41. Mat dstImageRGB = Mat.Zeros(img.Heigth, img.Width, MatType.CV_8UC3);
42.
43. //输入图像为灰度图
44. if (img.PixelFormat == ImagePixelFormate.MONO8)
45. {
46. //开辟内存空间
47. IntPtr grayPtr = Marshal.AllocHGlobal(img.Width * img.Heigth);
48. //向内存空间中写入数据
49. Marshal.Copy(img.Buffer, 0, grayPtr, img.Buffer.Length);
50. //ImageData 转 Mat
51. srcImage = new Mat(img.Heigth, img.Width, MatType.CV_8UC1, grayPtr);
52.
53. //调用OpenCV中函数接口进行图像处理
54. Cv2.Canny(srcImage, dstImage, 90, 230);
55. //创建一个序列来存放所找到的轮廓
56. Point[][] contours;
57. HierarchyIndex[] hierarchy;
58. Cv2.FindContours(dstImage, out contours, out hierarchy, RetrievalModes.Tree, ContourApproximationModes.ApproxSimple, new Point(0, 0));
59.
60. Mat dst_Image = Mat.Zeros(dstImage.Size(), srcImage.Type());
61.
62. //如果灰度图要绘制彩框
63. Cv2.CvtColor(dst_Image, dstImageRGB, ColorConversionCodes.GRAY2BGR);
64.
65. //绘制轮廓
66. Random rnd = new Random();
67. for (int i = 0; i < contours.Length; i++)
68. {
69. Scalar color = new Scalar(rnd.Next(0, 255), rnd.Next(0, 255), rnd.Next(0, 255));
70. Cv2.DrawContours(dstImageRGB, contours, i, color, 2, LineTypes.Link8, hierarchy);
71. }
72.
73. //Mat 转 ImageData
74. IntPtr intPtr = dstImageRGB.Data;
75. byte[] data = new Byte[dstImageRGB.Width * dstImageRGB.Height * 3];
76. Marshal.Copy(intPtr, data, 0, data.Length);
77.
78. imgOut.Buffer = data;
79. imgOut.Width = dstImageRGB.Width;
80. imgOut.Heigth = dstImageRGB.Height;
81. imgOut.PixelFormat = ImagePixelFormate.RGB24;
82.
83. //用完需要释放内存
84. Marshal.FreeHGlobal(grayPtr);
85. }
86.
87. SetImageValue("imageOut", imgOut);
88.
89. return true;
90. }
91. }
注:VM脚本中配置OpenCV开发环境方法,参见1.5.5,本文不再累述。
问题根因
不熟悉VM脚本联合OpenCV开发的方法。
2.1.7 定位引导:汽车挡风玻璃多相机对位组装案例
描述
环境:VM4.0.0
现象:如何使用VM实现多相机对位组装?
解答
1功能需求
某汽车外饰零部件供应商需要实现如下图所示的汽车挡风玻璃的对位组装。
具体需求:汽车挡风玻璃放置在预定工装上(定位精度±2mm),汽车车身放置在夹具上(定位精度±2mm),挡风玻璃使用两个下相机进行定位,车身使用两个上相机进行定位。组装时,机械手抓取到挡风玻璃后,移动到固定的下相机上方,两个下相机分别拍摄机械手上挡风玻璃的两处特征(轮廓、定位销等),视觉计算出挡风玻璃与基准位置处的偏差A。机械手带着挡风玻璃移动到车身附近,机械手上的两个上相机分别拍摄车身处的两处特征(轮廓、定位销等),视觉计算出夹具上车身的偏差B。视觉计算出最终装配点的坐标、位置偏差(定位精度±0.2mm)。
2场景分析
该场景可以分解为下相机玻璃处的纠偏与上相机车身处的放置两个场景。总偏差为挡风玻璃的偏差值与车身的偏差值之和。其中,两个下相机分别拍摄玻璃两个位置处的边缘角点,计算玻璃运行位置与基准位置的偏差值。两个上相机分别拍摄车身两个位置处的圆形特征点,计算车身运行位置与基准位置的偏差值。分别将上、下相机的两个特征点的中点与连线的角度作为各自偏差计算的特征点与特征角度。
3标定
由于机械手法兰盘中心与机械手抓取中心不共轴,因此本场景下的标定均为平移旋转(12点)标定。本场景下需要做4次平移旋转(12点)标定:上相机两次+下相机两次。
4通信协议设计
本项目采用TCP通信,通信协议的设计包含标定协议的设计以及生产协议的设计。
标定协议设计主要分为三个部分,开始标定(SC),标定(C),结束标定(EC)。其中,开始标定(SC)指令的作用为清空N点标定模块中的数据,确认通信正常,确认准备开始标定。标定(C)的作用为上位机每发送一次信号,机械手走一次,返回拍照状态。结束标定(EC)指令的作用为确认结束标定。
生产协议设计主要分为两个部分,基准拍照(B),运行拍照(R),其中基准拍照(B)指令的作用为获取基准位置特征点坐标。运行拍照(R)指令的作用为获取运行位置特征点坐标,在上相机车身处会计算总的偏差,并返回给上位机。
5本场景下的对位算法原理说明(基准与运行时拍照位不变)
总偏差=下相机处挡风玻璃偏差+上相机处车身偏差
Offset=UpOffset+DownOffset
下相机处挡风玻璃的偏差为玻璃绕旋转中心旋转偏差角度DR后的坐标偏差量
RotateX=( DownrunWorldX–RotateCenterX)*Cos(DR)–(DownrunWorldY– RotateCenterY)Sin(DR)+ RotateCenterX
RotateY =( DownrunWorldX– RotateCenterX) Sin(DR) + (DownrunWorldY– RotateCenterY)*Cos(DR)+ RotateCenterY
DownOffset=DownmarkWorld-Rotate
上相机处车身偏差=平移偏差+旋转偏差
UpOffset=UpTransOffset+UpRotateOffset
UpTransOffset = UprunWorld – UpmarkWorld
上相机基准坐标=基准图像转物理坐标+第五点拍照位
UpBaseWorld= UpmarkWorld+Snap
旋转偏差量UpRotateOffset为机械手旋转偏差角度DR后带来的坐标偏差量,示教位为TeachWorld:
RotateX=(TeachWorldX-UpBaseWorldX)*Cos(DR)-(TeachWorldY- UpBaseWorldY)*Sin(DR)
RotateY=(TeachWorldX-UpBaseWorldX)*Sin(DR)+(TeachWorldY- UpBaseWorldY)*Cos(DR)
UpRotateOffset= Rotate– TeachWorld
6方案搭建
根据上述分析,我们首先确定本场景下VM需要搭建的流程数目,为了方便项目实施本项目搭建了6个流程,其中前四个流程分别作为挡风玻璃与车身处的四个相机的标定流程,另外两个流程分别作为上相机与下相机处的运行流程,在运行流程中会分别进行偏差的计算。
下面对每个流程创建的目的以及搭建过程进行详细的阐述。
四个相机分别为下相机1,下相机2,上相机1,上相机2。下相机1的标定流程CalibDown1如下所示:
该流程的主要作用为对下相机1进行标定,并在每一步标定时与外界上位机实时返回标定的状态。
发送数据模块73返回N点标定模块的状态,发送数据模块56、57返回标定开始与标定结束的标志位。
其中高精度匹配模块的参数设置可以参考FAQ1.5.2,N点标定模块的参数设置可以参考FAQ1.5.1,其余模块的参数设置可以参考VM的帮助文档和其他FAQ文档。其他三个标定流程CalibDown2,CalibUp1,CalibUp2均与CalibDown1流程相近,这里不再做阐述,其流程搭建如下:
运行流程为DownRun流程和UpRun流程,其中DownRun流程用来计算运行时下相机挡风玻璃与基准位置的偏差,UpRun流程用来计算运行时上相机车身与基准位置的偏差并与DownRun流程计算出的下相机处的偏差相加,得到最终的偏差量,以及定位坐标。
为了与上位机进行通信,因此需要配置全局触发,全局触发的相关内容可以参考FAQ1.3.2,本项目中全局触发配置如下:
问题根因
对定位原理以及VM做定位项目的流程不熟悉。
提示
平移旋转(12点)标定的原理与标定方式选取标准可以参考FAQ的1.5.1节。
2.1.8 流程设计:控制多个模块之间的动态组合的方法
描述
环境:VM4.0.0及以上
现象:当有若干个互不干扰的模块,在不切换方案的情况下,实现任意选取对应模块进行执行,该如何设计流程。(1,2,3,4,5模块的执行方式如何通过合理设计随意变化为1,3,5或1,2,3,4等)
解答
第一步,在vm通讯管理中设置好通讯设备,连接。
第二步,根据通讯设备、接收的信息格式设置接收事件。其中cmd包含了各个模块的执行信息。例如有5个模块,A,B,C,D,E.可以模仿2进制的形式将其组合。例如1_0_0_1_1表示,A,D,E模块执行,B,C模块不执行。
第三步,将协议解析模块置于所有独立功能块前,将cmd的拆分为对应的信息。各个分支字符的条件就根据各个模块对应的字符进行触发。例如1执行,0不执行。此方案是控制直线查找模块、圆查找模块、BLOB分析模块的运行与否。
成果展示:第一张图表示执行了直线查找、圆查找、BLOB分析;第二张图表示执行了直线查找、BLOB分析;第三张图表示执行了直线查找、圆查找;第四张图表示执行了圆查找、BLOB分析。
文章来源:https://www.toymoban.com/news/detail-698707.html
同理,根据此设置一种方案,n个独立模块,可根据此方法,通过通讯实现2的n次方种形式。
问题根因
不熟悉模块之间的业务关系。文章来源地址https://www.toymoban.com/news/detail-698707.html
到了这里,关于【VM服务管家】专题_7.2 VM应用案例的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!