Cartographer MapBuilder各数据结构分析 -20241118

  • 目标:

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

    • 由于用的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 来确定纹理的位置。

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_:指示哪个队列在当前时刻阻塞,需要更多的数据才能继续进行合并排序和分发。

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

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

相关文章

Python中的TCP

文章目录 一. 计算机网络1. 网络的概念2. IP地址① IP地址的概念② IP地址的表现形式③ IP地址的作用④ 网络查询命令Ⅰ. ifconfig/ipconfigⅡ. ping 3. 端口和端口号的概念(计算机通信原理)① 端口的概念② 端口号的概念 4. socket套接字① socket概念② socket使用场景 二. T…

本地部署Apache Answer搭建高效的知识型社区并一键发布到公网流程

文章目录 前言1. 本地安装Docker2. 本地部署Apache Answer2.1 设置语言选择简体中文2.2 配置数据库2.3 创建配置文件2.4 填写基本信息 3. 如何使用Apache Answer3.1 后台管理3.2 提问与回答3.3 查看主页回答情况 4. 公网远程访问本地 Apache Answer4.1 内网穿透工具安装4.2 创建…

【数据结构】线性表——栈与队列

写在前面 栈和队列的关系与链表和顺序表的关系差不多&#xff0c;不存在谁替代谁&#xff0c;只有双剑合璧才能破敌万千~~&#x1f60e;&#x1f60e; 文章目录 写在前面一、栈1.1栈的概念及结构1.2、栈的实现1.2.1、栈的结构体定义1.2.2、栈的初始化栈1.2.3、入栈1.2.4、出栈…

科技改变工作方式:群晖NAS安装内网穿透实现个性化办公office文档分享(1)

文章目录 前言1. 本地环境配置2. 制作本地分享链接3. 制作公网访问链接4. 公网ip地址访问您的分享相册5. 制作固定公网访问链接 前言 本文将详细介绍如何在群晖NAS上安装Synology Office和Synology Drive Server&#xff0c;并利用Cpolar内网穿透工具为本地文档配置固定的公网…

无插件H5播放器EasyPlayer.js网页web无插件播放器选择全屏时,视频区域并没有全屏问题的解决方案

EasyPlayer.js H5播放器&#xff0c;是一款能够同时支持HTTP、HTTP-FLV、HLS&#xff08;m3u8&#xff09;、WS、WEBRTC、FMP4视频直播与视频点播等多种协议&#xff0c;支持H.264、H.265、AAC、G711A、MP3等多种音视频编码格式&#xff0c;支持MSE、WASM、WebCodec等多种解码方…

rocketmq5源码系列--(一)--搭建调试环境

说在前头&#xff1a;阿里的rocketmq的文档是真他妈的烂的1b&#xff0c;很多东西都不说&#xff0c;全靠自己看源码&#xff0c;摸索&#xff0c;草&#xff0c;真的要吐血了 rocketmq的版本5而不是版本4&#xff0c;版本5比版本4多了个proxy rocketmq5 三个组件&#xff1a;…

【网页设计】CSS3 进阶(动画篇)

1. CSS3 2D 转换 转换&#xff08;transform&#xff09;是CSS3中具有颠覆性的特征之一&#xff0c;可以实现元素的位移、旋转、缩放等效果 转换&#xff08;transform&#xff09;你可以简单理解为变形 移动&#xff1a;translate旋转&#xff1a;rotate缩放&#xf…

django安装与项目创建

一、安装 在终端输入 pip install django //或者(&#xff09;指定安装版本 pip install django2.2 二、创建项目 2.1创建项目 django-admin startproject 项目名 2.2Django 项目中的关键文件 _init_.py:将目录标识为python包setting.py:核心配置文件&#xff0c;定义项目…

【redis】—— 初识redis(redis基本特征、应用场景、以及重大版本说明)

序言 本文将引导读者探索Redis的世界&#xff0c;深入了解其发展历程、丰富特性、常见应用场景、使用技巧等&#xff0c;最后会对Redis演进过程中具有里程碑意义的版本进行详细解读。 目录 &#xff08;一&#xff09;初始redis &#xff08;二&#xff09;redis特性 &#…

