G2O框架

Posted npulzb

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了G2O框架相关的知识,希望对你有一定的参考价值。

(一)简介:

  g2o简称General Graphic Optimization,是一个用来优化非线性误差函数的c++框架

(二)g2o基本框架

技术图片

1: SparseOptimizer

SparseOptimizer是整个图的核心,我们注意右上角的 is-a 实心箭头,这个SparseOptimizer它是一个Optimizable Graph,从而也是一个超图(HyperGraph)

其中超图(HyperGraph)包括多个边与顶点;SparseOptimizer包括一个优化算法

2: 定点与边

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

3:  设置非线性优化的下降策略

OptimizationAlgorithm是通过OptimizationWithHessian 来实现的。其中迭代策略可以从Gauss-Newton(高斯牛顿法,简称GN), Levernberg-Marquardt(简称LM法), Powell‘s dogleg 三者中间选择一个(我们常用的是GN和LM)

4:  增量方程求解器

OptimizationWithHessian 内部包含一个求解器(Solver),这个Solver实际是由一个BlockSolver组成的。这个BlockSolver有两个部分,一个是SparseBlockMatrix ,利用增量方程系数的稀疏性进行求解;一个是线性方程的求解器(LinearSolver),它用于计算迭代过程中最关键的一步HΔx=−b,LinearSolver有几种方法可以选择:PCG, CSparse, Choldmod

(三)g2o的编程顺序

技术图片

 

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);

 

结合上面的流程图和代码。下面一步步解释具体步骤。

3.1: 创建一个线性求解器LinearSolver

我们要求的增量方程的形式是:H△X=-b,通常情况下想到的方法就是直接求逆,也就是△X=-H.inv*b。看起来好像很简单,但这有个前提,就是H的维度较小,此时只需要矩阵的求逆就能解决问题。但是当H的维度较大时,矩阵求逆变得很困难,求解问题也变得很复杂。

此时我们就需要一些特殊的方法对矩阵进行求逆,下图是GitHub上g2o相关部分的代码:

技术图片

 

如果你点进去看,可以分别查看每个方法的解释,如果不想挨个点进去看,看看下面的总结就行了:

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

3.2:  利用上面的线性求解器来初始化BlockSolver

BlockSolver 内部包含 LinearSolver,用上面我们定义的线性求解器LinearSolver来初始化。它的定义在如下文件夹内:

g2o/g2o/core/block_solver.h

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

我们来看g2o/g2o/core/ 目录下,发现Solver的优化方法有三种:分别是高斯牛顿(GaussNewton)法,LM(Levenberg–Marquardt)法、Dogleg法,也和前面的图相匹配;

点进去 GN、 LM、 Doglet算法内部,会发现他们都继承自同一个类:OptimizationWithHessian;点进去看 OptimizationAlgorithmWithHessian,发现它又继承自OptimizationAlgorithm

总之,我们可以使用这个阶段可以使用下面三种方法:

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

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

SparseOptimizer::setAlgorithm(OptimizationAlgorithm* algorithm)

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

SparseOptimizer::setVerbose(bool verbose)

不信我们来看一下它的定义:

技术图片

3.5: 设置定点与边

这部分比较复杂,下一篇介绍。

3.6: 设置优化参数,执行优化

SparseOptimizer::initializeOptimization(HyperGraph::EdgeSet& eset)
SparseOptimizer::optimize(int iterations, bool online) 

摘抄:  https://mp.weixin.qq.com/s/j9h9lT14jCu-VvEPHQhtBw

以上是关于G2O框架的主要内容,如果未能解决你的问题,请参考以下文章

g2o学习资料

graph slam tutorial :从推导到应用3(g2o+ceres实现)

ubuntu上G2O库的安装

ubuntu卸载g2o库

text 来自Codyhouse框架的Browserlist片段源代码

G2O 小记1: 验证解析偏导的正确性