【视觉惯性SLAM:六、图优化库(1):g2o的使用指南】

在SLAM领域,常用的图优化库有两个,一个是g2o,另一个是 Ceres Solver,它们都是基于C++的非线性优化库。其中g2o(General Graph Optimization)是一个通用的图优化框架,广泛用于 SLAM(Simultaneous Localization and Mapping)和机器人定位等领域。它提供了一种灵活、高效的方式来解决非线性优化问题,尤其是以图的形式建模的问题。
下面将以g2O优化库为例,从 g2o 编程框架、构建顶点、构建边 三个方面,介绍如何使用 g2o 进行图优化。

g2o 编程框架

g2o 的核心思想

g2o 的核心思想是将优化问题建模为一个图结构,图由**顶点(Vertices)和边(Edges)**组成:

  • 顶点:表示优化变量(如相机位姿、三维点等)。
  • 边:表示约束(如传感器的观测值、相对位姿等)。

优化过程的目标是通过调整顶点的值,使得所有边的误差最小化。

g2o 的主要组成部分

  • 顶点(Vertex):优化变量。
  • 边(Edge):定义误差函数,约束顶点之间的关系。
  • 优化器(SparseOptimizer):负责求解图优化问题。
  • 线性求解器:用于高效地求解稀疏线性方程组。
  • 损失函数(Loss Function):用于鲁棒化处理,减少异常值对优化的影响。

构建 g2o 顶点

顶点从哪里来?

  • 顶点表示优化变量,例如:
    • SLAM 中的相机位姿(例如,SE3 表示位姿)。
    • 地图中的三维点(例如,点云)。
    • 其他场景中的特定变量(例如,传感器状态)。
  • 顶点的数量和种类通常取决于问题的需求。例如,SLAM 中的图优化问题会有两类顶点:
    • 位姿顶点:表示相机的世界坐标系中的位置和姿态。
    • 三维点顶点:表示地图点的三维坐标。

如何自己定义顶点?

g2o 提供了一些基本顶点类,但也支持用户自定义顶点。自定义顶点需要继承 g2o::BaseVertex 并实现相应的虚函数。
示例:自定义一个三维点顶点

#include <g2o/core/base_vertex.h>
#include <Eigen/Core>// 定义一个3维点的顶点
class VertexPointXYZ : public g2o::BaseVertex<3, Eigen::Vector3d> {
public:EIGEN_MAKE_ALIGNED_OPERATOR_NEW// 初始化函数,设置顶点初始值void setToOriginImpl() override {_estimate.setZero();  // 初始化为 (0, 0, 0)}// 更新顶点值void oplusImpl(const double* update) override {Eigen::Map<const Eigen::Vector3d> v(update);_estimate += v;  // 更新顶点估计值}// 可选:保存和加载顶点信息(序列化)bool read(std::istream& /*is*/) override { return false; }bool write(std::ostream& /*os*/) const override { return false; }
};

如何向图中添加顶点?

添加顶点到图中,需要通过 g2o::SparseOptimizer 的 addVertex() 函数。
示例:添加顶点到优化器

#include <g2o/core/sparse_optimizer.h>
#include <g2o/core/block_solver.h>
#include <g2o/solvers/dense/linear_solver_dense.h>
#include <g2o/core/optimization_algorithm_levenberg.h>// 初始化优化器
g2o::SparseOptimizer optimizer;
auto linearSolver = g2o::make_unique<g2o::LinearSolverDense<g2o::BlockSolverX::PoseMatrixType>>();
auto blockSolver = g2o::make_unique<g2o::BlockSolverX>(std::move(linearSolver));
auto solver = new g2o::OptimizationAlgorithmLevenberg(std::move(blockSolver));
optimizer.setAlgorithm(solver);// 创建顶点并添加到优化器
auto vertex = new VertexPointXYZ();
vertex->setId(0);  // 设置顶点ID
vertex->setEstimate(Eigen::Vector3d(1.0, 2.0, 3.0));  // 设置初始值
optimizer.addVertex(vertex);  // 添加顶点到优化器

