不就是G2O嘛

2023-10-26

从零开始一起学习SLAM | 理解图优化,一步步带你看懂g2o代码

SLAM的后端一般分为两种处理方法,一种是以扩展卡尔曼滤波(EKF)为代表的滤波方法,一种是以图优化为代表的非线性优化方法。不过,目前SLAM研究的主流热点几乎都是基于图优化的。

顺便总结下滤波方法的优缺点:

优点:在当时计算资源受限、待估计量比较简单的情况下,EKF为代表的滤波方法比较有效,经常用在激光SLAM中。

缺点:它的一个大缺点就是存储量和状态量是平方增长关系,因为存储的是协方差矩阵,因此不适合大型场景。而现在基于视觉的SLAM方案,路标点(特征点)数据很大,滤波方法根本吃不消,所以此时滤波的方法效率非常低

在SLAM里,图优化一般分解为两个任务:

1、构建图。机器人位姿作为顶点,位姿间关系作为边。

2、优化图。调整机器人的位姿(顶点)来尽量满足边的约束,使得误差最小。

g2o安装很简单,参考GitHub上官网:

https://github.com/RainerKuemmerle/g2o

1.顶点和边 

注意看 has-many 箭头,你看这个超图包含了许多顶点(HyperGraph::Vertex)和边(HyperGraph::Edge)。而这些顶点顶点继承自 Base Vertex,也就是OptimizableGraph::Vertex,而边可以继承自 BaseUnaryEdge(单边), BaseBinaryEdge(双边)或BaseMultiEdge(多边),它们都叫做OptimizableGraph::Edge

2.配置SparseOptimizer的优化算法和求解器

整个图的核心SparseOptimizer 包含一个优化算法(OptimizationAlgorithm)的对象。OptimizationAlgorithm是通过OptimizationWithHessian 来实现的。其中迭代策略可以从Gauss-Newton(高斯牛顿法,简称GN), Levernberg-Marquardt(简称LM法), Powell's dogleg 三者中间选择一个(我们常用的是GN和LM)

3.如何求解 

OptimizationWithHessian 内部包含一个求解器(Solver),这个Solver实际是由一个BlockSolver组成的。这个BlockSolver有两个部分,一个是SparseBlockMatrix ,用于计算稀疏的雅可比和Hessian矩阵;一个是线性方程的求解器(LinearSolver),它用于计算迭代过程中最关键的一步HΔx=−b,LinearSolver有几种方法可以选择:PCG, CSparse, Choldmod,具体定义后面会介绍

高博在十四讲中g2o求解曲线参数的例子来说明,源代码地址

https://github.com/gaoxiang12/slambook/edit/master/ch6/g2o_curve_fitting/main.cpp

typedef g2o::BlockSolver< g2o::BlockSolverTraits<3,1> > Block;  // 每个误差项优化变量维度为3,误差值维度为1

// 第1步:创建一个线性求解器LinearSolver
Block::LinearSolverType* linearSolver = new g2o::LinearSolverDense<Block::PoseMatrixType>(); 

// 第2步:创建BlockSolver。并用上面定义的线性求解器初始化
Block* solver_ptr = new Block( linearSolver );      

// 第3步:创建总求解器solver。并从GN, LM, DogLeg 中选一个,再用上述块求解器BlockSolver初始化
g2o::OptimizationAlgorithmLevenberg* solver = new g2o::OptimizationAlgorithmLevenberg( solver_ptr );

// 第4步:创建终极大boss 稀疏优化器(SparseOptimizer)
g2o::SparseOptimizer optimizer;     // 图模型
optimizer.setAlgorithm( solver );   // 设置求解器
optimizer.setVerbose( true );       // 打开调试输出

// 第5步:定义图的顶点和边。并添加到SparseOptimizer中
CurveFittingVertex* v = new CurveFittingVertex(); //往图中增加顶点
v->setEstimate( Eigen::Vector3d(0,0,0) );
v->setId(0);
optimizer.addVertex( v );
for ( int i=0; i<N; i++ )    // 往图中增加边
{
  CurveFittingEdge* edge = new CurveFittingEdge( x_data[i] );
  edge->setId(i);
  edge->setVertex( 0, v );                // 设置连接的顶点
  edge->setMeasurement( y_data[i] );      // 观测数值
  edge->setInformation( Eigen::Matrix<double,1,1>::Identity()*1/(w_sigma*w_sigma) ); // 信息矩阵:协方差矩阵之逆
  optimizer.addEdge( edge );//设置迭代次数
}

// 第6步:设置优化参数,开始执行优化
optimizer.initializeOptimization();
optimizer.optimize(100);

 1.线性求解器

