【SLAM】VINS-MONO解析——sliding window

2023-05-16

8.sliding window

8.1 理论基础

实际上,这一部分跟后端非线性优化是一起进行的,这一部分对应的非线性优化的损失函数的先验部分。理论基础部分的代码基本在第7章部分。
8.1.1 上一次非线性优化结束,最后的H矩阵就是本轮非线性优化的先验矩阵的前身。
8.1.2 构造先验矩阵
(1)移动需要marg掉的pose和路标点
对应的,J矩阵的一些列需要删除掉;H矩阵的一些行需要删掉。在操作先验矩阵的时候,需要把要marg掉的行和列移动到H矩阵的左上角,b向量相应的部分移到上方去,方便采用shur的方法marg掉,如下式所示:
在这里插入图片描述
mm对应的是要被marg掉的部分,rr是要留下来的部分。

(2)边缘化:构造先验矩阵
在这里插入图片描述
这个就是边缘化操作,边缘化之后,得到的先验矩阵H和先验向量b分别是:
在这里插入图片描述
滑窗会导致H矩阵不再稀疏。

8.1.3 构造非线性优化的H矩阵
这一部分实际上就是非线性优化部分关于构造H矩阵的内容。
(1)对先验矩阵进行扩维,扩充的维度就是下一次优化增加的状态量的维度
在这里插入图片描述
和直接 Bundle Adjustment 相比,多了一个先验矩阵的维护。

(2)与IMU和视觉部分的H矩阵进行叠加
实际上,每一轮优化的时候,H矩阵都是一块一块地增加的,所以先加上H矩阵,之后每添加一个残差项,总的H矩阵都会对应地自动增加。

8.1.4 进行非线性优化(FEJ)
这一部分实际上就是非线性优化部分关于LM/DOGLEG算法求解部分的内容。最后得到优化后的状态量。
这块涉及到FEJ问题。
先说结论,FEJ的意思就是说在总的大H矩阵中,先验矩阵对应的部分在本次优化的迭代过程中的值保持不变。

再说理由,
(1)当状态量1被merg掉之后,原本相互独立的量会变的不独立,表现在H矩阵中就是产生fill-in现象。
在这里插入图片描述

(2)当再加入新的观测时,如8.1.3-(1)粉色部分,H矩阵有的部分,既与先验有关,也与新加入的信息有关。

(3)假如说这部分H矩阵块不固定,每次迭代会得到新的线性化点,这个新的线性化点会与先验的线性化点不一样,可能会导致信息矩阵的零空 间发生变化,从而在求解时引入错误信息。
为什么?
因为信息矩阵 Λ 不满秩。对应的零空间为 N, 用高斯牛顿求解时有
在这里插入图片描述
增量 δx 在零空间维度下变化,并不会改变我们的残差。这意味着 系统可以有多个满足最小化损失函数的解 x。

(4)假如说不固定先验部分对应的线性化点,会使原本不可观的信息变得可观,多个解的问题变成了一个确定解。而这个是错误的。
在这里插入图片描述

(5)所以,采用FEJ,也就是first estimated jacobian。不同残差对同一个状态求雅克比时,线性化点必须一致。这样就能避免零空间退化而使得不可观变量变得可观。也就是说,在总的大H矩阵中,先验矩阵对应的部分在本次优化的迭代过程中的值保持不变。

(6)先验矩阵不变,但是先验残差得变!
虽然先验信息矩阵固定不变,但随着迭代的推进,变量被 不断优化,先验残差需要跟随变化。否则,求解系统可能奔溃。
方法:先验残差的变化可以使用一阶泰勒近似。
在这里插入图片描述

其它部分,就和非线性优化一样了。但是有一点需要注意,就是在g20中,传入的是Jacobian,对于IMU残差和重投影残差,Jacobian都是知道的,但是这个先验部分,我们知道的是H,所以就需要根据marg后的状态量反解出一个Jacobian。

8.1.5 返回步骤8.1.1,进行下一次的非线性优化。
在vins中,实际上它是8.1.4是循环的第一步。另外,崔神说vins并没有采用FEJ。

8.2 代码

slideWhindow()这个函数在初始化和非线性优化部分都出现过。对于marg的是old还是new,这是在5.2.1部分进行的。

8.2.1 if (marginalization_flag == MARGIN_OLD)
删除的是滑窗第一帧。
(1) 保存最老帧信息

