接上一篇《 [PnP] PnP问题之DLT解法 》,本文介绍PnP的另一种常用解法 EPnP 。 EPnP 也是 ORB-SLAM2 中使用的方法。 本文对 EPnP 方法进行了详细的推导, 并基于Eigen库给出了实现 。
- 与其他方法相比,EPnP方法的复杂度为O(n)。对于点对数量较多的PnP问题,非常高效。
- 核心思想是将三维点表示为4个控制点的组合;优化也只针对4个控制点,所以速度很快;在求解 Mx = 0时,最多考虑了4个奇异向量,因此精度也很高。
1. 理论推导
1.1 homogeneous barycentric coordinates (hb坐标)
本文符号与原文[1]中相同。点对数量为 n ,三维点的非齐次坐标为
pi, i=1,⋯,n
利用上标 c 和 w 代表三维点在相机和世界坐标系下对应的坐标。EPnP引入了控制点,任何一个3D点都可以表示为四个控制点的线性组合。四个控制点的三维坐标记为
cj, j=1,⋯,4
世界坐标系下的每个三维点可以由4个控制点表达为
piw=j=1∑4αijcjw (1a)j=1∑4αij=1 (1b)
式中的 αij 称为(homogeneous barycentric coordinates),本文称为hb坐标。(1)式还可写为,
[piw1]=C[c1w1c2w1c3w1c4w1]αi1αi2αi3αi4 (2)
*实际上,3D点的齐次坐标被表示为控制点齐次坐标的线性组合。*记待求解的相机与世界坐标系之间的相对位姿为 [R∣t] ,控制点在相机坐标系下的坐标为
cjc=[R∣t][cjw1] (3)
那么,对于一个三维点,在相机坐标系下可表示为
pic=[R∣t][piw1]=[R∣t]j=1∑4αijcjwj=1∑4αij=j=1∑4αijcjc (4)
仔细看一下(4)式,发现在同一3D点在世界坐标系下的hb坐标与其在相机坐标系下的相同。这意味着,我们可以预先在世界坐标系下求取hb坐标αij,然后将其作为已知量拿到相机坐标系下使用。
1.2 如何选择控制点呢?
理论上,只要满足(2)式中的 C 可逆,hb坐标就唯一,我们可以任意选择控制点。但是为了系统的稳定性,采用如下策略进行控制点的选取。第一个控制点选择在所有3D点的质心位置
c1w = n1i=1∑npiw (5)
其余点选择在数据的主方向上。具体操作如下,计算矩阵
A=(p1w)T−(c1w)T⋯(pnw)T−(c1w)T (6)
计算 ATA 的3个特征值为 λ1,λ2,λ3 ,对应的特征向量为 v1,v2,v3 。那么剩余的三个控制点为
⎩⎨⎧c2w=c1w+nλ1v1c3w=c1w+nλ2v2c4w=c1w+nλ3v3 (7)
上述操作实际上是找到点云的重心,以及点云的三个主方向,可以参考***主成分分析(PCA)***。
到目前为止,我们已知可以知道4个控制点在世界坐标系下的坐标 cj ,每一个3D点的hd坐标 αij 。如果我们能把4个控制点在相机坐标系下的坐标求解出来,就可以计算出3D点在相机坐标系下的坐标,就可以求解出外参数 [R∣t] ,下面就沿着这个思路展开。
1.3 控制点在相机坐标系下的坐标
1.3.1 解析求解
把相机投影模型搬出来
ωi[ui1]=Kpic=Kj=1∑4αijcjc=fx000fy0cxcy1j=1∑4αijxjcyjczjc (8)
将(8)式展开,消掉最后一行,可得
⎩⎨⎧j=1∑4(αijfxxjc+αij(cx−ui)zjc)=0j=1∑4(αijfyyjc+αij(cy−vi)zjc)=0 (9)
仔细看一哈,(9)式中hd坐标 αij ,摄像机内参数 fx 、 fy 、 cx 、 cy ,以及2D点的坐标 ui , vi 都是已知变量,未知变量是4个控制点在摄像机坐标系下的坐标 xjc 、 yjc 和 zic ,共计12个未知参数。一个点可以确定2个方程,把所有n 个点对都用上,可以形成一个线性方程
Mx=0 (10)
其中, x 就是待求的12个未知参数。 M 的大小为 2n×12 。(10)式的解即为 M 的零空间:
x=i=1∑Nβivi (11)
其中 vi 为 M 的右奇异向量,对应的奇异值为0。具体解算方法为,求解 MTM 的特征值和特征向量,特征值为0的特征向量即为 vi 。需要注意,不论有多少个点对, MTM 的大小永远是 12×12 。而计算MTM的复杂度为 O(n) 。因此,算法的整体复杂度为 O(n)。
接下的问题就是如何确定(11)式的系数 βi ,从而获得一个确定的解。首先, N 为多少个呢?原文[1]中说与点对的数量,控制点,相机焦距和噪声有关,所以考虑了 N=1,2,3,4 四种情况。下面,分别讨论这四种情况。
CaseN=1
这是最简单的情况,(11)式这时变成了 x=βv。这时候把控制点在摄像机坐标系和世界坐标系的坐标联系起来:控制点间距在两种坐标系之下是相等的
cic−cjc2=ciw−cjw2 (12)
由此,引入一个约束。
βv[i]−βv[j]2=ciw−cjw2 (13)
其中, v[i] 表示 v 中第 i 个控制点所占据的3个元素组成的向量。4个控制点,可以得到 β 的一个闭式解
β={i,j}∈[1:4]∑v[i]−v[j]2{i,j}∈[1:4]∑v[i]−v[j]⋅ciw−cjw (14)
CaseN=2
此时,(11)式变为 x=β1v1+β2v2 ,带入到(12)式
(β1v1[i]+β2v2[i])−(β1v1[j]+β2v2[j])2=ciw−cjw2 (15)
将(15)展开
∣∣β1S1(v1[i]−v1[j]) + β2S2(v2[i]−v2[j])∣∣2=cciw−cjw2⇓β12S1TS1+2β1β2S1TS2+β22S2TS2=c (16)
引入三个中间变量
β11=β12,β22=β222,β12=β1β2 (17)
(16)式就变成了线性方程(中间变量法,可以将非线性方程转换成线性方程解算)。4个控制点可以构造出6个线性方程,组成
Lβ=ρ (18)
其中, β=[β11,β12,β22]T ,L 中为(16)式左边的系数大小为 6×3 , ρ 中为(16)式右边的系数,大小为 6×1 。解出 β 后,再结合(17)式,可以获得两组 β1, β2 的解。再加上一个条件,控制点在摄像机的前端,即 cjc 的 z 分量要大于0,从而β1, β2唯一确定。
CaseN=3
与Case N=2 的解法相同。这里的 β=[β11,β12,β13,β22,β23,β33]T , L的大小为 6×6 。
CaseN=4
再次使用Case N=2 的解法试一下,此时 β=[β11,β12,β13,β14,β22,β23,β24,β33,β34,β44]T , L 的大小为 6×10 。显然,无法得到唯一解。这次,就不能直接使用Case N=2,N=3 的解法了。因为,方程的数量少于待求解变量。看一下中间变量法,引入中间变量的同时,也增加了变量的个数。比如,Case N=3 的时候,引入中间变量后,变量个数由3个变成了6个。多出了3个变量 βab 。原文[1]提到,再引入约束,使用”Reinearization”的方法求解。(这个我也没看明白额)
实际上, OpenCV中开源代码 并没按照上述4个Case中的方法去求解,而是采用了近似的解法,具体的可以去看一下 源码 。 但是为什么可以近似,我没搞太明白 。我自己的实现目前也是按照OpenCV的方法去解的。
至此, β1∼β4 的初值求解完毕。
1.3.2 参数β 优化
下面,优化 β 。需要注意,这里的 β=[β1,β2,β2,β3]T 。 N=1,2,3 的情况中,剩余的 β 直接补0。同样,还是优化两个坐标系下控制点间距的差。
β∗=βargmin(i,j),s.t.i<j∑∥Errori,j(β)∥2Errori,j(β)=cccic−cjc2−cwciw−cjw2 (19)
其中,
cc=(β1v1[i]+β2v2[i]+β3v3[i]+β4v4[i])−(β1v1[j]+β2v2[j]+β3v3[j]+β4v4[j])2 = ∣∣β1S1(v1[i]−v1[j])+β2S2(v2[i]−v2[j])+β3S3(v3[i]−v3[j])+β4S4(v4[i]−v4[j])∣∣2 = β12S1TS1 + β1β2S1TS2 + β1β3S1TS3 + β1β4S1TS4+β1β2S2TS1 + β22S2TS2 + β2β3S2TS3 + β2β4S2TS4+β1β3S3TS1 + β2β3S3TS2 + β32S3TS3 + β3β4S3TS4+β1β4S4TS1 + β2β4S4TS2 + β3β4S4TS3 + β42S4TS4 = β12S1TS1 + 2β1β2S1TS2 + 2β1β3S1TS3 + 2β1β4S1TS4 + β22S2TS2+2β2β3S2TS3+2β2β4S2TS4 + β32S3TS3 + 2β3β4S3TS4 + β42S4TS4
下面,利用Gauss-Newton求解式(19)。首先求解 Error(β) 相对于 β 的雅克比
Ji,j=∂β∂[Errori,j(β)] = 2β1S1TS1+2β2S1TS2 + 2β3S1TS3+2β4S1TS42β1S1TS2 + 2β2S2TS2+2β3S2TS3+2β4S2TS42β1S1TS3+2β2S2TS3 + 2β3S3TS3 + 2β4S3TS42β1S1TS4+2β2S2TS4 + 2β3S3TS4 + 2β4S4TS4T
将六个小雅克比 Ji,j 合成为 6×4 的大雅克比
J=J1,2J1,3J1,4J2,3J2,4J3,4
记残差为
r=Error1,2(β)Error1,3(β)Error1,4(β)Error2,3(β)Error2,4(β)Error3,4(β)
那么,增量方程为
JTJδβ=−JTr (22)
更新方程
β=β+δβ (23)
迭代计算。上面其实就是标准的最小二乘优化,高博十四讲中就有详细的介绍。
至此, β 就解完了。那么,控制点在摄像机坐标系下的坐标cj 也全部解出来了。下面开始求解 [R∣t] 。
1.4 求解[R∣t]
首先,根据控制点 cj ,将所有的3D点在摄像机坐标系下的坐标 pic 恢复出来。我就知道所有3D-3D的匹配了。于是,就变成了已知匹配的ICP问题。这个求解方法在高博的十四讲中也有详细的介绍。具体的解法如下。
Step 1. 计算质心坐标
pcc=n1i=1∑npic
pcw=n1i=1∑npiw
Step 2. 去除质心
Pc = (p1c)T−(pcc)T⋯(pnc)T−(pcc)T
Pw = (p1w)T−(pcw)T⋯(pnw)T−(pcw)T
Step 3. 计算 R
W=(Pc)TPw
[UΣV]=SVD(W)
R = UVT
如果 ∣R∣<0 , R(2,:)=−R(2,:) 。
Step 4. 计算平移t
t=pcc−Rpcw
请注意,因为 β 有四种情况的解。所以,这里的 [R∣t] 实际上计算了四次,最终选择重投影误差最小的解。
2. 工程实现&实验
2.1 实现
我这里基于Eigen进行了实现,与OpenCV库里的EPnP实现稍有不同。详细代码请见:
ydsf16/PnP_Solver
2.2 实验
实验与上一篇《[PnP] PnP问题之DLT解法》相同,自己实现的EPnP(MY_EPnP)以及OpenCV自带的EnP(CV_EPnP)塞到了ORB-SLAM2的前端,与Motion Only BA(MOB)的方法和DLT的方法进行比较,结果如下图。
EPnP的结果已经十分接近MOB的方法了,轨迹也比较的平滑。不像DLT那样有很大的波动。(DLT的轨迹之所以波动比较大,可能是因为计算Ax=0的时候零空间只考虑了1个奇异向量,而EPnP最多考虑了4个奇异向量,而且还加了优化。)
从平移和旋转误差来看,EPnP的精度明显高于DLT方法。
运行时间上,EPnP方法比DLT稍慢,在配置为Intel i7-8700, Ubuntu 16.04的电脑上,计算200~300个点对,MY_EPnP运算时间为0.0852± 0.0153ms,CV_EPnP的运算时间为0.1202±0.0198 ms。
MY_EPnP与CV_EPnP的在精度、稳定性和运行时间三个方面都非常接近,可以说明自己编写的EPnp功能是正确的。在运算时间方面,MY_EPnP稍优于CV_EPnP方法。在精度方面,CV_EPnP稍优于MY_EPnP。
轨迹对比
误差对比
平移误差
旋转误差
参考资料
[1] Lepetit, V.; Moreno-Noguer, F.; Fua, P. Epnp: Efficient perspective-n-point camera pose estimation. International Journal of Computer Vision 2009, 81, 155-166.
[2] [原创]深入EPnP算法
[3] 【泡泡机器人公开课】第三十九课:PnP算法简介与代码解析
[4] OpenCV EPnP实现
----更多SLAM文章----
杨小东:[PnP] PnP问题之DLT解法
杨小东:[ORB-SLAM2]卡方分布(Chi-squared)外点(outlier)剔除
杨小东:[ORB-SLAM2] ORB特征提取策略对ORB-SLAM2性能的影响
杨小东:[PR-3]ArUco EKF SLAM 扩展卡尔曼SLAM
杨小东:[PR-2] PF 粒子滤波/蒙特卡罗定位
----相关代码----
ydsf16 - Overview