2D视觉定位引导系列 4 相机运动模式下的标定和仿真

相机运动就是相机在机械手上可以随机械手一起运动,也就是常说的眼在手上,前面三篇标定的文章说的都是眼在手外
先了解下这种模式使用方法。标定用的是9点/12点标定、9点标定包含在12点之中,本文只讨论相机移动模式的12点标定。
    1.12点标定。如何标定,是本篇讨论重点
    2.拍照位置确定 眼在手上多了个拍照位置的问题。当拍照位置确定以后,就是相机固定模式一样了。每次拍照先倒这个位置拍照。
       特别注明下移动拍照位置的,相机的角度 要与 标定的时候角度保持一致(角度不一致也可以通过计算来完成,但是比较麻烦,此处不表)
    3.基准示教,示教出基准的取放位置。
    3.偏差计算,发送给机械臂。这里需要获得真实物理坐标的参照第3篇文章,
在搞视觉初期,对这个相机移动的标定方式比较疑惑,问大师们总是没法说明白。后来经过自己摸索总算是整明白了,此文还是向以前一样
先介绍原理,后面halcon代码模拟仿真,方便大家看到代码的数据处理过程,这样大家在转换为自己代码的时候会很简单。
海康的N点标定 的相机模式里面有个相机运动的选项,想必这种模式的标定数据处理有特殊的地方。文中会给出答案。
先来看下模拟仿真的图,图片格式GIF。红色小圆圈可以认为是标定的Mark 固定不动。粉红大圆圈表示机械手法兰盘,通过一个连杆带着粉红色的矩形框(相机)运行。蓝色
矩形框,是相机开始时候的位置示意图,方便比较。
看下2中模式的区别
                                                相机移动
                                                相机静止
再看下2中模式前9点坐标
相机运动标定数据
WX  =[2500.0,  3000.0, 3500.0,  3500.0, 3000.0, 2500.0, 2500.0, 3000.0, 3500.0]
WY  =[4500.0,  4500.0, 4500.0,  5000.0, 5000.0, 5000.0, 5500.0, 5500.0, 5500.0]
Row =[1500.0, 1000.0, 500.0,    500.0,   1000.0, 1500.0, 1500.0, 1000.0,  500.0]
COL =[1500.0, 1500.0, 1500.0,  1000.0, 1000.0, 1000.0, 500.0,   500.0,    500.0]
相机静止标定数据
WX  =[2500.0, 3000.0, 3500.0, 3500.0, 3000.0, 2500.0, 2500.0, 3000.0, 3500.0]
WY  =[4500.0, 4500.0, 4500.0, 5000.0, 5000.0, 5000.0, 5500.0, 5500.0, 5500.0]
Row =[500,     1000,    1500,   1500,   1000,     500,      500,      1000,   1500]
COL =[500,     500,      500,     1000,   1000,    1000,     1500,    1500,   1500]
上面2组数据可以看出,物理坐标无疑都是一样的,但是对于像素坐标,可以发现是绕着中点那个点(第5点)对称关系。
这个很好理解,相机运动是机械手带着相机运行,Mark不动,与相机静止模式的Mark相对相机位置变化刚好方向相反。
关键看这里
对于相机运动的模式。9点数据的处理可以采用 上篇文章(海康N点标定)中提的数据处理方式,旋转归一化(说白了就是相对标定)。
虽然实际情况是相机运动,Mark 不动。但是也可以看成Mark运动,相机不动,这主要看以哪个为参照物了。
当认为Mark运动 相机不动时,每一次 Mark运动方向是真实机构运动方向相反,位移量一样。
那么前面9点的机械坐标就不能用直接取读出的坐标了,需要依照上面思路来处理一下。处理方式如下。
9点物理坐标减去标定的起始点(一般为第5点,海康是第5点),然后结果再取反。
减去标定起始点得到相对起始点的运动偏移,结果再取反,主要考虑将Mark相对相机的运行与机构实际运动方向相反。
计算公式如下
(WXnew,WYnew)= -((WX,WY)-(WXstart,WYstart))
(WXnew,WYnew)=(WXstart,WYstart)-(WX,WY)
WXnew,WYnew  最终相对物理坐标
WXstart,WYstart 标定起始点物理坐标
依据上面公式重新计算WX,WY 。后面就和12点标定一样的处理流程了。
还有点得提醒了。将上面带入海康N点标定模块中,有如下的提示 :旋转方向一致性=-1,表示随意机械旋转的角度是正的,但是物体在图像上显示却是负的方向旋转。这种情况在给机械手发送补偿角度要方向取个反。
后面关于纠偏的帖子遇到这种情况再继续探讨。
Halcon 代码仿真:代码可以复制到Halcon中直接运行
需要注意代码中的取机械坐标和像素坐标与前面2篇文章的区别。
*仿真相机在机械手上,也就是眼在手上的标定处理
*默认固定图像的横向为COL对应为Y轴,竖向 Row 对于X轴。