SpringBoot学习记录(三)之多表查询

SpringBoot学习记录&#xff08;三&#xff09;之多表查询 一、多表查询概述1、数据准备2、介绍3、分类 二、内连接三、外连接四、子查询1、标量子查询2、列子查询3、行子查询4、表子查询 三、案例1、准备环境2、需求实现3、&#xff08;附&#xff09;数据准备 一、多表查询概…

泰矽微重磅发布超高集成度车规触控芯片TCAE10

市场背景 智能按键和智能表面作为汽车智能化的重要部分&#xff0c;目前正处于快速发展阶段&#xff0c;电容式触摸按键凭借其操作便利性与小体积的优势&#xff0c;在汽车内饰表面的应用越来越广泛。对于空调控制面板、档位控制器、座椅扶手、门饰板、车顶控制器等多路开关的…

HarmonyOs学习笔记-布局单位

鸿蒙开发中布局存在很多单位 鸿蒙的默认单位是vp 下方先展示一下在RrkTsUI中我们应该怎么书写&#xff0c;然后讲一下各大单位具体的含义。 Text("这是一个文本, 用默认单位进行展示&#xff0c;也就是vp") .width(100) .height(100);//此段代码与上方代码是一样的…

操作系统实验 C++实现生产者-消费者问题

实验目的 1、进一步加深理解进程同步的概念 2、加深对进程通信的理解 3、了解Linux下共享内存的使用方法 实验内容 1、按照下面要求&#xff0c;写两个c程序&#xff0c;分别是生产者producer.c以及customer.c 2、一组生产者和一组消费者进程共享一块环形缓冲区 使用共…

Easyexcel(1-注解使用)

文章链接&#xff1a; Easyexcel&#xff08;1-注解使用&#xff09; 版本依赖 <dependency><groupId>com.alibaba</groupId><artifactId>easyexcel</artifactId><version>3.3.3</version> </dependency>ExcelProperty 指定…

最新版xAI LLM 模型Grok-2 上线

xAI&#xff01;Grok-2 最新版开启公测&#xff01;”。这是我注册成功的截图&#xff0c;使用国内的邮箱就可以注册使用了&#xff01; Grok API公测与免费体验: Grok API开启公测&#xff0c;提供免费体验128k上下文支持&#xff0c;。Grok-Beta与马斯克: 马斯克庆祝特朗普当…

css数据不固定情况下,循环加不同背景颜色

<template><div><p v-for"(item, index) in items" :key"index" :class"getBackgroundClass(index)">{{ item }}</p></div> </template><script> export default {data() {return {items: [学不会1, …

MySQL的聚簇索引和二级索引

索引按照物理实现方式&#xff0c;索引可以分为 2 种&#xff1a;聚簇&#xff08;聚集&#xff09;和非聚簇&#xff08;非聚集&#xff09;索引。也可以把非聚集索引称为二级索引或者辅助索引。 一.聚簇索引 聚簇索引并不是一种单独的索引类型&#xff0c;而是一种数据存储方…

【Pytorch】torch.nn.functional模块中的非线性激活函数

在使用torch.nn.functional模块时&#xff0c;需要导入包&#xff1a; from torch.nn import functional 以下是常见激活函数的介绍以及对应的代码示例&#xff1a; tanh (双曲正切) 输出范围&#xff1a;(-1, 1) 特点&#xff1a;中心对称&#xff0c;适合处理归一化后的数据…

神经网络11-TFT模型的简单示例

Temporal Fusion Transformer (TFT) 是一种用于时间序列预测的深度学习模型&#xff0c;它结合了Transformer架构的优点和专门为时间序列设计的一些优化技术。TFT尤其擅长处理多变量时间序列数据&#xff0c;并且能够捕捉到长期依赖关系&#xff0c;同时通过自注意力机制有效地…

学习threejs,使用TWEEN插件实现动画

&#x1f468;‍⚕️ 主页&#xff1a; gis分享者 &#x1f468;‍⚕️ 感谢各位大佬 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍⚕️ 收录于专栏&#xff1a;threejs gis工程师 文章目录 一、&#x1f340;前言1.1 ☘️THREE.PLYLoader PLY模型加…