从零学习VINS-Mono/Fusion源代码(五):VIO初始化

news2024/12/23 18:18:56

本节分析VIO初始化部分

VINS-Mono/Fusion代码学习系列:
从零学习VINS-Mono/Fusion源代码(一):主函数
从零学习VINS-Mono/Fusion源代码(二):前端图像跟踪
从零学习VINS-Mono/Fusion源代码(三):IMU预积分公式推导
从零学习VINS-Mono/Fusion源代码(四):误差卡尔曼滤波


从零学习VINS-Mono/Fusion源代码(五):VIO初始化

  • 1 为什么要做初始化?
  • 2 初始化流程
    • 2.1 参数读取
    • 2.2 回调函数
    • 2.3 Measurement_process{process} —— vio处理线程
      • 2.3.1 数据预处理
      • 2.3.2 VIO初始化任务
  • 3 VIO初始化实现
    • 3.1 特征点管理,检查是否是关键帧 addFeatureCheckParallax
    • 3.2 旋转外参初始化 CalibrationExRotation
    • 3.3 VIO初始化 initialStructure
      • 3.3.1 SFM部分
      • 3.3.2 视觉惯性对齐

1 为什么要做初始化?

提供初始值(良好的初始值,能够避免优化过程中系统陷入局部最小,减少迭代次数,降低计算量)

初始化的变量:
位姿、速度、零偏,硬件外参、地图点
在这里插入图片描述
其中,在没有先验条件的情况下,加速度计零偏和平移外参是比较难求解的,加计bias受到重力干扰,难以分离.
因此,平移外参一般是事先测量得到,加速度计零偏就设为0,在后续优化中进行处理.


2 初始化流程

estimator_node.cpp中找到主函数main(),主函数与光流部分类似,定义节点,设置参数,接收topic.
在这里插入图片描述

int main(int argc, char **argv)
{
    ros::init(argc, argv, "vins_estimator");
    ros::NodeHandle n("~");
    ros::console::set_logger_level(ROSCONSOLE_DEFAULT_NAME, ros::console::levels::Info);
    readParameters(n);
    estimator.setParameter();
#ifdef EIGEN_DONT_PARALLELIZE
    ROS_DEBUG("EIGEN_DONT_PARALLELIZE");
#endif
    ROS_WARN("waiting for image and imu...");

    registerPub(n);

    ros::Subscriber sub_imu = n.subscribe(IMU_TOPIC, 2000, imu_callback, ros::TransportHints().tcpNoDelay());
    ros::Subscriber sub_image = n.subscribe("/feature_tracker/feature", 2000, feature_callback);//前端光流结果
    ros::Subscriber sub_restart = n.subscribe("/feature_tracker/restart", 2000, restart_callback);//接收前端重启命令
    ros::Subscriber sub_relo_points = n.subscribe("/pose_graph/match_points", 2000, relocalization_callback);//回环检测fast_relocalization响应

    std::thread measurement_process{process};
    ros::spin();
 
    return 0;
}

2.1 参数读取

使用readParameters(n)读取yaml文件中估计器迭代计算参数、IMU参数、旋转平移外参,以及时间延迟参数.

estimator.setParameter() 估计器外参预设
本质上就是一个外参的赋值过程,然后设置特征点的置信度,默认在虚拟相机下,特征点投影后的坐标差为1.5个像素.

void Estimator::setParameter()
{
    for (int i = 0; i < NUM_OF_CAM; i++)
    {
        tic[i] = TIC[i];
        ric[i] = RIC[i];
    }
    f_manager.setRic(ric);

	//特征点置信度
    ProjectionFactor::sqrt_info = FOCAL_LENGTH / 1.5 * Matrix2d::Identity();
    ProjectionTdFactor::sqrt_info = FOCAL_LENGTH / 1.5 * Matrix2d::Identity();
    td = TD;
}

2.2 回调函数

IMU_callback()
IMU回调函数主要完成两个任务:

  • 把imu消息存入buffer
  • 根据IMU频率预测并发送位姿,提高里程计频率(对应系统框图propagation)

