2D手眼标定

2D手眼标定

9点标定是一种二维手眼标定方法,其中的重要假设为标定板所在平面与实际检测物体处于同一平面,相机所在平面与标定板平面的关系固定不变,可以相对标定板所在平面xy方向平移,z方向保持不变。有了以上假设,那么相机的成像面与标定板平面为仿射变换关系。仿射变换矩阵有6个未知数,那么至少需要3对点进行求解。

12点标定是将实际旋转中心与夹爪中心位置(长度)标定出来。首先还是9点标定(夹爪中心来做),然后用夹爪带着一个mark点(能被相机看到的),在相机视野内走3个点(mark点在9点标定所在平面高度),跟前面9点标定将这三个像素坐标点变换为机械坐标,拟合为圆,圆心即为实际旋转中心点,由此构建出实际旋转中心点与夹爪中心点坐标关系(或者长度关系)。后面求解实际坐标旋转的时候可以用到。

标定步骤

1、首先我们需要准备一块,标定板。如果条件不足,可以使用白纸画上九个圆进行代替。

在这里插入图片描述
2、相机位置,机械手位置全部固定好,标定针固定在机械手上,固定好后不能够再移动。
标定针的位置一定要与夹手或吸盘之内的工具同一位置高度。

3、将标定板放到相机下方,位置区域要与机械手工作的区域一样,包括高度必须尽量一致,这是标定准确度的关键

4、调整好相机焦距,拍照,然后识别9个圆圆心的坐标并进行记录。

5、将机械手依次移动到9个圆的圆心位置,记下机械手坐标

做完以上五步,我们会得到两个点集。一个为9个圆圆心坐标(points_camera),一个为9个圆心对应的机械手坐标(points_robot)。

#include “include/opencv2/video/tracking.hpp”
void CMFCTestDlg::OnBnClickedBtn9pointcalib()
{
std::vector points_camera;
std::vector points_robot;
/*points_camera.push_back(Point2f(1372.36f, 869.00F));
points_camera.push_back(Point2f(1758.86f, 979.07f));
points_camera.push_back(Point2f(2145.75f, 1090.03f));
points_camera.push_back(Point2f(2040.02f, 1461.64f));
points_camera.push_back(Point2f(1935.01f, 1833.96f));
points_camera.push_back(Point2f(1546.79f, 1724.21f));
points_camera.push_back(Point2f(1158.53f, 1613.17f));
points_camera.push_back(Point2f(1265.07f, 1240.49f));
points_camera.push_back(Point2f(1652.6f, 1351.27f));

points_robot.push_back(Point2f(98.884f, 320.881f));
points_robot.push_back(Point2f(109.81f, 322.27f));
points_robot.push_back(Point2f(120.62f, 323.695f));
points_robot.push_back(Point2f(121.88f, 313.154f));
points_robot.push_back(Point2f(123.11f, 302.671f));
points_robot.push_back(Point2f(112.23f, 301.107f));
points_robot.push_back(Point2f(101.626f, 299.816f));
points_robot.push_back(Point2f(100.343f, 310.447f));
points_robot.push_back(Point2f(111.083f, 311.665f));*/

points_camera.push_back(Point2f(1516.14f,1119.48f));
points_camera.push_back(Point2f(1516.24f,967.751f));
points_camera.push_back(Point2f(1668.55f,967.29f));
points_camera.push_back(Point2f(1668.44f,1118.93f));
points_camera.push_back(Point2f(1668.54f,1270.57f));
points_camera.push_back(Point2f(1516.22f,1271.37f));
points_camera.push_back(Point2f(1364.21f,1272.15f));
points_camera.push_back(Point2f(1364.08f,1120.42f));
points_camera.push_back(Point2f(1364.08f,968.496f));

points_robot.push_back(Point2f(35.7173f,18.8248f));
points_robot.push_back(Point2f(40.7173f, 18.8248f));
points_robot.push_back(Point2f(40.7173f, 13.8248f));
points_robot.push_back(Point2f(35.7173f, 13.8248f));
points_robot.push_back(Point2f(30.7173f, 13.8248f));
points_robot.push_back(Point2f(30.7173f, 18.8248f));
points_robot.push_back(Point2f(30.7173f, 23.8248f));
points_robot.push_back(Point2f(35.7173f, 23.8248f));
points_robot.push_back(Point2f(40.7173f, 23.8248f));

/*for (int i = 0;i < 9;i++)
{
    points_camera.push_back(Point2f(i+1, i+1));
    points_robot.push_back(Point2f(2*(i + 1), 2*(i + 1)));
}*/

//变换成 2*3 矩阵
Mat  affine;
estimateRigidTransform(points_camera, points_robot, true).convertTo(affine, CV_32F);
if (affine.empty())
{
    affine = LocalAffineEstimate(points_camera, points_robot, true);
}
float A, B, C, D, E, F;
A = affine.at<float>(0, 0);
B = affine.at<float>(0, 1);
C = affine.at<float>(0, 2);
D = affine.at<float>(1, 0);
E = affine.at<float>(1, 1);
F = affine.at<float>(1, 2);

//坐标转换
//Point2f src = Point2f(1652.6f, 1351.27f);
//Point2f src = Point2f(100, 100);
Point2f src = Point2f(1516.14f, 1119.48f);
Point2f dst;
dst.x = A * src.x + B * src.y + C;
dst.y = D * src.x + E * src.y + F;

//RMS 标定偏差
std::vector<Point2f> points_Calc;
double sumX = 0, sumY = 0;
for (int i = 0;i < points_camera.size();i++)
{
    Point2f pt;
    pt.x = A * points_camera[i].x + B * points_camera[i].y + C;
    pt.y = D * points_camera[i].x + E * points_camera[i].y + F;
    points_Calc.push_back(pt);
    sumX += pow(points_robot[i].x - points_Calc[i].x,2);
    sumY += pow(points_robot[i].y - points_Calc[i].y, 2);
}
double rmsX, rmsY;
rmsX = sqrt(sumX / points_camera.size());
rmsY = sqrt(sumY / points_camera.size());
CString str;
str.Format("偏差值:%0.3f,%0.3f", rmsX, rmsY);
AfxMessageBox(str);

}

