大家好,欢迎来到IT知识分享网。
2020.9.8
by 盒子先生KingO
一、理论篇
1.1 齐次变换矩阵(homogeneous transformation matrix )
我们知道,在三维空间准确描述执行器的状态,不仅要包括位置信息,也要包含姿态信息,即位姿(Pose)。齐次变换矩阵就是在机器人学中描述一个坐标系到另一个坐标系的变换关系的矩阵,包含了姿态旋转分量和位置平移分量。
其中,
表示从坐标系1到坐标系2的变换,
表示旋转矩阵,
此外,值得注意的是关于齐次变换矩阵的左乘右乘问题。我的理解是以全局坐标系为参照做变换时用左乘,以当前坐标系为参照做变换时用右乘。举例:
- 若执行器当前的位姿为
,若其绕全局坐标系xyz下做了一个变换,变换矩阵为$T_{B}$,则其当前位姿为;
- 若执行器当前的位姿为
,若其绕当前坐标系下做了一个变换,变换矩阵为,则其当前位姿为;
1.2 摄像机成像模型
1.3 成像相关坐标系定义
- 世界坐标系
:在环境中由用户任意选择一个基准坐标系作为参考来描述摄像机位置,在
相机标定中一般选择标定板坐标系作为世界坐标系。
- 相机坐标系:以针孔模型的聚焦中心(光心)为原点,摄像机光轴为 Z 轴建立三维坐标系,其中轴、轴与成像平面 x、y 轴平 行。光心到图像平面的距离为有效焦距。
- 成像平面坐标系
:光轴与成像平面交点,称为主点。成像平面坐标系以主点为原点,是以物理单位表示的平面直角坐标系。
- 图像坐标系
:固定在图像上以像素为单位的平面直角坐标系, 通常以图像左上角为像素原点,每一像素的$(u,v)$分别表示该像素在图像数组中的行数和列数。u、v 轴分别与 x、y 轴平行,该坐标系的单位为像素。
1.4 图像坐标系到相机坐标系的转换关系
如上图所示,设在图像坐标系有一点A坐标为
首先,由相似三角形定理我们可以知道,
其次,由像素平面和物理成像平面的关系有:
将(2)代入(3)得:
内参,后面我们会用标定的方法得到该矩阵。
小结:通过上述的理论推导,我们可以得到图片上像素点坐标与相机坐标系上坐标点的变换关系。
1.5 摄像机标定
当我们在拍照时,其实是将三维空间下的3D坐标转化为平面图像中的2D坐标,那么从三维到二维,这之间的转换关系是怎么样呢?摄像机标定解决的就是这个事。这里只需要知道通过相机标定我们可以获得相机的内参和外参。
那么内参和外参又是干什么用的呢?首先我们来梳理一下这其中的坐标变换。
标定板坐标系->相机坐标系->成像平面坐标系->图像坐标系
从1.4节我们可以看到,内参参与了从相机坐标系到图像坐标系的变换(公式(5)),很容易猜到,外参就是描述从标定板坐标系到相机坐标系的变换关系。
1.6 手眼标定
从摄像机标定我们可以得到目标在摄像机坐标系下的坐标变换关系,而对于一个机械臂抓取问题来说,我们还需要知道目标物体在机器人坐标系下的坐标,这就需要手眼标定,得到摄像机与工具手的变换关系。
设
变换关系为:
其中,
为定量保持不 变;
是摄像机相对于末端执行器的位姿关系,即为手眼标定所求量;
外参。
我们的问题可以转化为如何求出
?
保持标定板不动,机器人运动到两个不同的位置下,则:
所以:
简化为:
其中:
,
,
从上式可以看出,A 为工具手两次运动对应变换矩阵,B 为摄像机两次运动对应的变换矩阵。
我们的问题可以转化为如何解矩阵方程(9)?
为了实现唯一解,需要知道两组 A 和 B,需要3个位置的摄像机标定结果。
step1:
保持标定板不动,从三个不同的位置拍摄标定板采集图像,三个位置分别命名为:初始位置、位置1、位置2。如下图:
step2:
由机器人控制器提供的末端位姿经过变换可以得到机器人的三个位姿矩阵
step3:
利用相机标定得到初始位置、位置1、位置2分别对应的外参矩阵
step4:
最后求出手眼关系矩阵
本人数学功底有限,以下内容摘自他人。
参考博客:
手眼标定_全面细致的推导过程_weixin_41074793的博客-CSDN博客blog.csdn.net
下面利用李群知识求解
首先,将$X$移到方程的右边,得到:
根据李代数到李群的指数映射关系,在方程两边取对数,则可以得到:
令
展开得:
由上面推导
当存在多组观测值时,求解该方程转化为下面最小二乘拟合问题:
上述是一个典型的绝对定向问题,因而求解上式与绝对定向相同,其解为:
其中,
当只有两组$A、B$时,即有
旋转矩阵的解为:
其中,
求得旋转矩阵后,根据式(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;
}
免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://yundeesoft.com/27452.html