graph slam tutorial : g2o 的使用

2023-05-16

       g2o全称general graph optimization,是一个用来优化非线性误差函数的c++框架。如果阅读了前几篇graph slam tutorial的博客,再去读《g2o:a general framework for(hyper) graph optimization》这篇论文将变得轻松随意。读完论文以后,再看看github里面g2o example的例程就能上手g2o了。

        然而,看懂是一回事,自己写出来是另一回事,特别是如何将g2o用在自己的程序里面。因此,本文的主要目的是记录如何在自己程序里使用g2o,包括cmakelist编写,g2o函数的调用两方面。在往下读之前,希望你已经读完了g2o的那篇论文。

 

安装g2o

        github上有完整的教程,安装编译也很容易(g2o代码网站请点击)。

  • 首先确保电脑上面已经有了cmake/eigen3/suitesparse/qt4/libqglviewer这些依赖项:
sudo apt-get install libeigen3-dev libsuitesparse-dev libqt4-dev qt4-qmake libqglviewer-qt4-dev
其中libqglviewer是编译g2o_viewer必须的。

  • 接着把g2o代码下载到相应文件夹里(可以使用git clone命令或者自己手动到网站下载)
  • 编译和安装
mkdir build
cd build
cmake ../
make
编译完以后,在bin文件夹里就生成了各种可执行文件了,包括g2o_viewer,tutorial_slam2d等。为了把这个g2o当作一个外部库在自己程序中(像opencv一样)使用,make编译完以后,再用sudo make install 安装一下这个库。执行完命令,将看到 /usr/local/include文件夹里多了g2o这一项。注意,不install也行,但是自己程序里调用g2o时cmaklist的书写就不一样了,可以参看orb_slam。在下面我的程序中调用g2o,是基于make intall以后的。


在自己程序中使用g2o

       在该部分,我们调用g2o程序优化一个球,就是在论文中经常看到的那个例子,优化前后效果如下。首先自己创建一个g2o_test文件件用来存放我们将要编写的代码,在g2o_test文件里创建build,bin,lib这些cmake工程里常见的文件夹。lz测试g2o的数据和代码放在我的github里:https://github.com/HeYijia/GraphSLAM_tutorials_code/tree/master/g2o_test

        

先贴出源程序:

#include "g2o/core/sparse_optimizer.h"
#include "g2o/core/block_solver.h"
#include "g2o/core/factory.h"
#include "g2o/core/optimization_algorithm_levenberg.h"
#include "g2o/solvers/csparse/linear_solver_csparse.h"

#include "g2o/types/slam3d/vertex_se3.h"
//#include "g2o/types/slam3d/edge_se3.h"
// 使用 宏函数 声明边和顶点类型,注意注释掉了上面两个头文件 
G2O_USE_TYPE_GROUP(slam3d);
//G2O_USE_TYPE_GROUP(slam2d); //2d平面

#include <iostream>

using namespace std;
using namespace g2o;