LinearSolverCholmod :使用sparse cholesky分解法。继承自LinearSolverCCS
LinearSolverCSparse:使用CSparse法。继承自LinearSolverCCS
LinearSolverPCG :使用preconditioned conjugate gradient 法,继承自LinearSolver
LinearSolverDense :使用dense cholesky分解法。继承自LinearSolver
LinearSolverEigen: 依赖项只有eigen,使用eigen中sparse Cholesky 求解,因此编译好后可以方便的在其他地方使用,性能和CSparse差不多。继承自LinearSolver

2.创建BlockSolver。并用上面定义的线性求解器初始化。

BlockSolver 内部包含 LinearSolver,用上面我们定义的线性求解器LinearSolver来初始化。

你点进去会发现 BlockSolver有两种定义方式,一种是指定的固定变量的solver,我们来看一下定义

using BlockSolverPL = BlockSolver< BlockSolverTraits<p, l> >;

其中p代表pose的维度(注意一定是流形manifold下的最小表示),l表示landmark的维度

另一种是可变尺寸的solver,定义如下

using BlockSolverX = BlockSolverPL<Eigen::Dynamic, Eigen::Dynamic>;

 这是因为在某些应用场景,我们的Pose和Landmark在程序开始时并不能确定,那么此时这个块状求解器就没办法固定变量,此时使用这个可变尺寸的solver,所有的参数都在中间过程中被确定另外你看block_solver.h的最后,预定义了比较常用的几种类型,如下所示:

BlockSolver_6_3 :表示pose 是6维,观测点是3维。用于3D SLAM中的BA
BlockSolver_7_3:在BlockSolver_6_3 的基础上多了一个scale
BlockSolver_3_2:表示pose 是3维,观测点是2维

3.创建总求解器solver。并从GN, LM, DogLeg 中选一个,再用上述块求解器BlockSolver初始化

你点进去 GN、 LM、 Doglet算法内部,会发现他们都继承自同一个类OptimizationWithHessian,如下图所示,这也和我们最前面那个图是相符的。然后,我们点进去看 OptimizationAlgorithmWithHessian,发现它又继承自OptimizationAlgorithm,这也和前面的相符,总之,在该阶段,我们可以选则三种方法:

g2o::OptimizationAlgorithmGaussNewton
g2o::OptimizationAlgorithmLevenberg 
g2o::OptimizationAlgorithmDogleg 

4.创建终极大boss 稀疏优化器(SparseOptimizer),并用已定义求解器作为求解方法。

创建稀疏优化器

g2o::SparseOptimizer    optimizer;

用前面定义好的求解器作为求解方法:

SparseOptimizer::setAlgorithm(OptimizationAlgorithm* algorithm)

其中setVerbose是设置优化过程输出信息用的

SparseOptimizer::setVerbose(bool verbose)

5.定义图的顶点和边。并添加到SparseOptimizer中。

6.设置优化参数,开始执行优化。

设置SparseOptimizer的初始化、迭代次数、保存结果等。

SparseOptimizer::initializeOptimization(HyperGraph::EdgeSet& eset)

设置迭代次数,然后就开始执行图优化了。

SparseOptimizer::optimize(int iterations, bool online)

https://www.jianshu.com/p/e16ffb5b265d

https://blog.csdn.net/heyijia0327/article/details/47686523

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

不就是G2O嘛 的相关文章

