(一)简介:
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
原文地址:https://www.cnblogs.com/npulzb/p/11638540.html