#define MAXITERATION 50
int main()
{
    cout<< "Hello g2o"<<endl;
    // create the linear solver
    BlockSolverX::LinearSolverType * linearSolver = new LinearSolverCSparse<BlockSolverX::PoseMatrixType>();

    // create the block solver on the top of the linear solver
    BlockSolverX* blockSolver = new BlockSolverX(linearSolver);
    
    //create the algorithm to carry out the optimization
    OptimizationAlgorithmLevenberg* optimizationAlgorithm = new OptimizationAlgorithmLevenberg(blockSolver);

/*  //如果没用前面的宏函数,而是调用的是edge_se3和vertex_se3头文件
    //想让程序能识别VertexSE3这些数据类型,就要显示的调用它们,如下
    //如果只用了头文件,而没用显示调用,那么这些数据类型将不会link进来
    //在下面的optimizer.load函数将不能识别这些数据类型
    for(int f=0; f<10;++f)
    {
        VertexSE3* v = new VertexSE3;
        v->setId(f++);
    }
*/
    // create the optimizer
    SparseOptimizer optimizer;
    
    if(!optimizer.load("../data/sphere_bignoise_vertex3.g2o"))
    {
        cout<<"Error loading graph"<<endl;
        return -1;
    }else
    {
        cout<<"Loaded "<<optimizer.vertices().size()<<" vertices"<<endl;
        cout<<"Loaded "<<optimizer.edges().size()<<" edges"<<endl;
    }

    //优化过程中,第一个点固定,不做优化; 优化过程中,必须固定一个pose。
    VertexSE3* firstRobotPose = dynamic_cast<VertexSE3*>(optimizer.vertex(0));
    firstRobotPose->setFixed(true);

    optimizer.setAlgorithm(optimizationAlgorithm);
    optimizer.setVerbose(true);
    optimizer.initializeOptimization();
    cerr<<"Optimizing ..."<<endl;
    optimizer.optimize(MAXITERATION);
    cerr<<"done."<<endl;

    optimizer.save("../data/sphere_after.g2o");
    //optimizer.clear();

    return 0;
}

       程序解读:

       程序思路很简单,初始化优化方法,接着创建optimizer优化器,并load() g2o数据文件,然后就是优化。是不是很简单。自己写slam程序时难的部分在数据文件g2o的创建,也就是顶点vertex和边edge的创建,这里直接使用的是已经创建好的数据文件,load一下就行了。在g2o的论文里,把怎么创建顶点和边的数据类型讲的很详细,如tutorial_slam2d那个程序。然而实际使用的时候,g2o已经帮我们定义好了各种类型的顶点和边,如用于3d slam的vertex_se3和edge_se3等,这些数据类型在g2o/types文件里可以找到,因此实际使用的时候只要调用就行了,不需要像论文里那么复杂。

       上面部分程序主要参考的g2o/example里面的simple_optimize,点击这里。在这个程序里,我们发现有一个宏函数在论文g2o的论文中没提到过,并且调用vertex_se3和edge_se3的头文件指令被注释掉了。

       这是怎么回事呢?即为什么使用G2O_USE_TYPE_GROUP?

       答:一开始我程序里面用#include “.../edge3_se3.h”和#include “.../vertex_se3.h”,以为这样程序就能识别这种类型的顶点和边了。结果运行程序的时候发现然并卵,optimizer.load()竟然不识别这些数据类型。然后就想手动调用这些数据类型,看看头文件是否找到了,于是就有了注释掉的VertexSE3 *v这一句程序,加了这句以后,再运行程序,load数据成功。这时开始纳闷了,于是翻看g2o example里simple_optimize这个程序,发现了这个宏函数。google一下,有了这么一段话:Furthermore, the macro G2O_REGISTER_TYPE_GROUP allows to declare a type group. This is necessary if we use the factory to construct the types and we have to enforce that our code is linked to a specific type group. Otherwise the linker may drop our library, since we do not explicitly use any symbol provided by the library containing our type. Declaring the usage of a specific type library and hence enforcing the linking is done by the macro called G2O_USE_TYPE_GROUP.于是豁然开朗。如果你要是在程序里显式的调用边或者顶点数据类型,就可以不用这个宏函数,而用头文件的形式就行了。由于,我们这里是load已经构建好的g2o文件,没有显式调用VertexSE3这些数据类型,所以就使用这个宏函数。注意,以后在自己的SLAM程序中,顶点和边一般都是临时构建的,一般是用头文件和显式调用的形式,可以不用这个宏函数。

      cmakelist的编写:

       在上面程序中我们使用include来调用g2o的头文件。但是,我们得告诉工程文件这些头文件存放在那里,怎么调用,比如在windows下vs中使用opencv时要进行各种设置一样。在linux下,得依靠cmake来完成这些工作。g2o作为一个外接程序库在自己程序里怎么使用呢?如果不熟悉cmake的话,可以点击这里(在工程中查找和使用其他程序库的方法),这里以及这里

       有了cmake的知识以后,我们知道只要在cmakelist文件里写上find_package这条命令就能解决问题。并且要把编译好的g2o/cmake_modules文件里的cmake文件复制到自己的程序里,找到下载的g2o/cmake_modules文件夹,把所有的.cmake文件复制到你的程序目录的modules文件夹里,如g2o_test/cmake_modules.

       接下来就是编写cmakelist文件,如果你不熟悉cmake的写法,可以趁着这个机会自己练练,楼主也是一边看教程,一边照着rgbdslam、orb_slam的cmake写法自己捣鼓出来的。在这个过程中慢慢地就熟悉了cmake的命令和写法。cmakelist内容如下:

