相机标定、手眼标定(理论)

一、理论篇

1.1 齐次变换矩阵(homogeneous transformation matrix )

我们知道,在三维空间准确描述执行器的状态,不仅要包括位置信息,也要包含姿态信息,即位姿(Pose)。齐次变换矩阵就是在机器人学中描述一个坐标系到另一个坐标系的变换关系的矩阵,包含了姿态旋转分量位置平移分量
F = 1 2 T = [ R 3 ∗ 3 T 3 ∗ 1 0 1 ] = [ n x o x a x x n y o y a y y n z o z a z z 0 0 0 1 ] F = _{1}^{2}\textrm{T} = \begin{bmatrix} R_{3*3} & T_{3*1} \\ 0 & 1 \end{bmatrix}=\begin{bmatrix} n_{x} & o_{x} & a_{x} & x\\ n_{y} & o_{y} & a_{y} & y\\ n_{z} & o_{z} & a_{z} & z\\ 0 & 0 & 0 & 1 \end{bmatrix} F=12T=[R330T311]=nxnynz0oxoyoz0axayaz0xyz1
其中, 1 2 T _{1}^{2}\textrm{T} 12T表示从坐标系1到坐标系2的变换, R 3 ∗ 3 R_{3*3} R33表示旋转矩阵, T 3 ∗ 1 T_{3*1} T31表示平移矩阵。

此外,值得注意的是关于齐次变换矩阵的左乘右乘问题。我的理解是以全局坐标系为参照做变换时用左乘,以当前坐标系为参照做变换时用右乘。举例

  • 若执行器当前的位姿为 T A T_{A} TA,若其绕全局坐标系xyz下做了一个变换,变换矩阵为 T B T_{B} TB,则其当前位姿为 T B ∗ T A T_{B}*T_{A} TBTA;
  • 若执行器当前的位姿为 T A T_{A} TA,若其绕当前坐标系 n A o A a A n_{A}o_{A}a_{A} nAoAaA下做了一个变换,变换矩阵为 T B T_{B} TB,则其当前位姿为 T A ∗ T B T_{A}*T_{B} TATB;
1.2 摄像机成像模型

8

1.3 成像相关坐标系定义

3

  • 世界坐标系 ( X w , Y w , Z w ) (X_{w},Y_{w},Z_{w}) (Xw,Yw,Zw):在环境中由用户任意选择一个基准坐标系作为参考来描述摄像机位置,在相机标定中一般选择标定板坐标系作为世界坐标系
  • 相机坐标系 ( X c , Y c , Z c ) (X_{c},Y_{c},Z_{c}) (Xc,Yc,Zc):以针孔模型的聚焦中心 O c O_{c} Oc(光心)为原点,摄像机光轴为 Z 轴建立三维坐标系,其中 X c X_{c} Xc轴、 Y c Y_{c} Yc轴与成像平面 x、y 轴平 行。光心到图像平面的距离为有效焦距 f f f
  • **成像平面坐标系 ( x , y ) (x,y) (x,y):**光轴与成像平面交点,称为主点。成像平面坐标系以主点为原点,是以物理单位表示的平面直角坐标系。
  • **图像坐标系 ( u , v ) (u,v) (u,v):**固定在图像上以像素为单位的平面直角坐标系, 通常以图像左上角为像素原点,每一像素的 ( u , v ) (u,v) (u,v)分别表示该像素在图像数组中的行数和列数。u、v 轴分别与 x、y 轴平行,该坐标系的单位为像素。
1.4 图像坐标系到相机坐标系的转换关系

4