随机推荐

  • 使用PHP简单操作Memcached

    记得一定要先启动Memcached哦 root localhost usr bin memcached d l 127 0 0 1 p 11211 m 150 u root 使用ps命令查看一下运行状态 root localhost ps
  • 结构体内存对齐详解

    目录 一 结构体的对齐规则 二 修改默认对齐数 三 位段 四 枚举 五 联合 共用体 一 结构体的对齐规则 1 第一个成员在与结构体变量偏移量为0的地址处 2 其他成员变量要对齐到某个数字 对齐数 的整数倍的地址处 对齐数 编译器默认的一个
  • Ubuntu16.04 64位下安装VMware Tools过程

    因为在虚拟机下安装Ubuntu16 04 64位时无法进入全屏模式 采用另外一种方法解决了 但是还是想安装一下VMware Tools 防止以后出现相关问题 好啦 下面进入正题 1 在虚拟机下会看到安装 VMware Tools 单击安装
  • C# 各种符号用法

    参考 C 的7种用法 c 软泡芙的博客 CSDN博客 C 问号操作符详解 知乎 的7种用法 可空类型修饰符 int i num null 表示可空的整型 DateTime time dateTime null 表示可空的时间 三元 运算符
  • join(long millis)意思?join()作用

    join 作用 class JoinThread implements Runnable public void run for int i 0 i lt 100 i System out println Thread currentThr
  • FT2232作为JTAG烧录器的使用步骤详解

    FT2232作为JTAG烧录器的使用步骤详解 FT2232作为JTAG烧录器的使用步骤详解 配置OpenOCD环境 已经配置好的可以跳过 步骤 1 安装 FT2232HL 芯片的驱动 安装文件为 CDM21228 Setup exe 步骤
  • unity AR3D物体识别

    上篇讲到了各种AR插件的一些对比 因为上个项目需求用到3D物体追踪 所以使用了EasyAR和Vuforia两种进行了测试对比 因为如果需要AR识别 都需要有识别点 大致都是基于物体材质纹理来进行识别 1 先讲一下EasyAR的3D物体识别
  • 【百问网】物联网项目学习总结

    初步实现项目 项目程序流程图 TCP连接流程 模式1 站点模式 写代码去实现了这个TCP的连接流程 我们的思路就是 首先直接操作ESP8266 进行功能验证 ST Link Debugger 点击Settings 勾选Reset and R
  • 远程访问数据库出错的解决办法

    案例 在aws服务器中的项目访问在华为云中的测试服务器的数据库 报错 Access denied for user root ec2 XXX XXX XXX XXX cn north 1 compute amazonaws com cn u
  • Linux指令系统文件复制到U盘

    Linux指令系统文件复制到U盘 文章目录 Linux指令系统文件复制到U盘 1 建立U盘挂载点 2 查看U盘所在分区 3 查看U盘类型 4 挂载U盘 5 复制文件 6 移除U盘 1 建立U盘挂载点 在挂载点处建立文件夹 mkdir 路径
  • 【Java面试题汇总】设计模式篇(2023版)

    导航 黑马Java笔记 踩坑汇总 JavaSE JavaWeb SSM SpringBoot 瑞吉外卖 SpringCloud 黑马旅游 谷粒商城 学成在线 设计模式 牛客面试题 目录 谈谈你对设计模式的理解 谈谈你对单例模式的理解 手写一
  • [17]AWK记录、字段、模式、跨平台移植及正则表达式

    一 记录与字段 1 记录分隔符 默认行输入和输出的分隔符都是回车 保存在RS和ORS内部变量中 变量 0 awk每次一行取得整条记录 0随之改变 同时内部变量NF 字段的总数 也随之变化 变量NR 每条记录的行号 处理完一行将会加1 所以全
  • InnoDB undo log解析(二)

    在 上一篇中已经介绍了InnoDB undo log的组织结构 并通过一个示例并结合InnoSQL来分析insert undo log记录格式 本篇中介绍update undo log的记录格式 update undo log有以下三种类型
  • cocoeval 解析

    1 COCO目标检测比赛中的模型评价指标介绍 dulingwen的博客 CSDN博客 2 COCO目标检测测评指标 简书 1 Params类 对于COCO格式的数据检测 我们主要分为不同的IoU阈值 不同的面积范围 单张图片的最大检测数量
  • PAT 5 猴子吃桃问题(递归)

    猴子吃桃问题 15 分 一只猴子第一天摘下若干个桃子 当即吃了一半 还不过瘾 又多吃了一个 第二天早上又将剩下的桃子吃掉一半 又多吃了一个 以后每天早上都吃了前一天剩下的一半加一个 到第N天早上想再吃时 见只剩下一个桃子了 问 第一天共摘了
  • Nuxt 项目引入外部Js的正确姿势 ,问题描述:打包构建之后引入的外部 js失效,构建之后的 .nuxt 文件夹下的js文件中,引入 js 的script标签凭空消失!

    首先先说我遇到的问题描述 我是用vue nuxt项目 项目中使用了阿里播放器 于是我在vue页面中直接引入了阿里播放器的 css和js文件 然后运行没有任何问题 如下图所示 但是 当我打包以后 发现这个页面竟然报错 我打开 打包好的 nux
  • Debugger problem "The breakpoint will not currently be hit. No symbols have been loaded for this doc...

    网上收集到的相关解决办法 1st Start debugging Now select Debug gt Windows gt Modules In the modules window taht now appears check wha
  • 解决Windows缺少d3dcompiler_37.dll文件

    其实很多用户玩单机游戏或者安装软件的时候就出现过这种问题 如果是新手第一时间会认为是软件或游戏出错了 其实并不是这样 其主要原因就是你电脑系统的该dll文件丢失了或者损坏了 这时你只需下载这个d3dcompiler 37 dll文件进行安装
  • VSCode选择远程服务器的虚拟环境

    方式一 选择服务器虚拟环境conda Ctrl Shift P 选择需要使用的虚拟环境即可 方式二 选择Terminal 点击 通过命令行切换即可 参考 Work on Python in VSCode remotely over ssh
  • 不就是G2O嘛

    从零开始一起学习SLAM 理解图优化 一步步带你看懂g2o代码 SLAM的后端一般分为两种处理方法 一种是以扩展卡尔曼滤波 EKF 为代表的滤波方法 一种是以图优化为代表的非线性优化方法 不过 目前SLAM研究的主流热点几乎都是基于图优化的