g2o--ba代码解析

概要

g2o是常用的图优化理论c++库,其自带了很多example讲解如何使用该库文件,本文分析其中ba的示例代码。

所谓的图优化,就是把一个常规的优化问题,以图(Graph)的形式来表述。

在图中,以顶点表示优化变量,以边表示观测方程。于是总体优化问题变为n条边加和的形式(边是约束)。在具体编写g2o代码时,我们也需要明确哪些是顶点(优化项),哪些是边(约束项)。

业务场景

如上图所示,存在以下变量

f  -- 路标,或者观察点,或者物体的特征点,在ba中作为顶点出现;

b -- 观测者的位置和姿态,在ba中也是作为顶点出现;

z -- f在相机所在的b的投影,在ba中也是作为边出现;

代码解析

设定求解器

  g2o::SparseOptimizer optimizer;
  optimizer.setVerbose(false);
  string solverName = "lm_fix6_3";
  if (DENSE) {
    solverName = "lm_dense6_3";
  } else {
#ifdef G2O_HAVE_CHOLMOD
    solverName = "lm_fix6_3_cholmod";
#else
    solverName = "lm_fix6_3";
#endif
  }

  g2o::OptimizationAlgorithmProperty solverProperty;
  optimizer.setAlgorithm(
      g2o::OptimizationAlgorithmFactory::instance()->construct(solverName,
                                                               solverProperty));

初始化路标位置

  vector<Vector3d> true_points;
  for (size_t i = 0; i < 500; ++i) { // 随机生成500个路标
    true_points.push_back(
        Vector3d((g2o::Sampler::uniformRand(0., 1.) - 0.5) * 3,
                 g2o::Sampler::uniformRand(0., 1.) - 0.5,
                 g2o::Sampler::uniformRand(0., 1.) + 3));
  }

设置相机的相关参数

  double focal_length = 1000.;
  Vector2d principal_point(320., 240.);

  vector<g2o::SE3Quat, aligned_allocator<g2o::SE3Quat> > true_poses;
  g2o::CameraParameters* cam_params =
      new g2o::CameraParameters(focal_length, principal_point, 0.);
  cam_params->setId(0);

  if (!optimizer.addParameter(cam_params)) {
    assert(false);
  }

初始化观测者的位置

  int vertex_id = 0;
  for (size_t i = 0; i < 15; ++i) { // 15个观测者位置
    Vector3d trans(i * 0.04 - 1., 0, 0); // 平移的距离

    Eigen::Quaterniond q;
    q.setIdentity(); // 无旋转
    g2o::SE3Quat pose(q, trans);
    g2o::VertexSE3Expmap* v_se3 = new g2o::VertexSE3Expmap();
    v_se3->setId(vertex_id);
    if (i < 2) {
      v_se3->setFixed(true); // 设置不动点,在g2o的实现中,至少存在一个不动点
    }
    v_se3->setEstimate(pose); // 设置初始化位姿,引擎在迭代过程中会更新它
    optimizer.addVertex(v_se3); // 添加至顶点,作为优化项
    true_poses.push_back(pose);
    vertex_id++;
  } 

添加观测者所见路标在相机中的投影点

  for (size_t i = 0; i < true_points.size(); ++i) { // 遍历所有路标
    g2o::VertexPointXYZ* v_p = new g2o::VertexPointXYZ();
    v_p->setId(point_id);
    v_p->setMarginalized(true);
    v_p->setEstimate(true_points.at(i) +
                     Vector3d(g2o::Sampler::gaussRand(0., 1),
                              g2o::Sampler::gaussRand(0., 1),
                              g2o::Sampler::gaussRand(0., 1))); // 对初始化点进行扰动
    int num_obs = 0;
    for (size_t j = 0; j < true_poses.size(); ++j) { // 遍历观测者位姿
      Vector2d z = cam_params->cam_map(true_poses.at(j).map(true_points.at(i)));
      if (z[0] >= 0 && z[1] >= 0 && z[0] < 640 && z[1] < 480) {
        ++num_obs; // 在相机中能观察到该路标
      }
    }
    if (num_obs >= 2) { // 如果只有一个位置能观察到该路标,无法建立回环检测
      optimizer.addVertex(v_p); // 将该路标添加到顶点中
      bool inlier = true;
      for (size_t j = 0; j < true_poses.size(); ++j) { // 遍历观测者位姿 
        Vector2d z =
            cam_params->cam_map(true_poses.at(j).map(true_points.at(i))); // 计算在相机中的投影点位置,是一个二维量
        if (z[0] >= 0 && z[1] >= 0 && z[0] < 640 && z[1] < 480) {
          z += Vector2d(g2o::Sampler::gaussRand(0., PIXEL_NOISE),
                        g2o::Sampler::gaussRand(0., PIXEL_NOISE)); // 添加误差
          g2o::EdgeProjectXYZ2UV* e = new g2o::EdgeProjectXYZ2UV();
          e->setVertex(0, dynamic_cast<g2o::OptimizableGraph::Vertex*>(v_p)); // 第一个点为当前路标
          e->setVertex(1, dynamic_cast<g2o::OptimizableGraph::Vertex*>(
                              optimizer.vertices().find(j)->second)); // 第二个点为当前的位姿
          e->setMeasurement(z); // 设置边的值
          e->information() = Matrix2d::Identity();
if (ROBUST_KERNEL) {
            g2o::RobustKernelHuber* rk = new g2o::RobustKernelHuber; // 设定核函数
            e->setRobustKernel(rk);
          }
          e->setParameterId(0, 0);
          optimizer.addEdge(e); // 添加边
        }
      }

小结

在实现中,观测者的位姿和路标的位置均是存在初始值的,可以这么理解:观测者的位姿是通过融合定位的方式获得,而路标是静止的,可以进行一次测量。

在实际优化中,观测者的位姿和路标的位置均会被调整,以很好的满足测量值。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

单片机介绍

本文为博主 日月同辉&#xff0c;与我共生&#xff0c;csdn原创首发。希望看完后能对你有所帮助&#xff0c;不足之处请指正&#xff01;一起交流学习&#xff0c;共同进步&#xff01; > 发布人&#xff1a;日月同辉,与我共生_单片机-CSDN博客 > 欢迎你为独创博主日月同…

Spring Boot 模块工程(通过 Maven Archetype)建立

前言 看到我身边的朋友反馈说&#xff0c;IDEA 新建项目时&#xff0c;如果通过 Spring Initializr 来创建 Spring Boot , 已经无法选择 Java 8 版本&#xff0c;通过上小节的教程&#xff0c;不知道该如何创建 Spring Boot 模块工程。如下图所示&#xff1a; 一.IDEA 搭建 …

记录一下uniapp 集成腾讯im特别卡(已解决)

uniapp的项目运行在微信小程序 , 安卓 , ios手机三端 , 之前这个项目集成过im,不过版本太老了,0.x的版本, 现在需要添加客服功能,所以就升级了 由于是二开 , 也为了方便 , 沿用之前的webview嵌套腾讯IM的方案 , 选用uniapp集成ui ,升级之后所有安卓用户反馈点击进去特别卡,几…

【深度学习】CodeFormer训练过程,如何训练人脸修复模型CodeFormer

文章目录 BasicSR介绍环境数据阶段 I - VQGAN阶段 II - CodeFormer (w0)阶段 III - CodeFormer (w1) 代码地址&#xff1a;https://github.com/sczhou/CodeFormer/releases/tag/v0.1.0 论文的一些简略介绍&#xff1a; https://qq742971636.blog.csdn.net/article/details/134…

Mysql索引相关学习笔记:B+ Tree、索引分类、索引优化、索引失效场景及其他常见面试题

前言 索引是Mysql中常用到的一个功能&#xff0c;可以大大加快查询速度&#xff0c;同时面试中也是经常碰到。本文是学习Mysql索引的归纳总结。 索引采用的数据结构——B 树 本部分主要是参考自小林Coding B树的由来 二分查找可以每次缩减一半&#xff0c;从而提高查找效率…

【mongoDB】数据库的创建和删除

目录 1. 查看所有数据库 2.创建数据库 3.查看当前连接的数据库 4.删除数据库 1. 查看所有数据库 show dbs 2.创建数据库 use 数据库名 例如创建一个名为 aaa 的数据库 3.查看当前连接的数据库 db 4.删除数据库 use 数据库名 db.dropDataBase() 比如删除数据库 aaa

1.25号c++

1.引用 引用就是给变量起别名 格式&#xff1a; 数据类型 &引用名 同类型的变量名 &#xff08;& 引用符号&#xff09; eg: int a 10; int &b a; //b引用a,或者给a变量取个别名叫b int *p; //指针可以先定义 后指向 p &a; //int &a…

【MySQL】如何通过DDL去创建和修改员工信息表

&#x1f308;个人主页: Aileen_0v0 &#x1f525;热门专栏: 华为鸿蒙系统学习|计算机网络|数据结构与算法 ​&#x1f4ab;个人格言:“没有罗马,那就自己创造罗马~” #mermaid-svg-fmKISDBsFq74ab2Z {font-family:"trebuchet ms",verdana,arial,sans-serif;font-siz…

docker(第二部分)

来自尚硅谷杨哥 少一点胡思乱想&#xff0c;心中无女人&#xff0c;编码自然神&#xff0c;忘掉心上人&#xff0c;抬手灭红尘。人间清醒&#xff0c;赚钱第一。好好学习&#xff0c;天天向上。听懂六六六。 7.Dokcer容器数据卷 1,&#xff09;坑&#xff1a;容器卷记得加入 …

shared_ptr 与 unique_ptr 的转换 笔记

推荐B站文章&#xff1a; 6.shared_ptr与unique_ptr_哔哩哔哩_bilibilihttps://www.bilibili.com/video/BV18B4y187uL?p6&vd_sourcea934d7fc6f47698a29dac90a922ba5a3我的往期文章&#xff1a; 独占指针&#xff1a;unique_ptr 与 函数调用-CSDN博客https://blog.csdn.n…

银行数据仓库体系实践(5)--数据转换

数据转换作业主要是指在数据仓库内的结构化数据批量加工&#xff0c;对于非结构化数据以及在线查询接口、数据流的开发主要是遵循代码开发规范以及各中间件的开发规范&#xff0c;如使用java来开发遵守java开发规范&#xff0c;使用Kafka需要遵循Kafka的使用和设计规范。同时做…

对话泛能网程路:能源产业互联网,行至中程

泛能网的能源产业互联网的标杆价值还不仅于此。其在产业互联之外&#xff0c;也更大的特殊性在于其也更在成为整个碳市场的“辅助运营商”&#xff0c;包括电力、碳等一系列被泛能网帮助企业改造和沉淀的要素资产&#xff0c;都在构成着碳交易市场的未来底层。 这恰是产业互联…

有关Quick BI中Case子句中多次使用lod函数返回空值问题分析

一、Quick BI中的lod_ include函数 lod_ include {维度1[,维度2]...:聚合表达式[:过滤条件]} 作用&#xff1a;将表达式中的维度一起作为分组依据进行订算。其中&#xff0c; 1) 维度1[,维度2]... &#xff1a;声明维度&#xff0c;指定聚合表达式要连接到的一个或多个维…

开源项目Git Commit规范与ChangeLog

一&#xff0c;conventional commit(约定式提交) Conventional Commits 是一种用于给提交信息增加人机可读含义的规范。它提供了一组用于创建清晰的提交历史的简单规则。 1.1 作用 自动化生成 CHANGELOG基于提交类型&#xff0c;自动决定语义化的版本变更向项目相关合作开发…

OpenCV书签 #互信息的原理与相似图片搜索实验

1. 介绍 互信息&#xff08;Mutual Information&#xff09; 是信息论中的一个概念&#xff0c;用于衡量两个随机变量之间的关联程度。在图像处理和计算机视觉中&#xff0c;互信息常被用来度量两幅图像之间的相似性。 互信息可以看成是一个随机变量中包含的关于另一个随机变…

【网站项目】基于SSM的251国外摇滚乐队交流和周边售卖系统

&#x1f64a;作者简介&#xff1a;拥有多年开发工作经验&#xff0c;分享技术代码帮助学生学习&#xff0c;独立完成自己的项目或者毕业设计。 代码可以私聊博主获取。&#x1f339;赠送计算机毕业设计600个选题excel文件&#xff0c;帮助大学选题。赠送开题报告模板&#xff…

DAY30:回溯算法332\51\37基本思路了解+总结

Leetcode: 332 重新安排行程 代码随想录 这道题目有几个难点&#xff1a; 一个行程中&#xff0c;如果航班处理不好容易变成一个圈&#xff0c;成为死循环&#xff0c;容易出现环路。有多种解法&#xff0c;字母序靠前排在前面&#xff0c;让很多同学望而退步&#xff0c;如…

yolov8上使用gpu教程

yolov8上使用gpu教程 安装Cuda和Cudnnyolov8上使用gpu 安装Cuda和Cudnn 1.查看支持的cuda版本&#xff0c;并去官网下载。 nvidia-smi2.网址&#xff1a;https://developer.nvidia.com/cuda-toolkit-archive 3.安装细节 安装的前提基础是&#xff0c;有vs的C环境。我电脑有…

多流转换 (分流,合流,基于时间的合流——双流联结 )

目录 一&#xff0c;分流 1.实现分流 2.使用侧输出流 二&#xff0c;合流 1&#xff0c;联合 2&#xff0c;连接 三&#xff0c;基于时间的合流——双流联结 1&#xff0c;窗口联结 1.1 窗口联结的调用 1.2 窗口联结的处理流程 2&#xff0c;间隔联结 2.1 间隔联…

Qt单选按钮

前言 本篇文章介绍Qt的单选按钮&#xff0c;就是QRadioButton QRadioButton是一个选项按钮&#xff0c;可以打开&#xff08;选中&#xff09;或关闭&#xff08;取消选中&#xff09;。单选按钮通常向用户提供“众多之一”的选择。 在一组单选按钮中&#xff0c;一次只能选中…