在基于位置的视觉控制中,手眼标定起着关键性作用,手眼标定是解决相机与机器人之间位置关系的理论,手眼关系一般有两种形式,一种是相机固定在机器人执行器末端随机器人运动,称为Eye-in-Hand,另一种为相机固定在机器人附近的地面或设备上,不随机器人运动,称为Eye-to-Hand.无论是怎样的形式,最终解决的问题都是同样的,即看到目标物在什么位置,告诉机器人去什么位置执行加工动作。Eye-in-Hand的形式中,相机随机器人运动视野比较宽不会受到机械臂的阻挡,但是存在运动过量可能看不到目标物体,导致目标丢失。Eye-to-Hand的形式中,相机不随机器人运动,固定在一处,能够看到在视野中目标,但是在机械臂移动的过程中容易受到阻挡。两种形式各有利弊,下面是每种形式求取手眼关系的过程。
在上图Eye-in-Hand关系图中,机械臂末端带着相机从{C1}移动到{C2}的过程中相机和机械臂末端执行器的固定关系保持不变即:
\[{}^{C1}{T_{H1}} = {}^{C2}{T_{H2}}\]
同时带有Marker的目标物在机器人世界坐标系中的关系也不变。
\[{}^B{T_O} = {}^{\rm{B}}{T_{H1}} \bullet {}^{C1}T_{H1}^{ - 1} \bullet {}^{C1}{T_O} = {}^{\rm{B}}{T_{H2}} \bullet {}^{C2}T_{H2}^{ - 1} \bullet {}^{C2}{T_O}\]
\[({}^{\rm{B}}T_{H2}^{ - 1} \bullet {}^{\rm{B}}{T_{H1}}) \bullet {}^{C1}T_{H1}^{ - 1} = {}^{C2}T_{H2}^{ - 1} \bullet ({}^{C2}{T_O} \bullet {}^{C1}T_O^{ - 1})\]
写作:$AX = XB$,求解X即可得出手眼之间的关系。
在上图Eye-to-Hand关系图中对于机器人末端执行器夹着Marker从{O1}移动到和{O2}执行任务时,执行器末端和Marker之间的位置关系保持不变,因此在两个位置存在如下的转换关系:
\[{}^{O1}{T_{H1}} = {}^CT_{O1}^{ - 1} \bullet {}^C{T_B} \bullet {}^{\rm{B}}{T_{H1}}\]
\[{}^{O2}{T_{H2}} = {}^CT_{O2}^{ - 1} \bullet {}^C{T_B} \bullet {}^{\rm{B}}{T_{H2}}\]
\[{}^CT_{O1}^{ - 1} \bullet {}^C{T_B} \bullet {}^{\rm{B}}{T_{H1}} = {}^CT_{O2}^{ - 1} \bullet {}^C{T_B} \bullet {}^{\rm{B}}{T_{H2}}\]
\[({}^C{T_{O2}} \bullet {}^CT_{O1}^{ - 1}) \bullet {}^C{T_B} = {}^C{T_B} \bullet ({}^{\rm{B}}{T_{H2}} \bullet {}^{\rm{B}}T_{H1}^{ - 1})\]
写作:$AX = XB$,求解X即可得出手眼之间的关系。
注意在求取的过程中多采集几组A,B的数据结果才会准确,不然无法求解。
1 //向量转反对称矩阵 2 3 cv::Mat Calculate::skew(cv::Mat vec) 4 { 5 6 cv::Mat skewM(3, 3, CV_64FC1); 7 8 double vx = vec.at<double>(0, 0); 9 10 double vy = vec.at<double>(1, 0); 11 12 double vz = vec.at<double>(2, 0); 13 14 skewM.at<double>(0, 0) = 0.0; skewM.at<double>(0, 1) = -vz; skewM.at<double>(0, 2) = vy; 15 16 skewM.at<double>(1, 0) = vz; skewM.at<double>(1, 1) = 0.0; skewM.at<double>(1, 2) = -vx; 17 18 skewM.at<double>(2, 0) = -vy; skewM.at<double>(2, 1) = vx; skewM.at<double>(2, 2) = 0.0; 19 20 return skewM; 21 22 } 23 Solve AX=XB, A-RT_Tcpij, B-RT_Camij, B2A 24 25 Tsai_HandEye(cv::Mat Hcg, vector<cv::Mat> Hgij, vector<cv::Mat> Hcij) 26 27 { 28 29 CV_Assert(Hgij.size() == Hcij.size()); 30 31 int nStatus = Hgij.size(); 32 33 cv::Mat Rgij(3, 3, CV_64FC1); 34 35 cv::Mat Rcij(3, 3, CV_64FC1); 36 37 cv::Mat rgij(3, 1, CV_64FC1); 38 39 cv::Mat rcij(3, 1, CV_64FC1); 40 41 double theta_gij; 42 43 double theta_cij; 44 45 cv::Mat rngij(3, 1, CV_64FC1); 46 47 cv::Mat rncij(3, 1, CV_64FC1); 48 49 cv::Mat Pgij(3, 1, CV_64FC1); 50 51 cv::Mat Pcij(3, 1, CV_64FC1); 52 53 cv::Mat tempA(3, 3, CV_64FC1); 54 55 cv::Mat tempb(3, 1, CV_64FC1); 56 57 cv::Mat A; 58 59 cv::Mat b; 60 61 cv::Mat pinA; 62 63 cv::Mat Pcg_prime(3, 1, CV_64FC1); 64 65 cv::Mat Pcg(3, 1, CV_64FC1); 66 67 cv::Mat PcgTrs(1, 3, CV_64FC1); 68 69 cv::Mat Rcg(3, 3, CV_64FC1); 70 71 cv::Mat eyeM = cv::Mat::eye(3, 3, CV_64FC1); 72 73 cv::Mat Tgij(3, 1, CV_64FC1); 74 75 cv::Mat Tcij(3, 1, CV_64FC1); 76 77 cv::Mat tempAA(3, 3, CV_64FC1); 78 79 cv::Mat tempbb(3, 1, CV_64FC1); 80 81 cv::Mat AA; 82 83 cv::Mat bb; 84 85 cv::Mat pinAA; 86 87 cv::Mat Tcg(3, 1, CV_64FC1); 88 89 for (int i = 0; i < nStatus; i++) 90 91 { 92 93 Hgij[i](cv::Rect(0, 0, 3, 3)).copyTo(Rgij); 94 95 Hcij[i](cv::Rect(0, 0, 3, 3)).copyTo(Rcij); 96 97 Rodrigues(Rgij, rgij); 98 99 Rodrigues(Rcij, rcij); 100 101 theta_gij = norm(rgij); 102 103 theta_cij = norm(rcij); 104 105 rngij = rgij / theta_gij; 106 107 rncij = rcij / theta_cij; 108 109 Pgij = 2 * sin(theta_gij / 2)*rngij; 110 111 Pcij = 2 * sin(theta_cij / 2)*rncij; 112 113 tempA = skew(Pgij + Pcij); 114 115 tempb = Pcij - Pgij; 116 117 A.push_back(tempA); 118 119 b.push_back(tempb); 120 121 } 122 123 //Compute rotation 124 125 invert(A, pinA, cv::DECOMP_SVD); 126 127 Pcg_prime = pinA * b; 128 129 Pcg = 2 * Pcg_prime / sqrt(1 + norm(Pcg_prime) * norm(Pcg_prime)); 130 131 PcgTrs = Pcg.t(); 132 133 Rcg = (1 - norm(Pcg) * norm(Pcg) / 2) * eyeM + 0.5 * (Pcg * PcgTrs + sqrt(4 - norm(Pcg)*norm(Pcg))*skew(Pcg)); 134 135 //Compute Translation 136 137 for (int i = 0; i < nStatus; i++) 138 139 { 140 141 Hgij[i](cv::Rect(0, 0, 3, 3)).copyTo(Rgij); 142 143 Hcij[i](cv::Rect(0, 0, 3, 3)).copyTo(Rcij); 144 145 Hgij[i](cv::Rect(3, 0, 1, 3)).copyTo(Tgij); 146 147 Hcij[i](cv::Rect(3, 0, 1, 3)).copyTo(Tcij); 148 149 tempAA = Rgij - eyeM; 150 151 tempbb = Rcg * Tcij - Tgij; 152 153 AA.push_back(tempAA); 154 155 bb.push_back(tempbb); 156 157 } 158 159 invert(AA, pinAA, cv::DECOMP_SVD); 160 161 Tcg = pinAA * bb; 162 163 Rcg.copyTo(Hcg(cv::Rect(0, 0, 3, 3))); 164 165 Tcg.copyTo(Hcg(cv::Rect(3, 0, 1, 3))); 166 167 Hcg.at<double>(3, 0) = 0.0; 168 169 Hcg.at<double>(3, 1) = 0.0; 170 171 Hcg.at<double>(3, 2) = 0.0; 172 173 Hcg.at<double>(3, 3) = 1.0; 174 175 176 }