Cartographer MapBuilder各数据结构分析 -20241118

news2024/11/22 19:59:52
  • 目标:

    • 目前手上有一套小车和激光雷达,要实现定位和移动控制。

    • 由于用的arduino平台,硬件资源不够,因此传输数据到pc,pc进行算法分析后进行控制

    • 因此需要用MapBuilder模块实现2d-slam建模

MapBuilder

  1. 管理轨迹

    1. 支持多个轨迹的独立创建和管理,每个轨迹可以与不同的传感器数据绑定。
  2. 数据处理

    1. 整合来自激光雷达、IMU、里程计等多种传感器的数据,用于 SLAM(同步定位与建图)任务。
  3. 调用 Pose Graph 优化

    1. 将所有轨迹数据和子图(Submap)传递给 Pose Graph,优化整体的位姿和约束。
  4. 地图输出

    1. 支持生成点云地图或栅格化地图,保存为 pbstream 或其他格式。
//创建轨迹
mapping::TrajectoryBuilderInterface* AddTrajectoryBuilder(
    const std::vector<std::string>& expected_sensor_ids,
    const mapping::proto::TrajectoryBuilderOptions& trajectory_options,
    LocalSlamResultCallback local_slam_result_callback);

//完成轨迹
void FinishTrajectory(int trajectory_id);

//从 pbstream 加载轨迹
void LoadStateFromFile(const std::string& state_filename, bool load_frozen_state);

//保存地图
void SerializeStateToFile(bool include_unfinished_submaps, const std::string& filename);

//获取 Pose Graph
mapping::PoseGraphInterface* pose_graph();


TrajectoryBuilderInterface

  • 作为传感器数据的入口。

  • 实现传感器数据的预处理,如滤波或校准。

  • 调用本地 SLAM(Local SLAM)算法,生成轨迹节点。

  • 将轨迹节点和子图传递到全局优化模块(Pose Graph)

// 根据不同的重载,传输不同类型的传感器数据
激光雷达输入:

void AddSensorData(const std::string& sensor_id,
                   const sensor::TimedPointCloudData& data) override;

    参数:
        sensor_id:传感器 ID,与配置文件中定义的名称对应。
        data:时间戳点云数据。

IMU 输入:

void AddSensorData(const std::string& sensor_id,
                   const sensor::ImuData& data) override;

    参数:
        sensor_id:传感器 ID。
        data:IMU 数据,包括加速度和角速度。

里程计输入:

void AddSensorData(const std::string& sensor_id,
                   const sensor::OdometryData& data) override;

    参数:
        sensor_id:传感器 ID。
        data:里程计的位姿数据。

固定坐标系位姿:

void AddSensorData(const std::string& sensor_id,
                   const sensor::FixedFramePoseData& data) override;

    参数:
        sensor_id:传感器 ID。
        data:在固定参考系中的位姿数据(例如 GPS 提供的全球位姿)。

地标输入:

void AddSensorData(const std::string& sensor_id,
                   const sensor::LandmarkData& data) override;

    参数:
        sensor_id:传感器 ID。
        data:地标观测数据。

// 停止轨迹的构建,并触发一些清理操作。
void FinishTrajectory() override;

// 丢弃轨迹构建中的所有未处理数据。
void Flush() override;



PoseGraph

  1. 维护轨迹和子图的结构:包括节点、子图和它们的全局位姿。

  2. 处理约束

    1. 增量添加新的约束(如回环检测的结果)。

    2. 执行全局优化以调整所有轨迹。

  3. 与 Local SLAM 集成:接受本地 SLAM 生成的子图和轨迹节点。

  4. 支持多轨迹处理:处理多个独立的轨迹并合并到同一个全局坐标系中。