消息存入buffer的过程中用到了进程锁,避免数据放入和取出发生访问冲突,具体概念参见:
[c++11]多线程编程(六)——条件变量(Condition Variable)

void imu_callback(const sensor_msgs::ImuConstPtr &imu_msg)
{
    if (imu_msg->header.stamp.toSec() <= last_imu_t)
    {
        ROS_WARN("imu message in disorder!");
        return;
    }
    last_imu_t = imu_msg->header.stamp.toSec();

    m_buf.lock();
    imu_buf.push(imu_msg);
    m_buf.unlock();
    con.notify_one();

    last_imu_t = imu_msg->header.stamp.toSec();

    {
        std::lock_guard<std::mutex> lg(m_state);
        predict(imu_msg);
        std_msgs::Header header = imu_msg->header;
        header.frame_id = "world";
        if (estimator.solver_flag == Estimator::SolverFlag::NON_LINEAR)
            pubLatestOdometry(tmp_P, tmp_Q, tmp_V, header);
    }
}

feature_callback就是把前端光流数据丢进buffer
restart_callback做了一个状态器复位


2.3 Measurement_process{process} —— vio处理线程

在这里插入图片描述

2.3.1 数据预处理

  • getMeasurements()完成imu图像帧的时间同步(这个getMeasurements(),本质上就是把图像帧和imu数据分组,上一帧k到当前帧k+1之间的imu数据与当前图像帧k+1构成一组);
  • 之后,把imu送入estimator.processIMU做预积分,并且更新滑窗中的状态量Ps Vs Rs,给非线性优化提供更好的初值;
  • 光流结果(像素坐标、归一化坐标、速度)重新整理到7x1向量xyz_uv_velocity中去.

2.3.2 VIO初始化任务

estimator.processImage函数中完成了VIO初始化和后端优化求解,本节只讨论VIO初始化任务:

  1. 特征点管理,检查是否是关键帧 addFeatureCheckParallax
  2. 旋转外参初始化 CalibrationExRotation
  3. VIO初始化 initialStructure
    在这里插入图片描述

3 VIO初始化实现

3.1 特征点管理,检查是否是关键帧 addFeatureCheckParallax

把当前帧的特征点信息送入f_manager中进行维护,通过视差判断关键帧.

VINS特征点维护方式:
FeatureManager这个类中,有一项list< FeaturePerId > feature,用来维护下图中每一个特征点id对应的属性.
在这里插入图片描述

关键帧筛选条件:

  • 是第0帧或第1帧(frame_count<2)
  • 追踪到上一帧特征点数目少于20个(last_track_num<20)
  • 计算倒数第二帧和倒数第三帧之间的视差(本质是判断倒数第二帧是不是关键帧)compensatedParallax2
  • 与上一帧没有共视特征点(parallax_num==0)

如果倒数第二帧是关键帧,去掉最老帧;如果倒数第二帧不是关键帧,就把它去掉.


3.2 旋转外参初始化 CalibrationExRotation

只针对于 ESTIMATE_EXTRINSIC=2,才需要在初始化阶段计算旋转外参量.
思想就是利用k到k+n时刻中,各相邻两帧之间的旋转关系,构建超定方程求解.
这个旋转关系有两种途径得到:第一种是IMU预积分,第二种是特征点对极约束.

以k到k+1时刻为例,将外参 q c b q_{cb} qcb作为桥梁,可以构建下面这个等式:


q c b ⊗ q b k b k + 1 = q c k c k + 1 ⊗ q c b {q_{cb}} \otimes {q_{{b_k}{b_{k + 1}}}} = {q_{{c_k}{c_{k + 1}}}} \otimes {q_{cb}} qcbqbkbk+1=qckck+1qcb

[ q b k b k + 1 ] R q c b = [ q c k c k + 1 ] L q c b {\left[ {{q_{{b_k}{b_{k + 1}}}}} \right]_R}{q_{cb}} = {\left[ {{q_{{c_k}{c_{k + 1}}}}} \right]_L}{q_{cb}} [qbkbk+1]Rqcb=[qckck+1]Lqcb

对于k到k+n时刻:


( R k − L k ) q c b = 0 {\left( {{R_k} - {L_k}} \right){q_{cb}} = 0} (RkLk)qcb=0
( R k + 1 − L k + 1 ) q c b = 0 {\left( {{R_{k+1}} - {L_{k+1}}} \right){q_{cb}} = 0} (Rk+1Lk+1)qcb=0
⋮ \vdots
( R k + n − L k + n ) q c b = 0 {\left( {{R_{k+n}} - {L_{k+n}}} \right){q_{cb}} = 0} (Rk+nLk+n)qcb=0

然后就构建了Ax=0这样的问题,通过SVD奇异值分解来求解.


3.3 VIO初始化 initialStructure

initialStructure函数中完成的任务:检查imu能观性、SFM纯视觉三维重建、对所有帧pnp、视觉惯性对齐

3.3.1 SFM部分

假设滑窗中有11帧,先找一个枢纽帧(假设第4帧),要求它离最后一帧尽可能远(目的是避免纯旋转情况,便于求解t),通过对极约束求解枢纽帧和最后一帧之间的相对位姿;同时,距离太远会导致两帧共同观测到的特征点数目少,所以要做一个权衡。

然后,根据得到的位姿,通过三角化恢复观测到的特征点的3D世界坐标.

利用已经恢复的3D点,通过pnp(3D-2D)来恢复第5-9帧的图像帧相对位姿,在这个过程中也可以三角化出更多的3D点.

同样地,利用pnp求解第3-0帧的图像帧位姿.
在这里插入图片描述

然后,做一个global BA,来调整这些位姿和3D点.

3.3.2 视觉惯性对齐

  • solveGyroscopeBias求解陀螺零偏
    在这里插入图片描述

    理论上为单位四元数,取出虚部构造Ax=b问题,求解 δ b w \delta {b_w} δbw

