机器人手眼标定(九点标定)

1 篇文章 0 订阅

1. 简介

        工业生产中,视觉模块经常需要和机器人联动,相机拍照计算坐标并转换成机器人取料坐标后完成取放料。现在对整个标定流程进行讲解。这里主要讲的是眼在手外,和眼在手上是有区别的。

2.标定步骤

2.1 前期准备

        确定机械手的夹具平台水平和竖直方向上分别平行机器人的X和Y轴(例如将夹具推值机器人底座,用钢尺抵住底座,另一端和夹具垂直或平行),然后确定Z轴的工作高度和初始角度。

        调整相机,确保相机成像方正不倾斜,然后调整视野大小,调整焦距使成像清晰。

2.2 九点标定

        可以先贴一张A4纸在拍照位上,在相机视野范围内,移动机器人取九个位置(尽量覆盖全视野),做好标记点。

        拍照取图然后根据标定点算出九个标定点的像素坐标,对应九个机器人物理坐标(一般根据夹具尺寸换算成夹具的边顶角坐标)求出映射矩阵
        注意:实时机器人坐标是Z轴底部的坐标,但是我们在图像上计算像素坐标时,由于不好计算夹具上的Z轴中心点在图像上的坐标,所以会把这个点偏移到夹具的边顶角上测量像素坐标(使用标定锥的话就不需要,得到的就是图像坐标和法兰盘中心的映射关系),那么对应的机器人坐标也就要在原Z轴中心点坐标偏移夹具尺寸的一半(具体正负看实际坐标系的方向),也移到了夹具的边顶角(就是将法兰盘中心移动到之前夹具边顶角的坐标处,那后续图像坐标转换之后得到的就是法兰盘中心坐标)。两个平面所有的点坐标都符合这个映射矩阵关系。

        至此,图像像素坐标可以映射成机器人物理坐标

        验证九点标记精度:图像中随机取几个点,映射成机器人坐标,然后机器人移动到这个物理坐标,看机器人Z轴中心是否会呈现在图像的中心点。

2.3 旋转标定

        如果定位精度要求不是特别高,可以忽略这一部分。这里只介绍怎么操作。

        机器人旋转中心不一定就是Z轴中心,或者说,机器人坐标(末端法兰盘中心)不一定就是和Z轴中心重合(装配误差等),也就是说,直接拿标定时机器人返回的XY坐标可能会有一定的偏差。但是,即使不重合,那这个偏差也是固定的,让夹具在视野中旋转几个位置(尽可能跨幅大一点点),比如转动夹具取出五个像素坐标点,然后拟合成一个圆,圆心即机器人的旋转中心(现在还是像素坐标),应用上面的转换关系,换算算出了机器人实际旋转中心后,减去旋转标定时的那个定点物理坐标,就算出了这个固定差值,以后每次计算旋转关系的时候,旋转中心就是当时的机器人坐标加上这个差值。

2.4 角度标定

        机器人的角度和像素平面的角度单位是一致的,所以我们需要计算出两个平面的固定角度差值。常规操作是:取夹具的一个边缘(注意:要和你像素平面取角度的方向一致,否则会有90、180度之类的偏差),将边缘投影到像素平面(尺子贴紧边缘画线),记录下机器人此刻的角度,然后机器人不移动,只旋转一定角度,然后绘制、记录角度。重复以上步骤,画4到5条边,算出图像中画的边的角度,就得到了像素平面角度和机器人角度的对应关系,计算差值平均值。

2.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;
    }
}

 

  • 15
    点赞
  • 38
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值