此前,我们依次讲解了软硬件介绍及计数实例、相机的基本使用、基于形状匹配的视觉定位、BLOB有无检测、测量尺寸、机器视觉方案中使用到的标定功能以及ZDevelop软件实现识别条形码和二维码的功能。
本期课程我们和大家一起分享机器视觉的基本测量功能——测量点/直线/圆。
[《视频教程:VPLC系列机器视觉运动控制一体机快速入门(八)》](https://mp.weixin.qq.com/s?__biz=MzIzNjk2NTQ3OQ==&mid=2247487312&idx=1&sn=6c831268989289efd128d7a0e4b8747b&chksm=e8ce9458dfb91d4e57dc536cdfdbb6787cf11445b7aaf58258786172c950aa2e5effc5413f1a&mpshare=1&scene=1&srcid=0421jI24h0BdX4YejpHcXlR4&sharer_sharetime=1618967328391&sharer_shareid=9209a020d93d57821a4e8085600e8180&version=3.1.7.3005&platform=win#rd)
01 检测原理
机器视觉中使用到的测量点/线/圆的功能都是基于边缘检测的视觉算法。
边缘检测即是在一定的搜索区域内,按照指定的方向逐像素搜索满足极性变化和对比度要求的过渡边缘点。
02 测量点/线/圆特点
1.实现简单
测量点/线/圆的算法是基于边缘检测的算法,易于实现。
2.需要位置跟随
测量器本身不具有定位功能,如果检测产品位置不固定将无法确定测量器对应的位置,此时需要依赖匹配等定位功能做位置跟随。
01 量点应用场景
1.精准定位
检测特征边缘点的位置并输出对应坐标可实现精准定位,常配合匹配(粗定位)使用。
2.拟合直线
可通过检测两个特征边缘点拟合出一条直线,并输出直线数据做进一步检测。
02 测量线应用场景
1.精准定位
通常使用检测两条直线的交点位置坐标作为定位位置,输出的位置精度比匹配输出的位置精度高,常配合匹配(粗定位)使用。
2.检测直线度
可通过一个直线测量器检测多个边缘点拟合直线后根据不在直线上的异常边缘点数来判断直线度。
3.测量尺寸
可通过检测两条直线并计算两条直线的距离求出产品的边缘尺寸距离。
03 测量圆应用场景
1.测量圆
测量圆形特征,得出圆形的圆心、半径等数据。
2.定位
通过测量圆的特征输出圆心位置坐标数据做精准定位,常配合匹配(粗定位)使用。
3.九点标定
可通过检测实心圆矩阵标定板上的九个圆心的图像坐标数据,和标定板对应的世界坐标数据计算出标定系数。
测量点/线/圆流程图
01 实例演示
1.打开ZDevelop软件:新建项目→新建HMI文件→新建main.bas文件,用于编写界面响应函数→新建global_variable.bas文件用于存放全局变量→新建InitParam.bas文件用于初始化测量参数→新建camera.bas文件用于实现相机采集功能→新建draw.bas文件用于更新绘制图形刷新界面→文件添加到项目。
2.设计运行界面,运行界面只进行采集图像操作和检测执行操作,并显示数据结果。
3.在global_variable.bas文件添加使用到的全局变量。
''''全局变量大部分使用数组结构''''' ''注:basic编程中很多函数会以TABLE(系统的数据结构)做为参数 ''在这里table均是做为中间变量 ''table 0-20 作为匹配时使用到的中间变量 ''table 50-70 作为roi绘制时的中间变量使用 ''table 21-22,表示鼠标按键控件坐标系 ''table 31-35,表示控件坐标转换后对应的图像坐标 ''table 111-114,表示定位器区域roi参数,属于控件坐标系 ''table 121-124,表示橡皮擦区域roi参数,属于控件坐标系 ''table 81-98,表示标定使用到的九个点的图像坐标 ''table 131-148,表示标定使用到的九个点的世界坐标 '***********定义程序任务相关变量********************** '主任务状态 '0 - 未初始化 '1 - 停止 '2 - 运行中 '3 - 正在停止 GLOBAL DIM main_task_state main_task_state = 1 '是否标定 global is_ca_success '运行任务开关 GLOBAL DIM run_switch run_switch = 0 '采集任务开关 '0 - 停止采集 '1 - 请求采集 GLOBAL DIM grab_switch grab_switch = 0 '定位检测主任务id - 10 GLOBAL DIM main_task_id main_task_id = 10 '相机连续采集线程id - 7 GLOBAL DIM grab_task_id grab_task_id = 7 '***********结束定义程序任务相关变量****************** '***********定义相机采集相关变量********************** '相机种类,"",此处使用海康相机-"mvision" GLOBAL DIM CAMERA_TYPE(100) 'CAMERA_TYPE = "mindvision;basler;mvision;huaray;basler;zmotion" CAMERA_TYPE = "mvision" '相机个数 GLOBAL cam_num cam_num = 0 '相机模式,-1 连续采集,0-触发采集 GLOBAL cam_mode cam_mode = 0 '***********结束定义相机采集相关变量****************** '定义返回主界面标志,1-已返回,0-未返回 GLOBAL DIM d_is_rtn_loc d_is_rtn_loc = 1 '***********定义模板相关变量************************* '定义创建模板标志位,1-已创建模板,0-未创建模板 GLOBAL DIM d_is_creModel d_is_creModel = 0 '学习模板参数,starAngle、endAngle、minScale、maxScale、thresh、numlevel、reduce、angleStep、scaleStep GLOBAL DIM d_mod_param(9) '***********结束定义模板相关变量********************** '***********定义编辑模板相关变量********************* '定义编辑模板标志,0-表示不编辑模板,1-表示编辑模板 GLOBAL DIM d_edit_m d_edit_m = 0 '定义使用橡皮擦功能标志,0-表示恢复擦除的区域,1-表示擦除区域 GLOBAL DIM d_isMask_m d_isMask_m = 1 '定义橡皮擦的roi参数,依次是矩形左上角和右下角图像坐标x、y、x、y GLOBAL DIM d_locator_roi(4),d_eraser_roi(4) '定义正方形橡皮擦尺寸宽度 GLOBAL DIM d_eraser_size d_eraser_size = 5 '定义界面控件上橡皮擦的矩形区域 GLOBAL DIM c_rect(4) '定义鼠标状态标志,0-表示鼠标处于松开状态,1-表示鼠标处于按下状态 GLOBAL DIM d_mouse_s d_mouse_s = 0 '***********结束定义编辑模板相关变量****************** '***********定义匹配检测相关变量********************* '匹配检测参数,minScore、matchNum、minDist、thresh、accuracy、speed、polor GLOBAL DIM d_match_param(7) '定义学习模板的roi参数和橡皮擦的roi参数,依次是矩形左上角和右下角图像坐标x、y、x、y GLOBAL DIM d_locator_roi(4),d_eraser_roi(4) '匹配结果,score、x、y、angle、scale, 目前对于多目标匹配也只存第一个目标 GLOBAL DIM d_match_rst(5) '***********结束定义匹配检测相关变量****************** '定义程序执行过程中缓存中间图片和结果图片的变量 GLOBAL ZVOBJECT grabImg GLOBAL ZVOBJECT subImg,copy_subImg,colorSubImg, s_mod GLOBAL ZVOBJECT modRe '***********定义坐标标定相关变量********************* '定义是否使用标定功能标志,0-不使用标定功能,1-使用标定功能 GLOBAL DIM d_use_calib d_use_calib = 0 '定义标定成功标志,0-标定未成功,1-标定成功 GLOBAL DIM d_calib_success d_calib_success = 0 '标定参数 GLOBAL ZVOBJECT ca_param '标定参数数组,依次为:标定类型、对比度、极性、最小面积、最大面积、世界坐标点间距 GLOBAL DIM d_ca_param(6) 'd开头表示数据结构 '标定误差,最小误差、最大误差、平均误差 GLOBAL DIM ca_min_err,ca_max_err,ca_avg_err ca_min_err = 0 ca_max_err = 0 ca_avg_err = 0 '常用颜色变量 GLOBAL C_RED, C_GREEN, C_BLUE, C_YELLOW C_RED = RGB(255, 0, 0) C_GREEN = RGB( 0,255, 0) C_BLUE = RGB( 0, 0,255) C_YELLOW= RGB(255,255, 0) '标定矩阵 GLOBAL ZVOBJECT ca_mat '***********结束定义坐标标定相关变量****************** '***********定义测量点/线/圆相关变量****************** '定义检测消耗时间 GLOBAL DIM d_detect_time '定义是否使用补正源,0-不使用补正源,1-使用补正源 GLOBAL DIM d_use_locator ' '是否绘制测量器 GLOBAL DIM line_status,circle_status,point_status line_status=0 circle_status=0 point_status=0 '测量点/线/圆参数设置,包括边缘极性/边缘位置/对比度/滤波尺寸/扫描数量/扫描宽度 GLOBAL DIM d_point_param(4),d_line_param(6), d_circle_parm(6) '绘制结果参数 GLOBAL DIM draw_point(2),draw_line(4),draw_circle(3) '***********结束定义测量点/线/圆相关变量************** '定义点测量器区域/线测量器区域/圆测量器区域 GLOBAL DIM d_roi_rect1(5),d_roi_rect2(5),d_roi_arc(6) '设置创建模板的基准坐标 GLOBAL DIM d_match_base_rst(5) GLOBAL ZVOBJECT mat_rigid,latch ' '**********************点线圆测量数据结果***************** GLOBAL DIM point_result(5),line_result(5),circle_result(5) '***********定义读取本地文件功能相关变量************** ''注意,该功能只在使用仿真器时有效 '定义是否使用本地图片标志 GLOBAL DIM d_use_imgfile '定义本地图片索引 GLOBAL DIM d_index '定义读取图片的路径 GLOBAL DIM File_Name(100) '***********结束定义读取本地文件功能相关变量********** RUN "Hmi1.hmi",1
4.在InitParam.bas文件中初始化测量变量。
end GLOBAL SUB init_meas_param() '初始化参数 '初始化定位器roi参数 d_locator_roi(0) = 240 '左上角x d_locator_roi(1) = 180 '左上角y d_locator_roi(2) = 400 '右下角x d_locator_roi(3) = 300 '右下角y '初始化模板参数 d_mod_param(0) = -180 '起始角度 d_mod_param(1) = 180 '终止角度 d_mod_param(2) = 1 '最小缩放 d_mod_param(3) = 1 '最大缩放 d_mod_param(4) = 80 '阈值 d_mod_param(5) = 0 '默认金字塔层数 d_mod_param(6) = 0 '默认约简特征点 d_mod_param(7) = 0 '默认角度步长 d_mod_param(8) = 0 '默认缩放步长 '初始化匹配测量参数 d_match_param(0) = 50 '最小分数 d_match_param(1) = 1 '匹配个数 d_match_param(2) = 0 '默认最小间距 d_match_param(3) = 40 '最小阈值 d_match_param(4) = 0 '精度 d_match_param(5) = 9 '速度 d_match_param(6) = 0 '极性 '初始化匹配定位结果 d_match_rst(0) = 0 '分数 d_match_rst(1) = 0 '位置X d_match_rst(2) = 0 '位置Y d_match_rst(3) = 0 '角度 d_match_rst(4) = 0 '比例 '点的测量参数初始化 d_point_param(0)=0 d_point_param(1)=0 d_point_param(2)=30 d_point_param(3)=5 '线的测量参数初始化 d_line_param(0)=0 d_line_param(1)=0 d_line_param(2)=30 d_line_param(3)=5 d_line_param(4)=10 d_line_param(5)=8 '圆的测量参数初始化 d_circle_parm(0)=0 d_circle_parm(1)=0 d_circle_parm(2)=20 d_circle_parm(3)=5 d_circle_parm(4)=10 d_circle_parm(5)=8 '初始化坐标标定相关的变量 d_ca_param(0) = 0 '标定类型 d_ca_param(1) = 120 '对比度 d_ca_param(2) = 0 '极性 d_ca_param(3) = 80 '最小面积 d_ca_param(4) = 20000 '最大面积 d_ca_param(5) = 9 '世界坐标点间距 ca_min_err = 0 '最小误差 ca_max_err = 0 '最大误差 ca_avg_err = 0 '平均误差 d_detect_time = 0 d_use_locator = 0 '默认不使用补正源 d_use_imgfile = 1 d_index = 0 TABLE(31,100,100,60,40,60,8,5) '点测量器初始化 TABLE(231,100,100,60,40,60,8,5) '线测量器初始化 TABLE(551,100,100,60,50,0,360,8,5) '圆测量器初始化 END SUB
5.在InitParam.bas文件中初始化测量变量
6.在main.bas文件中添加运行界面初始化函数,并关联HMI界面初始化函数名。
'运行界面初始化函数,上电执行一次 GLOBAL SUB hmi_init() grab_switch = 0 '初始化采集任务开关,不开启采集任务 main_task_state = 1 '初始化定位检测主任务状态为停止状态1 ZV_SETSYSINT("LineWidth",5) '设置绘制画笔宽度为5个像素 ZV_RESETCLIPSIZE(1280, 1024) '初始化时依据图像分辨率设置区域的裁剪尺寸,此处图像分辨率为1280x1024 ZV_LATCHSETSIZE(0, HMI_CONTROLSIZEX(10, 1), HMI_CONTROLSIZEY(10, 1)) '设置锁存的大小 init_meas_param() '初始化测量参数 ZV_IMGGENCONST(subImg,40,30,1,0,0) '初始化模板子图像 '定义匹配中间结果变量 ZVOBJECT contlist1, tsContlist1, mat_rigid1 ZVOBJECT contlist2, tsContlist2, mat_rigid2 ZV_READIMAGE(grabImg,"\8\calib.bmp",1) '读取...\flash\8目录下的calib.bmp的灰度图像,用于加载本地图片仿真时进行坐标标定 ZV_LATCHCLEAR(0) '清空锁存通道0 ZV_LATCH(grabImg,0) '显示到锁存通道0中 END SUB
7.在camera.bas文件中添加采集相关按钮的功能函数,并关联动作函数名。
end '主界面按下扫描相机按钮时响应的函数 GLOBAL SUB cam_scan_all() if(d_use_imgfile=1)then ?"请先按下使用本地图片按钮关闭该功能" return endif ZV_SETSYSINT("LogLevel", 7) '设置控制器信息 ZV_SETSYSSTR("DataDir","") CAM_SCAN(CAMERA_TYPE) '扫描相机,CAMERA_TYPE="mvision" cam_num = CAM_COUNT() '获取扫描到的相机数量 if (0 = cam_num) then '如果相机数量=0,打印提示信息 ? "未找到相机" return '退出子函数,不往下执行 endif ?"cam_num = " cam_num '如果扫描到相机,打印相机数量 cam_mode = 0 '设置软触发采集 CAM_SEL(0) '选择扫描到的第一个相机进行操作 CAM_SETEXPOSURE(5000) '设置相机曝光时间为5000us CAM_SETMODE(cam_mode) '设置软件触发模式 CAM_START(0) '开启相机 END SUB '主界面按下单次采集按钮执行的函数 GLOBAL SUB btn_grab() ''如果d_use_imgfile=1时使用读取本地图片功能,该功能只在使用仿真器时有效,使用控制器时请将此部分代码注释掉 if (d_use_imgfile=1) then if(d_index=3) then d_index=0 endif File_Name="\8\"+TOSTR(d_index,1,0)+".bmp" '.../flash/3/目录下的图片所在的路径名称 ZV_IMGREAD(grabImg,File_Name,0) ZV_LATCH(grabImg, 0) d_index=d_index+1 return endif ''读取本地图片功能结束 if cam_num = 0 then ?"请先扫描相机!" return endif CAM_SETPARAM("TriggerSoftware", 0) '发送触发指令 CAM_GET(grabImg, 0) '获取一帧图像存放到grabImg变量中 ZV_LATCH(grabImg, 0) '将图像显示到锁存通道0中 END SUB '主界面按下连续采集按钮响应的函数 GLOBAL SUB btn_cgrab() if grab_switch =1 then '如果已经处于连续执行状态,打印提示信息并退出函数 ?"正在连续运行中,请勿重复操作!" return endif if( d_use_imgfile =0) then if cam_num = 0 then '如果相机数量=0,打印提示信息并退出函数 ?"请先扫描相机!" return endif endif grab_switch = 1 '采集任务开关置1 if (1 = grab_switch) then if (0 = PROC_STATUS(grab_task_id)) then RUNTASK grab_task_id, grab_task '开启连续采集任务 endif endif END SUB '采集任务实现函数 grab_task: while(1) if (0 = grab_switch) then '如果采集任务开关=0即停止采集按钮按下时 exit while '退出循环 endif '重复执行以下操作 btn_grab() wend END '主界面按下停止采集按钮响应的函数 GLOBAL SUB btn_stopCgrab() if grab_switch =0 then '如果已经处于停止采集状态,打印提示信息并退出函数 ?"未开启连续采集!" return endif grab_switch = 0 '将采集任务开关置0 END SUB
8.新建运行界面按下【参数设置】按钮时弹出的参数设置窗口,并设计界面布局。
9.添加在运行界面按下【参数设置】按钮时响应的函数,并关联动作函数名。
运行界面按下参数设置按钮时响应的函数 GLOBAL SUB btn_set_param() ZV_LATCHSETSIZE(0, HMI_CONTROLSIZEX(11, 1), HMI_CONTROLSIZEY(11, 1)) '设置锁存的大小 ZV_LATCHCLEAR(0) ZV_LATCH(grabImg,0) '显示到锁存通道0中,作为显示区域背景图片 HMI_SHOWWINDOW(11) END SUB
10.参考第三篇“基于形状匹配的视觉定位”,添加形状匹配功能,添加流程如下:
end '和绘制(即选择ROI)有关的界面刷新绘制函数放在这个bas文件里 DIM is_redraw '绘图标志,0表示未进行绘制,1表示正在进行绘制 is_redraw = 0 DIM sr_mpos_x, sr_mpos_y, hit_pos '主界面按下学习模板按钮时响应的函数 GLOBAL SUB btn_sel_loc() ZV_LATCHSETSIZE(0, HMI_CONTROLSIZEX(12, 60), HMI_CONTROLSIZEY(12, 60)) '设置创建模板窗口锁存通道0的锁存大小 ZV_LATCHSETSIZE(1, HMI_CONTROLSIZEX(12, 38), HMI_CONTROLSIZEY(12, 38)) '设置创建模板窗口锁存通道1的锁存大小 SET_COLOR(RGB(0,255,0)) '指定draw指令使用的颜色 ZV_LATCHCLEAR(0) '将锁存通道0清空 ZV_LATCH(grabImg, 0) '显示采集图像显示到锁存通道0中 ZV_LATCH(colorSubImg, 1) '显示模板图像显示到锁存通道1中 '图像roi坐标转控件roi is_redraw = 0 d_is_rtn_loc = 0 TABLE(111, d_locator_roi(0), d_locator_roi(1),d_locator_roi(2),d_locator_roi(3)) ZV_POSFROMIMG(0, 2, 111, 111) '图像坐标转换到HMI控件坐标 HMI_SHOWWINDOW(12) END SUB '根据鼠标操作更新定位器的区域即学习模板的有效区域 GLOBAL SUB update_locator() if mouse_scan(21) = 1 then '扫描鼠标按下操作 is_set_roi_m_down = 1 sr_mpos_x = table(21) sr_mpos_y = table(22) hit_pos = ZV_HMIADJRECT(sr_mpos_x, sr_mpos_y, 111, -1) '只有按下时可以改变击中位置 is_redraw = 1 endif if mouse_scan(21) = -1 then '扫描鼠标松开操作 is_set_roi_m_down = 0 sr_mpos_x = table(21) sr_mpos_y = table(22) ZV_HMIADJRECT(sr_mpos_x, sr_mpos_y, 111, hit_pos) is_redraw = 1 endif if (is_set_roi_m_down and MOUSE_state(21)) then sr_mpos_x = table(21) sr_mpos_y = table(22) ZV_HMIADJRECT(sr_mpos_x, sr_mpos_y, 111, hit_pos) is_redraw = 1 endif if (1 = is_redraw) then '控件roi坐标转图像roi坐标 is_redraw = 0 ZV_POSTOIMG(0, 2, 111, 50) 'TABLE(50)作为中间变量临时使用 d_locator_roi(0) = TABLE(50) d_locator_roi(1) = TABLE(51) d_locator_roi(2) = TABLE(52) d_locator_roi(3) = TABLE(53) SET_REDRAW endif END SUB '根据更新的鼠标位置坐标绘制定位器roi GLOBAL SUB draw_locator() DRAWRECT(TABLE(111), TABLE(112), TABLE(113), TABLE(114)) local cx,cy cx = (TABLE(111) + TABLE(113)) / 2 cy = (TABLE(112) + TABLE(114)) / 2 DRAWLINE(cx-5, cy, cx+5, cy) '中心十字线 DRAWLINE(cx, cy-5, cx, cy+5) END SUB '创建模板界面按下截取模板按钮后响应的函数 GLOBAL SUB btn_getSubImg() LOCAL mod_w,mod_h ZV_IMGGETSUB(grabImg, subImg, d_locator_roi(0), d_locator_roi(1), d_locator_roi(2)-d_locator_roi(0)+1, d_locator_roi(3)-d_locator_roi(1)+1) ZV_IMGINFO(subImg,0) mod_w = TABLE(0) mod_h = TABLE(1) ZV_REGENRECT(modRe,0,0,mod_w, mod_h) ZV_LATCHCLEAR(1) ZV_LATCH(subImg, 1) END SUB '创建模板界面按下橡皮擦按钮时响应的函数 GLOBAL SUB btn_sel_erase() ZV_LATCHSETSIZE(1, HMI_CONTROLSIZEX(13, 1), HMI_CONTROLSIZEY(13, 1)) '设置锁存的大小 ZV_LATCHSETSIZE(2, HMI_CONTROLSIZEX(13, 15), HMI_CONTROLSIZEY(13, 15)) '设置锁存的大小 SET_COLOR(RGB(0,255,0)) '设置绘制时画笔使用的颜色 ZV_LATCHCLEAR(1) '清空锁存 ZV_IMGCOPY(subImg, copy_subImg) '复制模板子图像到copy_subImg图像变量中 ZV_REGION(copy_subImg, modRe, 1, 0) '在模板图像上绘制modRe图像的非有效区域,绘制颜色为黑色,用于掩模 ZV_LATCH(copy_subImg, 1) '显示复制的模板图 HMI_SHOWWINDOW(13) '打开编辑模板窗口 end sub '根据鼠标操作更新橡皮擦擦除/恢复区域的位置 GLOBAL SUB update_eraser() DIM c_size_eraser '橡皮擦在控件上对应的尺寸 DIM eraser_pos_x,eraser_pos_y d_mouse_s = MOUSE_STATE(21) '鼠标处于按下状态时 eraser_pos_x = TABLE(21) eraser_pos_y = TABLE(22) c_size_eraser = ZV_LENFROMIMG(0, d_eraser_size) '将橡皮擦的图像尺寸转换成控件尺寸 c_rect(0, eraser_pos_x - c_size_eraser, eraser_pos_y - c_size_eraser, eraser_pos_x + c_size_eraser, eraser_pos_y + c_size_eraser) '绘制以(eraser_pos_x,eraser_pos_y)为中心,2*c_size_eraser为边长的正方形橡皮擦区域 DIM hmi_w,hmi_h if (eraser_pos_x >= c_size_eraser) and (eraser_pos_y >= c_size_eraser) and (eraser_pos_x <= HMI_CONTROLSIZEX(12, 1) - c_size_eraser)and (eraser_pos_y <= HMI_CONTROLSIZEy(12, 1) - c_size_eraser) THEN SET_REDRAW(0,0, HMI_CONTROLSIZEX(12, 1), HMI_CONTROLSIZEY(12, 1))'重新绘制编辑模板窗口上的锁存通道0区域 endif if d_mouse_s = 1 and d_edit_m = 1 then '如果鼠标处于按下状态且编辑模板标志=1时 btn_pro_eraser() '执行处理橡皮擦函数 endif END SUB '处理橡皮擦函数 global sub btn_pro_eraser() ZVOBJECT tmp_re TABLE(121, c_rect(0), c_rect(1)) ZV_POSTOIMG(1, 1, 121, 121) ZV_REGENRECT(tmp_re, TABLE(121), TABLE(122), 2 * d_eraser_size + 1, 2 * d_eraser_size + 1) if (d_isMask_m = 1) then '屏蔽 ZV_REDIFF(modRe, tmp_re, modRe) '计算modRe和tmp_re的差集并存放到modRe中 else '恢复 ZV_REUNION(modRe, tmp_re, modRe) '计算modRe和tmp_re的并集并存放到modRe中 endif ZV_IMGCOPY(subImg, copy_subImg) '复制模板子图像到copy_subImg图像变量中 ZV_REGION(copy_subImg, modRe, 1, 0) '在模板图像上绘制modRe图像的非有效区域,绘制颜色为黑色,用于掩模 ZV_LATCH(copy_subImg, 1) '显示复制的模板图 end sub '更新绘制橡皮擦区域 GLOBAL SUB draw_eraser() if d_edit_m = 0 then '如果编辑模板标志 return '返回子函数,不继续往下执行 endif DRAWRECT(c_rect(0), c_rect(1), c_rect(2), c_rect(3))'绘制橡皮擦区域 END SUB '编辑模板界面按下创建模板按钮时响应的函数 GLOBAL SUB btn_loc_creModel() d_is_creModel = 1 '创建模板 ZV_SHAPECREATERE(subImg, modRe,s_mod, d_mod_param(0), d_mod_param(1), d_mod_param(2), d_mod_param(3), d_mod_param(4), d_mod_param(5), d_mod_param(6), d_mod_param(7), d_mod_param(8)) ZV_SHAPECONTOURS(s_mod, contlist1, 0) '获取第0层金字塔上的模板轮廓 ZV_GRAYTORGB(subImg, colorSubImg) '灰度图转换成RGB图 ZV_IMGINFO(colorSubImg, 0) '获取colorSubImg图像信息,并存放到table0中 ZV_GETRIGIDVECTOR(mat_rigid1, 0, 0, 0, TABLE(0)/2, TABLE(1)/2, 0)'计算刚性变换矩阵 ZV_CONTAFFINE(contlist1, mat_rigid1, tsContlist1) '对轮廓或轮廓序列进行仿射变换 ZV_CONTLIST(colorSubImg, tsContlist1, ZV_COLOR(0, 255, 0), 0) '在colorSubImg图像上绘制绿色的轮廓序列 ZV_LATCHCLEAR(2) ZV_LATCH(colorSubImg, 2) btn_loc_test() d_match_base_rst(0) = d_match_rst(0) d_match_base_rst(1) = d_match_rst(1) d_match_base_rst(2) = d_match_rst(2) d_match_base_rst(3) = d_match_rst(3) d_match_base_rst(4) = d_match_rst(4) END SUB '编辑模板界面按下确定按钮时执行的函数 GLOBAL SUB btn_erase_cfm() ZV_LATCHCLEAR(0) ZV_LATCH(grabImg, 0) '显示图像在锁存上 HMI_CLOSEWINDOW(13) '关闭编辑模板窗口 END SUB '创建模板界面按下测试按钮时响应的函数 GLOBAL SUB btn_loc_test() if (d_is_creModel = 0) then ?"未创建模板!" return endif '开始匹配 ZVOBJECT match_rst, sImg, colorImg ZV_GAUSSBLUR(grabImg, sImg, 3) ZV_SHAPEFIND(s_mod, sImg, match_rst, d_match_param(0), d_match_param(1), d_match_param(2), d_match_param(3), d_match_param(4), d_match_param(5), d_match_param(6)) ZV_MATINFO(match_rst, 0) ZV_GRAYTORGB(sImg, colorImg) if TABLE(0) > 0 then local rowr for rowr = 0 to TABLE(0)-1 ZV_MATGETROW(match_rst, rowr, 5, 3) '获取match_rst矩阵中第rowr行的数据到table中,table最大长度5 if(rowr = 0) then if(is_ca_success = 1 AND d_use_calib=1) then ZV_CALTRANSW(ca_param, TABLE(4),TABLE(5),8) '像素坐标转世界坐标 d_match_rst(0) = TABLE(3) d_match_rst(1) = TABLE(8) d_match_rst(2) = TABLE(9) d_match_rst(3) = TABLE(6) d_match_rst(4) = TABLE(7) ZV_GETRIGIDVECTOR(mat_rigid1, 0, 0, 0, TABLE(4), TABLE(5), TABLE(6))'计算刚性变换矩阵 ZV_CONTAFFINE(contlist1, mat_rigid1, tsContlist1)'对轮廓或轮廓序列进行仿射变换 ZV_CONTLIST(colorImg, tsContlist1, ZV_COLOR(0, 255, 0), 0)'在colorSubImg图像上绘制绿色的轮廓序列 else d_match_rst(0) = TABLE(3) d_match_rst(1) = TABLE(4) d_match_rst(2) = TABLE(5) d_match_rst(3) = TABLE(6) d_match_rst(4) = TABLE(7) ZV_GETRIGIDVECTOR(mat_rigid1, 0, 0, 0, TABLE(4), TABLE(5), TABLE(6))'计算刚性变换矩阵 ZV_CONTAFFINE(contlist1, mat_rigid1, tsContlist1)'对轮廓或轮廓序列进行仿射变换 ZV_CONTLIST(colorImg, tsContlist1, ZV_COLOR(0, 255, 0), 0)'在colorSubImg图像上绘制绿色的轮廓序列 endif endif next else d_match_rst(0) = -1 d_match_rst(1) = -1 d_match_rst(2) = -1 d_match_rst(3) = -1 d_match_rst(4) = -1 endif ZV_LATCH(colorImg, 0) END SUB '创建模板界面按下确定按钮时响应的函数 GLOBAL SUB btn_loc_cfm() grab_switch = 0 '关闭补正源的连续采集 d_is_rtn_loc = 1 ZV_LATCHSETSIZE(0, HMI_CONTROLSIZEX(11, 1), HMI_CONTROLSIZEY(11, 1)) '设置锁存的大小 ZV_LATCHCLEAR(0) ZV_LATCH(grabImg, 0) '显示图像在锁存上 ZV_LATCH(grabImg, 1) '显示图像在锁存上 HMI_CLOSEWINDOW(12) END SUB
11.参考第六篇“标定”,添加坐标标定功能,添加流程如下:
'参数设置界面按下坐标标定按钮时响应的函数 GLOBAL SUB btn_calib() ZV_LATCHSETSIZE(0, HMI_CONTROLSIZEX(14, 91), HMI_CONTROLSIZEY(14, 91)) '设置坐标标定窗口锁存通道0的锁存大小 ZV_LATCHCLEAR(0) '将锁存通道0清空 ZV_LATCH(grabImg, 0) '显示采集图像显示到锁存通道0中 HMI_SHOWWINDOW(14) END SUB '坐标标定界面按下提取mark点按钮时响应的函数 GLOBAL SUB btn_ca_extract() ZVOBJECT inppts, ppts, wpts '提取像素坐标 ZV_CALGETSCAPTS(grabImg, inppts, d_ca_param(1), d_ca_param(2), d_ca_param(3), d_ca_param(4)) ZV_MATINFO (inppts, 400) DIM row,col row = TABLE(400) col = TABLE(401) if(row * col = 18) then TABLE(150) = 1 '提取mark点成功 else TABLE(150) = 0 '提取mark点失败 return endif '根据mrak点间距和像素坐标计算世界坐标 ZV_CALGETPTSMAP(inppts,ppts,wpts,d_ca_param(5)) ZV_MATINFO (ppts, 400) row = TABLE(400) col = TABLE(401) if(row * col = 18) then TABLE(150) = 1 '提取mark点成功 else TABLE(150) = 0 '提取mark点失败 return endif '像素坐标和世界坐标放入table中 DIM i FOR i=0 TO row-1 ZV_MATGETROW (ppts, i, col, 81 + i*col) ZV_MATGETROW (wpts, i, col, 131 + i*col) NEXT '设置用于绘制mark点的图像 ZVOBJECT color ZV_GRAYTORGB(grabImg, color) '和绘制mark点的十字架 DIM j, pixNum '像素个数 pixNum = 0 FOR i=0 TO 2 FOR j=0 TO 2 ZV_MARKER(color, TABLE(81 + 2 * pixNum), TABLE(81 + 2 * pixNum + 1), 0, 40, C_GREEN) pixNum = pixNum + 1 NEXT NEXT '用文本绘制mark点的序号 FOR i=0 TO 8 ZV_TEXT (color, TOSTR(i,1,0), TABLE(81+2*i)-20, TABLE(81+2*i +1)-40, 20, C_BLUE) NEXT ZV_LATCH(color, 0) END SUB '坐标标定界面按下标定按钮时响应的函数 GLOBAL SUB btn_ca_calib() ZV_IMGINFO(grabImg,0) ZV_CALCAM(ppts,wpts,ca_param,TABLE(0),TABLE(1),d_ca_param(0)) is_ca_success=1 '计算标定误差 ZV_CALERROR(ca_param, ppts, wpts, 0) ca_min_err = TABLE(1) ca_max_err = TABLE(2) ca_avg_err = TABLE(0) END SUB '坐标标定界面按下返回按钮时响应的函数 GLOBAL SUB btn_ca_param_rtn() HMI_CLOSEWINDOW(14) END SUB
12.在参数设置界面的【采集图像】按钮关联动作函数名btn_grab。
13.在draw.bas文件中添加点/线/圆测量器区域的更新绘制函数,并在自定义元件属性窗口中关联刷新函数和绘制函数。
'点,线,圆测量器的刷新函数 GLOBAL SUB update_roi () if point_status = 1 then SET_REDRAW '绘制第一次点击的 if (mouse_scan(21) = 1 ) then '扫描按下操作 hit_pos = ZV_HMIADJRECT2(table(21), table(22), 31, -1) '只有按下时可以改变击中位置 is_redraw = 1 endif if (mouse_scan(21) = -1 ) then '扫描松开操作 ZV_HMIADJRECT2(table(21), table(22), 31, hit_pos) is_redraw = 1 endif if (MOUSE_state(21)) then ZV_HMIADJRECT2(table(21), table(22), 31, hit_pos) is_redraw = 1 endif if (1 = is_redraw) then is_redraw = 0 ZV_POSTOIMG(0, 1, 31, 41) d_roi_rect1(0) = TABLE(41) d_roi_rect1(1) = TABLE(42) d_roi_rect1(2) = ZV_LENTOIMG(0, TABLE(33)) d_roi_rect1(3) = ZV_LENTOIMG(0, TABLE(34)) d_roi_rect1(4) = TABLE(35) SET_REDRAW endif elseif line_status=1 then SET_REDRAW '绘制第一次点击的 if (mouse_scan(21) = 1)then '扫描按下操作 hit_pos = ZV_HMIADJRECT2(table(21), table(22),231, -1) '只有按下时可以改变击中位置 is_redraw = 1 endif if (mouse_scan(21) = -1) then '扫描松开操作 ZV_HMIADJRECT2(table(21), table(22), 231, hit_pos) is_redraw = 1 endif if (MOUSE_state(21)) then ZV_HMIADJRECT2(table(21), table(22), 231, hit_pos) is_redraw = 1 endif if (1 = is_redraw) then is_redraw = 0 ZV_POSTOIMG(0, 1, 231, 41) d_roi_rect2(0) = TABLE(41) d_roi_rect2(1) = TABLE(42) d_roi_rect2(2) = ZV_LENTOIMG(0, TABLE(233)) d_roi_rect2(3) = ZV_LENTOIMG(0, TABLE(234)) d_roi_rect2(4) = TABLE(235) SET_REDRAW endif elseif circle_status=1 then SET_REDRAW is_redraw = 0 if (mouse_scan(21) = 1 AND TABLE(21)>0 AND TABLE(21)0 AND TABLE(22) hit_pos = ZV_HMIADJARC(table(21), table(22), 551, -1) '只有按下时可以改变击中位置 is_redraw = 1 endif if (mouse_scan(21) = -1 AND TABLE(21)>0 AND TABLE(21)0 AND TABLE(22) ZV_HMIADJARC(table(21), table(22), 551, hit_pos) is_redraw = 1 endif if (MOUSE_state(21)) then ZV_HMIADJARC(table(21), table(22), 551, hit_pos) is_redraw = 1 endif if (1 = is_redraw) then is_redraw = 0 '控件坐标转图像坐标 ZV_POSTOIMG(0, 1, 551, 61) TABLE(63) = ZV_LENTOIMG(0, TABLE(553)) TABLE(64) = ZV_LENTOIMG(0, TABLE(554)) TABLE(65, TABLE(555), TABLE(556)) d_roi_arc(0) = TABLE(61) d_roi_arc(1) = TABLE(62) d_roi_arc(2) = TABLE(63) d_roi_arc(3) = TABLE(64) d_roi_arc(4) = TABLE(65) d_roi_arc(5) = TABLE(66) SET_REDRAW endif else SET_REDRAW endif END SUB '点,线,圆的绘制函数 GLOBAL SUB draw_roi() if point_status = 1 and line_status=0 and circle_status=0 then SET_COLOR(C_BLUE) ZV_HMIRECT2(31, 300) DRAWLINE(TABLE(300), TABLE(301), TABLE(302), TABLE(303)) '外矩形 DRAWLINE(TABLE(302), TABLE(303), TABLE(304), TABLE(305)) DRAWLINE(TABLE(304), TABLE(305), TABLE(306), TABLE(307)) DRAWLINE(TABLE(306), TABLE(307), TABLE(300), TABLE(301)) DRAWLINE(TABLE(308), TABLE(309), TABLE(310), TABLE(311)) '方向箭头 DRAWLINE(TABLE(312), TABLE(313), TABLE(310), TABLE(311)) DRAWLINE(TABLE(314), TABLE(315), TABLE(310), TABLE(311)) endif if line_status=1 and point_status=0 and circle_status=0 then SET_COLOR(C_BLUE) TABLE(236,d_line_param(4),d_line_param(5)) '测量器扫描数量和宽度 ZV_HMIRECT2(231, 350) DRAWLINE(TABLE(350), TABLE(351), TABLE(352), TABLE(353)) '外矩形 DRAWLINE(TABLE(352), TABLE(353), TABLE(354), TABLE(355)) DRAWLINE(TABLE(354), TABLE(355), TABLE(356), TABLE(357)) DRAWLINE(TABLE(356), TABLE(357), TABLE(350), TABLE(351)) DRAWLINE(TABLE(358), TABLE(359), TABLE(360), TABLE(361)) '方向箭头 DRAWLINE(TABLE(362), TABLE(363), TABLE(360), TABLE(361)) DRAWLINE(TABLE(364), TABLE(365), TABLE(360), TABLE(361)) if (0 = TABLE(366)) then return SET_COLOR(C_GREEN) DIM idx for idx = 0 to TABLE(366)-1 DRAWLINE(TABLE(367+idx*4), TABLE(368+idx*4), TABLE(369+idx*4), TABLE(370+idx*4)) next endif if circle_status=1 and line_status=0 and point_status=0 then SET_COLOR(C_BLUE) TABLE(557) = d_circle_parm(4) '子区域的个数 TABLE(558) = d_circle_parm(5) '子区域宽度 ZV_HMIARC(551, 400) '绘制圆环 '绘制圆弧 DRAWARC(TABLE(400), TABLE(401), TABLE(402), TABLE(404), TABLE(405)) '内半径 DRAWARC(TABLE(400), TABLE(401), TABLE(403), TABLE(404), TABLE(405)) '外半径 DRAWLINE(TABLE(400), TABLE(401)-5, TABLE(400), TABLE(401)+5) DRAWLINE(TABLE(400)-5, TABLE(401), TABLE(400)+5, TABLE(401)) '绘制边界线 DIM idx for idx = 0 to TABLE(406)-1 DRAWLINE(TABLE(407+idx*4), TABLE(408+idx*4), TABLE(409+idx*4), TABLE(410+idx*4)) next SET_COLOR(C_GREEN) DIM startid startid = 407+TABLE(406)*4 for idx = 0 to TABLE(startid)-1 DRAWLINE(TABLE(startid+1+idx*4), TABLE(startid+2+idx*4), TABLE(startid+3+idx*4), TABLE(startid+4+idx*4)) next endif END SUB 14.在main.bas文件中添加参数设置界面按下【点测试】按钮时响应的函数,并关联动作函数名。 '参数界面按下点测量按钮时响应的函数 GLOBAL SUB btn_meas_p() Point_measure() ZV_GRAYTORGB(grabImg, latch) '在绘制图形前需要先将灰度图转成RGB图 ZV_MARKER(latch, draw_point(0), draw_point(1), 0, 20, C_GREEN) '绘制标志点 ZV_LATCH(latch,0)'显示图像结果 END SUB '测量点的实现子函数 GLOBAL SUB Point_measure() ZVOBJECT mr_p, tmp if d_use_locator=0 then '如果没有使用补正源 '生成矩形测量器 ZV_MRGENRECT2(mr_p, d_roi_rect1(0), d_roi_rect1(1),d_roi_rect1(2), d_roi_rect1(3),d_roi_rect1(4), 1) endif '检测点 ZV_MRPOS(mr_p, grabImg,tmp,d_point_param(3),d_point_param(2),d_point_param(0),d_point_param(1)) '获取矩阵对应行的数据 ZV_MATGETROW(tmp, 0, 3, 386) '赋值结果变量,用于绘制图形 draw_point(0)=TABLE(386) draw_point(1)=TABLE(387) if d_use_calib=1 and is_ca_success=1 then '如果使用标定功能且已经标定成功 ZV_CALTRANSW(ca_param, TABLE(386),TABLE(387),386) '像素坐标转世界坐标 endif '赋值结果变量,用于显示数据结果 point_result(0)=TABLE(386) point_result(1)=TABLE(387) END SUB
15.在main.bas文件中添加参数设置界面按下【线测试】按钮时响应的函数,并关联动作函数名。
参数界面按下线测量按钮时响应的函数
GLOBAL SUB btn_meas_l() btn_ml_test() ZV_GRAYTORGB(grabImg, latch)'在绘制图形前需要先将灰度图转成RGB图 ZV_LINE(latch,draw_line(0),draw_line(1),draw_line(2),draw_line(3),zv_color(0,255,0)) '绘制线 ZV_LATCH(latch,0)'显示图像结果 END SUB '测量直线的实现子函数 GLOBAL SUB btn_ml_test() ZVOBJECT mr_l, tmp if d_use_locator=0 then '如果没有使用补正源 '生成直线测量器 ZV_MRGENLINE(mr_l,d_roi_rect2(0), d_roi_rect2(1),d_roi_rect2(2), d_roi_rect2(3), d_roi_rect2(4), 1,d_line_param(4),d_line_param(5)) endif '设置测量器区域的高级参数,包括滤波尺寸、边缘阈值、边缘极性和边缘位置 ZV_MRSETADV(mr_l, d_line_param(3), d_line_param(2), d_line_param(0),d_line_param(1)) '初始化结果数组 TABLE(486, 0, 0, 0, 0) '检测直线,并将结果赋值给起始地址为486的数组 ZV_MRLINE(mr_l, grabImg, tmp, 486) '赋值结果变量,用于绘制图形 draw_line(0)=TABLE(486) draw_line(1)=TABLE(487) draw_line(2)=TABLE(488) draw_line(3)=TABLE(489) if d_use_calib=1 and is_ca_success=1 then '如果使用标定功能且已经标定成功 ZV_CALTRANSW(ca_param, TABLE(486),TABLE(487),486) '像素坐标转世界坐标 ZV_CALTRANSW(ca_param, TABLE(488),TABLE(489),488) '像素坐标转世界坐标 endif '赋值结果变量,用于显示数据结果 line_result(0)=TABLE(486) line_result(1)=TABLE(487) line_result(2)=TABLE(488) line_result(3)= TABLE(489) END SUB
16.添加在main.bas文件中添加参数设置界面按下【圆测试】按钮时响应的函数,并关联动作函数名。
'参数界面按下圆测量按钮时响应的函数 GLOBAL SUB btn_meas_c() btn_mc_test()'测量圆实现子函数 ZV_GRAYTORGB(grabImg, latch)'在绘制图形前需要先将灰度图转成RGB图 ZV_CIRCLE(latch, draw_circle(0),draw_circle(1),draw_circle(2), C_GREEN)'绘制圆 ZV_LATCH(latch,0)'显示图像结果 END SUB '测量圆的实现子函数 GLOBAL SUB btn_mc_test()'测量圆 ZVOBJECT mr_c, tmp if d_use_locator=0 then '生成环形测量器 ZV_MRGENCIRCLE(mr_c,d_roi_arc(0), d_roi_arc(1), d_roi_arc(2), d_roi_arc(3),d_roi_arc(4),d_roi_arc(5), 1, d_circle_parm(4),d_circle_parm(5)) endif '设置测量参数,包括滤波尺寸、边缘阈值、边缘极性和边缘位置 ZV_MRSETADV(mr_c,d_circle_parm(3), d_circle_parm(2), d_circle_parm(0),d_circle_parm(1)) '初始化结果数组 TABLE(586, 0, 0, 0) '测量圆,并将结果赋值给起始地址为586的数组 ZV_MRCIRCLE(mr_c, grabImg, tmp, 586) '赋值结果变量,用于绘制图形 draw_circle(0)=TABLE(586) draw_circle(1)=TABLE(587) draw_circle(2)=TABLE(588) if d_use_calib=1 and is_ca_success=1 then '如果使用标定功能且已经标定成功 ZV_CALTRANSW(ca_param, TABLE(586),TABLE(587),590) '像素坐标转世界坐标 ZV_CALTRANSW(ca_param, TABLE(586)+TABLE(588),TABLE(587),592) '圆心加半径的坐标 '赋值标定结果变量,用于显示数据结果 circle_result(0)=TABLE(590) circle_result(1)=TABLE(591) '半径等于圆心点和圆上的点之间的距离 circle_result(2)=ZV_DISTPP(TABLE(590),TABLE(591),TABLE(592),TABLE(593)) ZV_LATCH(grabImg, 0) RETURN endif '赋值像素结果变量,用于显示数据结果 circle_result(0)=TABLE(586) circle_result(1)=TABLE(587) circle_result(2)=TABLE(588) END SUB
17.在main.bas文件中添加参数设置界面按下【返回运行界面】按钮时响应的函数,并关联动作函数名。
参数设置界面按下返回运行界面按钮时响应的函数
GLOBAL SUB btn_goto_main() HMI_CLOSEWINDOW(11) END SUB
18.添加在运行界面按下【测试】按钮时响应的函数,并关联动作函数名。
'运行界面按下测试按钮时响应的函数 GLOBAL SUB btn_test() TICKS=0'开始计时 if(d_use_locator=1) then '如果使用补正源 btn_loc_test() '定位 endif btn_measure_test() '测量点/线/圆 d_detect_time=ABS(TICKS)'计算检测消耗时间 END SUB '运行界面按下测试按钮时响应的函数 GLOBAL SUB btn_test() TICKS=0'开始计时 if(d_use_locator=1) then '如果使用补正源 btn_loc_test() '定位 endif btn_measure_test() '测量点/线/圆 d_detect_time=ABS(TICKS)'计算检测消耗时间 END SUB '点线圆同时测试子函数 GLOBAL SUB btn_measure_test() if d_use_locator=1 then '如果使用补正源 '初始化测量器 ZV_MRGENRECT2(mr_p, d_roi_rect1(0), d_roi_rect1(1),d_roi_rect1(2), d_roi_rect1(3),d_roi_rect1(4), 1) ZV_MRGENLINE(mr_l,d_roi_rect2(0), d_roi_rect2(1),d_roi_rect2(2), d_roi_rect2(3), d_roi_rect2(4), 1,d_line_param(4),d_line_param(5)) ZV_MRGENCIRCLE(mr_c,d_roi_arc(0), d_roi_arc(1), d_roi_arc(2), d_roi_arc(3),d_roi_arc(4),d_roi_arc(5), 1, d_circle_parm(4),d_circle_parm(5)) '计算刚性变换矩阵 ZV_GETRIGIDVECTOR(mat_rigid2, d_match_base_rst(1), d_match_base_rst(2), d_match_base_rst(3),TABLE(4),TABLE(5), d_match_rst(3)) ZV_MRCORRECT (mr_p, mat_rigid2, mr_p) '点测量器补正 ZV_MRCORRECT (mr_l, mat_rigid2, mr_l) '线测量器补正 ZV_MRCORRECT (mr_c, mat_rigid2, mr_c) '圆测量器补正 endif Point_measure() '测量点 btn_ml_test() '测量线 btn_mc_test() '测量圆 ZV_GRAYTORGB(grabImg, latch) ZV_MARKER(latch, draw_point(0), draw_point(1), 0, 20, C_BLUE) ZV_LINE(latch,draw_line(0),draw_line(1),draw_line(2),draw_line(3),C_BLUE) ZV_CIRCLE(latch, draw_circle(0),draw_circle(1),draw_circle(2), C_BLUE) ZV_LATCH(latch,0) END SUB
19.添加在运行界面按下【运行】按钮时响应的函数,并关联动作函数名。
'运行界面按下运行按钮时响应的函数 GLOBAL SUB btn_run() if (1 = main_task_state) then if (0 = PROC_STATUS(main_task_id)) then main_task_state = 2 RUNTASK main_task_id, main_task endif endif END SUB '主任务执行的函数 main_task: while(1) if (3 = main_task_state) then main_task_state = 1 exit while endif '持续采集图像,对图像进行操作 btn_grab() btn_test() wend END
20.添加在运行界面按下【停止】按钮时响应的函数,并关联动作函数名。
'运行界面按下停止按钮时响应的函数 GLOBAL SUB btn_stop() if (2 = main_task_state) then main_task_state = 3 endif END SUB
02 仿真效果
本次,正运动技术VPLC系列机器视觉运动控制一体机快速入门(八)——测量点/直线/圆,就分享到这里,更多精彩内容请关注“正运动小助手”公众号。
本文由正运动技术原创,欢迎大家转载,共同学习,一起提高中国智能制造水平。文章版权归正运动技术所有,如有转载请注明文章来源。