dev_update_window('off')
*窗口坐标 宽度
winw:=10000
winh:=10000


dev_open_window(0, 0, 1000, 1000, 'black', WindowHandle)
dev_set_part(0,0,winw,winh)
set_system ('clip_region', 'false')
dev_clear_window()
dev_set_draw ('margin')
gen_region_line(xaxis, 0, 0, 0, winw)
gen_region_line(yaxis, 0, 0, winw, 0)
dev_set_color ('red')
dev_display(xaxis)
dev_display(yaxis)
*定义相机视野长宽
camstartrow:=5000
camstartcol:=5000
cam_width:=1000
cam_degre:=0
*定义相机左上角坐标,后面计算像素坐标用
*以Cam的左上角为0,0 坐标 相机原点在机械坐标系中的位置是 (arm_center_row-cam_width),(camstartcol-cam_width)
cam_origin_row:=camstartrow-cam_width
cam_origin_col:=camstartcol-cam_width


*生成相机区域
gen_rectangle2(camrect, camstartrow, camstartcol, cam_degre, cam_width, cam_width)


*定义相机的轴
gen_region_line(camRowAxis, cam_origin_row, cam_origin_col, cam_origin_row+3000, cam_origin_col)
gen_region_line(camColAxis, cam_origin_row, cam_origin_col, cam_origin_row, cam_origin_col+3000)


*相机的三个坐表轴坐标,原点,X轴上一点,Y轴上一点。 方便旋转时候计算像素坐标,通过distance_pl计算点到直线距离
CamRowAxis:=[cam_origin_row, cam_origin_row+3000,cam_origin_row]
CamColAxis:=[cam_origin_col,cam_origin_col,cam_origin_col+3000]


dev_set_color ('blue')
dev_set_draw ('margin')
dev_display(camrect)




*生成清空区域,用于清空绘图区域
gen_rectangle1(clearRect,2000,2000,8000,8000)


*定义 Arm 法兰盘位置,旋转标定的物理中心
arm_center_row:=3000
arm_center_col:=5000


gen_circle(arm_center, arm_center_row, arm_center_col, 200)
*定义末端工具 可以理解为一更长杆一端连接在法兰盘中心,一端带着吸盘 ,初始将吸盘的中心也放到相机中心
gen_region_line(tool,arm_center_row,arm_center_col,camstartrow-(cam_width),camstartcol)
*定义标定用的Mark圆心
gen_circle(MarkCicle, camstartrow, camstartcol, 100)

gen_circle(cam_origin, cam_origin_row, cam_origin_col, 30)

concat_obj(arm_center,tool,ObjectsConcat)

concat_obj(ObjectsConcat, cam_origin, Tool_ARM)
*将相机和机械臂结合到一起
concat_obj(Tool_ARM, camrect, Tool_ARM)

*相机轴Col,Row轴也和机械臂结合到一起,
concat_obj(Tool_ARM,camRowAxis,Tool_ARM)
concat_obj(Tool_ARM,camColAxis,Tool_ARM)

*Tool_ARM 总元素6个,第5个是相机Row 轴先,第6个是相机Col 轴线

dev_set_color ('pink')
dev_set_draw ('margin')
dev_display(Tool_ARM)
dev_set_color ('red')
dev_set_draw ('margin')


dev_display(MarkCicle)


dev_set_color ('pink')
*定义2个轴的移动步长系
move9Row:=[ -1,  0,  1, 1, 0, -1, -1,  0,  1 ]
move9Col:=[ -1, -1, -1, 0, 0,  0,  1,  1,  1 ]