如上图所示,设在图像坐标系有一点A坐标为 [ u , v ] T [u,v]^{T} [u,v]T,在成像平面坐标系有一点B坐标为 [ X ′ , Y ′ , Z ′ ] T [X',Y',Z']^{T} [X,Y,Z]T,相机坐标系有一点C坐标为 [ X , Y , Z ] T [X,Y,Z]^{T} [X,Y,Z]T。我们的问题可以转化为求A点坐标与C点坐标之间的变换关系。

首先,由相似三角形定理我们可以知道,
X ′ = f X Z Y ′ = f Y Z X' = f\frac{X}{Z} \\ Y' = f\frac{Y}{Z} X=fZXY=fZY
其次,由像素平面和物理成像平面的关系有:
{ u = α X ′ + c x v = β Y ′ + c y \left\{\begin{matrix} u = \alpha X' + c_{x}\\ v = \beta Y' + c_{y} \end{matrix}\right. {u=αX+cxv=βY+cy

将(2)代入(3)得:
u = f x X Z + c x v = f y Y Z + c y u = f_{x}\frac{X}{Z}+c_{x} \\ v = f_{y}\frac{Y}{Z}+c_{y} u=fxZX+cxv=fyZY+cy
将(4)写成矩阵的形式:
Z ( u v 1 ) = ( f x 0 c x 0 f y c y 0 0 1 ) ( X Y Z ) Z\begin{pmatrix} u\\ v\\ 1 \end{pmatrix}=\begin{pmatrix} f_{x} & 0 & c_{x}\\ 0 & f_{y} & c_{y}\\ 0 & 0 & 1 \end{pmatrix}\begin{pmatrix} X\\ Y\\ Z \end{pmatrix} Zuv1=fx000fy0cxcy1XYZ
(5)式就是我们所需要的变换关系,其中,矩阵 ( f x 0 c x 0 f y c y 0 0 1 ) \begin{pmatrix} f_{x} & 0 & c_{x}\\ 0 & f_{y} & c_{y}\\ 0 & 0 & 1 \end{pmatrix} fx000fy0cxcy1就是摄像机的内参,后面我们会用标定的方法得到该矩阵。

**小结:**通过上述的理论推导,我们可以得到图片上像素点坐标与相机坐标系上坐标点的变换关系。

1.5 摄像机标定

当我们在拍照时,其实是将三维空间下的3D坐标转化为平面图像中的2D坐标,那么从三维到二维,这之间的转换关系是怎么样呢?摄像机标定解决的就是这个事。具体的标定方法我会在下一次的博客中向大家介绍。这里只需要知道通过相机标定我们可以获得相机的内参和外参

那么内参和外参又是干什么用的呢?首先我们来梳理一下这其中的坐标变换。

标定板坐标系->相机坐标系->成像平面坐标系->图像坐标系

从1.4节我们可以看到,内参参与了从相机坐标系到图像坐标系的变换(公式(5)),很容易猜到,外参就是描述从标定板坐标系到相机坐标系的变换关系

1.6 手眼标定

从摄像机标定我们可以得到目标在摄像机坐标系下的坐标变换关系,而对于一个机械臂抓取问题来说,我们还需要知道目标物体在机器人坐标系下的坐标,这就需要手眼标定,得到摄像机与工具手的变换关系

G G G 为末端执行器(工具手)坐标系, B B B 为标定板坐标系, C C C 为摄像机坐标系, W W W 为机器人坐标系。

变换关系为:
B W T = G W T ∗ C G T ∗ B C T _{B}^{W}\textrm{T} = _{G}^{W}\textrm{T}*_{C}^{G}\textrm{T}*_{B}^{C}\textrm{T} BWT=GWTCGTBCT
其中, B W T _{B}^{W}\textrm{T} BWT为标定板相对于机器人的位姿关系,当标定板不动时, B W T _{B}^{W}\textrm{T} BWT为定量保持不 变; G W T _{G}^{W}\textrm{T} GWT是执行器相对于机器人的位姿关系,由机器人控制器给出; C G T _{C}^{G}\textrm{T} CGT是摄像机相对于末端执行器的位姿关系,即为手眼标定所求量; B C T _{B}^{C}\textrm{T} BCT是标定板相对于摄像机 的位姿关系,即摄像机标定所得的外参

我们的问题可以转化为如何求出 C G T _{C}^{G}\textrm{T} CGT

保持标定板不动,机器人运动到两个不同的位置下,则:
B W T 1 = B W T 2 _{B}^{W}\textrm{T}_1 = _{B}^{W}\textrm{T}_2 BWT1=BWT2
所以:
G W T 1 ∗ C G T 1 ∗ B C T 1 = G W T 2 ∗ C G T 2 ∗ B C T 2 _{G}^{W}\textrm{T}_{1}*_{C}^{G}\textrm{T}_{1}*_{B}^{C}\textrm{T}_{1}=_{G}^{W}\textrm{T}_{2}*_{C}^{G}\textrm{T}_{2}*_{B}^{C}\textrm{T}_{2} GWT1CGT1BCT1=GWT2CGT2BCT2
简化为:
A X = X B AX = XB AX=XB
其中: A = ( G W T 2 ) − 1 ∗ G W T 1 A = (_{G}^{W}\textrm{T}_{2} )^{-1}*_{G}^{W}\textrm{T}_{1} A=(GWT2)1GWT1 B = G W T 2 ∗ ( G W T 1 ) − 1 B = _{G}^{W}\textrm{T}_{2}*(_{G}^{W}\textrm{T}_{1} )^{-1} B=GWT2(GWT1)1 X = C G T X = _{C}^{G}\textrm{T} X=CGT

从上式可以看出,A 为工具手两次运动对应变换矩阵,B 为摄像机两次运动对应的变换矩阵。

我们的问题可以转化为如何解矩阵方程(9)?

为了实现唯一解,需要知道两组 A 和 B,需要3个位置的摄像机标定结果。

step1:

保持标定板不动,从三个不同的位置拍摄标定板采集图像,三个位置分别命名为:初始位置、位置1、位置2。如下图:

![1](https://img-blog.csdnimg.cn/20200913103814551.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQxMTg0OTEw,size_16,color_FFFFFF,t_70#pic_center)

step2:

由机器人控制器提供的末端位姿经过变换可以得到机器人的三个位姿矩阵 H 0 、 H 1 、 H 2 H_{0}、H_{1}、H_{2} H0H1H2,由此可得工具手两次运动对应变换矩阵 A 1 A_{1} A1 A 2 A_{2} A2 A 1 A_{1} A1:初始位置到位置1, A 1 A_{1} A1:初始位置到位置2)
A 1 = ( H 1 ) − 1 ∗ H 0 A 2 = ( H 2 ) − 1 ∗ H 0 A_{1}=(H_{1})^{-1}*H_{0}\\ A_{2}=(H_{2})^{-1}*H_{0} A1=(H1)1H0A2=(H2)1H0
step3:

利用相机标定得到初始位置、位置1、位置2分别对应的外参矩阵 M 0 、 M 1 、 M 2 M_{0}、M_{1}、M_{2} M0M1M2,由此可得摄像机两次运动对应的变换矩阵 B 1 B_{1} B1 B 2 B_{2} B2 B 1 B_{1} B1:初始位置到位置1, B 2 B_{2} B2:初始位置到位置2):B −,B −
B 1 = M 1 ∗ M 0 − 1 B 2 = M 2 ∗ M 0 − 1 B_{1}=M_{1}*M_{0}^{-1}\\ B_{2}=M_{2}*M_{0}^{-1} B1=M1M01B2=M2M01
step4:

最后求出手眼关系矩阵 C G T _{C}^{G}\textrm{T} CGT

本人数学功底有限,以下内容摘自他人。

参考博客:

https://blog.csdn.net/weixin_41074793/article/details/89477476?utm_medium=distribute.pc_aggpage_search_result.none-task-blog-2alltop_click~default-1-89477476.nonecase&utm_term=%E6%89%8B%E7%9C%BC%E6%A0%87%E5%AE%9A%E7%AE%97%E6%B3%95

下面利用李群知识求解 A X = X B AX = XB AX=XB

首先,将 X X X移到方程的右边,得到:
A = X B X T A = XBX^{T} A=XBXT
根据李代数到李群的指数映射关系,在方程两边取对数,则可以得到:
l o g A = l o g X B X T logA = logXBX^{T} logA=logXBXT
l o g A = [ α ] logA = [\alpha] logA=[α] l o g B = [ β ] logB = [\beta] logB=[β],则上式可以化为 [ α ] = X [ β ] X T = [ X β ] [\alpha]= X[\beta]X^{T}=[X\beta] [α]=X[β]XT=[Xβ],从而
α = X β \alpha=X\beta α=Xβ
A X = X B AX = XB AX=XB**写成矩阵形式为:

[ θ A b A 0 1 ] [ θ X b X 0 1 ] = [ θ X b X 0 1 ] [ θ B b B 0 1 ] \begin{bmatrix} \theta _{A} & b_{A} \\ 0& 1 \end{bmatrix}\begin{bmatrix} \theta_{X} & b_{X}\\ 0 & 1 \end{bmatrix}=\begin{bmatrix} \theta_{X} &b_{X} \\ 0 & 1 \end{bmatrix}\begin{bmatrix} \theta_{B} & b_{B}\\ 0 & 1 \end{bmatrix} [θA0bA1][θX0bX1]=[θX0bX1][θB0bB1]

展开得:

θ A θ X = θ X θ B θ A b X + b A = θ X b B + b X \theta_{A}\theta_{X}=\theta_{X}\theta_{B}\\ \theta_{A}b_{X}+b_{A}=\theta_{X}b_{B}+b_{X} θAθX=θXθBθAbX+bA=θXbB+bX
采用“两步法“求解上述方程,先解算旋转矩阵,再求平移向量。

由上面推导 α = X β \alpha=X\beta α=Xβ,因此 θ A θ X = θ X θ B \theta_{A}\theta_{X}=\theta_{X}\theta_{B} θAθX=θXθB可以写成 α = θ X β \alpha=\theta_{X}\beta α=θXβ.

当存在多组观测值时,求解该方程转化为下面最小二乘拟合问题:
m i n ∑ i = 1 k ∥ θ X β i − α i ∥ min\sum_{i=1}^{k}\left \| \theta_{X}\beta_{i}-\alpha_{i} \right \| mini=1kθXβiαi
上述是一个典型的绝对定向问题,因而求解上式与绝对定向相同,其解为:
θ X = ( M T M ) − 1 2 M T \theta_{X}=(M^{T}M)^{-\frac{1}{2}}M^{T} θX=(MTM)21MT
其中, M = ∑ i = 1 k β i α i T M = \sum_{i=1}^{k}\beta_{i}\alpha_{i}^{T} M=i=1kβiαiT

当只有两组 A 、 B A、B AB时,即有 A 1 、 A 2 、 B 1 、 B 2 A_{1}、A_{2}、B_{1}、B_{2} A1A2B1B2,
α 1 = l o g A 1 , α 2 = l o g A 2 , β 1 = l o g B 1 , β 2 = l o g B 2 \alpha_{1} = logA_{1},\alpha_{2}=logA_{2},\beta_{1}=logB_{1},\beta_{2}=logB_{2} α1=logA1,α2=logA2,β1=logB1,β2=logB2
旋转矩阵的解为:
θ X = M N − 1 \theta_{X}=MN^{-1} θX=MN1
其中, M = ( α 1 α 2 α 1 × α 2 ) M=(\alpha_{1} \quad \alpha_{2}\quad \alpha_{1}\times \alpha_{2}) M=α1α2α1×α2) N = ( β 1 β 2 β 1 × β 2 ) N=(\beta_{1} \quad \beta_{2}\quad \beta_{1}\times \beta_{2}) N=β1β2β1×β2)

求得旋转矩阵后,根据式(16)算出平移向量。

没有学过李代数,上述式(13)(14)并没有弄懂,有大佬看懂可以评论区解答!

源码:

//Hgij为机器人执行器末端坐标系之间相对位置姿态的齐次变换矩阵;[A1,A2]

//Hcij为摄像机坐标系之间相对位置姿态的齐次变换矩阵;[B1,B2]
	
//Hcg为相机与机器人执行器末端之间的相对位置姿态齐次矩阵。

void Navy_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 alpha1(3, 1, CV_64FC1);
    Mat beta1(3, 1, CV_64FC1);
    Mat alpha2(3, 1, CV_64FC1);
    Mat beta2(3, 1, CV_64FC1);
    Mat A(3, 3, CV_64FC1);
    Mat B(3, 3, CV_64FC1);

    Mat alpha(3, 1, CV_64FC1);
    Mat beta(3, 1, CV_64FC1);
    Mat M(3, 3, CV_64FC1, Scalar(0));

    Mat MtM(3, 3, CV_64FC1);
    Mat veMtM(3, 3, CV_64FC1);
    Mat vaMtM(3, 1, CV_64FC1);
    Mat pvaM(3, 3, CV_64FC1, Scalar(0));

    Mat Rx(3, 3, CV_64FC1);

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

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

    Mat tempCC(3, 3, CV_64FC1);
    Mat tempdd(3, 1, CV_64FC1);

    Mat C;
    Mat d;
    Mat Tx(3, 1, CV_64FC1);

    //Compute rotation
    if (Hgij.size() == 2) // Two (Ai,Bi) pairs
    {
        Rodrigues(Hgij[0](Rect(0, 0, 3, 3)), alpha1);
        Rodrigues(Hgij[1](Rect(0, 0, 3, 3)), alpha2);
        Rodrigues(Hcij[0](Rect(0, 0, 3, 3)), beta1);
        Rodrigues(Hcij[1](Rect(0, 0, 3, 3)), beta2);

        alpha1.copyTo(A.col(0));
        alpha2.copyTo(A.col(1));
        (alpha1.cross(alpha2)).copyTo(A.col(2));

        beta1.copyTo(B.col(0));
        beta2.copyTo(B.col(1));
        (beta1.cross(beta2)).copyTo(B.col(2));

        Rx = A*B.inv();

    }
    else // More than two (Ai,Bi) pairs
    {
        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, alpha);
            Rodrigues(Rcij, beta);

            M = M + beta*alpha.t();
        }

        MtM = M.t()*M;
        eigen(MtM, vaMtM, veMtM);

        pvaM.at<double>(0, 0) = 1 / sqrt(vaMtM.at<double>(0, 0));
        pvaM.at<double>(1, 1) = 1 / sqrt(vaMtM.at<double>(1, 0));
        pvaM.at<double>(2, 2) = 1 / sqrt(vaMtM.at<double>(2, 0));

        Rx = veMtM*pvaM*veMtM.inv()*M.t();
    }

    //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);

        tempCC = eyeM - Rgij;
        tempdd = Tgij - Rx * Tcij;

        C.push_back(tempCC);
        d.push_back(tempdd);
    }

    Tx = (C.t()*C).inv()*(C.t()*d);

    Rx.copyTo(Hcg(Rect(0, 0, 3, 3)));
    Tx.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;

}

gij - Rx * Tcij;

    C.push_back(tempCC);
    d.push_back(tempdd);
}

Tx = (C.t()*C).inv()*(C.t()*d);

Rx.copyTo(Hcg(Rect(0, 0, 3, 3)));
Tx.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;

}


  • 2
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值