double t_0 = Headers[0].stamp.toSec();
back_R0 = Rs[0];
back_P0 = Ps[0];

(2) 依次把滑窗内信息前移

if (frame_count == WINDOW_SIZE)
{
    for (int i = 0; i < WINDOW_SIZE; i++)
    {
        Rs[i].swap(Rs[i + 1]);
        std::swap(pre_integrations[i], pre_integrations[i + 1]);
        dt_buf[i].swap(dt_buf[i + 1]);
        linear_acceleration_buf[i].swap(linear_acceleration_buf[i + 1]);
        angular_velocity_buf[i].swap(angular_velocity_buf[i + 1]);
        Headers[i] = Headers[i + 1];
        Ps[i].swap(Ps[i + 1]);
        Vs[i].swap(Vs[i + 1]);
        Bas[i].swap(Bas[i + 1]);
        Bgs[i].swap(Bgs[i + 1]);
    }

(3) 把滑窗末尾(10帧)信息给最新一帧(11帧)

Headers[WINDOW_SIZE] = Headers[WINDOW_SIZE - 1];
Ps[WINDOW_SIZE] = Ps[WINDOW_SIZE - 1];
Vs[WINDOW_SIZE] = Vs[WINDOW_SIZE - 1];
Rs[WINDOW_SIZE] = Rs[WINDOW_SIZE - 1];
Bas[WINDOW_SIZE] = Bas[WINDOW_SIZE - 1];
Bgs[WINDOW_SIZE] = Bgs[WINDOW_SIZE - 1];


注意,在(2)中,已经实现了所有信息的前移,此时,最新一帧已经成为了滑窗中的第10帧,这里只是把原先的最新一帧的信息作为下一次最新一帧的初始值。

(4) 新实例化一个IMU预积分对象给下一个最新一帧

delete pre_integrations[WINDOW_SIZE];
pre_integrations[WINDOW_SIZE] = new IntegrationBase{acc_0, gyr_0, Bas[WINDOW_SIZE], Bgs[WINDOW_SIZE]};

(5) 清空第11帧的buf

dt_buf[WINDOW_SIZE].clear();
linear_acceleration_buf[WINDOW_SIZE].clear();
angular_velocity_buf[WINDOW_SIZE].clear();

(6)删除最老帧对应的全部信息

map<double, ImageFrame>::iterator it_0;
it_0 = all_image_frame.find(t_0);//6.找到滑窗内最老一帧信息
delete it_0->second.pre_integration;//删掉这一帧的预积分信息
it_0->second.pre_integration = nullptr;//置空这一帧的预积分信息
 
for(map<double, ImageFrame>::iterator it = all_image_frame.begin(); it!= it_0; ++it)
{//7.把滑窗内最老一帧以前的帧的预积分信息全删掉
    if (it->second.pre_integration)
        delete it->second.pre_integration;
    it->second.pre_integration = NULL;
}
//8.删掉滑窗内最老帧以前的所有帧(不包括最老帧),和最老帧
all_image_frame.erase(all_image_frame.begin(), it_0);
all_image_frame.erase(t_0);   
}


(7) slideWindowOld()

void Estimator::slideWindowOld()
{
    sum_of_back++;//统计一共有多少次merge滑窗第一帧的情况

    bool shift_depth = solver_flag == NON_LINEAR ? true : false;
    if (shift_depth)
    {
        Matrix3d R0, R1;
        Vector3d P0, P1;
        R0 = back_R0 * ric[0];//滑窗原先的最老帧(被merge掉)的旋转(c->w)
        R1 = Rs[0] * ric[0];//滑窗原先第二老的帧(现在是最老帧)的旋转(c->w)
        P0 = back_P0 + back_R0 * tic[0];//滑窗原先的最老帧(被merge掉)的平移(c->w)
        P1 = Ps[0] + Rs[0] * tic[0];//滑窗原先第二老的帧(现在是最老帧)的平移(c->w)
        f_manager.removeBackShiftDepth(R0, P0, R1, P1);//把首次在原先最老帧出现的特征点转移到原先第二老帧的相机坐标里(仅在slideWindowOld()出现过)
    }
    else
        f_manager.removeBack();//当最新一帧是关键帧时,用于merge滑窗内最老帧(仅在slideWindowOld()出现过)
}



8.2.2 if (marginalization_flag == MARGIN_NEW)
删除的是滑窗第10帧。

(1)取出最新一帧的信息