*机械坐标
xwolrd:=[]
yworld:=[]
*像素坐标
row_pixel:=[]
col_pixel:=[]
*每次移动补偿
step:=500.0
set_display_font(WindowHandle, 24, 'mono', 'true', 'false')
set_tposition(WindowHandle, 2800, 2800)
write_string (WindowHandle, '准备开始标定')
fill_up(clearRect,RegionFillUp)
for Index := 0 to 8 by 1
    
    *清空绘图区域
    dev_set_color ('black')
    dev_set_draw ('fill')
    dev_display(clearRect)
    dev_set_color ('pink')
    dev_set_draw ('margin')
    dev_set_color ('blue')
    dev_set_draw ('margin')
    dev_display(camrect)
    *构建每次平移矩阵
    hom_mat2d_identity(HomMat2DIdentity)
    hom_mat2d_translate(HomMat2DIdentity, move9Row[Index]*step, move9Col[Index]*step, HomMat2DTranslate)
    
   * 下面是处理物理坐标的过程,
   * 0-(arm_center_row+move9Row[Index]*step-arm_center_row)=0-move9Row[Index]*step
   * 0-(arm_center_col+move9Col[Index]*step-arm_center_col)=0-move9Col[Index]*step
   * tuple_concat(xwolrd,arm_center_row+move9Row[Index]*step,xwolrd)
   * tuple_concat(yworld,arm_center_col+move9Col[Index]*step,yworld)
     
     tuple_concat(xwolrd,0-move9Row[Index]*step,xwolrd)
     tuple_concat(yworld,0-move9Col[Index]*step,yworld)
     
    *模拟移动机械臂带动相机一起移动
    affine_trans_region(Tool_ARM, RegionAffineTrans, HomMat2DTranslate, 'nearest_neighbor')
   *通过点到直线的距离来重新计算像素坐标
    affine_trans_pixel(HomMat2DTranslate,CamRowAxis,CamColAxis,RowTrans, ColTrans)
    distance_pl(camstartrow,camstartcol, RowTrans[0],ColTrans[0], RowTrans[1],ColTrans[1], Pcol)
    distance_pl(camstartrow,camstartcol, RowTrans[0],ColTrans[0], RowTrans[2],ColTrans[2], Prow)
    
    tuple_concat(row_pixel,Prow, row_pixel)
    tuple_concat(col_pixel,Pcol, col_pixel)
    
        dev_set_color ('pink')
    dev_display(RegionAffineTrans)
    dev_set_color ('red')
    dev_set_draw ('margin')
    dev_display(MarkCicle)
    dev_set_draw ('margin')
    set_tposition(WindowHandle, 2800, 3200)
    tuple_concat('点',Index+1,distxt)
    write_string (WindowHandle, distxt)
      wait_seconds (0.5)
endfor
*上面模拟九点标定以后计算标定数据,HomMat2D 就是9点标定的转移矩阵
vector_to_hom_mat2d(row_pixel, col_pixel, xwolrd, yworld, HomMat2D)

    dev_set_color ('black')
    dev_set_draw ('fill')
    dev_display(clearRect)


*3点旋转 标定起点 旋转3此,计算坐标的方式和上面9点标定计算一致
rotate3Point:=[20,0,-20]
arc_row:=[]
arc_col:=[]
gen_rectangle1(textrect,2800,3200,3100,3800)
for Index1 := 0 to 2 by 1
    
        *清空绘图区域
    dev_set_color ('black')
    dev_set_draw ('fill')
    dev_display(textrect)
    dev_set_color ('pink')
    dev_set_draw ('margin')
    
   hom_mat2d_identity(HomMat2DIdentity)
   *标定起点 选择
   hom_mat2d_rotate(HomMat2DIdentity, rad(rotate3Point[Index1]), arm_center_row, arm_center_col, HomMat2DRotate)
     *模拟移动机械臂
   affine_trans_region(Tool_ARM, RegionAffineTrans, HomMat2DRotate, 'nearest_neighbor')


   affine_trans_pixel(HomMat2DRotate,CamRowAxis,CamColAxis,RowTrans, ColTrans)
   *通过点到直线的距离来重新计算像素坐标
   distance_pl(camstartrow,camstartcol, RowTrans[0],ColTrans[0], RowTrans[1],ColTrans[1], Pcol)
   distance_pl(camstartrow,camstartcol, RowTrans[0],ColTrans[0], RowTrans[2],ColTrans[2], Prow)


   tuple_concat(arc_row, Prow, arc_row)
   tuple_concat(arc_col,Pcol, arc_col)
   
   
   dev_set_color ('turquoise')
   dev_display(RegionAffineTrans)
   dev_set_color ('red')
   dev_set_draw ('margin')
   dev_display(MarkCicle)
   dev_set_color ('blue')
   dev_set_draw ('margin')
   dev_display(camrect)
   set_tposition(WindowHandle, 2800, 3200)
   tuple_concat('点',Index1+10,distxt)
   write_string (WindowHandle, distxt)
   wait_seconds (0.5)
