vins-mono初始化代码分析

news2025/1/12 0:46:44

大体流程

初始化主要分成2部分,第一部分是纯视觉SfM优化滑窗内的位姿,然后在融合IMU信息。
这部分代码在estimator::processImage()最后面。
在这里插入图片描述

主函数入口:

void Estimator::processImage(const map<int, vector<pair<int, Eigen::Matrix<double, 7, 1>>>> &image, const std_msgs::Header &header)

相机和imu旋转外参数的估计,分两步走:

  1. 获取最新两帧之间匹配的特征点对
vector<pair<Vector3d, Vector3d>> FeatureManager::getCorresponding(int frame_count_l, int frame_count_r)
  1. 计算相机-IMU之间的旋转
    利用旋转约束去估计
    q b k b k + 1 ⊗ q b c = q b c ⊗ q c k c k + 1 q_{b_kb_{k+1}} \otimes q_{bc} = q_{bc} \otimes q_{c_kc_{k+1}} qbkbk+1qbc=qbcqckck+1
    在这里插入图片描述
bool CalibrationExRotation(vector<pair<Vector3d, Vector3d>> corres, Quaterniond delta_q_imu, Matrix3d &calib_ric_result)
{
 //! Step1: 滑窗內幀數加1
    frame_count++;
    //! Step2: 计算两帧之间的旋转矩阵
    // 利用帧可视化的3D点求解相邻两帧之间的旋转矩阵R_{ck, ck+1}
    Rc.push_back(solveRelativeR(corres)); //两帧图像之间的旋转通过solveRelativeR计算出本质矩阵E,再对矩阵进行分解得到图像帧之间的旋转R。
    //delta_q_imu为IMU预积分得到的旋转四元数,转换成矩阵形式存入Rimu当中。则Rimu中存放的是imu预积分得到的旋转矩阵
    Rimu.push_back(delta_q_imu.toRotationMatrix());
    //每帧IMU相对于起始帧IMU的R,ric初始化值为单位矩阵,则Rc_g中存入的第一个旋转向量为IMU的旋转矩阵
    Rc_g.push_back(ric.inverse() * delta_q_imu * ric);

    Eigen::MatrixXd A(frame_count * 4, 4);
    A.setZero();
    int sum_ok = 0;
    //遍历滑动窗口中的每一帧
    for (int i = 1; i <= frame_count; i++)
    {
        Quaterniond r1(Rc[i]);
        Quaterniond r2(Rc_g[i]);
        
        //!Step3:求取估计出的相机与IMU之间旋转的残差 公式(9)
        double angular_distance = 180 / M_PI * r1.angularDistance(r2);
        ROS_DEBUG(
            "%d %f", i, angular_distance);
        
        //! Step4:计算外点剔除的权重 Huber函数 公式(8) 
        double huber = angular_distance > 5.0 ? 5.0 / angular_distance : 1.0;
        ++sum_ok;
        Matrix4d L, R;
        
        //! Step5:求取系数矩阵        
        //! 得到相机对极关系得到的旋转q的左乘
        //四元数由q和w构成,q是一个三维向量,w为一个数值
        double w = Quaterniond(Rc[i]).w();
        Vector3d q = Quaterniond(Rc[i]).vec();
        //L为相机旋转四元数的左乘矩阵,Utility::skewSymmetric(q)计算的是q的反对称矩阵
        L.block<3, 3>(0, 0) = w * Matrix3d::Identity() + Utility::skewSymmetric(q);
        L.block<3, 1>(0, 3) = q;
        L.block<1, 3>(3, 0) = -q.transpose();
        L(3, 3) = w;
        
        //! 得到由IMU预积分得到的旋转q的右乘
        Quaterniond R_ij(Rimu[i]);
        w = R_ij.w();
        q = R_ij.vec();
        R.block<3, 3>(0, 0) = w * Matrix3d::Identity() - Utility::skewSymmetric(q);
        R.block<3, 1>(0, 3) = q;
        R.block<1, 3>(3, 0) = -q.transpose();
        R(3, 3) = w;

        A.block<4, 4>((i - 1) * 4, 0) = huber * (L - R);
    }
    
    //!Step6:通过SVD分解,求取相机与IMU的相对旋转    
    //!解为系数矩阵A的右奇异向量V中最小奇异值对应的特征向量
    JacobiSVD<MatrixXd> svd(A, ComputeFullU | ComputeFullV);
    Matrix<double, 4, 1> x = svd.matrixV().col(3);
    Quaterniond estimated_R(x);
    ric = estimated_R.toRotationMatrix().inverse();
    //cout << svd.singularValues().transpose() << endl;
    //cout << ric << endl;

    //!Step7:判断对于相机与IMU的相对旋转是否满足终止条件    
    //!1.用来求解相对旋转的IMU-Camera的测量对数是否大于滑窗大小。    
    //!2.A矩阵第二小的奇异值是否大于某个阈值,使A得零空间的秩为1
    Vector3d ric_cov;
    ric_cov = svd.singularValues().tail<3>();
    if (frame_count >= WINDOW_SIZE && ric_cov(1) > 0.25)
    {
        calib_ric_result = ric;
        return true;
    }
    else
        return false;
}