cmake_minimum_required(VERSION 2.8)
project(g2o_test)

#Enable support for C++11
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x")

#设定二进制文件路径
SET(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin)
#设定库文件编译路径
SET(LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/lib)
#设定.cmake文件存放路径
#SET(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "{CMAKE_CURRENT_SOURCE_DIR}/cmake_modules")
LIST(APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake_modules)
#ADD_SUBDIRECTORY(${PROJECT_SOURCE_DIR}/src)

# find g2o lib
find_package(G2O REQUIRED)
IF(G2O_FOUND)
    include_directories(${G2O_INCLUDE_DIR})
    message("G2O lib is found:"${G2O_INCLUDE_DIR})
ENDIF(G2O_FOUND)

find_package(Eigen3 REQUIRED)
find_package(CSparse REQUIRED)
#find_package(Cholmod REQUIRED)
include_directories(${CSPARSE_INCLUDE_DIR})
include_directories(${EIGEN3_INCLUDE_DIR})
#include_directories(${CHOLMOD_INCLUDE_DIR})

SET(G2O_LIBS g2o_cli g2o_ext_freeglut_minimal g2o_simulator g2o_solver_slam2d_linear g2o_types_icp g2o_types_slam2d g2o_core g2o_interface g2o_solver_csparse g2o_solver_structure_only g2o_types_sba g2o_types_slam3d g2o_csparse_extension g2o_opengl_helper g2o_solver_dense g2o_stuff g2o_types_sclam2d g2o_parser g2o_solver_pcg g2o_types_data g2o_types_sim3 cxsparse )

ADD_EXECUTABLE(g2o_test main.cpp)
target_link_libraries(g2o_test ${G2O_LIBS})
#target_link_libraries(g2o_test csparse g2o_core g2o_solver_cholmod g2o_solver_csparse g2o_types_slam3d)
       编写完以后,编译,在bin文件里生成了g2o_test可执行文件,切换到bin文件夹下 运行 ./g2o_test就可以看效果了。程序最后只是生成的sphere_after.g2o文件,请使用官方g2o/bin文件里的g2o_viewer程序来打开这个文件,效果如上图所示。

       在上面的程序中,优化开始前,我们固定了第一个顶点不进行优化。其他条件不变的情况下,固定第一个顶点比不固定效果要好,下图左为固定第一个顶点。

 


      知道了如何在自己的程序中使用g2o进行优化以后,解决了grpah-based slam的back-end问题,因此编写front-end的程序(点云匹配,闭环检测等)就能完成自己的rgbdslam程序。在下个教程中,将使用公共RGBD数据集来完成整个RGBDSLAM程序。