// 添加 IMU 数据
  virtual void AddImuData(int trajectory_id,
                          const sensor::ImuData& imu_data) = 0;

  // 添加里程计数据
  virtual void AddOdometryData(int trajectory_id,
                               const sensor::OdometryData& odometry_data) = 0;

  // 添加固定参考帧的位姿数据
  virtual void AddFixedFramePoseData(
      int trajectory_id,
      const sensor::FixedFramePoseData& fixed_frame_pose_data) = 0;

  // 添加地标数据
  virtual void AddLandmarkData(int trajectory_id,
                               const sensor::LandmarkData& landmark_data) = 0;

  // 完成指定轨迹的构建
  virtual void FinishTrajectory(int trajectory_id) = 0;

  // 冻结指定轨迹,不再优化该轨迹的位姿
  virtual void FreezeTrajectory(int trajectory_id) = 0;

  // 从序列化的子图中添加子图信息
  virtual void AddSubmapFromProto(const transform::Rigid3d& global_pose,
                                  const proto::Submap& submap) = 0;

  // 从序列化的节点中添加轨迹节点
  virtual void AddNodeFromProto(const transform::Rigid3d& global_pose,
                                const proto::Node& node) = 0;

  // 从序列化数据中设置轨迹信息
  virtual void SetTrajectoryDataFromProto(
      const mapping::proto::TrajectoryData& data) = 0;

  // 向子图中添加节点信息
  virtual void AddNodeToSubmap(const NodeId& node_id,
                               const SubmapId& submap_id) = 0;

  // 添加约束
  virtual void AddSerializedConstraints(
      const std::vector<Constraint>& constraints) = 0;

  // 添加轨迹修剪器
  virtual void AddTrimmer(std::unique_ptr<PoseGraphTrimmer> trimmer) = 0;

  // 获取当前轨迹的连通关系
  virtual std::vector<std::vector<int>> GetConnectedTrajectories() const = 0;

  // 获取指定子图的全局位姿及其本体
  virtual SubmapData GetSubmapData(const SubmapId& submap_id) const = 0;

  // 将当前的 PoseGraph 转换为 proto 格式
  proto::PoseGraph ToProto() const override;

  // 获取 IMU 数据
  virtual sensor::MapByTime<sensor::ImuData> GetImuData() const = 0;

  // 获取里程计数据
  virtual sensor::MapByTime<sensor::OdometryData> GetOdometryData() const = 0;

  // 获取固定参考帧的位姿数据
  virtual sensor::MapByTime<sensor::FixedFramePoseData> GetFixedFramePoseData()
      const = 0;

  // 获取地标数据
  virtual std::map<std::string /* landmark ID */, PoseGraph::LandmarkNode>
  GetLandmarkNodes() const = 0;

  // 设置初始轨迹位姿(用于多轨迹合并)
  virtual void SetInitialTrajectoryPose(int from_trajectory_id,
                                        int to_trajectory_id,
                                        const transform::Rigid3d& pose,
                                        const common::Time time) = 0;

SubmapData

  • pose类型:cartographer::transform::Rigid3d表示子图的全局位姿,通常是通过图优化计算得到的。全局位姿是相对于全局坐标系的 3D 刚体变换。

    • 类型:cartographer::transform::Rigid3d

    • 表示子图的全局位姿,通常是通过图优化计算得到的。

    • 全局位姿是相对于全局坐标系的 3D 刚体变换。

  • submap类型:std::shared_ptr<const Submap>子图本身的数据结构。Submap 是子图的具体表示,包含了扫描数据、状态信息(如是否完成)、以及栅格地图等。

    • 类型:std::shared_ptr<const Submap>

    • 子图本身的数据结构。

    • Submap 是子图的具体表示,包含了扫描数据、状态信息(如是否完成)、以及栅格地图等。

Submap

  • 一个表示局部地图数据的核心数据结构。它通常是由 Cartographer 在 SLAM 过程中创建的,并且在地图构建的不同阶段,子图代表了一个小区域或一个时间段的扫描数据

  • 包含以下信息:

    • 局部位姿(Local Pose):表示子图在全局坐标系中的位置。

    • 栅格地图数据:如 ProbabilityGridHybridGrid,这两种数据结构用于存储空间中的占用情况或距离信息。

    • 扫描数据:表示该子图生成时所接收到的激光扫描数据。

  • 这个只是接口类,看不到数据存在哪

  • Submap2D 继承该类,数据存在

  std::unique_ptr<Grid2D> grid_;
  ValueConversionTables* conversion_tables_;

Grid2D

  • Grid2D 是 Cartographer 中用于表示二维网格数据的类,通常用于表示地图、栅格图(grid maps)等二维数据结构。在 Cartographer 中,Grid2D 通常用于构建子图中的栅格地图,如概率网格(probability grid)或 TSDF(Truncated Signed Distance Field)等

  • 主要成员变量

    • MapLimits limits_;
      这个成员表示地图的限制或边界,例如地图的尺寸、分辨率或坐标范围。MapLimits 可能包括地图的宽度、高度以及每个单元格的物理尺寸。

    • std::vector correspondence_cost_cells_;
      这是一个存储 uint16_t 类型的数组,用于存储地图中每个单元格的对应成本值(correspondence cost)。这些成本值通常用于表示该位置的置信度或与某些传感器数据的匹配度。例如,它可以表示在某些位置上传感器观测到的信号的强度或可信度。

    • float min_correspondence_cost_; 和 float max_correspondence_cost_;
      这两个变量分别表示对应成本的最小值和最大值。它们用于限制 correspondence_cost_cells_ 中存储的值范围,以确保成本不会低于或高于某个阈值。

    • std::vector update_indices_;
      这个成员变量用于存储已更新的单元格索引。每当地图数据发生变化时,相关的索引会被添加到 update_indices_ 中。这个数组通常用于增量更新的场景中,帮助追踪哪些区域发生了变化。

    • Eigen::AlignedBox2i known_cells_box_;
      这是一个 Eigen::AlignedBox2i 类型的对象,表示已知单元格的边界框(bounding box)。它用于高效地计算地图的裁剪范围,确保只处理已知区域,从而减少计算量。

  • 主要成员函数

    • const MapLimits& limits() const { return limits_; }
      这个函数返回地图的限制(如尺寸和边界),即 limits_。

    • void FinishUpdate();
      这个函数标志着更新序列的完成,通常用于结束一次地图数据的更新过程。

    • float GetCorrespondenceCost(const Eigen::Array2i& cell_index) const;
      该方法返回给定单元格索引 cell_index 对应的成本值。cell_index 是一个二维数组,表示网格中的某个位置。

    • bool IsKnown(const Eigen::Array2i& cell_index) const;
      这个方法判断给定位置的单元格是否已知(即是否有有效数据)。通常,这用于检查该位置是否已被传感器观测到。

    • void ComputeCroppedLimits(Eigen::Array2i* const offset, CellLimits* const limits) const;
      这个方法用来计算一个包含所有已知单元格的子区域,并将该区域的偏移量和限制存储到 offset 和 limits 中。

    • void GrowLimits(const Eigen::Vector2f& point);
      这个方法用于根据给定的点 point 来扩展地图的限制。当新的数据点需要添加到地图中时,这个方法会更新地图的边界。

    • std::unique_ptr ComputeCroppedGrid() const = 0;
      这个纯虚函数返回一个裁剪后的 Grid2D 对象,通常用于创建一个新的只包含已知区域的网格。

    • proto::Grid2D ToProto() const;
      这个方法将 Grid2D 对象转换为一个协议缓冲区(proto::Grid2D),用于序列化和数据传输。

    • bool DrawToSubmapTexture(proto::SubmapQuery::Response::SubmapTexture* const texture, transform::Rigid3d local_pose) const = 0;
      这个纯虚函数将当前的 Grid2D 数据绘制到子地图的纹理中。它可能涉及图形渲染,并使用给定的姿态 local_pose 来确定纹理的位置。

class ProbabilityGrid : public Grid2D {}

ProbabilityGrid 是一个表示二维网格的概率分布的类,用于地图或图形处理

  • 继承Grid2D。

class TSDF2D : public Grid2D {}

CollatorInterface

CollatorInterface 是一个抽象接口类,它定义了多轨迹数据处理和合并的基本框架。继承自 CollatorInterface 的类通常会实现这些方法,提供具体的逻辑来处理轨迹和传感器数据。

  • AddTrajectory: 为轨迹 ID 设置回调函数,这样当数据添加到轨迹时,回调就会触发。
  • FinishTrajectory: 当轨迹完成时,将其 ID 添加到已完成轨迹集合 finished_trajectories_。
  • AddSensorData: 将传感器数据按顺序添加到指定轨迹中,并触发回调函数处理数据。
  • Flush: 清空所有待处理的数据并调用相应的回调函数。
  • GetBlockingTrajectoryId: 检查是否有轨迹尚未完成,若有则返回需要更多数据的轨迹 ID
class Collator : public CollatorInterface {...}
class TrajectoryCollator : public CollatorInterface {...}
// 等待看到所有传感器 ID 的至少一项数据,并按合并排序的顺序分发数据。
// 与 'Collator' 不同,它不等待其他轨迹的数据。
// 同样,与 'Collator' 的输出是确定性不同,数据分发的顺序不是排序的,因此非确定性的输入序列
// 将导致非确定性的输出。

OrderedMultiQueue

  • OrderedMultiQueue 类用于维护多个已排序的传感器数据队列,并按合并排序的顺序(merge-sorted order)分发数据。它会确保在分发数据时,每个未完成的队列至少有一个数据项可用,才能继续合并并分发下一个排序的数据项。

简而言之,这个类通过合并多个已排序队列来生成一个按顺序排列的数据流,并保证每次分发的数据来自所有队列的最小项。

线程兼容性:此类设计为线程安全的,意味着它可以在多线程环境下安全地操作,而不会出现数据竞争问题。

  • queues_:维护一个队列集合,键是 QueueKey,值是包含 Queue 对象的 map。Queue 包含了队列、回调和是否已完成的标志。
  • last_dispatched_time_:记录上一个分发的时间,确保数据按时间顺序被处理。
  • blocker_:指示哪个队列在当前时刻阻塞,需要更多的数据才能继续进行合并排序和分发。

LocalSlamResultCallback

//LocalSlamResultCallback 的目的是在局部SLAM过程中处理每次传感器数据的结果。该回调函数会返回处理后的信息,告知系统:
//    这笔数据是属于哪个轨迹的;
//    数据对应的时间戳;
//    局部位姿的估计(机器人或传感器在当前时刻的位置与姿态);
//    在本地坐标系下的范围数据;
//    数据是否成功插入到子图中,若成功则返回插入结果
//数据插入到子图的情况:
//    如果这段传感器数据(sensor::RangeData)成功地被插入到一个子图(Submap)中,那么回调函数会报告该数据所对应的 NodeId。
//数据被过滤的情况:
//    如果传感器数据被过滤掉(比如,数据不符合要求或者不被插入到子图中),回调函数会返回 nullptr,表示没有插入数据。
  using LocalSlamResultCallback =
      std::function<void(int /* trajectory ID */, common::Time,
                         transform::Rigid3d /* local pose estimate */,
                         sensor::RangeData /* in local frame */,
                         std::unique_ptr<const InsertionResult>)>;
//原点:光线的起始位置,通常是激光雷达传感器的位置。
//返回点 (Returns):光线遇到障碍物的位置,这些点用于生成环境的点云,帮助构建地图。
//未命中点 (Misses):光线未检测到障碍物的地方,通常记录为最大距离,用于表示空旷区域或无障碍区域。
struct RangeData {
  Eigen::Vector3f origin;
  PointCloud returns;
  PointCloud misses;
};

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

人工智能之机器学习5-回归算法2【培训机构学习笔记】

培训班ppt内容&#xff1a; 个人精进总结&#xff1a; 可解释方差 定义 可解释方差的回归评分函数是一种用于评估回归模型性能的指标&#xff0c;以下从其定义、计算公式、取值范围及意义、应用场景等方面进行详细介绍&#xff1a; 可解释方差&#xff08;Explained Varian…

vue2中引入cesium全步骤

1.npm 下载cesium建议指定版本下载&#xff0c;最新版本有兼容性问题 npm install cesium1.95.0 2.在node_models中找到cesium将此文件下的Cesium文件复制出来放在项目的静态资源public中或者static中&#xff0c;获取去github上去下载zip包放在本地也可以 3.在index.html中引…

数据结构(顺序栈——c语言实现)

栈的基本概念&#xff1a; 栈是限制在一端进行插入操作和删除操作的线性表&#xff08;俗称堆栈&#xff09;&#xff0c;允许进行操作的一端称为“栈顶”&#xff0c;另一固定端称为“栈底”&#xff0c;当栈中没有元素时称为“空栈” 特点&#xff1a;先进后出&#xff08;FI…

基于Windows系统用C++做一个点名工具

目录 一、前言 二、主要技术点 三、准备工作 四、主界面 1.绘制背景图 2、实现读取花名册功能 3.实现遍历花名册功能 4.实现储存功能 4.1创建数据库 4.2存储数据到数据库表 4.3读取数据库表数据 一、前言 人总是喜欢回忆过去&#xff0c;突然回忆起…

前端监控之sourcemap精准定位和还原错误源码

一、概述 在前端开发中&#xff0c;监控和错误追踪是确保应用稳定性和用户体验的重要环节。 随着前端应用的复杂性增加&#xff0c;JavaScript错误监控变得尤为重要。在生产环境中&#xff0c;为了优化加载速度和性能&#xff0c;前端代码通常会被压缩和混淆。这虽然提升了性…

算法编程题-排序

算法编程题-排序 比较型排序算法冒泡排序选择排序插入排序希尔排序堆排序快速排序归并排序 非比较型排序算法计数排序基数排序 本文将对七中经典比较型排序算法进行介绍&#xff0c;并且给出golang语言的实现&#xff0c;还包括基数排序、计数排序等非比较型的算法的介绍和实现…

Jenkins修改LOGO

重启看的LOGO和登录页面左上角的LOGO 进入LOGO存在的目录 [roottest-server01 svgs]# pwd /opt/jenkins_data/war/images/svgs [roottest-server01 svgs]# ll logo.svg -rw-r--r-- 1 jenkins jenkins 29819 Oct 21 10:58 logo.svg #jenkins_data目录是我挂载到了/opt目录&…

【MyBatisPlus·最新教程】包含多个改造案例,常用注解、条件构造器、代码生成、静态工具、类型处理器、分页插件、自动填充字段

文章目录 一、MyBatis-Plus简介二、快速入门1、环境准备2、将mybatis项目改造成mybatis-plus项目&#xff08;1&#xff09;引入MybatisPlus依赖&#xff0c;代替MyBatis依赖&#xff08;2&#xff09;配置Mapper包扫描路径&#xff08;3&#xff09;定义Mapper接口并继承BaseM…

云讷科技Kerloud无人飞车专利发布

云讷科技Kerloud无人飞车获得了“一种室内外两用的四旋翼无人飞车”的实用新型专利证书&#xff0c;作为科教社区第一款四旋翼飞车&#xff0c;这项技术结合了无人机和无人车的优势&#xff0c;提供了一种能够在多种环境下使用的多功能飞行器。 这项设计的优势如下&#xff…

Applied Intelligence投稿

一、关于手稿格式&#xff1a; 1、该期刊是一个二区的&#xff0c;模板使用Springer nature格式&#xff0c; 期刊投稿要求&#xff0c;详细期刊投稿指南&#xff0c;大部分按Soringernature模板即可&#xff0c;图片表格声明参考文献命名要求需注意。 2、参考文献&#xff…

Spark SQL大数据分析快速上手-完全分布模式安装

【图书介绍】《Spark SQL大数据分析快速上手》-CSDN博客 《Spark SQL大数据分析快速上手》【摘要 书评 试读】- 京东图书 大数据与数据分析_夏天又到了的博客-CSDN博客 Hadoop完全分布式环境搭建步骤-CSDN博客,前置环境安装参看此博文 完全分布模式也叫集群模式。将Spark目…

零基础上手WebGIS+智慧校园实例(1)【html by js】

请点个赞收藏关注支持一下博主喵&#xff01;&#xff01;&#xff01; 等下再更新一下1. WebGIS矢量图形的绘制&#xff08;超级详细&#xff01;&#xff01;&#xff09;&#xff0c;2. WebGIS计算距离&#xff0c; 以及智慧校园实例 with 3个例子&#xff01;&#xff01;…

[开源] 告别黑苹果!用docker安装MacOS体验苹果系统

没用过苹果电脑的朋友可能会对苹果系统好奇&#xff0c;有人甚至会为了尝鲜MacOS去折腾黑苹果。如果你只是想体验一下MacOS&#xff0c;这里有个更简单更优雅的解决方案&#xff0c;用docker安装MacOS来体验苹果系统。 一、项目简介 项目描述 Docker 容器内的 OSX&#xff08…

IDEA:2023版远程服务器debug

很简单&#xff0c;但是很多文档没有写清楚&#xff0c;wocao 一、首先新建一个远程jvm 二、配置 三、把上面的参数复制出来 -agentlib:jdwptransportdt_socket,servery,suspendn,address5005 四、然后把这串代码放到服务器中&#xff08;这里的0.0.0.0意思是所有IP都能访问&a…

卷积神经网络的padding是什么?如何计算?

文章目录 为什么需要padding&#xff1f;1.Valid Padding&#xff08;有效填充&#xff09;2.Same Padding&#xff08;相同填充&#xff09;2.1.如何计算padding&#xff1f;1. 计算总 padding2. 分配 padding&#xff1a; 2.2.举例子1. 步幅为 1 的 Same Padding2. 步幅不为 …

介绍一下strncmp(c基础)

strncmp是strcmp的进阶版 链接介绍一下strcmp(c基础)-CSDN博客 作用 比较两个字符串的前n位 格式 #include <string.h> strncmp (arr1,arr2,n); 工作原理&#xff1a;strcmp函数按照ACII&#xff08;字符编码顺序&#xff09;比较两个字符串。它从两个字符串的第一…

列出D3的所有交互方法,并给出示例

D3.js 提供了丰富的交互方法&#xff0c;可以用来增强图表的用户交互体验。以下是一些常用的交互方法及其示例&#xff1a; 1. 鼠标事件 on("mouseover", function) 用途: 当鼠标悬停在元素上时触发。示例:svg.selectAll(".bar").on("mouseover&qu…

丹摩征文活动 | AI创新之路,DAMODEL助你一臂之力GPU

目录 前言—— DAMODEL&#xff08;丹摩智算&#xff09; 算力服务 直观的感受算力提供商的强大​ 平台功能介绍​ 镜像选择 云磁盘创建 总结 前言—— 只需轻点鼠标,开发者便可拥有属于自己的AI计算王国 - 从丰富的GPU实例选择,到高性能的云磁盘,再到预配置的深度学习…

基于大数据爬虫数据挖掘技术+Python的网络用户购物行为分析与可视化平台(源码+论文+PPT+部署文档教程等)

#1024程序员节&#xff5c;征文# 博主介绍&#xff1a;CSDN毕设辅导第一人、全网粉丝50W,csdn特邀作者、博客专家、腾讯云社区合作讲师、CSDN新星计划导师、Java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和学生毕业项目实战,高校老…

六、卷积神经网络(CNN)基础

卷积神经网络&#xff08;CNN&#xff09;基础 前言一、CNN概述二、卷积层2.1 卷积2.2 步幅(Stride)2.3 填充(Padding)2.4 多通道卷积2.5 多卷积计算2.6 特征图大小计算2.7 代码演示 三、池化层3.1 池化层计算3.1.1 最大池化层3.1.2 平均池化层 3.2 填充(Padding)3.3 步幅(Stri…