计算出 q b c q_{bc} qbc后,对 b g bg bg, [ v 0 , v 1 , . . . , v n , g , s {v_0, v_1, ...,v_n, g, s} v0,v1,...,vn,g,s]进行初始化

bool Estimator::initialStructure()

在这里插入图片描述
IMU陀螺仪bias初始化:
在这里插入图片描述

void solveGyroscopeBias(map<double, ImageFrame> &all_image_frame, Vector3d* Bgs)
{
    Matrix3d A;
    Vector3d b;
    Vector3d delta_bg;
    A.setZero();
    b.setZero();
    map<double, ImageFrame>::iterator frame_i;
    map<double, ImageFrame>::iterator frame_j;
    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();
        Eigen::Quaterniond q_ij(frame_i->second.R.transpose() * frame_j->second.R);
        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);
    ROS_WARN_STREAM("gyroscope bias initial calibration " << delta_bg.transpose());

    for (int i = 0; i <= WINDOW_SIZE; i++)
        Bgs[i] += delta_bg;

    for (frame_i = all_image_frame.begin(); next(frame_i) != all_image_frame.end( ); frame_i++)
    {
        frame_j = next(frame_i);
        frame_j->second.pre_integration->repropagate(Vector3d::Zero(), Bgs[0]);
    }
}

[ v 0 , v 1 , . . . , v n , g c 0 , s {v_0, v_1, ...,v_n, g^{c0}, s} v0,v1,...,vn,gc0,s]初始化:
α b k + 1 b k = R w b k ( P b k + 1 w − P b k w − v b k w Δ t + 1 2 g w Δ t 2 ) \alpha_{b_{k+1}}^{b_k} = R_{w}^{b_k}(P_{b_{k+1}}^w - P_{b_{k}}^w - v_{b_k}^w \Delta t + \frac{1}{2}g^w \Delta t^2 ) \\ αbk+1bk=Rwbk(Pbk+1wPbkwvbkwΔt+21gwΔt2)
β b k + 1 b k = R w b k ( v b k + 1 w − v b k w + g w Δ t ) \beta_{b_{k+1}}^{b_k} = R_{w}^{b_k}(v_{b_{k+1}}^w - v_{b_k}^w + g^w \Delta t) βbk+1bk=Rwbk(vbk+1wvbkw+gwΔt)
通过平移约束 s p b k c 0 = s p c k c 0 − R b c 0 p c b sp_{b_k}^{c_0} = sp_{c_k}^{c_0} - R_b^{c_0}p_c^b spbkc0=spckc0Rbc0pcb带入上式可得:
α b k + 1 b k = R c 0 b k ( s ( P b k + 1 c 0 − P b k c 0 ) − R b k c 0 v b k b k Δ t + 1 2 g c 0 Δ t 2 ) \alpha_{b_{k+1}}^{b_k} = R_{c_0}^{b_k}(s(P_{b_{k+1}}^{c_0} - P_{b_{k}}^{c_0}) - R_{b_k}^{c_0}v_{b_k}^{b_k} \Delta t + \frac{1}{2}g^{c_0} \Delta t^2 ) \\ αbk+1bk=Rc0bk(s(Pbk+1c0Pbkc0)Rbkc0vbkbkΔt+21gc0Δt2)

β b k + 1 b k = R c 0 b k ( R b k + 1 c 0 v b k + 1 b k + 1 − R b k c 0 v b k b k + g c 0 Δ t ) \beta_{b_{k+1}}^{b_k} = R_{c_0}^{b_k}(R_{b_{k+1}}^{c_0}v_{b_{k+1}}^{b_{k+1}} - R_{b_k}^{c_0}v_{b_k}^{b_k} + g^{c_0} \Delta t) βbk+1bk=Rc0bk(Rbk+1c0vbk+1bk+1Rbkc0vbkbk+gc0Δt)
在这里插入图片描述
在这里插入图片描述
同样将 δ β b k + 1 b k 转 为 矩 阵 形 式 \delta \beta_{b_{k+1}}^{b_k}转为矩阵形式 δβbk+1bk
在这里插入图片描述
即: H 6 × 10 X I 10 × 1 = b 6 × 1 H^{6 \times 10}X_{I}^{10 \times 1} = b^{6 \times 1} H6×10XI10×1=b6×1
这样,可以通过Cholosky分解下面方程求解 X I X_{I} XI:
H T H X I 10 × 1 = H T b H_{T}HX_{I}^{10 \times 1} = H^{T}b HTHXI10×1=HTb

