一、g2o简介
g2o(General Graphic Optimization)是一个基于图优化的库,将非线性优化与图论结合起来的理论,我们可以利用g2o求解任何可以表示为图优化的最小二乘问题。
图优化就是把优化问题表现成图的方式。图由顶点和边组成,其中顶点表示优化变量,边表示误差项,对任意一个非线性?> 最小二乘问题,我们都可以构建与之对应的图。
(注:这里的图是图论意义上的图,可以用概率论里面的定义,贝叶斯图或因子图。)
二、g2o安装
首先安装g2o的依赖
1 sudo apt install qt5-qmake qt5-default libqglviewer-dev-qt5 libsuitesparse-dev libcxsparse3 libcholmod3
然后到github下clone此工程,然后编译安装,指令如下:
1 2 3 4 5 6 git clone https://github.com/RainerKuemmerle/g2o.git cd g2o/mkdir buildcd buildcmake ../ make
g2o的头文件在/usr/local/g2o
下,库文件在/usr.local/lib
下。
三、利用g2o拟合曲线
1. 拟合步骤
① 定义顶点和边的类型(优化变量与误差项)
② 构建图
③ 选择优化算法
④ 调用g2o进行优化,返回结果
2. 实验-拟合曲线
此示例程序还依赖opencv、Eigen、Ceres库,需要预先安装。
main.cpp文件 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 #include <iostream> #include <g2o/core/g2o_core_api.h> #include <g2o/core/base_vertex.h> #include <g2o/core/base_unary_edge.h> #include <g2o/core/block_solver.h> #include <g2o/core/optimization_algorithm_levenberg.h> #include <g2o/core/optimization_algorithm_gauss_newton.h> #include <g2o/core/optimization_algorithm_dogleg.h> #include <g2o/solvers/dense/linear_solver_dense.h> #include <Eigen/Core> #include <opencv2/core/core.hpp> #include <cmath> #include <chrono> using namespace std;class CurveFittingVertex : public g2o::BaseVertex<3 , Eigen::Vector3d> {public : EIGEN_MAKE_ALIGNED_OPERATOR_NEW virtual void setToOriginImpl () override { _estimate << 0 , 0 , 0 ; } virtual void oplusImpl (const double *update) override { _estimate += Eigen::Vector3d (update); } virtual bool read (istream &in) {} virtual bool write (ostream &out) const {} }; class CurveFittingEdge : public g2o::BaseUnaryEdge<1 , double , CurveFittingVertex> {public : EIGEN_MAKE_ALIGNED_OPERATOR_NEW CurveFittingEdge (double x) :BaseUnaryEdge(),_x(x) { } virtual void computeError () override { const CurveFittingVertex *v = static_cast <const CurveFittingVertex *> (_vertices[0 ]); const Eigen::Vector3d abc = v->estimate (); _error(0 , 0 ) = _measurement - std::exp (abc (0 , 0 ) * _x * _x + abc (1 ,0 ) * _x + abc (2 , 0 )); } virtual void linearizeOplus () override { const CurveFittingVertex *v = static_cast <const CurveFittingVertex *> (_vertices[0 ]); const Eigen::Vector3d abc = v->estimate (); double y = exp (abc[0 ] * _x * _x + abc[1 ] * _x + abc[2 ]); _jacobianOplusXi[0 ] = -_x * _x * y; _jacobianOplusXi[1 ] = -_x * y; _jacobianOplusXi[2 ] = -y; } virtual bool read (istream &in) {} virtual bool write (ostream &out) const {} public : double _x; }; int main () { double ar = 1.0 , br = 2.0 , cr = 1.0 ; double ae = 2.0 , be = -1.0 , ce = 5.0 ; int N = 100 ; double w_sigma = 1.0 ; double inv_sigma = 1.0 / w_sigma; cv::RNG rng; vector<double > x_data, y_data; for (int i = 0 ; i < N; i++){ double x = i / 100.0 ; x_data.push_back (x); y_data.push_back (exp (ar * x * x + br * x + cr) + rng.gaussian (w_sigma * w_sigma)); } typedef g2o::BlockSolver<g2o::BlockSolverTraits<3 , 1 >> BlockSolverType; typedef g2o::LinearSolverDense<BlockSolverType::PoseMatrixType> LinearSolverType; auto solver = new g2o::OptimizationAlgorithmGaussNewton (g2o::make_unique <BlockSolverType>(g2o::make_unique <LinearSolverType>())); g2o::SparseOptimizer optimizer; optimizer.setAlgorithm (solver); optimizer.setVerbose (true ); CurveFittingVertex *v = new CurveFittingVertex (); v->setEstimate (Eigen::Vector3d (ae, be, ce)); 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); } cout << "Start optimization" << endl; chrono::steady_clock::time_point t1 = chrono::steady_clock::now (); optimizer.initializeOptimization (); optimizer.optimize (10 ); chrono::steady_clock::time_point t2 = chrono::steady_clock::now (); chrono::duration<double > time_used = chrono::duration_cast<chrono::duration<double >>(t2 - t1); cout << "Solve time cost = " << time_used.count () << " s." << endl; Eigen::Vector3d abc_estimate = v->estimate (); cout << "estimated model: " << abc_estimate.transpose () << endl; return 0 ; }
CMakeLists.txt文件 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 cmake_minimum_required (VERSION 3.20 )project (g2oCurveFitting)set (CMAKE_CXX_STANDARD 14 )# OpenCV库 find_package (OpenCV REQUIRED)include_directories (${OpenCV_INCLUDE_DIRS})# Eigen库 include_directories ("/usr/include/eigen3" )# Ceres库 find_package (Ceres REQUIRED)include_directories (${CERES_INCLUDE_DIRS})# g2o库 list ( APPEND CMAKE_MODULE_PATH /home/huffie/slam/3 rdparty/g2o/cmake_modules ) #刚才clone的项目文件夹set (G2O_ROOT /usr/local/include/g2o)find_package (G2O REQUIRED)include_directories (${G2O_INCLUDE_DIRS})add_executable (g2oCurveFitting main.cpp)target_link_libraries (g2oCurveFitting ${OpenCV_LIBS})target_link_libraries (g2oCurveFitting g2o_stuff g2o_core )target_link_libraries (g2oCurveFitting ${CERES_LIBRARIES})