Mat CMFCTestDlg::LocalAffineEstimate(const std::vector& shape1, const std::vector& shape2,
bool fullAfine)
{
Mat out(2, 3, CV_32F);
int siz = 2 * (int)shape1.size();

if (fullAfine)
{
    Mat matM(siz, 6, CV_32F);
    Mat matP(siz, 1, CV_32F);
    int contPt = 0;
    for (int ii = 0; ii<siz; ii++)
    {
        Mat therow = Mat::zeros(1, 6, CV_32F);
        if (ii % 2 == 0)
        {
            therow.at<float>(0, 0) = shape1[contPt].x;
            therow.at<float>(0, 1) = shape1[contPt].y;
            therow.at<float>(0, 2) = 1;
            therow.row(0).copyTo(matM.row(ii));
            matP.at<float>(ii, 0) = shape2[contPt].x;
        }
        else
        {
            therow.at<float>(0, 3) = shape1[contPt].x;
            therow.at<float>(0, 4) = shape1[contPt].y;
            therow.at<float>(0, 5) = 1;
            therow.row(0).copyTo(matM.row(ii));
            matP.at<float>(ii, 0) = shape2[contPt].y;
            contPt++;
        }
    }
    Mat sol;
    solve(matM, matP, sol, DECOMP_SVD);
    out = sol.reshape(0, 2);
}
else
{
    Mat matM(siz, 4, CV_32F);
    Mat matP(siz, 1, CV_32F);
    int contPt = 0;
    for (int ii = 0; ii<siz; ii++)
    {
        Mat therow = Mat::zeros(1, 4, CV_32F);
        if (ii % 2 == 0)
        {
            therow.at<float>(0, 0) = shape1[contPt].x;
            therow.at<float>(0, 1) = shape1[contPt].y;
            therow.at<float>(0, 2) = 1;
            therow.row(0).copyTo(matM.row(ii));
            matP.at<float>(ii, 0) = shape2[contPt].x;
        }
        else
        {
            therow.at<float>(0, 0) = -shape1[contPt].y;
            therow.at<float>(0, 1) = shape1[contPt].x;
            therow.at<float>(0, 3) = 1;
            therow.row(0).copyTo(matM.row(ii));
            matP.at<float>(ii, 0) = shape2[contPt].y;
            contPt++;
        }
    }
    Mat sol;
    solve(matM, matP, sol, DECOMP_SVD);
    out.at<float>(0, 0) = sol.at<float>(0, 0);
    out.at<float>(0, 1) = sol.at<float>(1, 0);
    out.at<float>(0, 2) = sol.at<float>(2, 0);
    out.at<float>(1, 0) = -sol.at<float>(1, 0);
    out.at<float>(1, 1) = sol.at<float>(0, 0);
    out.at<float>(1, 2) = sol.at<float>(3, 0);
}
return out;

}

得出来的6个double类型的参数,就是我们此次标定最终得到的标定参数了。

之后我们把检测得到的图像坐标(t_px,t_py)代入,就可以得到与之相对应的机械手坐标(t_rx,t_ry)

t_rx= (A * t_px) + B * t_py + C);
t_ry= (D * t_px) + E * t_py+ F);

至此标定结束,我们可以控制相机拍照进行定位,然后转换成机械手坐标。

函数estimateRigidTransform 定义如下:
Mat estimateRigidTransform(InputArraysrc,InputArraydst,boolfullAffine)
前两个参数,可以是 :src=srcImage (变换之前的图片Mat) dst=transImage(变换之后的图片Mat)
也可以: src=array(变换之前的关键点Array) dst=array(变换之后的关键点Array)
第三个参数: 1(全仿射变换,包括:rotation, translation, scaling,shearing,reflection)
其主要原理为:如果我们有一个点变换之前是[x,y,1],变换后是[x’,y’,1] 则fullAffine表示如下:

在这里插入图片描述
TX=Y
在这里插入图片描述
展开后表示
在这里插入图片描述
如果我们想求这【a-f】 6个变量需要有6个方程,也就是3组点。但是比三个点多呢?
比如:20个点。那就是用最小方差。
在这里插入图片描述

在完成上述9点标定的基础上:
带动mark点在相机视野范围内旋转3次(机械手初始位置),拍摄3张mark点图像,拟合出实际旋转中心点坐标(像素坐标、机械坐标);
在这里插入图片描述

12点标定使用步骤(旋转+平移)

1、在初始位置旋转角度θ使得夹爪与工件姿态相同(θ是通过图像处理得到);

2、旋转。vector_angle_to_rigid(R.x, R.y, 0, R.x, R.y, θ, HomMat2D),都是像素坐标,HomMat2D是夹爪旋转的仿射变换矩阵;

3、旋转。夹爪中心旋转前后坐标变换:C’=HomMat2D * C,affine_trans_point_2d(HomMat2D, C.x, C.y, C’.x, C’.y)旋转中心像素坐标可以通过图像得到;

4、平移。工件中心像素坐标P通过图像处理可得,并用9点标定获得机械坐标。vector_angle_to_rigid(C’.x, C’.y, 0, P.x, P.y, 0, HomMat2D), 这里函数内都是机械坐标。

5、平移。

在这里插入图片描述

  • 7
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

MK-ZHENG

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值