else
{
    if (frame_count == WINDOW_SIZE)
    {
        for (unsigned int i = 0; i < dt_buf[frame_count].size(); i++)
        {
            double tmp_dt = dt_buf[frame_count][i];
            Vector3d tmp_linear_acceleration = linear_acceleration_buf[frame_count][i];
            Vector3d tmp_angular_velocity = angular_velocity_buf[frame_count][i];

(2) 当前帧和前一帧之间的 IMU 预积分转换为当前帧和前二帧之间的 IMU 预积分

    pre_integrations[frame_count - 1]->push_back(tmp_dt, tmp_linear_acceleration, tmp_angular_velocity);
    dt_buf[frame_count - 1].push_back(tmp_dt);
    linear_acceleration_buf[frame_count - 1].push_back(tmp_linear_acceleration);
    angular_velocity_buf[frame_count - 1].push_back(tmp_angular_velocity);
}


(3) 用最新一帧的信息覆盖上一帧信息

Headers[frame_count - 1] = Headers[frame_count];
Ps[frame_count - 1] = Ps[frame_count];
Vs[frame_count - 1] = Vs[frame_count];
Rs[frame_count - 1] = Rs[frame_count];
Bas[frame_count - 1] = Bas[frame_count];
Bgs[frame_count - 1] = Bgs[frame_count];

(4) 因为已经把第11帧的信息覆盖了第10帧,所以现在把第11帧清除

delete pre_integrations[WINDOW_SIZE];
pre_integrations[WINDOW_SIZE] = new IntegrationBase{acc_0, gyr_0, Bas[WINDOW_SIZE], Bgs[WINDOW_SIZE]};

dt_buf[WINDOW_SIZE].clear();
linear_acceleration_buf[WINDOW_SIZE].clear();
angular_velocity_buf[WINDOW_SIZE].clear();

(5) 滑窗

        slideWindowNew();//为什么这里可以不对前一帧进行边缘化而是直接丢弃,原因就是当前帧和前一帧很相似。
    }//因此当前帧与地图点之间的约束和前一帧与地图点之间的约束是接近的,直接丢弃并不会造成整个约束关系丢失信息
}

再看看slideWindowNew()里面的具体内容。

void Estimator::slideWindowNew()
{//因为已经把第11帧的信息覆盖了第10帧,所以现在把第11帧清除
    sum_of_front++;//统计一共有多少次merge滑窗第10帧的情况
    f_manager.removeFront(frame_count);//唯一用法:当最新一帧(11)不是关键帧时,用于merge滑窗内最新帧(10)(仅在slideWindowNew()出现过)
}

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

【SLAM】VINS-MONO解析——sliding window 的相关文章