构建 g2o 边

初步认识图的边

  • 边(Edge)表示顶点之间的关系,也可以单独与一个顶点关联。
  • 边定义了误差函数(Error Function),即优化目标。
  • 边的核心:
    • 误差计算(computeError())。
    • 信息矩阵(Information Matrix):衡量误差的置信度。

如何自定义边?

自定义边需要继承 g2o::BaseUnaryEdge(一元边)、g2o::BaseBinaryEdge(二元边)或 g2o::BaseMultiEdge(多元边)。
示例:自定义二元边

#include <g2o/core/base_binary_edge.h>
#include <Eigen/Core>// 定义一个简单的二元边
class EdgeSE3Point : public g2o::BaseBinaryEdge<3, Eigen::Vector3d, g2o::VertexSE3, VertexPointXYZ> {
public:EIGEN_MAKE_ALIGNED_OPERATOR_NEW// 误差计算函数void computeError() override {const g2o::VertexSE3* v1 = static_cast<const g2o::VertexSE3*>(_vertices[0]);const VertexPointXYZ* v2 = static_cast<const VertexPointXYZ*>(_vertices[1]);_error = _measurement - (v1->estimate() * v2->estimate());  // 假设误差为测量值减去估计值}// 可选:保存和加载边的信息bool read(std::istream& /*is*/) override { return false; }bool write(std::ostream& /*os*/) const override { return false; }
};

如何向图中添加边?

边的构造需要关联顶点,并设置误差信息和信息矩阵。
示例:添加边到优化器

// 创建边并关联顶点
auto edge = new EdgeSE3Point();
edge->setId(0);  // 设置边ID
edge->setVertex(0, vertexSE3);  // 关联顶点1
edge->setVertex(1, vertexPointXYZ);  // 关联顶点2
edge->setMeasurement(Eigen::Vector3d(1.0, 0.0, 0.0));  // 设置测量值
edge->setInformation(Eigen::Matrix3d::Identity());  // 设置信息矩阵
optimizer.addEdge(edge);  // 添加边到优化器

完整示例:构建并优化一个简单图

示例:包含 2 个顶点和 1 条边的简单图

#include <g2o/core/sparse_optimizer.h>
#include <g2o/core/block_solver.h>
#include <g2o/solvers/dense/linear_solver_dense.h>
#include <g2o/core/optimization_algorithm_levenberg.h>
#include <iostream>int main() {// 初始化优化器g2o::SparseOptimizer optimizer;auto linearSolver = g2o::make_unique<g2o::LinearSolverDense<g2o::BlockSolverX::PoseMatrixType>>();auto blockSolver = g2o::make_unique<g2o::BlockSolverX>(std::move(linearSolver));auto solver = new g2o::OptimizationAlgorithmLevenberg(std::move(blockSolver));optimizer.setAlgorithm(solver);// 添加顶点auto vertex1 = new g2o::VertexSE3();vertex1->setId(0);vertex1->setEstimate(g2o::SE3Quat());optimizer.addVertex(vertex1);auto vertex2 = new VertexPointXYZ();vertex2->setId(1);vertex2->setEstimate(Eigen::Vector3d(1.0, 2.0, 3.0));optimizer.addVertex(vertex2);// 添加边auto edge = new EdgeSE3Point();edge->setId(0);edge->setVertex(0, vertex1);edge->setVertex(1, vertex2);edge->setMeasurement(Eigen::Vector3d(1.0, 2.0, 3.0));edge->setInformation(Eigen::Matrix3d::Identity());optimizer.addEdge(edge);// 优化optimizer.initializeOptimization();optimizer.optimize(10);// 输出优化结果std::cout << "Vertex 1: " << vertex1->estimate().translation().transpose() << std::endl;std::cout << "Vertex 2: " << vertex2->estimate().transpose() << std::endl;return 0;
}

