1. 概述
PnP是求解3D到2D点对运动的方法,它描述了当知道n个3D空间点及其投影位置时,如何估计相机的位姿变换。最少只需要3个点对就可以估计相机的运动。
该方法使用的条件是,参考点为世界坐标系下的特征点,其空间位置已知,并且知道query帧中对应参考点的像素坐标。
PnP问题的求解方法有很多种:
- 直接线性变换
- P3P
- 非线性优化方法——Bundle Ajustment
本节着眼于BA求解方法,其他方法暂时不做展开。
2. Bundle Ajustment
利用优化求解的思路是:最小化重投影误差——期望计算query帧的相机位姿$R, t$,它的李代数为$\xi$,空间特征点的坐标为$P_i = [X_i, Y_i, Z_i]^T$,其在query帧上的像素坐标为$u_i = [u_i, v_i]^T$,那么理论上:
构建成最小二乘问题就是:寻找最优的相机位姿$\xi$,使得误差最小化:
在使用优化库来求解之前,还有一个问题——每个误差项$e_i = u_i -\frac{1}{s_i}Kexp(\xi^{\wedge})P_i$的导数$J_i$。
回忆图优化中讲过的,优化问题最终转化成为矩阵的线性求解$H\Delta x = g$,其中矩阵$H$是由单个误差项一阶展开$e(x+\Delta x) = e(x) + J\Delta x$中的雅可比矩阵$J_i$ 构成的稀疏对称阵。
误差项是一个二维向量(像素坐标差),优化变量是一个六维向量(空间位姿李代数),因此$J$是一个2*6的矩阵。
其中$P^{‘}$是特征点转换到相机坐标系下的空间坐标:
因此误差项导数的第一项为:
误差项的第二项为变换后的点关于李代数的导数,参考李代数节:
其中$P^{‘\wedge}$是$P^{‘}$的反对称阵:
因此得到完整的雅可比矩阵:
除了优化相机位姿以外,还可以同时优化特征点的空间位置$P$:
其中的第二项为:
3. 优化库使用
构建图模型:
- 优化变量1:节点1:query相机位姿(六维向量李代数)
- 优化变量2:节点2:特征点空间位置(三维向量坐标描述)
- 预测值:边n:根据当前estimate的优化量,投影到投影平面的像素坐标$z_i = h(\xi, P_i)$
- 观测值:能够直接读出的,query帧上对应特征点的实际投影坐标$u_i$
g2o中已经提供了相近的基类(在g2o/types/sba/types_six_dof_expmap.h中):李代数位姿节点VertexSE3Expmap、空间点位置节点VertexSBAPointXYZ、投影方程边EdgeProjectXYZ2UV。
边类里面要关注linearizeOplus函数,这个函数描述的是非线性函数进行线性化的过程中,求导的解析解(当然也可以使用数值解),最后函数给出的是每个节点的导数矩阵($\frac{\partial e}{\partial \delta \xi} $和$\frac{\partial e}{\partial P_i}$) 。这点是Ceres库和g2o库的一点主要差别:Ceres都是使用自动的数值导数,g2o要自己求导。
1 | void EdgeProjectXYZ2UV::linearizeOplus() |
4. OpenCV内置函数
basic:
1 | void cv::solvePnP ( pts3d, pts2d, K, Mat(), r, t, false ); |
advanced:
1 | bool cv::solvePnPRansac( |
- Ransac:考虑到我们提供的匹配里面存在误匹配的情况,OpenCV采用“随机采样一致性算法”(Random Sample Consensus),从现有匹配中随机取一部分用来估计运动(PnP的解析解法最少只需要三个点就能计算相对位姿),正确的匹配结果都是近似的,从而剔除误匹配。
- inlier:内点,函数最终给出的匹配可信的点。
- RANSAC只采用少数几个随机点来计算PnP,容易受到噪声影响。工程上通常使用RANSAC的解作为初值,再使用非线性优化方法求解最优值。
1 | // Ransac粗匹配 |