在这里插入图片描述

  • LinearAlignment求解速度、尺度、重力方向

    论文中指出的待求解量:
    在这里插入图片描述
    构造的观测方程:
    在这里插入图片描述
    推导一下这个方程怎么来的:
    首先把式(5)换到 c 0 {c_0} c0系下面去(就是枢纽帧),然后带入式(14).
    在这里插入图片描述在这里插入图片描述

    ①平移预积分量构成方程:

    R c 0 b k p b k + 1 c 0 = R c 0 b k ( p b k c 0 + v b k c 0 Δ t k − 1 2 g c 0 Δ t k 2 ) + α R_{{c_0}}^{{b_k}}p_{{b_{k + 1}}}^{{c_0}} = R_{{c_0}}^{{b_k}}(p_{{b_k}}^{{c_0}} +v_{{b_k}}^{{c_0}}\Delta {t_k} - \frac{1}{2}{g^{{c_0}}}\Delta t_k^2) + \alpha Rc0bkpbk+1c0=Rc0bk(pbkc0+vbkc0Δtk21gc0Δtk2)+α

    R c 0 b k p b k + 1 c 0 = R c 0 b k ( s p c k c 0 − R b k c 0 p b c + v b k c 0 Δ t k − 1 2 g c 0 Δ t k 2 ) + α R_{{c_0}}^{{b_k}}p_{{b_{k + 1}}}^{{c_0}} = R_{{c_0}}^{{b_k}}(sp_{{c_k}}^{{c_0}} - R_{{b_k}}^{{c_0}}p_b^c + v_{{b_k}}^{{c_0}}\Delta {t_k} - \frac{1}{2}{g^{{c_0}}}\Delta t_k^2) + \alpha Rc0bkpbk+1c0=Rc0bk(spckc0Rbkc0pbc+vbkc0Δtk21gc0Δtk2)+α

    R c 0 b k ( s p c k + 1 c 0 − R b k + 1 c 0 p b c ) = R c 0 b k ( s p c k c 0 − R b k c 0 p b c + v b k c 0 Δ t k − 1 2 g c 0 Δ t k 2 ) + α R_{{c_0}}^{{b_k}}(sp_{{c_{k + 1}}}^{{c_0}} - R_{{b_{k + 1}}}^{{c_0}}p_b^c) = R_{{c_0}}^{{b_k}}(sp_{{c_k}}^{{c_0}} - R_{{b_k}}^{{c_0}}p_b^c + v_{{b_k}}^{{c_0}}\Delta {t_k} - \frac{1}{2}{g^{{c_0}}}\Delta t_k^2) + \alpha Rc0bk(spck+1c0Rbk+1c0pbc)=Rc0bk(spckc0Rbkc0pbc+vbkc0Δtk21gc0Δtk2)+α

    α − R c 0 b k R b k c 0 p b c + R c 0 b k R b k + 1 c 0 p b c = R c 0 b k ( s p c k + 1 c 0 − s p c k c 0 − v b k c 0 Δ t k + 1 2 g c 0 Δ t k 2 ) \alpha - R_{{c_0}}^{{b_k}}R_{{b_k}}^{{c_0}}p_b^c + R_{{c_0}}^{{b_k}}R_{{b_{k + 1}}}^{{c_0}}p_b^c = R_{{c_0}}^{{b_k}}(sp_{{c_{k + 1}}}^{{c_0}} - sp_{{c_k}}^{{c_0}} - v_{{b_k}}^{{c_0}}\Delta {t_k} + \frac{1}{2}{g^{{c_0}}}\Delta t_k^2) αRc0bkRbkc0pbc+Rc0bkRbk+1c0pbc=Rc0bk(spck+1c0spckc0vbkc0Δtk+21gc0Δtk2)

    α − p b c + R c 0 b k R b k + 1 c 0 p b c = R c 0 b k ( s p c k + 1 c 0 − s p c k c 0 − v b k c 0 Δ t k + 1 2 g c 0 Δ t k 2 ) \alpha - p_b^c + R_{{c_0}}^{{b_k}}R_{{b_{k + 1}}}^{{c_0}}p_b^c = R_{{c_0}}^{{b_k}}(sp_{{c_{k + 1}}}^{{c_0}} - sp_{{c_k}}^{{c_0}} - v_{{b_k}}^{{c_0}}\Delta {t_k} + \frac{1}{2}{g^{{c_0}}}\Delta t_k^2) αpbc+Rc0bkRbk+1c0pbc=Rc0bk(spck+1c0spckc0vbkc0Δtk+21gc0Δtk2)

    ②旋转预积分量构成方程:

    R c 0 b k v b k + 1 c 0 = R c 0 b k ( v b k c 0 − g c 0 Δ t k ) + β R_{{c_0}}^{{b_k}}v_{{b_{k + 1}}}^{{c_0}} = R_{{c_0}}^{{b_k}}(v_{{b_k}}^{{c_0}} - {g^{{c_0}}}\Delta t_k^{}) + \beta Rc0bkvbk+1c0=Rc0bk(vbkc0gc0Δtk)+β

    R c 0 b k R b k + 1 c 0 v b k + 1 = R c 0 b k ( R b k c 0 v b k − g c 0 Δ t k ) + β R_{{c_0}}^{{b_k}}R_{{b_{k + 1}}}^{{c_0}}{v^{{b_{k + 1}}}} = R_{{c_0}}^{{b_k}}(R_{{b_k}}^{{c_0}}{v^{{b_k}}} - {g^{{c_0}}}\Delta t_k^{}) + \beta Rc0bkRbk+1c0vbk+1=Rc0bk(Rbkc0vbkgc0Δtk)+β

    β = R c 0 b k R b k + 1 c 0 v b k + 1 − v b k + R c 0 b k g c 0 Δ t k \beta = R_{{c_0}}^{{b_k}}R_{{b_{k + 1}}}^{{c_0}}{v^{{b_{k + 1}}}} - {v^{{b_k}}} + R_{{c_0}}^{{b_k}}{g^{{c_0}}}\Delta t_k^{} β=Rc0bkRbk+1c0vbk+1vbk+Rc0bkgc0Δtk

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/66759.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Orin PPS failed to request pps gpio修改