bool LinearAlignment(map<double, ImageFrame> &all_image_frame, Vector3d &g, VectorXd &x)
{
   int all_frame_count = all_image_frame.size();
   // 速度维度:all_frame_count * 3; 重力维度:3; scale维度:1;
   int n_state = all_frame_count * 3 + 3 + 1;

   // 构建 Ax = b 方程求解
   MatrixXd A{n_state, n_state};
   A.setZero();
   VectorXd b{n_state};
   b.setZero();

   map<double, ImageFrame>::iterator frame_i;
   map<double, ImageFrame>::iterator frame_j;
   int i = 0;
   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);
       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;
       VectorXd r_b = tmp_A.transpose() * cov_inv * tmp_b;

       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>();
   }
   A = A * 1000.0;
   b = b * 1000.0;
   x = A.ldlt().solve(b);
   double s = x(n_state - 1) / 100.0;
   ROS_DEBUG("estimated scale: %f", s);
   g = x.segment<3>(n_state - 4);
   ROS_DEBUG_STREAM(" result g     " << g.norm() << " " << g.transpose());
   if(fabs(g.norm() - G.norm()) > 1.0 || s < 0)
   {
       return false;
   }

   RefineGravity(all_image_frame, g, x);
   s = (x.tail<1>())(0) / 100.0;
   (x.tail<1>())(0) = s;
   ROS_DEBUG_STREAM(" refine     " << g.norm() << " " << g.transpose());
   if(s < 0.0 )
       return false;   
   else
       return true;
}

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

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

相关文章

maven大全(概述、maven安装配置、IDEA配置maven、IDEA创建maven项目并如何使用)

目录 一、概述 1.什么是maven&#xff1f; 2.maven有什么作用&#xff1f; &#xff08;1&#xff09;提供了一套标准化的项目结构 &#xff08;2&#xff09;提供了标准化的构建流程&#xff08;编译、测试、打包、发布&#xff09; &#xff08;3&#xff09;提供了一套…

Java -- 每日一问:后台服务出现明显“变慢”,谈谈你的诊断思路?

典型回答 首先&#xff0c;需要对这个问题进行更加清晰的定义: 服务是突然变慢还是长时间运行后观察到变慢&#xff1f;类似问题是否重复出现&#xff1f;“慢”的定义是什么&#xff0c;我能够理解是系统对其他方面的请求的反应延时变长吗? 第二&#xff0c;理清问题的症状…

【计算机考研必备常识】24考研你开始准备了吗?

前言 23考研只剩下一个多月了&#xff0c;准备 【24考研】 的小伙伴是否有一丝丝焦虑了呢&#xff1f; 对于考研相关的常识问题&#xff0c;你又是否有了解呢&#xff1f;考研全流程&#xff1f;计算机考研考什么&#xff1f;学硕和专硕怎么选 … 一系列考研相关的常识问题博…

JWT和token是什么?如何利用token进行身份验证?

什么是token&#xff1f;什么是JWT&#xff1f;如何基于token进行身份验证&#xff1f; 我们都知道session信息需要保存一份在服务器端。这种方式会带来一些麻烦&#xff0c;比如需要我们保证保存session信息服务器的可用性、不适合移动端等。 有没有一种不需要自己存放sessi…

五、DMSQL

五、数据类型与操作符和常用DMSQL语句 1、数据类型与操作符介绍 达梦数据库支持的数据类型有很多&#xff0c;具体如下&#xff1a; 其中&#xff1a; 常规数据类型 数值数据类型字符数据类型多媒体数据类型日期时间数据类型 一般日期时间类型时区数据类型时间间隔数据类型 B…

辰奕智能在创业板过会:计划募资约4亿元,约有五成来自境外

11月18日&#xff0c;深圳证券交易所创业板披露的信息显示&#xff0c;广东辰奕智能科技股份有限公司&#xff08;下称“辰奕智能”&#xff09;获得上市委会议通过&#xff0c;即IPO过会。据贝多财经了解&#xff0c;辰奕智能于2021年12月31日在创业板递交上市申请材料。 本次…

【论文阅读】社交网络传播最大化问题-01

问题定义&#xff1a;构建传播最大化模型&#xff08;最大化末态时的激活节点数量 &#xff09;& 确定最具影响力节点 思考问题&#xff1a; 影响节点影响力的因素&#xff1f;有向图和无向图的模型构建区别&#xff1f; 定义参数&#xff1a; 节点影响力的取值范围节点…

Thinkphp6.0.x反序列化漏洞复现

