VINS-Mono源码解析(四)后端: Initialization
来源:互联网 发布:轰炸手机号码软件下载 编辑:程序博客网 时间:2024/06/09 03:42
VINS-Mono源码解析(四)后端: Initialization
整个Initialization部分对应Estimator::initialStructure()函数, 包括Vision-only SFM和Visual Inertial Alignment两部分, 它们的实现分别在GlobalSFM类和VisualIMUAlignment()函数中.
initialStructure()函数的内部流程如下:
1. 先判断IMU是否有足够的variance, 求all_image_frame中所有帧的平均加速度, 并计算加速度的方差, 判断方差是否足够大
2. 调用relativePose判断sliding window是否某一帧l与当前帧视差足够大, 如果存在的话, 则根据特征匹配用五点法计算l和当前帧的relative R,t, 用来做后面的SFM
3. 调用construct()进行SFM, 估计slidingwindow中的camera pose和features的3D position.
4. 然后对all_image_frame中没有参与SFM的帧的位姿进行了PnP求解.
这里暂时还没太弄明白all_image_frame的含义, 除了SFM的帧, 其他还有哪些帧?
5. 调用visualInitialAlign()进行视觉和IMU的校准.
1. Vision-only SFM: GlobalSFM::construct()
Vision-only SFM具体实现在GlobalSFM类中. initialStructure()先调用relativePose()来判断slidingwindow中是否存在某一帧l和当前帧(即最后一帧)的视差足够大, 如果有足够大的视差, 则计算l帧和当前帧的relative R,t, 然后进行后面的SFM初始化,也就是调GlobalSFM::construct()函数.
GlobalSFM在initial_sfm.h中,里面还有另外两个类:
- SFMFeature是SFM中的特征点,包括id,state(是否被三角化),各帧中观测,3D坐标等.
- ReprojectionError3D 定义了重投影误差计算函数, 供ceres优化用的, ceres要求重载一个()操作符的模板函数.
GlobalSFM声明如下:
class GlobalSFM{public: GlobalSFM(); /*\brief 外部调用接口,主要处理函数. 输入第l帧和最后一帧的相对R,t, 根据特征点的观测估计所有帧的位姿和特征点的3D坐标 \param[in] frame_num: pose的个数, elements in q,T \param[out] q: SFM结果,每帧在l帧参考系下的quaternion \param[out] T: SFM结果,每帧在l帧参考系下的position \param[in] l: 以第l帧为参考系,即l帧的pose为坐标原点 \param[in] relative_R: 第l帧到最后一帧的相对旋转 \param[in] relative_T: 第l帧到最后一帧的相对平移 \param[in] sfm_f: feature list,每个SFMFeature中包含多个观测 \param[out] sfm_tracked_point: 优化后的3D特征点在l帧参考系的position */ bool construct(int frame_num, Quaterniond* q, Vector3d* T, int l, const Matrix3d relative_R, const Vector3d relative_T, vector<SFMFeature> &sfm_f, map<int, Vector3d> &sfm_tracked_points);private: /**\brief 根据当前已经三角化的特征点估计某一帧的R,t */ bool solveFrameByPnP(Matrix3d &R_initial, Vector3d &P_initial, int i, vector<SFMFeature> &sfm_f); /**\brief 输入两个pose和2D观测点, 三角化3D特征点 */ void triangulatePoint(Eigen::Matrix<double, 3, 4> &Pose0, Eigen::Matrix<double, 3, 4> &Pose1, Vector2d &point0, Vector2d &point1, Vector3d &point_3d); /**\brief 输入两帧的pose, 三角化它们共同观测的特征点, 之前已经被三角化的特征点不再处理 */ void triangulateTwoFrames(int frame0, Eigen::Matrix<double, 3, 4> &Pose0, int frame1, Eigen::Matrix<double, 3, 4> &Pose1, vector<SFMFeature> &sfm_f); int feature_num;};
其中,construct()函数的内部流程如下:
- step 1: 所有pose和feature点的初始化, 以第l帧为参考坐标系
1. 先第l帧和最后一帧做三角化,根据三角化的点PnP求解第l+1的位姿,再l+1和最后一帧三角化,再PnP求l+2位姿,依次求到倒数第二帧的位姿.
2. 第l+1帧~倒数第二帧的所有帧与第l帧三角化剩下的特征点
3. 依次PnP求解第l-1帧~第0帧的位姿并将第l-1帧~第0帧 与 第l帧做三角化.
4. 遍历所有特征点, 对那些还没有三角化的点,如果观测大于2个, 根据首尾观测进行三角化.
- step 2: Local Bundle Adjustment, ceres
5. ceres求解, residuals用的特征点到每一帧重投影误差, 将求解结果写出到q,T和sfm_tracked_points
2. Visual Inertial Alignment: Estimator::visualInitialAlign()
1) Gyroscope Bias Calib: solveGyroscopeBias()
gyro的标定思路是:对于相邻的两帧, vision有一个相对旋转
其中,
左边的jacobian就是代码中的tmp_A, 右边对应代码中的tmp_b, 每两帧之间都可以构造一个这样的等式约束, 超定方程用
for (frame_i = all_image_frame.begin(); next(frame_i) != all_image_frame.end(); frame_i++){ frame_j = next(frame_i); MatrixXd tmp_A(3, 3); tmp_A.setZero(); VectorXd tmp_b(3); tmp_b.setZero(); // 视觉给出的i帧->j帧的旋转(四元数) Eigen::Quaterniond q_ij(frame_i->second.R.transpose() * frame_j->second.R); // IMU预积分中旋转对gyro bias的jacobian tmp_A = frame_j->second.pre_integration->jacobian.template block<3, 3>(O_R, O_BG); tmp_b = 2 * (frame_j->second.pre_integration->delta_q.inverse() * q_ij).vec(); A += tmp_A.transpose() * tmp_A; b += tmp_A.transpose() * tmp_b;}delta_bg = A.ldlt().solve(b);
2) Velocity, Gravity Vector, Metric Scale Initialization: LinearAlignment()
这部分标定需要标定的未知量有(向量长度=3*N+3+1)
貌似论文有好几个版本, 有一个版本是估计
标定思路为: IMU预积分的结果和SFM的结果之间的关系可写成下式:
等式左边是IMU预积分, 右边是SFM的结果和要标定的量. 其中,
下面将上式改写成Ax=b的等式约束形式, 式中, 未知数为
将外参转换公式带到前面的式子中, 可以写成如下形式, 直接上图把,懒得敲公式了
![Alt text](./Screenshot from 2017-07-20 11:47:28.png)
每相邻两帧都有一个上面的等式约束对应代码中的
注意:公式中有10个未知数, 只有6个等式, 为什么是超定呢? 是因为对每一帧来说,除去首尾帧, 都有两组这个等式约束. 总的未知数是
3n+3+1 个, 约束方程的个数是6(n−1) 个, 当n>2 ,3n+3+1<6(n−1) , 所以超定.
Alignment求解代码:
for (frame_i = all_image_frame.begin(); next(frame_i) != all_image_frame.end(); frame_i++, i++){ frame_j = next(frame_i); MatrixXd tmp_A(6, 10); // 10 = 3(vel_i)+3(vel_j)+3(gravity)+1(scale) tmp_A.setZero(); VectorXd tmp_b(6); tmp_b.setZero(); double dt = frame_j->second.pre_integration->sum_dt; tmp_A.block<3, 3>(0, 0) = -dt * Matrix3d::Identity(); tmp_A.block<3, 3>(0, 6) = frame_i->second.R.transpose() * dt * dt / 2 * Matrix3d::Identity(); tmp_A.block<3, 1>(0, 9) = frame_i->second.R.transpose() * (frame_j->second.T - frame_i->second.T) / 100.0; tmp_b.block<3, 1>(0, 0) = frame_j->second.pre_integration->delta_p + frame_i->second.R.transpose() * frame_j->second.R * TIC[0] - TIC[0]; //cout << "delta_p " << frame_j->second.pre_integration->delta_p.transpose() << endl; tmp_A.block<3, 3>(3, 0) = -Matrix3d::Identity(); tmp_A.block<3, 3>(3, 3) = frame_i->second.R.transpose() * frame_j->second.R; tmp_A.block<3, 3>(3, 6) = frame_i->second.R.transpose() * dt * Matrix3d::Identity(); tmp_b.block<3, 1>(3, 0) = frame_j->second.pre_integration->delta_v; //cout << "delta_v " << frame_j->second.pre_integration->delta_v.transpose() << endl; Matrix<double, 6, 6> cov_inv = Matrix<double, 6, 6>::Zero(); //cov.block<6, 6>(0, 0) = IMU_cov[i + 1]; //MatrixXd cov_inv = cov.inverse(); cov_inv.setIdentity(); MatrixXd r_A = tmp_A.transpose() * cov_inv * tmp_A; //r_A 10*10 VectorXd r_b = tmp_A.transpose() * cov_inv * tmp_b; //r_b 10*1 A.block<6, 6>(i * 3, i * 3) += r_A.topLeftCorner<6, 6>(); b.segment<6>(i * 3) += r_b.head<6>(); A.bottomRightCorner<4, 4>() += r_A.bottomRightCorner<4, 4>(); b.tail<4>() += r_b.tail<4>(); A.block<6, 4>(i * 3, n_state - 4) += r_A.topRightCorner<6, 4>(); A.block<4, 6>(n_state - 4, i * 3) += r_A.bottomLeftCorner<4, 6>();}
这里有一处不解: 代码中有一处地方除100, 后面方程求解前
A,b 都乘以了1000,不知是为何? 是有numerical issue吗?
3)Gravity Refinement: RefineGravity()
上一步求出重力向量可以进一步refine, 因为重力加速度的模值通常是已知的(约9.8), 所以可以用重力加速度大小这个prior去refine重力向量, 但
*一开始有点不理解,若
w1,w2 不为0,g 的模值不就大于G 了吗?
看代码后发现, 每次估计完g 以后, 都会做一次归一化使得其模值为G , 所以可以理解为这里的refine其实是refine重力的方向, 同时refine其他估计量. 因为方向基本不会差太多,w1,w2 通常很小, 所以可以做这样的近似.*
至此,IMU和VisionSFM算是标定完成, 这里漏了一块IMU和相机的互标定, 也就是相机外参估计, 主要就标一下
- VINS-Mono源码解析(四)后端: Initialization
- VINS-Mono源码解析(三)后端: IMU预积分
- VINS-Mono源码解析(五)后端: 紧耦合优化
- VINS-Mono源码解析(一)系统框架
- VINS-Mono源码解析(二)前端:特征跟踪
- VINS mono 系统学习 四
- 【VINS算法分析之一】VINS-Mono(草稿)
- 港科VINS-Mono系统学习(1)
- VINS-MONO学习
- VINS-Mono 代码解读
- 港科VINS-Mono系统学习(2) 预处理
- 港科VINS-Mono系统学习(3) 初始化
- 港科VINS-Mono系统学习(4) 里程计
- 港科VINS-Mono系统学习(5) 闭环检测
- vins-mono slam刚开始接触
- vins mono 刚开始学习 一
- VINS mono 系统学习 二
- VINS mono 系统学习 三
- 3478数值分解
- VINS-Mono源码解析(三)后端: IMU预积分
- 小白一个 。。。搭建一个小小的静态网站
- MySQL 索引
- 数学: HDU1005 Number Sequence
- VINS-Mono源码解析(四)后端: Initialization
- 优先队列几个应用详解
- 0035_Search Insert Position
- 【HDU 6038 Problem Description】+ 思维
- 华为机试——合并表记录
- MySQL 临时表
- Dijkstra's Algorithm分析以及其优化
- 关于Tomcat中乱码的产生原因详解
- VINS-Mono源码解析(五)后端: 紧耦合优化