精华内容
下载资源
问答
  • 手眼标定

    千次阅读 多人点赞 2018-12-31 11:45:35
    这里标定不仅包括摄像头标定,也包括机器人系统的手眼标定。这里的构型为Eye-in-hand。 1、摄像机标定 (1)理论 张正友棋盘标定法对摄像机进行标定。 由于摄像机标定结果要用到后面的手眼标定中...

    转载请注明出处 https://blog.csdn.net/wanggao_1990/article/details/81435660

    机器人视觉系统中要实现像素坐标与实际坐标的转换,首先要进行标定。这里标定不仅包括摄像头标定,也包括机器人系统的手眼标定。这里的构型为Eye-in-hand。
    这里写图片描述

    1、摄像机标定

    (1)理论

    张正友棋盘标定法对摄像机进行标定。
    由于摄像机标定结果要用到后面的手眼标定中,棋盘图片拍摄时需要遵守:标定板固定位置不动,手眼组合体变换姿态拍摄图片
    目的:两组坐标系的两两转化矩阵:T1T_1T2T_2
    1) 摄像机坐标系C转化为图片像素坐标系P的转换矩阵T1

    P=T1CP=T_1*C

    T1在摄像机标定中内参矩阵3*3
    2) 棋盘世界坐标系G转化为摄像机坐标系C的转换矩阵T2

    C=T2GC=T_2*G

    T2在摄像机标定中外参矩阵4*4,由旋转矩阵r33和平移向量t31构成[ t r; 0 0 0 1]

    (2) 方法

    OpenCv 或者 Matlab

    2、手眼标定

    (1)理论部分

    手眼标定目的:得到机器手坐标系H转化为摄像机坐标系CC的转化矩阵T3T_3。可表示为:

    C=T3HC=T_3*H

    T3需要根据公式AX=XBAX=XB得到;实际中,分别知道AABB求出来的X有无穷多个解。所以为了实现唯一解至少需要两组AABB,即至少需要3个位置的摄像机标定结果。
    下图为AX=XBAX=XB图解,棋盘相对机器人基坐标系固定,因此有

    B1X1A1=B2X1A2B_1*X^{-1}*A_1 = B_2*X^{-1}*A_2

    转换A2A11X=XB21B1A_2*A_1^{-1}*X = X*B_2^{-1}*B_1

    可令 A=A2A11A= A_2*A_1^{-1}B=B21B1B=B_2^{-1}*B_1
    在这里插入图片描述

    1) AA的求法

    AA是两个摄像机坐标系之间的变换矩阵。假设上述摄像机标定中有3张标定图片的外参标定结果分别是Hc1H_{c1}Hc2H_{c2}Hc3H_{c3},那么可以得到两个AA矩阵:

    A1=Hc2Hc11A_1=H_{c2}*H_{c1}^{-1}

    A2=Hc3Hc21A_2=H_{c3}*H_{c2}^{-1}

    2) BB的求法

    B是两个机器手坐标系之间的变换矩阵。假设上述摄像机标定中的3张标定图片所一一对应的机器手坐标系在基坐标系(也可以是工件坐标系或者其他固定的参考坐标系)中的坐标系描述矩阵结果分别是Hg1H_{g1}Hg2H_{g2}Hg3H_{g3}(需要从机器人控制器或示教器中读取或转换),那么可以得到两个BB矩阵:

    B1=Hc21Hc1B_1=H_{c2}^{-1}*H_{c1}

    B2=Hc31Hc2B_2=H_{c3}^{-1}*H_{c2}

    由以上两组AABB,代入AX=XBAX=XB就可以得到唯一解X,从而T3=XT_3=X
    至少2组数据,可以进行求解,参考Navy_HandEyeTsai_HandEye等方法。

    (2)计算过程

    1. 获取摄像机标定已知摄像机外参矩阵Hc1H_{c1}Hc2H_{c2}Hc3H_{c3}(摄像机标定时已有)

    2. 从机器人控制器中读取对应的机械手坐标末端姿态描述数据
      机器人系统的坐标系描述数据可能有2种形式:
      - 平移向量+欧拉角: x,y,z,rz,ry,rzx, y, z, r_z, r_y, r_z , 需要知道转换关系.
      - 平移向量+四元数模式: x,y,z,qw,qx,qy,qzx, y, z, q_w, q_x, q_y, q_z,唯一转换。
      推荐选择四元数模式,转换成旋转矩阵方式唯一,如下

      R=[2(qw2+qx2)12(qxqyqwqz)2(qxqz+qwqx)2(qxqy+qwqz)2(qw2+qy2)12(qyqzqwqx)2(qxqz+qwqx)2(qyqz+qwqx)2(qw2+qz2)1]R = \begin{bmatrix} 2(q_w^2+q_x^2)-1 & 2(q_xq_y - q_wq_z) & 2(q_xq_z+q_wq_x) \\ 2(q_xq_y+q_wq_z) & 2(q_w^2 + q_y^2)-1 & 2(q_yq_z-q_wq_x) \\ 2(q_xq_z+q_wq_x) & 2(q_yq_z + q_wq_x) & 2(q_w^2 + q_z^2)-1\\ \end{bmatrix}

    3. 求解摄像机坐标系到机械手坐标系的变换矩阵X
      例如采集图像25幅,进行相机标定,可得到25个外参矩阵,同时对应可以从控制器界面读取或者转换得到25个姿态矩阵。接着可以利用 1) 2)得到C252C_{25}^2AABB,再利用AX=XBAX=XB计算得到XX

    标题3、根据标定结果对目标定位

    ##(1)理论部分
    由上述1、2标定得到:

    矩阵 含义 说明
    HpcH_{pc} 内参矩阵 摄像机坐标系C -> 像素坐标系P
    HcgH_{cg} 手眼矩阵 机械手坐标系H -> 摄像机坐标系C
    HgH_g 从控制器读取的末端姿态矩阵 机械手坐标系H -> 基坐标系B

    基坐标系转化为像素坐标系的变换矩阵,可由下面两式求解(注意使用齐次坐标系)。

    zc[u,v,1]T=Hpc[xc,yc,zc]Tz_c* [u,v,1]^{T} = H_{pc} * [x_c,y_c,z_c]^{T}

    [x,y,z]T=HgHcg1[xc,yc,zc]T[x,y,z]^{T} = H_g * H_{cg}^{-1}*[x_c,y_c,z_c]^{T}


    未完 …

    win10,vs2015。 linux下使用有几个小错误或警告地方,根据编译器输出略作修改。
    源码 https://github.com/wanggao1990/HandEyeCalibration, 有用烦请star一下。
    新浪演示视频地址 http://t.cn/RDW1sAf

    opencv4.1 也加入了手眼标定,可以尝试。

    展开全文
  • 一、背景 ...简单来说手眼标定的目的就是获取机器人坐标系和相机坐标系的关系,最后将视觉识别的结果转移到机器人坐标系下。 手眼标定行业内分为两种形式,根据相机固定的地方不同,如果相机和机器...

    一、背景

    Calibration是机器人开发者永远的痛。虽然说方法说起来几十年前就有,但每一个要用摄像头的人都还是要经过一番痛苦的踩坑,没有轻轻松松拿来就效果好的包。其实人类不就是个手眼协调的先进“机器人”吗,O(∩_∩)O哈哈~

    机器人视觉应用中,手眼标定是一个非常基础且关键的问题。简单来说手眼标定的目的就是获取机器人坐标系和相机坐标系的关系,最后将视觉识别的结果转移到机器人坐标系下。

    手眼标定行业内分为两种形式,根据相机固定的地方不同,如果相机和机器人末端固定在一起,就称之为“眼在手”(eye in hand),如果相机固定在机器人外面的底座上,则称之为“眼在外”(eye to hand)。 

    eye to hand
    眼在外
    eye in hand
    ​​​​​​眼在手

    二、手眼关系的数学描述

    1. eye in hand,这种关系下,两次运动,机器人底座和标定板的关系始终不变。求解的量为相机和机器人末端坐标系的位姿关系。

     

    2. eye to hand,这种关系下,两次运动,机器人末端和标定板的位姿关系始终不变。求解的量为相机和机器人底座坐标系之间的位姿关系。

     

    三、AX = XB问题的求解

    旋转和平移分步法求解: 

    • Y. Shiu, S. Ahmad Calibration of Wrist-Mounted Robotic Sensors by Solving Homogeneous Transform Equations of the Form AX = XB. In IEEE Transactions on Robotics and Automation, 5(1):16-29, 1989. 
    • R. Tsai, R. Lenz A New Technique for Fully Autonomous and Efficient 3D Robotics Hand/Eye Calibration. In IEEE Transactions on Robotics and Automation, 5(3):345-358, 1989. 

    迭代求解及相关资料可以看看相关网上的英文教程 http://math.loyola.edu/~mili/Calibration/index.html其中也有一些AX= XB的matlab代码可以使用。  

    ROS 下也有相关的一些package可以利用

    https://github.com/IFL-CAMP/easy_handeye

    http://wiki.ros.org/handeye

    https://visp-doc.inria.fr/doxygen/visp-daily/tutorial-calibration-extrinsic.html

    http://campar.in.tum.de/Chair/HandEyeCalibration TUM这里也有很多手眼标定求解的参考链接

    四、其他参考资料

    https://mp.weixin.qq.com/s/nJx1dlpBXaL2_iT_J4W5Kg 邱强Flyqq 微信文章

    https://blog.csdn.net/u011089570/article/details/47945733 图不错

    https://blog.csdn.net/qq_16481211/article/details/79764730 部分halcon代码

    https://blog.csdn.net/qq_16481211/article/details/79767100 halcon代码

    https://blog.csdn.net/happyjume/article/details/80847822 部分原理

    https://blog.csdn.net/zhang970187013/article/details/81098175 UR5 与easy hand eye

    一般用“两步法”求解基本方程,即先从基本方程上式求解出旋转部分,再代入求解出平移部分。

    https://blog.csdn.net/yunlinwang/article/details/51622143

    ============== Halcon 官方示例-手眼标定 ==================

    五、Matlab下手眼标定解算

    相机与机器人是eye-to-hand模式,机器人为加拿大Kinova 6轴机械臂,机器人pose为基座相对于末端的x,y,z,rx,ry,rz,rw, 单位为米。姿态使用单位四元数表示。

    2017.08.29Kinova_pose_all_8_1.txt

    0.4285232296,0.1977596461,-0.5595823047,0.4738016082,0.3209333657,-0.2268795901,-0.7880605703
    0.2265712272,0.0261943502,-0.6661910850,0.5222343809,0.0461668455,-0.1315038186,-0.8413362107
    0.1058885008,0.0527681997,-0.7419267756,0.5081638565,-0.0263721340,-0.1696570449,-0.8439730402
    0.1127224767,-0.0420359377,-0.7413071786,0.5101706595,-0.0322252464,-0.1864402870,-0.8390038445
    0.2592932751,-0.0329068529,-0.7162865014,0.5101641882,0.1265739325,-0.0153077050,-0.8505746380
    -0.1724239544,0.0084144761,-0.6998332592,0.4989369998,-0.0973210400,-0.1244194561,-0.8521210503
    -0.0534271258,0.0771779706,-0.6845820453,0.5195499916,0.0511217081,-0.1691595167,-0.8359661686
    0.2598500029,0.0933230213,-0.6638257382,0.5673912325,0.1686973705,-0.1427337629,-0.7932436318

    pattern pose为标定板相对于相机的x,y,z,rx,ry,rz,rw, 单位为米。姿态使用单位四元数表示。

    2017.08.29Pattern_pose_all_8_1.txt

    0.4277459616,-0.0267754900,0.0822384718,0.3555198802,-0.2313832954,-0.6738796808,-0.6049409568
    0.4610891450,-0.0089776615,0.0394863697,-0.4168581229,0.4389318994,0.4953132086,0.6230833961
    0.4159190432,0.0112235849,0.0112504715,-0.3559197420,0.4898053987,0.4542453721,0.6535081871
    0.4143974465,0.0094036803,0.1073298638,-0.3438949365,0.4875942762,0.4507613906,0.6639294113
    0.3473267442,-0.1866610227,0.0651366494,0.4966101920,-0.4291949558,-0.5361047392,-0.5308123169
    0.3883165305,-0.2249779584,0.0093256602,-0.3595692001,0.5529429756,0.4090559739,0.6305848604
    0.4309364801,-0.2354261800,-0.0925819097,-0.3831460128,0.4244707870,0.5043032275,0.6470718186
    0.4362292324,-0.0778107597,-0.1007970399,0.4568889439,-0.3144470665,-0.5302104578,-0.6412896427
    

    此Matlab文件调用数据进行离线解算。http://math.loyola.edu/~mili/Calibration/index.html 的这部分Registering Two Sets of 6DoF Data with 1 Unknown,有code下载,下载好命名为shiu.m和tsai.m供下面程序调用就行。我这里贴出

    tsai.m

    function X = tsai_wechat(A,B)
    % Calculates the least squares solution of
    % AX = XB
    % 
    % A New Technique for Fully Autonomous 
    % and Efficient 3D Robotics Hand/Eye Calibration
    % Lenz Tsai
    %
    % Mili Shah
    % July 2014
    
    [m,n] = size(A); n = n/4;
    S = zeros(3*n,3);
    v = zeros(3*n,1);
    %Calculate best rotation R
    for i = 1:n
        A1 = logm(A(1:3,4*i-3:4*i-1)); 
        B1 = logm(B(1:3,4*i-3:4*i-1));
        a = [A1(3,2) A1(1,3) A1(2,1)]'; a = a/norm(a);
        b = [B1(3,2) B1(1,3) B1(2,1)]'; b = b/norm(b);
        S(3*i-2:3*i,:) = skew(a+b);
        v(3*i-2:3*i,:) = a-b;
    end
    x = S\v;
    theta = 2*atan(norm(x));
    x = x/norm(x);
    R = (eye(3)*cos(theta) + sin(theta)*skew(x) + (1-cos(theta))*x*x')';
    %Calculate best translation t
    C = zeros(3*n,3);
    d = zeros(3*n,1);
    I = eye(3);
    for i = 1:n
        C(3*i-2:3*i,:) = I - A(1:3,4*i-3:4*i-1);
        d(3*i-2:3*i,:) = A(1:3,4*i)-R*B(1:3,4*i);
    end
    t = C\d;
    %Put everything together to form X
    X = [R t;0 0 0 1];

    Jaco_handeye_test_10.m 测试程序中用到了Peter Corke老师的机器人工具箱。我的Matlab版本R2013a,利用机器人工具箱的一些转换函数(四元数的构建,欧拉角转换等),它安装和基本使用参考这里:https://blog.csdn.net/yaked/article/details/48933603

    clear;
    close all;
    clc;
     
    % Robot pose in Quatenion xyzw
    
     JacoCartesianPose = importdata('E:\\Matlab_work\\handeye\\yake_handeye\\2017.08.29Kinova_pose_all_8_1.txt');
     
     [m,n] = size(JacoCartesianPose); % 8* 7
    A = cell(1,m); % 1*8
     
    for i = 1: m
        
        robotHtool_qua = Quaternion([ JacoCartesianPose(i, 7), JacoCartesianPose(i, 4), JacoCartesianPose(i, 5) , JacoCartesianPose(i, 6)]) ; 
        A{1,i}  = transl(JacoCartesianPose(i, 1), JacoCartesianPose(i, 2), JacoCartesianPose(i, 3)) *  robotHtool_qua.T;
       
    end
     
    % Pattern Pose(Homogeneous) stored in  cell B.
    patternInCamPose = importdata('E:\\Matlab_work\\handeye\\yake_handeye\\2017.08.29Pattern_pose_all_8_1.txt');
     
    [melem,nelem] = size(patternInCamPose); % 8*7
    B=cell(1,melem);
    for x=1: melem
        camHmarker_qua = Quaternion([ patternInCamPose(x, 7) , patternInCamPose(x, 4), patternInCamPose(x, 5) , patternInCamPose(x, 6)])    ;
        B{1,x} = transl(patternInCamPose(x, 1), patternInCamPose(x, 2), patternInCamPose(x, 3)) *  camHmarker_qua.T;
    end
    %
    
    n2=m;
    TA=cell(1,n2);
    TB=cell(1,n2); 
     
    %--------------------- 8 -----------------------------------
    M1=[];
    M2=[];
     for j=[1: m-1]% 1-7.
     
         TA{1, j} = inv(A{1, j}) * A{1, j+1};    
         M1=[M1 TA{1, j}];
         
         TB{1, j} = B{1, j} * inv(B{1, j+1});
         M2=[M2 TB{1, j}];
         
     end 
     
     % M1=[TA{1,1} TA{1,2} TA{1,3} TA{1,4} TA{1,5} TA{1,6} TA{1,7} ];
     % M2=[TB{1,1} TB{1,2} TB{1,3} TB{1,4} TB{1,5} TB{1,6} TB{1,7} ];
    %--------------------- 8 -----------------------------------
     
    C_Tsai=tsai(M1, M2)
    T_Tsai =  (transl(C_Tsai))';
    C_Tsai_rad = tr2rpy(C_Tsai);
    C_Tsai_rpy_rx_ry_rz =rad2deg(C_Tsai_rad);
    Q_Tsai_Qxyzw=Quaternion(C_Tsai);
    fprintf('Tsai(rad) = \n%f, %f, %f, %f, %f, %f\n',T_Tsai(1,1), T_Tsai(1,2), T_Tsai(1,3), C_Tsai_rad(1,1), C_Tsai_rad(1,2), C_Tsai_rad(1,3));
    fprintf('Tsai(deg) = \n%f, %f, %f, %f, %f, %f\n\n',T_Tsai(1,1), T_Tsai(1,2), T_Tsai(1,3), C_Tsai_rpy_rx_ry_rz(1,1), C_Tsai_rpy_rx_ry_rz(1,2), C_Tsai_rpy_rx_ry_rz(1,3));
    fprintf('Tsai(Qxyzw) = \n %f, %f, %f, %f\n\n',  Q_Tsai_Qxyzw.v(1),  Q_Tsai_Qxyzw.v(2), Q_Tsai_Qxyzw.v(3), Q_Tsai_Qxyzw.s);

    稍微解释一下,程序做的就是读入机器人和相机的两两姿态信息,转换为4x4 的齐次变换矩阵,送入tsai.m程序求解。

    手眼标定eye in hand 和eye to hand 的区别主要是机器人那边,一个是end相对于base,另一个是base相对于end。千万注意。 

    ====================平面九点标定法====================

    当利用RGB相机或者是机器人只进行平面抓取(也即固定姿态抓取,机器人六自由度位置和姿态简化为只考虑平移,姿态人为给定并且固定,这时机器人可以移动到目标点上方),问题可以简化为平面RGB图像的目标像素点集A(x1,y1)与机器人在平面(X1,Y1)的点对关系。具体做法是相机识别像素点给到A,然后利用示教器查看机器人在基座标系下的坐标,当做B。

    相机坐标和机器人坐标写成齐次的形式,投影矩阵X是一个3x3的矩阵我们需要6组对应点来求解最小配置解。利用奇异值分解SVD来求取。

    D:\opencv_work\cubeSolver\cv_solver\ConsoleApplication1\CV_SVD.cpp

    D:\Matlab_work\handeye\NinePoints_Calibration.m

    //Solve equation:AX=b
    
    #include <cv.h>
    #include<opencv2/opencv.hpp>
    using namespace std;
    using namespace cv;
    
    int main(int argc, char** argv)
    {
    	printf("\nSolve equation:AX=b\n\n");
    
    	//Mat A = (Mat_<float>(6, 3) <<
    	//480.8, 639.4, 1,
    	//227.1, 317.5, 1,
    	//292.4, 781.6, 1,
    	//597.4, 1044.1, 1,
    	//557.7, 491.6, 1,
    	//717.8, 263.7, 1
    	//		 );// 4x3
    
    	//Mat B = (Mat_<float>(6, 3) <<
    	//197170, 185349, 1,
    	//195830, 186789, 1,
    	//196174, 184591, 1,
    	//197787, 183176, 1,
    	//197575, 186133, 1,
    	//198466, 187335, 1
    	//		 );
    
    	Mat A = (Mat_<float>(4, 3) << 
    	2926.36, 2607.79, 1, 
    	587.093, 2616.89, 1, 
    	537.031, 250.311, 1, 
    	1160.53, 1265.21, 1);// 4x3
    
    	Mat B = (Mat_<float>(4, 3) << 
    	320.389, 208.197, 1,
    	247.77, 209.726, 1,
    	242.809, 283.182, 1,
    	263.152, 253.715, 1);
    
    
    	Mat X;
    	cout << "A=" << endl << A << endl;
    	cout << "B=" << endl << B << endl;
    
    	solve(A, B, X, CV_SVD);
    
    	cout << "X=" << endl << X << endl;
    	Mat a1 = (Mat_<float>(1, 3) << 1864, 1273, 1);
    	Mat b1 = a1*X;
    	cout << "b1=" << endl << b1 << endl;
    	cout << "真实值为:" << "283.265, 253.049, 1" << endl;
    
    
    	getchar();
    
    	return 0;
    }
    

    https://docs.opencv.org/3.1.0/d2/de8/group__core__array.html#ga12b43690dbd31fed96f213eefead2373

    结果对比:左halcon C#(第三列为0,0,1,没做显示),右opencv c++,底下为Matlab结果,三者一致,算法检测通过。

    =============

    https://blog.csdn.net/Stones1025/article/details/100085338

    这种方法利用点对,求仿摄变换矩阵 

    #include "pch.h"
    #include <iostream>
    
    #include <cv.h>
    #include<opencv2/opencv.hpp>
    
    using namespace cv;
    
    
    int main(int argc, char** argv)
    {
    	printf("\nSolve equation:AX=b\n\n");
    
    	Point2f srcTri[3];
    	srcTri[0] = Point2f(480.8f, 639.4f);
    	srcTri[1] = Point2f(227.1f, 317.5f);
    	srcTri[2] = Point2f(292.4f, 781.6f);
    
    	Point2f dstTri[3];
    	dstTri[0] = Point2f(197170.0f, 185349.0f);
    	dstTri[1] = Point2f(195830.0f, 186789.0f);
    	dstTri[2] = Point2f(196174.0f, 184591.0f);
    
    	Mat warp_mat = getAffineTransform(srcTri, dstTri);
    	// 计算图像坐标
    	std::cout << "img_corners:\n" << srcTri << std::endl;
    	std::cout << "robot_corners:\n" << dstTri << std::endl;
    	std::cout << warp_mat << std::endl;
    
    	//
    	//[5.284835627018051, -0.00236967559639317, 194630.5662011061;
    	//0.4056177440315953, -4.793119669651512, 188218.6997054448]
    
    	return 0;
    }

    ================= Eye in hand 数据及Ground truth ========================= 

    Marker in Camera 八组数据,单位:米及弧度,姿态用的是RotVector表示

    0.11725,0.05736,0.32176,0.02860,-0.1078,3.06934
    0.07959,0.06930,0.29829,0.66126,-0.2127,2.99042
    0.14434,0.03227,0.40377,-0.9465,-0.1744,2.80668
    0.11008,0.05605,0.35730,0.12422,-1.0665,2.87604
    0.1131,0.0438171,0.299624,-0.058278,-0.514154,-3.06309
    0.057039,0.109464,0.415275,0.414777,0.750109,-2.49495
    0.13702,0.00520,0.38190,0.26431,-0.7554,2.26885
    -0.03670,-0.04433,0.237292,0.807501,-0.27347,0.614594

    Robot end-effector in Base 八组数据,单位:米及弧度,姿态用的是RotVector表示

    0.400422,-0.0262847,0.365594,3.12734,-0.0857978,-0.0168582
    0.423347,-0.16857,0.340184,-2.73844,0.089013,0.123284
    0.42540,0.19543,0.29062,2.44351,-0.1777,-0.0722
    0.58088,-0.0541,0.32633,-2.9303,0.06957,0.98985
    0.2760,-0.033,0.3301,3.0813,0.0724,0.6077
    0.54011,0.09071,0.34623,2.62279,0.75876,-0.6927
    0.57732,-0.1346,0.37990,-2.6764,1.04868,0.82177
    0.27844,-0.1371,0.28799,-0.8233,2.26319,0.36110

    Ground truth:Camera in end-effector

    Calibration Result:

    -0.005816946773 -0.997849042178 -0.065295115862 0.053457653403

    0.999355366046 -0.003487653357 -0.035730779845 -0.009463725064

    0.035426197714 -0.065460868457 0.997226082298 -0.045113271057

    0 0 0 1

     

    RotVector(rad): -0.023440 -0.079412 1.57466

    AngVector(rad): 1.576836 <-0.014865, -0.050362, 0.998620>

    AngVector(deg): 90.346026 <-0.014865, -0.050362, 0.998620>

    rx_ry_rz_rw: -0.010543 -0.079412 1.574660 0.704968

    rx_ry_rz_rw_norm: -0.010543 -0.035718 0.708260 0.704968

    交流QQ群:876693529

    展开全文
  • Opencv4实现手眼标定及手眼系统测试(一)前言程序环境原理如何改变文本的样式插入链接与图片如何插入一段漂亮的代码片生成一个适合你的列表创建一个表格设定内容居中、居左、居右SmartyPants创建一个自定义列表如何...

    前言

    由于项目需要,要在win10环境下实现“眼在手上”的手眼系统,为此查阅了不少资料。但大多是理论资料,或者不可用的代码。虽然本人基于Halcon 12.0实现了手眼标定,但代码太冗余,效率低。因此本人拟通过Opencv4实现手眼标定。
    (第一次写博客,不足之处敬请批评指正!)

    1 程序环境

    1. 编译器:Visual Studio 2015;
    2. Opencv版本:Opencv3.4.6、或Opencv4以上版本;

    2 原理

    (1)主要使用Opencv的calibrateHandEye()函数,其中包括五种方法,相关文献见链接link
    (2)本代码主要根据Tsai两步法进行,利用公式AX=XB,先计算旋转矩阵R,再计算平移矩阵T。
    (3)Tsai两步法的原理可参考博主一“手眼标定”: link;博主二“机械臂的手眼标定 opencv实现”:link.

    3 程序源码

    主要参考github代码1: link;github代码2:link.
    本人整理优化后的源码如下:

    /***********************************************************************
    说明:Opencv4实现手眼标定及手眼测试
    作者:jian xu @CUG
    日期:2020年01月08日
    ***********************************************************************/
    
    #include <iostream>
    #include <string>
    
    #include <opencv2/opencv.hpp>
    #include <opencv2/core.hpp>
    #include <opencv2/imgproc.hpp>
    #include <opencv2/calib3d.hpp>
    
    //导入静态链接库
    #if (defined _WIN32 || defined WINCE || defined __CYGWIN__)
    #define OPENCV_VERSION "412"
    #pragma comment(lib, "opencv_world" OPENCV_VERSION ".lib")
    
    #endif
    
    using namespace cv;
    using namespace std;
    
    //相机中13组标定板的位姿,x,y,z,rx,ry,rz,
    Mat_<double> CalPose = (cv::Mat_<double>(13, 6) <<
    	-0.072944147641399, -0.06687830562048944, 0.4340418493881254, -0.2207496117519063, 0.0256862005614321, 0.1926014162476009,
    	-0.01969337858360518, -0.05095294728651902, 0.3671266719105768, 0.1552099329677287, -0.5763323472739464, 0.09956130526058841,
    	0.1358164536530692, -0.1110802522656379, 0.4001396735998251, -0.04486168331242635, -0.004268942058870162, 0.05290073845562016,
    	0.1360676260120161, -0.002373036366121294, 0.3951670952829301, -0.4359637938379769, 0.00807193982932386, 0.06162504121755787,
    	-0.1047666520352697, -0.01377729010376614, 0.4570029374109721, -0.612072103513551, -0.04939465180949879, -0.1075464055169537,
    	0.02866460103085085, -0.1043911269729344, 0.3879127305077527, 0.3137563103168434, -0.02113958397023016, 0.1311397970432597,
    	0.1122741829392126, 0.001044006395747612, 0.3686697279333643, 0.1607160803445018, 0.2468677059920437, 0.1035103912091547,
    	-0.06079521129779342, -0.02815190820828123, 0.4451740202390909, 0.1280935541917056, -0.2674407142401368, 0.1633865613363686,
    	-0.02475533256363622, -0.06950841248698086, 0.2939836207787282, 0.1260629671933584, -0.2637748974005461, 0.1634102148863728,
    	0.1128618887222624, 0.00117877722121125, 0.3362496409334229, 0.1049541359309871, -0.2754352318773509, 0.4251492928748009,
    	0.1510545750008333, -0.0725019944548204, 0.3369908269102371, 0.2615745097093249, -0.1295598776133405, 0.6974394284203849,
    	0.04885313290076512, -0.06488755216394324, 0.2441532410787161, 0.1998243391807502, -0.04919417529483511, -0.05133193756053007,
    	0.08816140480523708, -0.05549965109057759, 0.3164905645998022, 0.164693654482863, 0.1153894876338608, 0.01455551646362294);
    
    //机械臂末端13组位姿,x,y,z,rx,ry,rz
    Mat_<double> ToolPose = (cv::Mat_<double>(13, 6) <<
    	-0.3969707, -0.460018, 0.3899877, 90.2261, -168.2015, 89.7748,
    	-0.1870185, -0.6207147, 0.2851157, 57.2636, -190.2034, 80.7958,
    	-0.1569776, -0.510021, 0.3899923, 90.225, -178.2038, 81.7772,
    	-0.1569787, -0.5100215, 0.3299975, 90.2252, -156.205, 81.7762,
    	-0.3369613, -0.4100348, 0.3299969, 90.2264, -146.2071, 71.778,
    	-0.2869552, -0.6100449, 0.4299998, 90.2271, -199.2048, 86.7806,
    	-0.2869478, -0.6600489, 0.4299948, 105.2274, -189.2053, 86.7814,
    	-0.286938, -0.6300559, 0.4299997, 75.2279, -189.2056, 86.783,
    	-0.2869343, -0.5700635, 0.2800084, 75.2291, -189.2055, 86.7835,
    	-0.1669241, -0.5700796, 0.280015, 75.2292, -189.205, 101.7845,
    	-0.236909, -0.4700997, 0.3600046, 87.2295, -196.2063, 118.7868,
    	-0.2369118, -0.6201035, 0.2600001, 87.2297, -192.2087, 75.7896,
    	-0.2468983, -0.620112, 0.359992, 97.2299, -190.2082, 80.7908);
    
    //R和T转RT矩阵
    Mat R_T2RT(Mat &R, Mat &T)
    {
    	Mat RT;
    	Mat_<double> R1 = (cv::Mat_<double>(4, 3) << R.at<double>(0, 0), R.at<double>(0, 1), R.at<double>(0, 2),
    												R.at<double>(1, 0), R.at<double>(1, 1), R.at<double>(1, 2),
    												R.at<double>(2, 0), R.at<double>(2, 1), R.at<double>(2, 2),
    												0.0, 0.0, 0.0);
    	cv::Mat_<double> T1 = (cv::Mat_<double>(4, 1) << T.at<double>(0, 0), T.at<double>(1, 0), T.at<double>(2, 0),1.0);
    
    	cv::hconcat(R1, T1, RT);//C=A+B左右拼接
    	return RT;
    }
    
    //RT转R和T矩阵
    void RT2R_T(Mat &RT, Mat &R, Mat &T)
    {
    	cv::Rect R_rect(0, 0, 3, 3);
    	cv::Rect T_rect(3, 0, 1, 3);
    	R = RT(R_rect);
    	T = RT(T_rect);
    }
    
    //判断是否为旋转矩阵
    bool isRotationMatrix(const cv::Mat & R)
    {
    	cv::Mat tmp33 = R({ 0,0,3,3 });
    	cv::Mat shouldBeIdentity;
    
    	shouldBeIdentity = tmp33.t()*tmp33;
    
    	cv::Mat I = cv::Mat::eye(3, 3, shouldBeIdentity.type());
    
    	return  cv::norm(I, shouldBeIdentity) < 1e-6;
    }
    
    /** @brief 欧拉角 -> 3*3 的R
    *	@param 	eulerAngle		角度值
    *	@param 	seq				指定欧拉角xyz的排列顺序如:"xyz" "zyx"
    */
    cv::Mat eulerAngleToRotatedMatrix(const cv::Mat& eulerAngle, const std::string& seq)
    {
    	CV_Assert(eulerAngle.rows == 1 && eulerAngle.cols == 3);
    
    	eulerAngle /= 180 / CV_PI;
    	cv::Matx13d m(eulerAngle);
    	auto rx = m(0, 0), ry = m(0, 1), rz = m(0, 2);
    	auto xs = std::sin(rx), xc = std::cos(rx);
    	auto ys = std::sin(ry), yc = std::cos(ry);
    	auto zs = std::sin(rz), zc = std::cos(rz);
    
    	cv::Mat rotX = (cv::Mat_<double>(3, 3) << 1, 0, 0, 0, xc, -xs, 0, xs, xc);
    	cv::Mat rotY = (cv::Mat_<double>(3, 3) << yc, 0, ys, 0, 1, 0, -ys, 0, yc);
    	cv::Mat rotZ = (cv::Mat_<double>(3, 3) << zc, -zs, 0, zs, zc, 0, 0, 0, 1);
    
    	cv::Mat rotMat;
    
    	if (seq == "zyx")		rotMat = rotX*rotY*rotZ;
    	else if (seq == "yzx")	rotMat = rotX*rotZ*rotY;
    	else if (seq == "zxy")	rotMat = rotY*rotX*rotZ;
    	else if (seq == "xzy")	rotMat = rotY*rotZ*rotX;
    	else if (seq == "yxz")	rotMat = rotZ*rotX*rotY;
    	else if (seq == "xyz")	rotMat = rotZ*rotY*rotX;
    	else {
    		cv::error(cv::Error::StsAssert, "Euler angle sequence string is wrong.",
    			__FUNCTION__, __FILE__, __LINE__);
    	}
    
    	if (!isRotationMatrix(rotMat)) {
    		cv::error(cv::Error::StsAssert, "Euler angle can not convert to rotated matrix",
    			__FUNCTION__, __FILE__, __LINE__);
    	}
    
    	return rotMat;
    	//cout << isRotationMatrix(rotMat) << endl;
    }
    
    /** @brief 四元数转旋转矩阵
    *	@note  数据类型double; 四元数定义 q = w + x*i + y*j + z*k
    *	@param q 四元数输入{w,x,y,z}向量
    *	@return 返回旋转矩阵3*3
    */
    cv::Mat quaternionToRotatedMatrix(const cv::Vec4d& q)
    {
    	double w = q[0], x = q[1], y = q[2], z = q[3];
    
    	double x2 = x * x, y2 = y * y, z2 = z * z;
    	double xy = x * y, xz = x * z, yz = y * z;
    	double wx = w * x, wy = w * y, wz = w * z;
    
    	cv::Matx33d res{
    		1 - 2 * (y2 + z2),	2 * (xy - wz),		2 * (xz + wy),
    		2 * (xy + wz),		1 - 2 * (x2 + z2),	2 * (yz - wx),
    		2 * (xz - wy),		2 * (yz + wx),		1 - 2 * (x2 + y2),
    	};
    	return cv::Mat(res);
    }
    
    /** @brief ((四元数||欧拉角||旋转向量) && 转移向量) -> 4*4 的Rt
    *	@param 	m				1*6 || 1*10的矩阵  -> 6  {x,y,z, rx,ry,rz}   10 {x,y,z, qw,qx,qy,qz, rx,ry,rz}
    *	@param 	useQuaternion	如果是1*10的矩阵,判断是否使用四元数计算旋转矩阵
    *	@param 	seq				如果通过欧拉角计算旋转矩阵,需要指定欧拉角xyz的排列顺序如:"xyz" "zyx" 为空表示旋转向量
    */
    cv::Mat attitudeVectorToMatrix(cv::Mat& m,bool useQuaternion, const std::string& seq)
    {
    	CV_Assert(m.total() == 6 || m.total() == 10);
    	if (m.cols == 1)
    		m = m.t();
    	cv::Mat tmp = cv::Mat::eye(4, 4, CV_64FC1);
    
    	//如果使用四元数转换成旋转矩阵则读取m矩阵的第第四个成员,读4个数据
    	if (useQuaternion)	// normalized vector, its norm should be 1.
    	{
    		cv::Vec4d quaternionVec = m({ 3, 0, 4, 1 });
    		quaternionToRotatedMatrix(quaternionVec).copyTo(tmp({ 0, 0, 3, 3 }));
    		// cout << norm(quaternionVec) << endl; 
    	}
    	else
    	{
    		cv::Mat rotVec;
    		if (m.total() == 6)
    			rotVec = m({ 3, 0, 3, 1 });		//6
    		else
    			rotVec = m({ 7, 0, 3, 1 });		//10
    
    		//如果seq为空表示传入的是旋转向量,否则"xyz"的组合表示欧拉角
    		if (0 == seq.compare(""))
    			cv::Rodrigues(rotVec, tmp({ 0, 0, 3, 3 }));
    		else
    			eulerAngleToRotatedMatrix(rotVec, seq).copyTo(tmp({ 0, 0, 3, 3 }));
    	}
    	tmp({ 3, 0, 1, 3 }) = m({ 0, 0, 3, 1 }).t();
    	//std::swap(m,tmp);
    	return tmp;
    }
    
    
    int main()
    {
    	//定义手眼标定矩阵
    	std::vector<Mat> R_gripper2base;
    	std::vector<Mat> t_gripper2base;
    	std::vector<Mat> R_target2cam;
    	std::vector<Mat> t_target2cam;
    	Mat R_cam2gripper = (Mat_<double>(3, 3));
    	Mat t_cam2gripper = (Mat_<double>(3, 1));
    
    	vector<Mat> images;
    	size_t num_images =13;//13组手眼标定数据
    
    	// 读取末端,标定板的姿态矩阵 4*4
    	std::vector<cv::Mat> vecHg, vecHc;
    	cv::Mat Hcg;//定义相机camera到末端grab的位姿矩阵
    	Mat tempR,tempT;
    
    	for (size_t i = 0; i < num_images; i++)//计算标定板位姿
    	{
    		cv::Mat tmp = attitudeVectorToMatrix(CalPose.row(i), false,""); //转移向量转旋转矩阵
    		vecHc.push_back(tmp);
    		RT2R_T(tmp, tempR, tempT);
    
    		R_target2cam.push_back(tempR);
    		t_target2cam.push_back(tempT);
    	}
    
    	for (size_t i = 0; i < num_images; i++)//计算机械臂位姿
    	{
    		cv::Mat tmp = attitudeVectorToMatrix(ToolPose.row(i), false, "xyz"); //机械臂位姿为欧拉角-旋转矩阵
    		vecHg.push_back(tmp);
    		RT2R_T(tmp, tempR, tempT);
    
    		R_gripper2base.push_back(tempR);
    		t_gripper2base.push_back(tempT);
    	}
    	//手眼标定,CALIB_HAND_EYE_TSAI法为11ms,最快
    	calibrateHandEye(R_gripper2base, t_gripper2base, R_target2cam, t_target2cam, R_cam2gripper, t_cam2gripper, CALIB_HAND_EYE_TSAI);
    
    	Hcg = R_T2RT(R_cam2gripper, t_cam2gripper);//矩阵合并
    
    	std::cout << "Hcg 矩阵为: " << std::endl;
    	std::cout << Hcg << std::endl;
    	cout<<"是否为旋转矩阵:"<< isRotationMatrix(Hcg) << std::endl << std::endl;//判断是否为旋转矩阵
    
    	//Tool_In_Base*Hcg*Cal_In_Cam,用第一组和第二组进行对比验证
    	cout << "第一组和第二组手眼数据验证:" << endl;
    	cout << vecHg[0] * Hcg*vecHc[0] << endl << vecHg[1] * Hcg * vecHc[1] << endl << endl;//.inv()
    
    	cout << "标定板在相机中的位姿:" << endl;
    	cout << vecHc[1] << endl;
    	cout << "手眼系统反演的位姿为:" << endl;
    	//用手眼系统预测第一组数据中标定板相对相机的位姿,是否与vecHc[1]相同
    	cout << Hcg.inv()* vecHg[1].inv()* vecHg[0] * Hcg*vecHc[0] << endl << endl;
    
    	cout << "----手眼系统测试----" << endl;
    	cout << "机械臂下标定板XYZ为:" << endl;
    	for (int i = 0; i < vecHc.size(); ++i)
    	{
    		cv::Mat cheesePos{ 0.0,0.0,0.0,1.0 };//4*1矩阵,单独求机械臂下,标定板的xyz
    		cv::Mat worldPos = vecHg[i] * Hcg*vecHc[i] * cheesePos;
    		cout << i << ": " << worldPos.t() << endl;
    	}
    	getchar();
    }
    

    4 程序输出

    Hcg 矩阵为:
    [0.008453865805544414, -0.9990489305401753, 0.04277577047200548, 0.07638705395676357;
     0.01898771468630467, 0.04292996492156131, 0.9988976347968919, -0.08206354381579613;
     -0.9997839760888014, -0.007632332432839339, 0.01933258021323825, -0.1311098155037144;
     0, 0, 0, 1]
    是否为旋转矩阵:1
    
    第一组和第二组手眼数据验证:
    [-0.9827262517458556, 0.1843060418844967, 0.01674506059741485, -0.4457787409127387;
     0.1838216085000257, 0.9825904904541024, -0.02693592986383497, -0.6886150828753749;
     -0.02141799192277936, -0.02339254141892869, -0.9994969027605635, 0.08284331808916068;
     0, 0, 0, 1]
    [-0.9824787510996111, 0.1850986855040433, 0.0217710877639008, -0.441259542992489;
     0.1843694850759817, 0.9823437476322023, -0.03175932084817229, -0.6894248600904512;
     -0.02726530048551778, -0.02718893364210982, -0.9992584076588193, 0.08159252779194137;
     0, 0, 0, 1]
    
    标定板在相机中的位姿:
    [0.8341198940795272, -0.1369737069955237, -0.5343053489276171, -0.01969337858360518;
     0.05021760995408052, 0.983511189246883, -0.1737352361401729, -0.05095294728651902;
     0.5492924484746335, 0.1180844791582967, 0.8272447411925057, 0.3671266719105768;
     0, 0, 0, 1]
    手眼系统反演的位姿为:
    [0.8373227680667449, -0.1343311553869149, -0.5299487925917564, -0.01514939120843839;
     0.05049903955148193, 0.9842033941157671, -0.1696865522526224, -0.05055927730944099;
     0.5443715909940763, 0.1153205085985169, 0.8308795046305798, 0.3684833472702467;
     0, 0, 0, 1]
    
    ----手眼系统测试----
    机械臂下标定板XYZ为:
    0: [-0.4457787409127387, -0.6886150828753749, 0.08284331808916068, 1]
    1: [-0.441259542992489, -0.6894248600904512, 0.08159252779194137, 1]
    2: [-0.4422208339914133, -0.6864831180641821, 0.08013587355743718, 1]
    3: [-0.4434900953499247, -0.6870035574330987, 0.07976634930332338, 1]
    4: [-0.4450797367777684, -0.6874268887433959, 0.08143249774989553, 1]
    5: [-0.4418885185926491, -0.6886217895474291, 0.07975714114113902, 1]
    6: [-0.4409575603391271, -0.6878847949543246, 0.07934926736540387, 1]
    7: [-0.4418588641213109, -0.6896930064251171, 0.08193358154038349, 1]
    8: [-0.4437857547008249, -0.6894962022042577, 0.08123334852187797, 1]
    9: [-0.4434430428709751, -0.6885152411299985, 0.08092995069314945, 1]
    10: [-0.4447502944365486, -0.6878924537730967, 0.08293636801126125, 1]
    11: [-0.4422290895104752, -0.6884201696013612, 0.07993373823200106, 1]
    12: [-0.4418946218302015, -0.6878072561612861, 0.07990104035206125, 1]
    

    5 数据分析

    使用excel绘制机械臂坐标系下标定板z方向的数据,获得下图曲线。之后使用excel的STDEV函数求均方差,得到z方向的误差为0.0012042,即±1.2mm的标定误差,满足项目需求。
    标定板z方向数据曲线

    6 总结

    (1)机械臂位姿类型有xyz,zyx,zyz几种,注意区分(可以找机械臂公司问)。
    (2)注意弄清楚手眼矩阵的方向!!!
    (3)采集标定数据时,标定板拍摄视角尽可能大,如此标定精度更高。

    7 其他

    后面将进行Opencv4五种方法的性能对比、halcon手眼标定及测试。望大家一起学习交流。

    (原创不易。若有用,请左下角点赞,谢谢!)

    展开全文
  • 手眼标定基于Tsai的两步法标定,是经典的Ax = xB 求解模型。Tsai的两步法标定是基于径向校正约束;第一步:利用最小二乘法求解线性方程组,得出相机的外参数;第二步:根据获得的相机外参数,求取相机的内参数;如果...

    手眼标定基于Tsai的两步法标定,是经典的Ax = xB 求解模型。

    Tsai的两步法标定是基于径向校正约束;第一步:利用最小二乘法求解线性方程组,得出相机的外参数;第二步:根据获得的相机外参数,求取相机的内参数;如果无透视畸变,可以使用一个线性方程求出。

    罗第6、7章;特别:P164-166)

    1.手眼标定就是对机械手和相机的位置关系进行标定,这样根据识别得到的像素位置去引导机械手去抓取。

    2.eye-in-hand:通过相机标定确定相机坐标系和世界坐标系之间的关系;即P&&R。这时如果知道相机坐标系和机械手基础坐标系之间的关系,即可得到物体在机械手坐标系中的坐标。

    3.eye-to-hand:通过相机标定确定相机坐标系和世界坐标系之间的关系;即P&&R;因为相机和机械手基础坐标系之间的关系是固定的,即:只要求出相机坐标系在基础坐标系中的位置,即可获得物体在机械手基础坐标系中的位置。

    ****************手眼标定执行流程

    **创建数据模型

    create_calib_data ('calibration_object', 1, 1, CalibDataID)

    ***设置相机参数

    set_calib_data_cam_param (CalibDataID, 0, 'area_scan_division', [])

    **设置标定板描述文件

    set_calib_data_calib_object (CalibDataID, 0, 'calplate.cpd')

    **循环读取标定板图像

    for index := 1 to 10 by 1

    read_image (Image, 'fabrik')

    *读取机械手法兰盘在基础坐标系中的位置姿态

    read_pose ('campose.dat', Pose)

    *将机械手法兰盘在基础坐标系中的位置姿态添加到标定数据模型中

    set_calib_data (CalibDataID, 'model', 'general', 'reference_camera', Pose)

    **获取标定对象,并添加到数据模型中

    find_calib_object (Image, CalibDataID, 0, 0, 0, [], [])

    *获得世界坐标系和相机坐标系的相对位置关系

    get_calib_data_observ_pose (CalibDataID, 0, 0, 0, ObjInCameraPose)

    endfor

    calibrate_hand_eye (CalibDataID, Errors)

    *获得机械手基础坐标系在摄像机坐标系下的坐标

    get_calib_data (CalibDataID, 'camera', 0, 'params', DataValue)

    **获得摄像机坐标系在机械手基础坐标系下的坐标

    pose_invert (ObjInCameraPose, PoseInvert)

    *根据摄像机在机械手基础坐标系下的姿态和目标在摄像机坐标系下的姿态,求解目标在摄像机基础坐标系下的姿态

    pose_compose (PoseInvert, PoseInvert, PoseCompose)

    展开全文
  • (5-III) 机器人手眼标定(手眼标定系列) 第一节:机器人标定介绍以及位姿 第二节位姿变换讲解 。。。。。 ...
  • 在(一)中已经介绍了线结构光标定,通过线结构光可以得到物体被照射激光...将相机三维坐标转化为机械臂坐标需要标定相机与机械臂之间的位置关系,这个标定就称为手眼标定手眼标定分为两种方式:眼在手上(eye in ha...
  • halcon 手眼标定资料

    2018-02-01 21:42:41
    halcon 手眼标定资料halcon 手眼标定资料halcon 手眼标定资料
  • 对于标定来说,一般常见的有用于测量的线性与非线性标定,另一类就是和运动相关的标定,手眼标定。勇哥写过许多epson机器人的手眼标定,也有halcon仿制这种方式的实现方法。但是halcon有自己的手眼标定。...
  • 机器人手眼标定.docx

    2020-01-03 15:29:14
    机器人手眼标定机器人手眼标定
  • 笔者最近两日在做一些手眼标定相关的工作,经过小半周的不懈学习,算是大致了解了目前使用最为广泛的Tsai方法的原理以及eye-to-hand和eye-in-hand的代码修改。之前在自学的过程中大多都是参考了网络上的博客,其中以...
  • 基于Halcon的工业机器人手眼标定方法研究田春林1,陈李博1,马国庆1,侯茂盛2,刘涛2【摘要】机器人手眼标定是机器人视觉技术的重要研究内容,基于Halcon所提供的标定板,充分考虑相机镜头径向畸变对手眼标定的影响...
  • 手眼标定 tsai

    2017-06-15 20:40:03
    手眼标定 tsai
  • 最近在学习手眼标定,做下笔记,和大家分享下学习经历:一 手眼标定的两种情形首先讲一下在工业应用中,手和眼(摄像机)的两种位置关系,第一种是将摄像机(眼)固定在机械手(手)上面,眼随手移动;第二种是摄像机(眼)...
  • 由于标定的传感器各异,好像没有特别通用的方法手眼标定法是标定摄像头与机械臂的一个经典方法,不过这个思想也适用于其他传感器,比如自动驾驶中激光雷达与摄像头之间的标定,比如东京大学的这篇工作《LiDAR and ...
  • 来源 · 知乎作者 ·张贶恩编辑 · 睿小妹原文·...笔者做过一些机器人手眼标定的工作,在此用尽量简单的语言来描述下机器人手眼标定的流程。本文的目的在于让大家对于相机标定有一个感性的认知,能够知道不...
  • 3D视觉 | 机器人3D手眼标定实验复现Cognex VisionPro 3D的大部分内容,涵盖眼在手外、眼在手上,包括相机标定、手眼标定、3D定位计算位移偏差。最后的位移偏差与Cognex的结果在1mm左右。— Edited By Hugo# 1用...
  • halcon手眼标定

    2017-11-08 11:31:42
    手眼标定实现,halcon案例,实现相机固定,机械手末端固定标定板
  • 干货分享3D视觉 | 机器人3D手眼标定实验复现Cognex VisionPro 3D的大部分内容,涵盖眼在手外、眼在手上,包括相机标定、手眼标定、3D定位计算位移偏差。最后的位移偏差与Cognex的结果在1mm左右。—Edited By Hugo# 1...
  • SCARA机器人手眼标定之目标抓取的例子:显示如何基于由SCARA手眼校准确定的校准信息,使用SCARA机器人执行拾取和放置应用程序。 在第一步骤中,根据模型图像定义形状模型。 然后,基于该形状模型,在每个图像中搜索...
  • halcon培训班内部用的讲义,主要包括:01 手眼标定的线性代数基础知识;02 手眼标定的3D知识;03 手眼标定的3D位姿;04 手眼标定之介绍;05 手眼标定之摄像机内参和外参;06 手眼标定之机器人位姿设置;07 六轴关节...
  • 经典手眼标定算法C++代码,程序是基于OpenCV 2.0以上版本,下载程序后需要配置OpenCV。工程主要包括三个文件,handeye.h为各种手眼标定的实现,quaternion.h为四元数运算文件,handeye_test.cpp为主程序,测试各手眼...
  • 所谓手眼系统,就是人眼睛看到一...相机知道的是像素坐标,机械手是空间坐标系,所以手眼标定就是得到像素坐标系和空间机械手坐标系的坐标转化关系。在实际控制中,相机检测到目标在图像中的像素位置后,通过标定好...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 777
精华内容 310
关键字:

手眼标定