随机推荐

  • Docker 镜像 Tag 管理

    Author xff1a rab 良好的镜像版本命名习惯能让我们更好的管理和使用镜像 xff08 如项目上线失败后可有效的进行版本回退等 xff09 xff0c 以下是 Docker 社区常用的 tag 方案 比如我现在已经构建了一个 co
  • APM与Pixhawk间的关系

    1 APM 本文APM指代 xff1a https github com ArduPilot ardupilot 2 Pixhawk 本文Pixhawk指代 xff1a https github com PX4 Firmware 3 关系
  • Pixhawk串口名称与硬件接口对应关系

    Pixhawk提供的串口较多 xff0c 通过ls dev 可以看到有如下7个tty设备 xff1a ttyACM0 ttyS0 ttyS1 ttyS2 ttyS3 ttyS4 ttyS5 ttyS6 但每个串口名称对应到Pixhawk硬件
  • Linux系统大小端判断

    大端模式 大端模式 xff0c 是指数据的低位保存在内存的高地址中 xff0c 而数据的高位保存在内存的低地址中 小端模式 小端模式 xff0c 是指数据的低位保存在内存的低地址中 xff0c 而数据的高位保存在内存的高地址中 判断程序 文
  • C preprocessor fails sanity check

    编译某一产品固件时 xff0c 遇到如下现象 xff1a checking how to run the C preprocessor opt mipsel 24kec linux uclibc bin mipsel 24kec linux
  • VLC同时开启播放多个视频流BAT脚本

    工作中 xff0c 难免会遇到要用同一个程序连续打开多个URL资源 路径的情况 xff0c 一个窗口一个窗口的启动效率太低 这里以VLC同时播放多个码流图像为例 xff0c 写个简单的BAT脚本 xff0c 供需要者参考 PS 1 使用方式
  • 【AI】Ubuntu14.04安装OpenCV3.2.0

    在ubuntu14 04系统上安装OpenCV3 2 0 环境要求 GCC 4 4 x or later CMake 2 8 7 or higher Git if failed you can replace it with git cor
  • 若依代码生成器(mybatis-plus)

    看这篇文章之前 xff0c 先去看一下我前面的文章 xff1a 若依前后端分离整合mybatis plus wjdsg的博客 CSDN博客 用过若依都知道 xff0c 若依自带的代码生成器 xff0c 是下载下来 xff0c 然后自己粘贴到
  • 【AI】基于OpenCV开发自定义程序编译方法

    基于OpenCV开发自定义程序编译方法 OpenCV自带的程序 xff0c 编译均采用cmake统一编译 若我们要基于OpenCV开发自己的程序 xff0c 如何快速编译 xff1f 本文以OpenCV库自带的facedetect cpp程
  • H3C SNMPv3 配置

    1 xff09 H3C SNMPv3 配置 snmp agent mib view included MIB 2 mib 2 noAuthNoPriv xff1a snmp agent group v3 mygroup read view
  • 【SLAM】VINS-MONO解析——综述

    目前网上有很多分析文章 xff0c 但是都只是一些比较基础的原理分析 xff0c 而且很多量 xff0c 虽然有推倒 xff0c 但是往往没有讲清楚这些量是什么 xff0c 为什么要有这些量 xff0c 这些量是从哪来的 xff0c 也没有
  • 【SLAM】VINS-MONO解析——前端

    各个部分的讲解如下链接 xff1a SLAM VINS MONO解析 综述 SLAM VINS MONO解析 feature tracker SLAM VINS MONO解析 IMU预积分 SLAM VINS MONO解析 vins est
  • 【SLAM】VINS-MONO解析——IMU预积分

    4 IMU预积分 IMU预积分主要干了2件事 xff0c 第一个是IMU预积分获得 值 xff0c 另一个是误差传递函数的获取 本部分的流程图如下图所示 各个部分的讲解如下链接 xff1a SLAM VINS MONO解析 综述 SLAM
  • 【SLAM】VINS-MONO解析——vins_estimator流程

    5 vins estimator 基本上VINS里面绝大部分功能都在这个package下面 xff0c 包括IMU数据的处理 前端 xff0c 初始化 我觉得可能属于是前端 xff0c 滑动窗口 后端 xff0c 非线性优化 后端 xff0
  • 【SLAM】VINS-MONO解析——初始化(理论部分)

    6 初始化 第一个问题 xff0c 为什么要初始化 xff1f 对于单目系统而言 xff0c 1 视觉系统只能获得二维信息 xff0c 损失了一维信息 深度 所以需要动一下 xff0c 也就是三角化才能重新获得损失的深度信息 xff1b 2
  • 【SLAM】VINS-MONO解析——初始化(代码部分)

    6 2 代码解析 这部分代码在estimator processImage 最后面 初始化部分的代码虽然生命周期比较短 xff0c 但是 xff0c 代码量巨大 xff01 主要分成2部分 xff0c 第一部分是纯视觉SfM优化滑窗内的位姿
  • 【SLAM】VINS-MONO解析——后端优化(理论部分)

    7 后端非线性优化 7 1 理论基础 7 1 1 bayes模型 xff0c 因子图和最小二乘 这一部分主要是对董靖博士在公开课 因子图的理论基础 上的回忆和总结 1 bayes模型 假设有黄色是机器人在不同时刻的位姿 xff0c 蓝色是机
  • 【SLAM】VINS-MONO解析——后端优化(代码部分)

    7 2 代码 在estimator cpp的processImage 的最后 xff0c 代码如下 xff1a span class token keyword else span span class token comment solv
  • 51单片机通过两个按键控制流水灯方向

    按键一接单片机P3 1 xff0c 按键2接P3 0 8个流水灯接P2口 以下是代码 xff1a include lt regx52 H gt include lt INTRINS H gt 延时函数 xff0c xms等于1 xff0c
  • 【SLAM】VINS-MONO解析——sliding window

    8 sliding window 8 1 理论基础 实际上 xff0c 这一部分跟后端非线性优化是一起进行的 xff0c 这一部分对应的非线性优化的损失函数的先验部分 理论基础部分的代码基本在第7章部分 8 1 1 上一次非线性优化结束 x