前言 在使用Orin PPS过程中,已经配置了设备树文件,但是遇到了申请GPIO失败的问题,如下图: 申请GPIO失败。 1.分析及解决过程 1.1 设备树文件修改 在设备树文件hardware/nvidia/platform/t23x/concord/kernel-dts/cvb/tegra234-p3737-0000-a00.dtsi 中,添加关于pps gpi…

k8s,30分钟部署一个kubernetes集群【1.17】

作者:李振良 官方网站:http://www.ctnrs.com kubeadm是官方社区推出的一个用于快速部署kubernetes集群的工具。 这个工具能通过两条指令完成一个kubernetes集群的部署: # 创建一个 Master 节点 $ kubeadm init# 将一个 Node 节点加入到当前集群中 $ kubeadm join <Mast…

基于jsp+java+ssm的农产品购物商城系统-计算机毕业设计

项目介绍 随着计算机、信息化网络的普及&#xff0c;电子商务的兴起&#xff0c;网络支付以及网络安全体系逐渐完善&#xff0c;将人们的生活带入到网络时代&#xff0c;越来越多的人喜欢网上购物&#xff0c;消费者足不出户便可以买到自己喜欢的物品&#xff0c;只要轻轻点击…

TypeScript算法题实战——二叉搜索树篇

二叉搜索树&#xff0c;也叫二叉查找树、二叉排序树&#xff0c;是具有下列性质的二叉树&#xff1a; 若它的左子树不空&#xff0c;则左子树上所有结点的值均小于它的根结点的值&#xff1b; 若它的右子树不空&#xff0c;则右子树上所有结点的值均大于它的根结点的值。 注意…

零拷贝(Zero Copy)技术

概念 我们知道Linux系统分为用户态和内核态&#xff0c;在用户态每发起一次IO请求&#xff0c;就需要进行2次上下文切换&#xff08;分别是用户态->内核态&#xff0c;内核态→用户态&#xff09;&#xff0c;和一次CPU拷贝&#xff08;将数据从内核缓存拷贝到用户缓存&…

redis设置密码并修改查看的几种方式

前言 最近正值世纪杯期间&#xff0c;不知道大家心目中的球队成绩如何&#xff0c;最近在工作中需要设置redis服务器的密码的场景 设置密码可以在很大的程度保护redis服务器&#xff0c;但是相关的命令需要通过密码校验之后才能使用 下面就分享给大家两种设置redis服务器的密…

Java并发编程—CompletableFuture的异步执行案例

在博主前几篇博客中&#xff0c;https://blog.csdn.net/qq_52545155/article/details/128167519?spm1001.2014.3001.5501&#xff0c;给大家分享了关于多线程中异步任务的执行和一些相关概念&#xff0c;在这篇博客中&#xff0c;主要是通过一个实际的案例让大家对于Completab…

利用卷神经网络实现IOSLab数字手写图像识别

利用卷神经网络实现IOSLab数字手写图像识别 文章目录利用卷神经网络实现IOSLab数字手写图像识别一、前言二、作业要求三、数据集样本分析四、代码实现1、运行环境2、导入依赖项3、导入数据集4、加载数据和数据预处理5、划分数据集6、CNN网络结构构建7、编译模型8、训练模型9、模…

性能分析之解析 RESAR 性能分析七步法

文章目录一、压力场景数据分析二、架构分析三、响应时间分析四、全局监控分析五、定向监控六、判断性能瓶颈七、提出解决方案一、压力场景数据分析 下面是⼀个登录接⼝的基准场景测试&#xff0c;JMeter 启动后&#xff0c;线程在 1-2 个的时候 TPS 已经达到 200 左右&#xf…

JavaScript -- 字符串常用方法及示例代码介绍

文章目录字符串的方法1 length2 索引3 str.at()4 str.charAt()5 str.concat()6 str.includes()7 str.indexOf() 和 str.lastIndexOf()8 str.startsWith() 和 str.endsWith()9 str.padStart() 和 str.padEnd()10 str.replace() 和 str.replaceAll()11 str.slice() 和 str.substr…

Mysql调优(一)——性能监控