注意事项

  • 顶点 ID 和边 ID 必须唯一,否则会报错。
  • 信息矩阵必须为正定矩阵,否则优化可能失败。
  • 在定义边的误差计算时,需要确保误差函数的正确性,否则可能导致优化结果不收敛。
  • 优化器的初始化和配置(如线性求解器的选择)会显著影响性能和结果。

通过以上步骤和示例,g2o 可高效解决图优化问题,并为 SLAM 等复杂任务提供支持。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/496446.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Markdown语法字体字号讲解

学习目录 语法详解改变字体样式[电脑要自带该样式字体]改变局部字号全局字体字号的设置使用场景及应用实例 > 快乐试试吧&#x1f603; &#x1f447; &#x1f447; &#x1f448;点击该图片即可跳转至Markdown学习网站进行 Markdown语法字体字号讲解&#x1f448;点击这里…

ESP-NETIF L2 TAP 接口-物联网嵌入式开发应用

ESP-NETIF L2 TAP 概述 ESP-NETIF L2 TAP 接口是 ESP-IDF 访问用户应用程序中的数据链路层&#xff08;OSI/ISO 中的 L2&#xff09;以进行帧接收和传输的机制。在嵌入式开发中&#xff0c;它通常用于实现非 IP 相关协议&#xff0c;如 PTP 和 Wake on LAN 等。 Tips : 目前…

xterm遇到的问题及解决方案

xterm遇到的问题及解决方案 /r插入终端导致的之后插入的数据覆盖了改行头部的数据 问题说明 如图所示&#xff0c;当在一行输入的候&#xff0c;输入的l插入到了改行的头部。 查看ws返回数据 可见ws返回的信息存在\r字符&#xff0c;在xterm.js中\r是回车字符的意思&…

springboot 工程使用proguard混淆

在 Maven 构建的 Spring Boot 项目中使用 ProGuard 进行代码混淆时&#xff0c;需要正确配置 Maven 插件和 ProGuard 的混淆规则。由于 Spring Boot 项目通常会依赖大量的反射机制和动态代理&#xff0c;因此必须特别小心确保这些部分在混淆过程中不会被破坏。 步骤 1&#xf…

我的秋招总结

我的秋招总结 个人背景 双非本&#xff0c;985硕&#xff0c;科班 准备情况 以求职为目的学习Java的时间大概一年。 八股&#xff0c;一开始主要是看B站黑马的八股文课程&#xff0c;背JavaGuide和小林coding还有面试鸭。 算法&#xff0c;250&#xff0c;刷了3遍左右 项目&…

Java Stream流详解——串行版

Stream流——串行版 ​ Stream流是java8引入的特性&#xff0c;极大的方便了我们对于程序内数据的操作&#xff0c;提高了性能。通过函数式编程解决复杂问题。 1.BaseStream<T,S extense BaseStream<T,S>> ​ 他是流处理的基石概念&#xff0c;重点不在于这个接…

fisco-bcos系统架构

系统架构 整体架构 标签&#xff1a;架构 强扩展性 模块设计 整体架构上&#xff0c;FISCO BCOS划分成基础层、核心层、管理层和接口层&#xff1a; 基础层:提供区块链的基础数据结构和算法库 核心层: 实现了区块链的核心逻辑&#xff0c;核心层分为两大部分&#xff1a…

探秘仓颉编程语言:使用体验与功能剖析

目录 一、引言&#xff1a;仓颉登场&#xff0c;编程新纪元开启 二、初体验&#xff1a;搭建环境与 “Hello World” &#xff08;一&#xff09;环境搭建指南 &#xff08;二&#xff09;Hello World 初印象 三、核心特性剖析&#xff1a;智能、高效、安全多维解读 &…

Java 面试合集(2024版)

种自己的花&#xff0c;爱自己的宇宙 目录 第一章-Java基础篇 1、你是怎样理解OOP面向对象??? 难度系数&#xff1a;? 2、重载与重写区别??? 难度系数&#xff1a;? 3、接口与抽象类的区别??? 难度系数&#xff1a;? 4、深拷贝与浅拷贝的理解??? 难度系数&…

指针与数组:深入C语言的内存操作艺术

