赞
踩
工业生产中,视觉模块经常需要和机器人联动,相机拍照计算坐标并转换成机器人取料坐标后完成取放料。现在对整个标定流程进行讲解。这里主要讲的是眼在手外,和眼在手上是有区别的。
确定机械手的夹具平台水平和竖直方向上分别平行机器人的X和Y轴(例如将夹具推值机器人底座,用钢尺抵住底座,另一端和夹具垂直或平行),然后确定Z轴的工作高度和初始角度。
调整相机,确保相机成像方正不倾斜,然后调整视野大小,调整焦距使成像清晰。
可以先贴一张A4纸在拍照位上,在相机视野范围内,移动机器人取九个位置(尽量覆盖全视野),做好标记点。
拍照取图然后根据标定点算出九个标定点的像素坐标,对应九个机器人物理坐标(一般根据夹具尺寸换算成夹具的边顶角坐标)求出映射矩阵
注意:实时机器人坐标是Z轴底部的坐标,但是我们在图像上计算像素坐标时,由于不好计算夹具上的Z轴中心点在图像上的坐标,所以会把这个点偏移到夹具的边顶角上测量像素坐标(使用标定锥的话就不需要,得到的就是图像坐标和法兰盘中心的映射关系),那么对应的机器人坐标也就要在原Z轴中心点坐标偏移夹具尺寸的一半(具体正负看实际坐标系的方向),也移到了夹具的边顶角(就是将法兰盘中心移动到之前夹具边顶角的坐标处,那后续图像坐标转换之后得到的就是法兰盘中心坐标)。两个平面所有的点坐标都符合这个映射矩阵关系。
至此,图像像素坐标可以映射成机器人物理坐标
验证九点标记精度:图像中随机取几个点,映射成机器人坐标,然后机器人移动到这个物理坐标,看机器人Z轴中心是否会呈现在图像的中心点。
如果定位精度要求不是特别高,可以忽略这一部分。这里只介绍怎么操作。
机器人旋转中心不一定就是Z轴中心,或者说,机器人坐标(末端法兰盘中心)不一定就是和Z轴中心重合(装配误差等),也就是说,直接拿标定时机器人返回的XY坐标可能会有一定的偏差。但是,即使不重合,那这个偏差也是固定的,让夹具在视野中旋转几个位置(尽可能跨幅大一点点),比如转动夹具取出五个像素坐标点,然后拟合成一个圆,圆心即机器人的旋转中心(现在还是像素坐标),应用上面的转换关系,换算算出了机器人实际旋转中心后,减去旋转标定时的那个定点物理坐标,就算出了这个固定差值,以后每次计算旋转关系的时候,旋转中心就是当时的机器人坐标加上这个差值。
机器人的角度和像素平面的角度单位是一致的,所以我们需要计算出两个平面的固定角度差值。常规操作是:取夹具的一个边缘(注意:要和你像素平面取角度的方向一致,否则会有90、180度之类的偏差),将边缘投影到像素平面(尺子贴紧边缘画线),记录下机器人此刻的角度,然后机器人不移动,只旋转一定角度,然后绘制、记录角度。重复以上步骤,画4到5条边,算出图像中画的边的角度,就得到了像素平面角度和机器人角度的对应关系,计算差值平均值。
图像坐标和机器人坐标的转换关系:
代码转换实例:
- /// <summary>
- /// 计算机器人位置
- /// </summary>
- /// <param name="args">转换参数</param>
- /// <returns>返回是否转换成功</returns>
- public bool OnRequestConvertToRobotPostion(ImagePointToRobotPostionArgs args)
- {
- args.RobotRow = 0;
- args.RobotColumn = 0;
- args.RobotAngle = 0;
- args.ConvertResult = EnumConvertToRobotPostionResult.OK;
-
- try
- {
- //取出对应的机器人参数
- var calibInfo = RobotParams.CalibrationParams[args.CalibInfoIndex];
- var compenInfo = RobotParams.CompensationInfos[args.CompenInfoIndex];
-
- //将图像坐标转换为机器人坐标
- HOperatorSet.AffineTransPoint2d(calibInfo.HomMat2D, args.ImageColumn, args.ImageRow, out HTuple centerColumn_Robot, out HTuple centerRow_Robot);
-
- //获取机器人旋转前实际取料位置的参考点坐标
- var robotPickPtRow_BeforeRotate = centerRow_Robot.D + calibInfo.DistanceRow_RobotCenter_TargetCenter;
- var robotPickPtCol_BeforeRotate = centerColumn_Robot.D + calibInfo.DistanceCol_RobotCenter_TargetCenter;
- Log?.Write($"{Name}机器人取料坐标(旋转前) X:{robotPickPtCol_BeforeRotate} Y:{robotPickPtRow_BeforeRotate}");
-
- //获取机器人旋转中心偏差后的实际旋转中心坐标
- var rotateCenterRow = robotPickPtRow_BeforeRotate + calibInfo.RotationCenterRow;
- var rotateCenterColumn = robotPickPtCol_BeforeRotate + calibInfo.RotationCenterColumn;
- Log?.Write($"{Name}旋转中心坐标 X:{rotateCenterColumn} Y:{rotateCenterRow}");
-
- //图像角度与机器人角度同向时不用额外操作,反向时需要把角度取反
- var diffAngle = (args.ImageAngle + compenInfo.CompensationU + calibInfo.ActualToZeroedAngleValue) * (calibInfo.BIsImageAndRobotAngleSameDirection ? 1 : -1);
-
- //获取机器人绕旋转中心点旋转的仿射矩阵
- HOperatorSet.VectorAngleToRigid(rotateCenterRow, rotateCenterColumn, new HTuple(diffAngle).TupleRad(), rotateCenterRow, rotateCenterColumn, 0, out HTuple homMat2D);
-
- //对原始的机器人的取料坐标应用旋转仿射矩阵
- HOperatorSet.AffineTransPoint2d(homMat2D, centerRow_Robot, centerColumn_Robot, out HTuple robotPickPtRow, out HTuple robotPickPtCol);
-
- //实际机器人取料坐标需要加上旋转角度产生的X、Y偏差
- double offsetRow = centerRow_Robot - robotPickPtRow;
- double offsetCol = centerColumn_Robot - robotPickPtCol;
- robotPickPtRow = robotPickPtRow_BeforeRotate + offsetRow;
- robotPickPtCol = robotPickPtCol_BeforeRotate + offsetCol;
-
- //计算实际输出的机器人取料坐标
- args.RobotRow = (float)Math.Round(robotPickPtRow.D + compenInfo.CompensationRow, 3);
- args.RobotColumn = (float)Math.Round(robotPickPtCol.D + compenInfo.CompensationCol, 3);
- args.RobotAngle = (float)Math.Round(args.ImageAngle + compenInfo.CompensationU + calibInfo.CameraRobotAngle, 3);
- Log?.Write($"{Name}机器人取料坐标 X:{args.RobotColumn} Y:{args.RobotRow} U:{args.RobotAngle}");
-
- //范围防呆
- if (args.RobotRow < calibInfo.RowMin || calibInfo.RowMax < args.RobotRow)
- {
- args.ConvertResult = EnumConvertToRobotPostionResult.RowOverrun;
- Log?.Write($"{Name}:取料Y超限!当前值:{args.RobotRow} 范围:{calibInfo.RowMin}-{calibInfo.RowMax}", EnumLogType.Error);
- }
- else if (args.RobotColumn < calibInfo.ColumnMin || calibInfo.ColumnMax < args.RobotColumn)
- {
- args.ConvertResult = EnumConvertToRobotPostionResult.ColumnOverrun;
- Log?.Write($"{Name}:取料X超限!当前值:{args.RobotColumn} 范围:{calibInfo.ColumnMin}-{calibInfo.ColumnMax}", EnumLogType.Error);
- }
- else if (args.RobotAngle < calibInfo.AngleMin || calibInfo.AngleMax < args.RobotAngle)
- {
- args.ConvertResult = EnumConvertToRobotPostionResult.AngleOverrun;
- Log?.Write($"{Name}:取料U超限!当前值:{args.RobotAngle} 范围:{calibInfo.AngleMin}-{calibInfo.AngleMax}", EnumLogType.Error);
- }
-
- return true;
- }
- catch (Exception ex)
- {
- args.ConvertResult = EnumConvertToRobotPostionResult.Error;
- Log?.Write($"{Name}:计算机器人位置出错!{ex}!", EnumLogType.Error);
- return false;
- }
- }
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。