endfor


*通过上面的3点  计算圆心
gen_contour_polygon_xld(Contour, arc_row, arc_col)
get_part(WindowHandle, Row11, Column11, Row2, Column2)
fit_circle_contour_xld(Contour, 'algebraic', -1, 0, 0, 3, 2, Row1, Column1, Radius, StartPhi, EndPhi, PointOrder)
*Row1,Column1 就是圆心的像素坐标,当前值(-1000,1000),
gen_cross_contour_xld(Cross, Row1,Column1, 100, 0)
set_system ('clip_region', 'false')
dev_set_color ('red')
dev_display(Cross)


affine_trans_point_2d(HomMat2D, Row1, Column1, Qx, Qy)
dev_set_color ('blue')
gen_cross_contour_xld(Cross1, Qx,Qy, 200, 0)
*显示旋转标定中心坐标
dev_display(Cross1)
*计算偏移


*现在物理旋转中心被设定为(0,0),所以计算偏移不用(arm_center_row,arm_center_col)这个数据了,希望能理解
Dx:=0-Qx
Dy:=0-Qy
*修正9点标定的机械坐标
xwolrd1:=xwolrd+Dx
ywolrd1:=yworld+Dy
*重新计算标定矩阵
vector_to_hom_mat2d(row_pixel, col_pixel, xwolrd1, ywolrd1, HomMat2D1)
*重新验算标定的旋转中心是否与法兰中心重合


*旋转中心验证
affine_trans_point_2d(HomMat2D1, Row1, Column1, Qx1, Qy1)


*(1499.5,1499.5 )
affine_trans_point_2d(HomMat2D1, 1499.5,1499.5 , Qx2, Qy2)
上面代码最后验证了(1499.5,1499.5) 对应的物理坐标为 (2500,500)
海康N点标定 验证
海康N点标定数据,复制到Txt中,可以导入的N点标定模块中。
1500.0 1500   2500.000   4500.000   0.000     
1000   1500   3000.000   4500.000   0.000     
500    1500   3500.000   4500.000   0.000     
500    1000  3500.000   5000.000   0.000     
1000   1000   3000.000   5000.000   0.000     
1500   1000   2500.000   5000.000   0.000     
1500   500   2500.000   5500.000   0.000     
1000   500   3000.000   5500.000   0.000     
500    500    3500.000   5500.000   0.000
879.526    315.759   3000.000   5000.000   20.000    
1000     1000    3000.000   5000.000   0.000     
879.184    1684.18    3000.000   5000.000   -20.000
N 点标定后用标定转换工具验证如下与halcon 计算记过基本一致。
扩展思考:
此处各位自己去思考:
如果上面的WX,WY 不做特殊处理,就当成相机静止模式会怎么样。我直接说结果吧,如果直接当相机静止处理,
那么12点标定处理以后 ,像素转换的结果与相机运动模式刚好符合相反。那是不是可以这么说,如果知道是相机运动模式
标定时候可以直接用统一的12点标定,只是最终转换物理坐标是数据取个反就行了。我觉得是可以的。至于为什么,大家自己思考吧。
总结:
相机运动和相机静止标定过程一致,只是处理的数据有些许不一致
如果要保证标定方法一致,可以采用上面扩展思考里的方法,在最终转换的时候做下符号处理。这样可以保证标定过程一致。
转载本文需要标明出处。疑问留言 或者 添加下面2种方式 交流
加群(277957302)交流

暂无评论

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注