一、大纲图 【Mysql调优.xmind】 二、Mysql调优简写 2.1 性能监控 2.1.1 使用show profile查询剖析工具&#xff0c;可以指定具体的type 此工具默认是禁用的&#xff0c;可以通过服务器变量在会话级别动态的修改&#xff1a; set profiling1;当设置完成之后&#xff0c;在…

某Android大厂面试100题,涵盖测试技术、环境搭建、人力资源......【速度领取】

最近看到网上流传着各种面试经验及面试题&#xff0c;往往都是一大堆技术题目贴上去&#xff0c;但是没有答案。 为此&#xff0c;小编用业余时间整理了这份软测常见的面试题及详细答案&#xff0c;包含测试技术面试题、开发及环境搭建类面试题以及人力资源方向的面试题&#…

代码随想录训练营第42天|01背包问题、LeetCode 416. 分割等和子集

参考 代码随想录 01背包问题 01背包是在M件物品取出若干件放在空间为W的背包里&#xff0c;每件物品的体积为W1&#xff0c;W2至Wn&#xff0c;与之相对应的价值为P1,P2至Pn。01背包是背包问题中最简单的问题。01背包的约束条件是给定几种物品&#xff0c;每种物品有且只有一…

java计算机毕业设计ssm网上拍卖系统vdum4(附源码、数据库)

java计算机毕业设计ssm网上拍卖系统vdum4&#xff08;附源码、数据库&#xff09; 项目运行 环境配置&#xff1a; Jdk1.8 Tomcat8.5 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。…

MySql分库分表

问题分析&#xff1a; 随着互联网及移动互联网的发展&#xff0c;应用系统的数据量也是成指数式增长&#xff0c;若采用单数据库进行数据存储&#xff0c;存在以下性能瓶颈&#xff1a; 1、IO瓶颈&#xff1a;热点数据太多&#xff0c;数据库缓存不足&#xff0c;产生大量磁盘…

小快轻准,5分钟自助上线,中小型制造企业数字化转型

中小企业作为国民经济的重要组成部分&#xff0c;占据我国企业数量的90%以上。 继《中小企业数字化赋能专项行动方案》发布后&#xff0c;工信部近日印发《中小企业数字化转型指南》&#xff0c;提出14条具体举措&#xff0c;为中小企业明确数字化转型路径。 地方政府也纷纷出…

Linux系统管理、运行级别、关闭防火墙

目录 一、Linux服务管理 1.1 基本概念 1.2 systemctl&#xff08;centos7&#xff0c;很重要&#xff09; 1.2.1 基本语法 1.2.2 查看服务 1.2.3 防火墙操作 1.3 systemctl 设置后台服务的自启配置 二、系统运行级别 2.1 运行级别 2.2 查看当前运行级别 2.2修改当前运行…

8年三届世界杯,8年前端开发,梅西一共踢没了我八千八

转眼2022年即将过去&#xff0c;我已经做了那么久的开发了&#xff0c;一路走来&#xff0c;不断的工作&#xff0c;换工作&#xff0c;找工作&#xff0c;不断的学习新知识。 同时也看着梅西参加了3届世界杯了&#xff0c;逝者如斯夫&#xff0c;不舍昼夜啊。 在这8年里&#…

2023最新SSM计算机毕业设计选题大全(附源码+LW)之java焦作旅游网站q5msq

首先选择计算机题目的时候先看定什么主题&#xff0c;一般的话都选择当年最热门的话题进行组题&#xff0c;就比如说&#xff0c;今年的热门话题有奥运会&#xff0c;全运会&#xff0c;残运会&#xff0c;或者疫情相关的&#xff0c;这些都是热门话题&#xff0c;所以你就可以…

Elasticsearch Data Stream 数据流使用

本文是《Elasticsearch索引生命周期管理ILM》中数据流索引补充篇&#xff0c;文章地址如下&#xff1a; https://mp.weixin.qq.com/s/ajhFp-xBU1dJm8a1dDdRQQ 并且在另一片Elasticsearch的进阶使用-动态模版中也提到了相关数据流索引的内容&#xff0c;有兴趣的可以回过头看一下…