数组名的理解 在上⼀个章节我们在使⽤指针访问数组的内容时&#xff0c;有这样的代码&#xff1a; int arr[10] {1,2,3,4,5,6,7,8,9,10}; int *p &arr[0]; 这⾥我们使⽤ &arr[0] 的⽅式拿到了数组…

使用RabbitMQ

一、MQ是什么 MQ全称 Message Queue&#xff08;消息队列&#xff09;&#xff0c;是在消息的传输过程中保存消息的容器。多用于分布式系统之间进行通信&#xff0c;主要功能业务解耦。 二、市面上常见的MQ产品 RabbitMQ、RocketMQ&#xff08;阿里的&#xff09;、Kafka 、…

大模型的实践应用33-关于大模型中的Qwen2与Llama3具体架构的差异全解析

大家好,我是微学AI,今天给大家介绍一下大模型的实践应用33-关于大模型中的Qwen2与Llama3具体架构的差异全解析。Qwen2模型与Llama3模型在架构上存在一些细微的差异,这些差异主要体现在注意力机制、模型尺寸相关参数以及嵌入层处理等方面。以下是对这些差异的详细分析。 文章…

NAT 技术如何解决 IP 地址短缺问题?

NAT 技术如何解决 IP 地址短缺问题&#xff1f; 前言 这是我在这个网站整理的笔记,有错误的地方请指出&#xff0c;关注我&#xff0c;接下来还会持续更新。 作者&#xff1a;神的孩子都在歌唱 随着互联网的普及和发展&#xff0c;IP 地址的需求量迅速增加。尤其是 IPv4 地址&…

kafka的备份策略:从备份到恢复

文章目录 一、全量备份二、增量备份三、全量恢复四、增量恢复 前言&#xff1a;Kafka的备份的单元是partition&#xff0c;也就是每个partition都都会有leader partiton和follow partiton。其中leader partition是用来进行和producer进行写交互&#xff0c;follow从leader副本进…

使用sam进行零样本、零学习的分割实践

参照&#xff1a;利用SAM实现自动标注_sam标注-CSDN博客&#xff0c;以及SAM&#xff08;分割一切模型&#xff09;的简单调用_sam使用-CSDN博客 sam简介&#xff1a; Segment Anything Model&#xff08;SAM&#xff09;是Meta公司于2023年发布的一种AI模型&#xff0c;它打破…

【Git】—— 使用git操作远程仓库(gitee)

目录 一、远程仓库常用命令 1、从远程仓库克隆项目 2、查看关联的远程仓库 3、添加关联的远程仓库 4、移除关联的远程仓库 5、将本地仓库推送到远程仓库 6、从远程仓库拉取项目 二、分支命令 1、查询分支 2、创建分支 3、切换分支 4、推送到远程分支 5、合并分支 …

攻防世界web新手第五题supersqli

这是题目&#xff0c;题目看起来像是sql注入的题&#xff0c;先试一下最常规的&#xff0c;输入1&#xff0c;回显正常 输入1‘&#xff0c;显示错误 尝试加上注释符号#或者–或者%23&#xff08;注释掉后面语句&#xff0c;使1后面的单引号与前面的单引号成功匹配就不会报错…

【MySQL】SQL 优化经验

1. 表的设计优化 参考依据&#xff1a;参考阿里开发手册嵩山版&#xff0c;其中有很多关于MySQL表设计的内容。类型选择&#xff1a;根据存储内容选择合适类型&#xff0c;如数值存储可选tinyint、bigint等&#xff0c;字符串可选varchar或text&#xff0c;根据内容长短选择合…

使用 .NET 6 或 .NET 8 上传大文件

如果您正在使用 .NET 6&#xff0c;并且它拒绝上传大文件&#xff0c;那么本文适合您。 我分享了一些处理大文件时需要牢记的建议&#xff0c;以及如何根据我们的需求配置我们的服务&#xff0c;并提供无限制的服务。 本文与 https://blog.csdn.net/hefeng_aspnet/arti…