A New Technique for Fully Autonomous and Efficient 3D Robotics Hand/Eye Calibration(Tsai手眼标定算法)
符号定义
坐标系定义
G i G_i Gi: 手爪(末端执行器)的坐标系。
C i C_i Ci: 相机的坐标系。
C W CW CW: 对于相机的世界坐标系(一般由标定板确定)。
R W RW RW: 机器人基座坐标系。
坐标系变换定义
手爪——>基座
[ R g i T g i 0 1 ] \begin{bmatrix} R_{gi} & T_{gi}\\ 0 &1 \end{bmatrix} [Rgi0Tgi1]camera world ——>camera
[ R c i T c i 0 1 ] \begin{bmatrix} R_{ci} & T_{ci}\\ 0 &1 \end{bmatrix} [Rci0Tci1]手爪——>手爪
[ R g i j T g i j 0 1 ] \begin{bmatrix} R_{gij} & T_{gij}\\ 0 &1 \end{bmatrix} [Rgij0Tgij1]camera——>camera
[ R c i j T c i j 0 1 ] \begin{bmatrix} R_{cij} & T_{cij}\\ 0 &1 \end{bmatrix} [Rcij0Tcij1]camera——>手爪
[ R c g T c g 0 1 ] \begin{bmatrix} R_{cg} & T_{cg}\\ 0 &1 \end{bmatrix} [Rcg0Tcg1]
θ R \theta_R θR: 旋转矩阵 R R R的旋转角
P g i j P_{gij} Pgij: 旋转矩阵 R g i j R_{gij} Rgij对应的旋转向量,它可以用于表示 G i G_i Gi到 G j G_j Gj的旋转变换
P c i j P_{cij} Pcij: 同上
P c g P_{cg} Pcg: 旋转矩阵 R c g R_{cg} Rcg对应的旋转向量
P c g ′ = 1 2 cos ( θ R c 8 2 ) P c g = 1 4 − ∣ P c g ∣ 2 P c g P_{c g}^{\prime}=\frac{1}{2 \cos \left(\frac{\theta_{R_{c 8}}}{2}\right)} P_{c g}=\frac{1}{\sqrt{4-\left|P_{c g}\right|^{2}}} P_{c g} Pcg′=2cos(2θRc8)1Pcg=4−∣Pcg∣21Pcg
S k e w ( V ) Skew(V) Skew(V): 反对称矩阵,相当于 V × V\times V×, V V V是3维列向量
S k e w ( V ) = [ 0 − v z v y v z 0 − v x − v y v x 0 ] Skew(V)= \begin{bmatrix} 0 & -v_z & v_y\\ v_z & 0 & -v_x\\ -v_y & v_x & 0 \end{bmatrix} Skew(V)=⎣⎡0vz−vy−vz0vxvy−vx0⎦⎤
N N N: 数据组数,每组数据由两个位姿组成
计算步骤
-
利用罗德里格斯公式将旋转矩阵转换为旋转向量
{ r g i j = rodrigues ( R g i j ) r c i j = rodrigues ( R c i j ) \left\{\begin{array}{l} r_{g i j}=\text {rodrigues}\left(R_{g i j}\right) \\ r_{c i j}=\text {rodrigues }\left(R_{c i j}\right) \end{array}\right. {rgij=rodrigues(Rgij)rcij=rodrigues (Rcij)从线性代数的角度看,旋转向量实际上是旋转矩阵的特征值为1所对应的特征向量
a. 旋转矩阵必有一个特征值为1
b. 根据特征值和特征向量的定义有:
R P r = λ P r λ = 1 RP_r=\lambda P_r \quad \lambda=1 RPr=λPrλ=1
因此对应的几何意义是该向量经过 R R R旋转作用后依然是自身,那很显然它就是旋转轴 -
旋转向量归一化,得到单位旋转轴和旋转角*(将旋转向量解耦)*
{ θ g i j = ∥ r g i j ∥ 2 N r g i j = r g i j θ g i j θ c i j = ∥ r c i j ∥ 2 N r c i j = r c i j θ c i j \left\{\begin{array}{c} \theta_{g i j}=\left\|r_{g i j}\right\|_{2} \quad N_{r_{g i j}}=\frac{r_{g i j}}{\theta_{g i j}} \\ \theta_{c i j}=\| r_{c i j} \|_{2} \quad N_{r_{c i j}}=\frac{r_{c i j}}{\theta_{c i j}} \end{array}\right. {θgij=∥rgij∥2Nrgij=θgijrgijθcij=∥rcij∥2Nrcij=θcijrcij -
修正的罗德里格斯参数表示姿态变换
P g i j = 2 sin ( θ g i j / 2 ) N r g i j P c i j = 2 sin ( θ c i j / 2 ) N r c i j P_{gij}=2\sin(\theta_{gij}/2)N_{rgij}\\ P_{cij}=2\sin(\theta_{cij}/2)N_{rcij} Pgij=2sin(θgij/2)NrgijPcij=2sin(θcij/2)Nrcij -
计算初始的 P c g ′ {P_{cg}}' Pcg′
对于每对位姿,它们之间的旋转变化要尽可能大,然后计算:
S k e w ( P g i j + P c i j ) P c g ′ = P c i j − P g i j (8) Skew(P_{gij}+P_{cij}){P_{cg}}'=P_{cij}-P_{gij}\tag{8} Skew(Pgij+Pcij)Pcg′=Pcij−Pgij(8)
其中, P g i j P_{gij} Pgij容易由机械臂的正向运动学公式求得, P c i j P_{cij} Pcij容易由机器视觉方法求得,因此式(8)实际上就是 A x = b Ax=b Ax=b的求解问题,可以直接对 A A A求逆,即 x = A − 1 b x=A^{-1}b x=A−1b,如果有多组数据,那就构建出优化问题:
min f ( x ) = ∥ A x − b ∥ 2 (12) \min\quad f(x)=\|Ax-b\|^2\tag{12} minf(x)=∥Ax−b∥2(12)
即优化出外参中的旋转部分 -
计算旋转角 θ R g c \theta_{R_{gc}} θRgc
-
计算 P c g P_{cg} Pcg
P c g = 2 P c g ′ 1 + ∣ P c g ′ ∣ 2 P_{c g}=\frac{2 P_{c g}{ }^{\prime}}{\sqrt{1+\left|P_{c g}{}^{\prime}\right|^{2}}} Pcg=1+∣Pcg′∣22Pcg′ -
利用罗德里格斯计算旋转矩阵 R c g R_{cg} Rcg
R = ( 1 − ∣ P r ∣ 2 2 ) I + 1 2 ( P r P r T + α ⋅ Skew ( P r ) ) R=\left(1-\frac{\left|P_{r}\right|^{2}}{2}\right) I+\frac{1}{2}\left(P_{r} P_{r}^{T}+\alpha \cdot \operatorname{Skew}\left(P_{r}\right)\right) R=(1−2∣Pr∣2)I+21(PrPrT+α⋅Skew(Pr))
或者
R = [ n 1 2 + ( 1 − n 1 2 ) cos θ n 1 n 2 ( 1 − cos θ ) − n 3 sin θ n 1 n 3 ( 1 − cos θ ) + n 2 sin θ n 1 n 2 ( 1 − cos θ ) + n 3 sin θ n 2 2 + ( 1 − n 2 2 ) cos θ n 2 n 3 ( 1 − cos θ ) − n 1 sin θ n 1 n 3 ( 1 − cos θ ) + n 2 sin θ n 2 n 3 ( 1 − cos θ ) + n 1 sin θ n 3 2 + ( 1 − n 3 2 ) cos θ ) ] R=\left[\begin{array}{ccc} n_{1}^{2}+\left(1-n_{1}^{2}\right) \cos \theta & n_{1} n_{2}(1-\cos \theta)-n_{3} \sin \theta & n_{1} n_{3}(1-\cos \theta)+n_{2} \sin \theta \\ n_{1} n_{2}(1-\cos \theta)+n_{3} \sin \theta & n_{2}^{2}+\left(1-n_{2}^{2}\right) \cos \theta & n_{2} n_{3}(1-\cos \theta)-n_{1} \sin \theta \\ n_{1} n_{3}(1-\cos \theta)+n_{2} \sin \theta & n_{2} n_{3}(1-\cos \theta)+n_{1} \sin \theta & \left.n_{3}^{2}+\left(1-n_{3}^{2}\right) \cos \theta\right) \end{array}\right] R=⎣⎡n12+(1−n12)cosθn1n2(1−cosθ)+n3sinθn1n3(1−cosθ)+n2sinθn1n2(1−cosθ)−n3sinθn22+(1−n22)cosθn2n3(1−cosθ)+n1sinθn1n3(1−cosθ)+n2sinθn2n3(1−cosθ)−n1sinθn32+(1−n32)cosθ)⎦⎤ -
计算平移向量 T c g T_{cg} Tcg
( R g i j − I ) T c g = R c g T c i j − T g i j (15) \left(R_{g i j}-I\right) T_{c g}=R_{c g} T_{c i j}-T_{g i j}\tag{15} (Rgij−I)Tcg=RcgTcij−Tgij(15)
实际上(15)也是一个求解 A x = b Ax=b Ax=b的问题,可以对 A A A求逆计算,如果有多组数据,则类似(12)构建最小二乘的优化问题,即优化得到外参中的平移部分
算法代码
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;
// 论文公式(12)
tempA = skew(Pgij + Pcij);
tempb = Pcij - Pgij;
A.push_back(tempA);
b.push_back(tempb);
}
//Compute rotation
// 论文公式(12)
invert(A, pinA, DECOMP_SVD);
// 求初始的旋转向量P_{cg}'
Pcg_prime = pinA * b;
// 论文公式(14)
Pcg = 2 * Pcg_prime / sqrt(1 + norm(Pcg_prime) * norm(Pcg_prime));
PcgTrs = Pcg.t(); // 矩阵转置
// 论文公式(10) 旋转向量->旋转矩阵
Rcg = (1 - norm(Pcg) * norm(Pcg) / 2) * eyeM + 0.5 * (Pcg * PcgTrs + sqrt(4 - norm(Pcg)*norm(Pcg))*skew(Pcg));
//Computer Translation
// 计算平移向量, 论文公式(15)
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;
}
参考: