手眼标定算法---Sai-Lenz(A New Technique for Fully Autonomous and Efficient 3D Robotics Hand/Eye Calibrati)

本文详细介绍了TSAI-LENZ文献中的手眼标定方法,包括标定问题描述、基本方程及求解过程,并提供了基于OpenCV2.0的C++实现代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 本文主要是讲解经典手眼标定问题中的TSAI-LENZ 文献方法,参考文献为“A New Technique for Fully Autonomous and Efficient 3D Robotics Hand/Eye Calibration”  转自:https://blog.youkuaiyun.com/YunlinWang/article/details/51622143


手眼标定问题描述

       在机器人校准测量、机器人手眼协调以及机器人辅助测量等领域,都要求知道机器人执行器末端(抓取臂)坐标系和传感器(比如用来测量三维空间中目标位置和方向并固定在机器人手上的摄像机)坐标系之间的相互关系,确定这种转换关系在机器人领域就是通常所说的手眼标定

       将手眼标定系统如下图所示,其中HgijHgij为机器人执行器末端坐标系之间相对位置姿态的齐次变换矩阵;HcijHcij为摄像机坐标系之间相对位置姿态的齐次变换矩阵;HcgHcg为像机与机器人执行器末端之间的相对位置姿态齐次矩阵。

手眼标定系统

       经过坐标系变换,HgijHgij、HcijHcij和HcgHcg满足如下关系: 

               

将上式展开,可以得到手眼标定的基本方程: 

                                                   
       因此,手眼标定问题也就转化为从上述方程组中求解出RcgRcg和TcgTcg,下面就按照TSAI文献所述求解该方程组。


“两步法”手眼标定

       一般用“两步法”求解基本方程,即先从基本方程上式求解出RcgRcg,再代入下式求解出TcgTcg。在TSAI文献中引入旋转轴-旋转角系统来描述旋转运动来进行求解该方程组,具体的公式推导可以查看原始文献,这里只归纳计算步骤,不明白的地方可阅读文献,计算步骤如下:

Step1:利用罗德里格斯变换将旋转矩阵转换为旋转向量

                                            

Step2:向量归一化

                                           

Step3:修正的罗德里格斯参数表示姿态变化

                                            

Step4:计算初始旋转向量P′cg

                                    

       其中,skew为反对称运算,假设一个三维向量V=[vx;vy;vz],其反对称矩阵为: 

                                     

Step5:计算旋转向量Pcg

                                        

Step6:计算旋转矩阵Rcg

                      

Step7:计算平移向量Tcg

                                   

算法源代码

根据上述基本计算步骤,在利用OpenCV 2.0开源库的基础上,编写Tsai手眼标定方法的c++程序,其实现函数代码如下:

void Tsai_HandEye(Mat Hcg, vector<Mat> Hgij, vector<Mat> Hcij)
{
    CV_Assert(Hgij.size() == Hcij.size());
    int nStatus = Hgij.size();

    Mat Rgij(3, 3, CV_64FC1);
    Mat Rcij(3, 3, CV_64FC1);

    Mat rgij(3, 1, CV_64FC1);
    Mat rcij(3, 1, CV_64FC1);

    double theta_gij;
    double theta_cij;

    Mat rngij(3, 1, CV_64FC1);
    Mat rncij(3, 1, CV_64FC1);

    Mat Pgij(3, 1, CV_64FC1);
    Mat Pcij(3, 1, CV_64FC1);

    Mat tempA(3, 3, CV_64FC1);
    Mat tempb(3, 1, CV_64FC1);

    Mat A;
    Mat b;
    Mat pinA;

    Mat Pcg_prime(3, 1, CV_64FC1);
    Mat Pcg(3, 1, CV_64FC1);
    Mat PcgTrs(1, 3, CV_64FC1);

    Mat Rcg(3, 3, CV_64FC1);
    Mat eyeM = Mat::eye(3, 3, CV_64FC1);

    Mat Tgij(3, 1, CV_64FC1);
    Mat Tcij(3, 1, CV_64FC1);

    Mat tempAA(3, 3, CV_64FC1);
    Mat tempbb(3, 1, CV_64FC1);

    Mat AA;
    Mat bb;
    Mat pinAA;

    Mat Tcg(3, 1, CV_64FC1);

    for (int i = 0; i < nStatus; i++)
    {
        Hgij[i](Rect(0, 0, 3, 3)).copyTo(Rgij);
        Hcij[i](Rect(0, 0, 3, 3)).copyTo(Rcij);

        Rodrigues(Rgij, rgij);
        Rodrigues(Rcij, rcij);

        theta_gij = norm(rgij);
        theta_cij = norm(rcij);

        rngij = rgij / theta_gij;
        rncij = rcij / theta_cij;

        Pgij = 2 * sin(theta_gij / 2)*rngij;
        Pcij = 2 * sin(theta_cij / 2)*rncij;

        tempA = skew(Pgij + Pcij);
        tempb = Pcij - Pgij;

        A.push_back(tempA);
        b.push_back(tempb);
    }

    //Compute rotation
    invert(A, pinA, DECOMP_SVD);

    Pcg_prime = pinA * b;
    Pcg = 2 * Pcg_prime / sqrt(1 + norm(Pcg_prime) * norm(Pcg_prime));
    PcgTrs = Pcg.t();   
    Rcg = (1 - norm(Pcg) * norm(Pcg) / 2) * eyeM + 0.5 * (Pcg * PcgTrs + sqrt(4 - norm(Pcg)*norm(Pcg))*skew(Pcg));

    //Computer Translation 
    for (int i = 0; i < nStatus; i++)
    {
        Hgij[i](Rect(0, 0, 3, 3)).copyTo(Rgij);
        Hcij[i](Rect(0, 0, 3, 3)).copyTo(Rcij);
        Hgij[i](Rect(3, 0, 1, 3)).copyTo(Tgij);
        Hcij[i](Rect(3, 0, 1, 3)).copyTo(Tcij);


        tempAA = Rgij - eyeM;
        tempbb = Rcg * Tcij - Tgij;

        AA.push_back(tempAA);
        bb.push_back(tempbb);
    }

    invert(AA, pinAA, DECOMP_SVD);
    Tcg = pinAA * bb;

    Rcg.copyTo(Hcg(Rect(0, 0, 3, 3)));
    Tcg.copyTo(Hcg(Rect(3, 0, 1, 3)));
    Hcg.at<double>(3, 0) = 0.0;
    Hcg.at<double>(3, 1) = 0.0;
    Hcg.at<double>(3, 2) = 0.0;
    Hcg.at<double>(3, 3) = 1.0;

}

 