漏洞起点 起因: 在做 [安洵杯 2019]iamthinking 时发现是 thinkphp6 的反序列化&#xff0c;那么就去复现一下呗。 看了其他大佬的 wp&#xff0c;上面说 tp6 的反序列化漏洞的后半段利用和 tp5.2.x 是一样的&#xff0c;也就是 __toString 函数上。 第一步相信大家都知道&a…

USV合伙人反思FTX:应以更长远的眼光看待Web3

潜力博主推荐&#xff0c;点击上面关注博主 ↑↑ FTX的事件动摇了许多人的信心。那么&#xff0c;最大的加密货币交易所之一是如何迅速崩溃的&#xff1f;为什么加密世界的类似崩溃似乎一直在发生&#xff1f; 在这个时候&#xff0c;我们要对Web3整个行业&#xff0c;有一个更…

FA-PEG-N3,Folic acid-PEG-Azide,叶酸-聚乙二醇-叠氮一种叶酸PEG试剂

叶酸PEG试剂叶酸-聚乙二醇-叠氮&#xff0c;其英文名为Folic acid-PEG-Azide&#xff08;FA-PEG-N3&#xff09;&#xff0c;它所属分类为Azide PEG Folic acid&#xff08;FA&#xff09; PEG。 叶酸-PEG-叠氮的的分子量均可定制&#xff0c;有&#xff1a;FA-PEG-N3 5000、叶…

感受Vue (1) —— Hello world

虽然一直定位自己是个后端&#xff0c;但是我一直钟情于好看精致的界面&#xff0c;我觉得前端界面是门艺术并结合编程的美。爱美之心&#xff0c;人皆有之&#xff0c;不要怪我&#xff0c;也不能怪我。 vue 在前端框架中&#xff0c;世界范围内能排第三&#xff0c;也是很不简…

UE5笔记【零】快捷键

F&#xff1a;快速聚焦到所选中的对象。 Q&#xff1a;选择 W&#xff1a;移动、 E&#xff1a;旋转、 R&#xff1a;伸缩。 End&#xff1a;物体落在它下方的物体上。 组合键&#xff1a; 鼠标左键或者右键&#xff1a;E是跳跃&#xff0c;Q是蹲下。 Ctrl L:控制太阳高…

[附源码]SSM计算机毕业设计在线学习网站的设计与实现JAVA

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

使用docker 注册runner

获取gitlab 信息 需要从gitlab中获取两个信息&#xff0c;一个是gitlab的域名&#xff0c;一个是需要注册runner的token gitalb 的runner按照范围可以有三种 全局类型即整个gitlab 的项目都可使用的runnergroup类型&#xff1a;即当前group中的项目可使用的runner,不同group之…

1-4 Linux 标准目录结构FHS

文章目录前言标准目录结构/ (根目录)/bin/boot/dev/etc/home/lib/media/mnt/opt/run/sbin/srv/tmp/proc/sys/var/lostfound/root/usr前言 Linux操作系统中的目录(文件夹)结构遵循Linux基金会定义和维护的Linux文件系统层次标准(FHS)。有了定义良好的标准&#xff0c;用户和软件…

【VC】【全局修改windows系统环境变量】 实现和原理详解

文章目录导读开发环境实现通过procexp打开1836进程的环境变量列表修改注册表&#xff08;手动/编码实现&#xff09;广播WM_SETTINGCHANGE消息再次通过procexp打开1836进程的环境变量列表也可以通过《系统属性 > 环境变量》来查看是否生效文章小结参考资料导读 一直都很好奇…

[附源码]java毕业设计水果商城

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

十三、Mysql的存储引擎

Mysql的存储引擎十三、Mysql的存储引擎一、什么是存储引擎二、存储引擎的常见功能三、存储引擎的种类及特性对比1、存储引擎的种类2、常见存储引擎的特性对比3、查看存储引擎四、InnoDB存储引擎1、InnoDB存储引擎介绍2、InnoDB存储引擎的优点3、InnoDB与MyISAM的区别4、存储引擎…

PTA题目 三天打鱼两天晒网

中国有句俗语叫“三天打鱼两天晒网”。假设某人从某天起&#xff0c;开始“三天打鱼两天晒网”&#xff0c;问这个人在以后的第N天中是“打鱼”还是“晒网”&#xff1f; 输入格式&#xff1a; 输入在一行中给出一个不超过1000的正整数N。 输出格式&#xff1a; 在一行中输…

【网页设计】基于HTML在线图书商城购物项目设计与实现

⛵ 源码获取 文末联系 ✈ Web前端开发技术 描述 网页设计题材&#xff0c;DIVCSS 布局制作,HTMLCSS网页设计期末课程大作业 | 在线商城购物 | 水果商城 | 商城系统建设 | 多平台移动商城 | H5微商城购物商城项目 | HTML期末大学生网页设计作业&#xff0c;Web大学生网页 HTML&a…