(转载请注明作者和出处:http://blog.csdn.net/heyijia0327 未经允许请勿用于商业用途)


reference:

1. Rainer & Grisetti  《g2o: A General Framework for Graph Optimization》

2. Grisetti  《g2o:a general framework for(hyper) graph optimization》


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

graph slam tutorial : g2o 的使用 的相关文章

  • matplotlib 仅显示一组 10 个图形中的一个,就像幻灯片一样

    I have a set of 10 graphs based on X Y pairs In this example only 3 Displaying one graph is easy same to all graphs in t
  • 在 ggplot 或lattice 中利用 Surv 对象

    有人知道如何利用 ggplot 或lattice 进行生存分析吗 制作网格或类似面的生存图会很好 所以最后我尝试了一下 找到了卡普兰 迈耶图的解决方案 对于将列表元素放入数据框中的混乱代码 我深表歉意 但我无法找到其他方法 注意 它仅适用于
  • 数据集和渲染器不应为空,并且在 AChartEngine 中应具有相同数量的系列

    我使用多 Y 轴图来绘制点 我有 1 X 轴 2 Y1 轴 3 Y2 轴 a 最初我用来在Y1轴 高度 上绘制5系列线 在Y2轴上绘制相同的5系列线 重量 两者都相对于x轴 这里不存在问题 这些系列线是标准线 用于每次计算 b 现在 如果用
  • Stata 中各个图表的条形图颜色一致

    我在 Stata 中输出堆积条形图 每个堆积条形图从下到上排序 最大 gt 每个团队的最小获胜百分比 clear set obs 10 gen team yankees if inlist n 1 6 replace team red so
  • 具有 1 亿个节点的图中的连接组件

    我正在尝试获取具有 1 亿个节点的图中的连接组件列表 对于较小的图 我通常使用连接组件 http networkx lanl gov reference generated networkx algorithms components co
  • 用Python绘制不等式图

    我正在创建一个程序 它将随机生成线 即不等式 并显示满足约束的区域 我不介意使用哪些库 所以可以随意使用 sympy numpy 等 我将显示我当前的代码 但这只是填充了两行之间的区域 并且根本不使用不等式 如果可能的话 有一个图例就好了
  • 获取图表中走过的最长路线

    我有一组相互连接的节点 我有以下节点网络 这里0是起点 我想遍历尽可能多的节点 并且一个节点只遍历一次 另外 在从 0 到目标节点的旅程中 我只想有一个奇数编号的节点 如 1 3 5 7 现在我需要找出从起始位置 0 开始可以行驶的最长路线
  • java 的地理图表

    谁能推荐一个 Java 组件 它可以让您创建一个漂亮的世界地图图像 突出显示某些国家 基于一些统计数据 与此图像类似的东西 类似于 Google 地理图表 但适用于 Java https developers google com char
  • D3.js 的 Python 等效项

    谁能推荐一个可以做到这一点的Python库交互的图形可视化 我特别想要类似的东西d3 js https d3js org 但对于python理想情况下它也是 3D 的 我看过 NetworkX https networkx readthed
  • qgraph可以在实际边缘之外渲染边缘标签吗?

    为了便于阅读 我正在尝试在 qgraph 中的实际边缘之外插入边缘标签 我特别不喜欢在标签下方添加白色背景的选项 它会弄乱边缘 根据手册 只能沿线调整边缘标签位置 而不能在侧面调整 以前有人为此苦苦挣扎吗 是否可以规避这个问题 干杯 似乎没
  • 在 Python 中为方法生成控制流图的最简单方法是什么?

    我正在编写一个程序来尝试比较两种方法 我想为所有匹配的方法生成控制流图 CFG 并使用拓扑排序来比较两个图 有一个Python包叫做staticfg https pypi org project staticfg 它正是从一段 Python
  • Floyd Warshall 算法的时间复杂度

    Skiena 的算法书包含以下解释弗洛伊德 沃歇尔算法 http en wikipedia org wiki Floyd E2 80 93Warshall algorithm floyd adjacency matrix g int i j
  • NetworkX:翻转图

    有没有办法以相反的顺序生成图形 即我想生成垂直翻转的图形 或者如果我可以在绘制之前用一些 matplotlib 子例程翻转它 F e 我希望 357 和 358 位于顶部 1 6 位于底部 只需交换您的位置坐标即可 import netwo
  • 为什么使用 Dijkstra 算法而不是最佳(最便宜)优先搜索?

    从我到目前为止所读到的来看 这最佳优先搜索 https en wikipedia org wiki Best first search在找到到达目标的最短路径方面似乎更快 因为 Dijkstra 算法在遍历图时必须放松所有节点 是什么让 D
  • 用于生成交互式图的 Java 库

    我想将我们的 SOA 服务可视化为图表 我们有商业服务和领域服务 gt domain service 1 e g business service 1 gt domain service 2 gt domain service 3 我目前使
  • 在 Python 中使用邻接表构建节点图

    我有一个Node类如下 class Node def init self val 0 neighbors None self val val self neighbors neighbors if neighbors is not None
  • Java - 哪个是 Graph 的最佳实现结构?

    图很大但是无向 边缘未加权 在我的实现中 我必须找到具有最大度数的顶点并在顶点和边上进行删除 链接列表 数组列表 地图 哪一种更适合我的实施 表示图的两个基本数据结构是 adjacency list the adjacency matrix
  • 带回溯的 Dijkstra 算法?

    In a 相关主题 https stackoverflow com questions 28333756 finding most efficient path between two nodes in an interval graph
  • 向图节点添加标签

    我使用 visnetwork 库制作了下图 library tidyverse library igraph set seed 123 n 15 data data frame tibble d paste 1 n relations da
  • 在 python 中使用 graphviz 从 DOT 文件绘制有向图

    这是API参考 http graphviz readthedocs io en latest api html for graphviz 我找不到任何从现有的生成有向图的方法dot源文件 方法如render and view保存在新文件中

随机推荐

  • 【蓝牙系列】蓝牙5.4到底更新了什么?(1)--- PAwR

    蓝牙系列 蓝牙5 4到底更新了什么 xff08 1 xff09 PAwR 一 背景 蓝牙技术联盟最近发布了蓝牙5 4的核心规范 xff0c 蓝牙5 4规范的主要改进之一就是实现了单个接入点与数千个终端节点进行双向无连接通信 xff0c 这一
  • UP Squared Board,工业级创新开发板,为您的物联网应用注入升级能量

    研扬科技自推出UP Board xff08 世界首创 Intel 平台信用卡大小开发板 xff09 以来 xff0c 便成功于业界打开名号 xff0c 后续 xff0c 研扬持续开发 UP 系列产品 xff0c 至今 xff0c 除了 UP
  • 【蓝牙系列】蓝牙5.4到底更新了什么(2)

    蓝牙系列 蓝牙5 4到底更新了什么 xff08 2 xff09 一 背景 上一篇文章讲了蓝牙5 4的PAwR特征 xff0c 非常适合应用在电子货架标签 xff08 ESL xff09 领域 xff0c 但是实际应用场景中看 xff0c 只
  • 【转载】【Nordic博文分享系列】详解Zephyr设备树(DeviceTree)与驱动模型

    详解Zephyr设备树 xff08 DeviceTree xff09 与驱动模型 转载自nordic半导体微信公众号 1 前言 Nordic最新的开发包NCS xff08 nRF Connect SDK xff09 相对于原来的nRF5 S
  • 感受一下SPL06气压计+APM三阶互补的高度融合

    不得不说 xff0c spl06气压计很强 xff0c 原始数据也比较干净 xff0c 短时间可以保持在30cm内浮动 xff0c 滤波后在10cm内浮动 就是这么夸张 使用APM的三阶互补滤波融合出 高度 xff0c 速度 xff0c 效
  • 6种串口协议的实现

    串口协议开发 以下解析范式都是采用数据队列的形似来存储 xff0c 并且根据设备运行速度差异 xff0c 还需增加数据包队列来存储解析完毕的数据包 1 范式一 固定长度 无校验 0x6B 20字节 0xB6 上面数据中有一个帧头0x6B x
  • html页面实时刷新显示服务器数据

    在上一篇中我说到浏览器和服务器交互数据 xff0c 是实现了服务器发数据给浏览器 xff0c 并在页面上显示 xff0c 但是是通过按钮点击刷新的 xff0c 而且数据是和html页面一起发过来的 xff0c 在这里我是数据放到页面数组里
  • 平衡小车之家客服真差

    我同事送了我一台直流电机平衡车 xff0c 然后同事又买了一台步进电机平衡车 都是在平衡小车之家买的 xff0c 好好看看下面的图片 最近在研究同事的步进平衡小车 xff0c 然后跑去问一下客服步进电机的参数 xff0c 一看我说 xff0
  • C++编译流程

    C 43 43 编译流程 C C 43 43 是编译型高级语言 xff0c 程序要执行 xff0c 必须要有编译器和链接器 编译过程分为四步 xff1a 预处理 编译 汇编 链接 1 预处理 读取源代码并对其中的以 开头的指令和特殊符号进行
  • 卡尔曼滤波 -- 从推导到应用(一)

    前言 卡尔曼滤波器是在估计线性系统状态的过程中 xff0c 以 最小均方误差为目的而推导出的几个递推数学等式 也可以从贝叶斯推断的角度来推导 本文将分为两部分 xff1a 第一部分 xff0c 结合例子 xff0c 从最小均方误差的角度 x
  • 卡尔曼滤波 -- 从推导到应用(二)

    该文是自我总结性文章 xff0c 有纰漏 xff0c 请指出 xff0c 谢谢 白巧克力 这部分主要是通过对第一部分中提到的匀加速小车模型进行位移预测 先来看看状态方程能建立准确的时候 xff0c 状态方程见第一部分分割线以后内容 xff0
  • LQR 的直观推导及简单应用

    本文主要介绍LQR的直观推导 xff0c 说明LQR目标函数J选择的直观含义以及简单介绍矩阵Q R的选取 xff0c 最后总结LQR控制器的设计步奏 xff0c 并将其应用在一个简单的倒立摆例子上 假设有一个线性系统能用状态向量的形式表示成
  • STM32学习路线-长图

    最近好好整理了一下学习STM32的路程 xff0c 做成了一个长图 xff1a STM32学习路线 xff0c 供初学者们参考一下
  • ROS 教程之 vision: 摄像头标定camera calibration

    在上一个ROS教程视觉文章中 xff0c 我们使用usb cam包读入并发布了图像消息 xff0c 但是图像没有被标定 xff0c 因此存在畸变 ROS官方提供了用于单目或者双目标定的camera calibration包 这个包是使用op
  • ROS 基础: 在同一个节点里订阅和发布消息

    在一些应用中 xff0c 可能有的人需要在同一个节点中实现订阅一个消息 xff0c 然后在该消息的回调函数中处理一下这些数据后再发布到另一个topic上 ROS answers中也有人有相同的疑问 xff0c 这里贴出Martin Peri
  • ROS : 修改ROS源代码(overlaying package)

    ROS官方或者其他个人提供了很多package供大家使用 xff0c 但是随着学习的深入 xff0c 很多人可能想去修改这些package的源代码 xff0c ROS提供了一种称之为overlaying的机制 它允许 ROS原有安装的pac
  • graph slam tutorial :从推导到应用3

    为了更好地理解graph based slam的过程 xff0c 本文以二维平面的激光SLAM为例子 xff0c 先简单介绍如何根据传感器信息构建图 xff0c 即图优化的前端 xff08 front end xff09 然后再针对上篇博客
  • graph slam tutorial : 从推导到应用1

    前言 SLAM问题的处理方法主要分为滤波和图优化两类 滤波的方法中常见的是扩展卡尔曼滤波 粒子滤波 信息滤波等 xff0c 熟悉滤波思想的同学应该容易知道这类SLAM问题是递增的 实时的处理数据并矫正机器人位姿 比如基于粒子滤波的SLAM的
  • graph slam tutorial :从推导到应用2

    在上一部分中通过一个例子大致了解了graph based slam的优化过程 在本篇博客中将提升一个层次 xff0c 对图优化的求解过程进行推导 由于博文关注的在图构建好以后 xff0c 如何调整机器人位姿使误差最下 因此 xff0c 本文
  • graph slam tutorial : g2o 的使用

    g2o全称general graph optimization xff0c 是一个用来优化非线性误差函数的c 43 43 框架 如果阅读了前几篇graph slam tutorial的博客 xff0c 再去读 g2o xff1a a gen