### 手眼标定 Eye-in-Hand 的实现方法及原理 手眼标定的核心目标是确定相机与机械臂末端执行器之间的相对位置和姿态关系。对于 eye-in-hand 模式,摄像机安装在机械臂的末端执行器上,随着机械臂的移动而移动。其标定过程可以分为以下几个方面进行描述: #### 1. 标定原理 在 hand-eye 标定中,eye-in-handeye-to-hand 的数学模型都可以归结为求解齐次变换方程 \( A X = X B \) 的问题[^3]。其中: - \( A \) 表示机械臂从一个位姿到另一个位姿的变化。 - \( B \) 表示相机观察到的场景从一个位姿到另一个位姿的变化。 - \( X \) 是需要求解的手眼关系矩阵。 对于 eye-in-hand 情况,\( X \) 表示摄像机相对于机械臂末端执行器的变换矩阵。通过多次移动机械臂并记录对应的位姿信息,可以构建多个方程组来求解 \( X \)。 #### 2. 数据采集 为了实现手眼标定,需要收集以下数据: - **机械臂位姿**:机械臂控制器提供末端执行器在不同位置的齐次变换矩阵 \( {}^{W}T_{E} \),表示机械臂末端执行器相对于世界坐标系的位置和姿态。 - **相机图像**:在每个机械臂位姿下,使用摄像机拍摄校准板的图像,并通过标定算法计算出校准板相对于相机的位姿 \( {}^{C}T_{B} \)。 #### 3. 算法实现 OpenCV 提供了专门的手眼标定函数 `cv2.calibrateHandEye`,可以用于求解 eye-in-hand手眼矩阵。具体实现步骤如下: ```python import numpy as np import cv2 # 假设已经获取了机械臂末端执行器相对于基座的位姿序列 R_fbs = [np.eye(3)] * N # 机械臂末端旋转矩阵序列 T_fbs = [np.zeros((3, 1))] * N # 机械臂末端平移向量序列 # 假设已经获取了标定板相对于相机的位姿序列 R_tcs = [np.eye(3)] * N # 相机相对于标定板的旋转矩阵序列 T_tcs = [np.zeros((3, 1))] * N # 相机相对于标定板的平移向量序列 # 使用 OpenCV 的手眼标定函数 R_cf, T_cf = cv2.calibrateHandEye(R_fbs, T_fbs, R_tcs, T_tcs, method=cv2.CALIB_HAND_EYE_ANDREFF) # 输出结果 print("手眼标定结果:") print("旋转矩阵 R_cf:\n", R_cf) print("平移向量 T_cf:\n", T_cf) ``` 上述代码中,`R_fbs` 和 `T_fbs` 是机械臂末端执行器相对于基座的位姿序列,`R_tcs` 和 `T_tcs` 是标定板相对于相机的位姿序列。通过调用 `cv2.calibrateHandEye` 函数,可以得到相机相对于机械臂末端执行器的旋转矩阵 \( R_{cf} \) 和平移向量 \( T_{cf} \)[^2]。 #### 4. 实际应用 在实际应用中,eye-in-hand 的典型场景包括机器人抓取、采摘等任务。例如,在 SCARA 机器人的手眼标定中,摄像机连接到机器人工具上,校准对象相对于机器人静止不动。通过提供校准板的描述文件和已标定的相机参数,可以完成整个标定过程[^4]。 此外,在完成标定后,可以通过测试验证标定结果是否准确。例如,随机移动机械臂到某一位置,利用计算出的手眼矩阵和当前机械臂位姿,预测物体的实际位置,并通过吸嘴吸取物体进行验证[^5]。 --- ###
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值