cartographer 代码思想解读(10)- slam前端LocalTrajectoryBuilder2D类(主流程)

本文详细解析了Cartographer中LocalTrajectoryBuilder2D类的处理流程,它是Cartographer前端SLAM的核心。该类负责接收激光雷达、里程计和IMU数据,通过预处理、匹配、位置估计和子图构建,生成匹配结果。流程包括点云融合、运动滤波、实时相关匹配和Ceres优化,最终更新子图。LocalTrajectoryBuilder2D实现了传感器数据的融合、点云投影、扫描匹配等功能,输出匹配后的子图和轨迹节点信息。


前面9节描述和分析了cartographer在slam前端使用的算法、原理以及具体实现。每个知识点都是任何slam中几乎必不可少的,也是slam技术的关键点。而slam则是将每个算法串联起来,构建slam的前端处理流程,从而实现slam(暂时缺少后端闭环处理)。而LocalTrajectoryBuilder2D类即为cartographer的前端核心流程和调用接口,即local slam。完成了传感器预处理、scanmatch、位置估计、submap构建及更新调用。其代码目录:
cartographer/mapping/internal/2d/local_trajectory_builder_2d.cc

local_trajectory_builder_2d处理流程

引入官方的总体流程图
在这里插入图片描述

其中红色框圈起来的则是local_trajectory_builder_2d所处理的所有内容和流程,其输入仅为激光数据(点云)、odometry,Imu DATA。而经过一系列处理后输出结果为cartographer定义的InsertionResult结果类,包含了时间戳、当前机器人位置、submap及在世界坐标的位置、激光点云结合等。即经过前端处理可以得到一系列的submap,如此后续其他模块可将submap进行后端处理拼接形成整个地图。
整体处理流程如下:
1.激光原始数据预处理融合转换成cartographer使用的点云格式;
2.激光点云数据经过两次滤波后进行真正使用;
3.odometry、imu及其匹配后的精确位置构建位置估计器实时估计下刻位置;
4.根据预估位置和点云数据进行scan match获取优化后更精确的位置;
5.经过运动滤波器(目的是降采样)后的位置及其对应的点云进行维护和更新submap;
6.当submap满足一定数量的激光帧时,输出MatchingResult类型结果;

注意:
1.其中Fixed Frame Pose在2DSLAM中暂未找到传入接口。
2.输出的结果类型应该是MatchingResult,而不是图中的InsertionResult

local_trajectory_builder_2d类定义

类含有变量

  const proto::LocalTrajectoryBuilderOptions2D options_;    // 轨迹跟踪器的配置信息
  ActiveSubmaps2D active_submaps_;                          // 维护正在使用的submap,已分析过,包括栅格地图类型,插入,更新

  MotionFilter motion_filter_;                              // 运动滤波器,用来对运动pose进行降采样
  scan_matching::RealTimeCorrelativeScanMatcher2D           // 相关匹配器,已分析过
      real_time_correlative_scan_matcher_;
  scan_matching::CeresScanMatcher2D ceres_scan_matcher_;    // 优化匹配器,已分析过

  std::unique_ptr<PoseExtrapolator> extrapolator_;          // 位置估计器,可根据历史轨迹及其传感器估计下时刻位置

  int num_accumulated_ = 0;                                 // 累计激光点云个数
  sensor::RangeData accumulated_range_data_;                // 预处理后激光点云,也包含激光原点origin坐标,即经过融合,矫正等操作
                                                            // 也是核心算法使用的数据结构,用于map 更新和匹配
  absl::optional<std::chrono::steady_clock::time_point> last_wall_time_;
  absl::optional<double> last_thread_cpu_time_seconds_;
  absl::optional<common::Time> last_sensor_time_;

  RangeDataCollator range_data_collator_;                   // 激光数据收集器,几种激光按照时间戳融合

从类成员变量可看出包括
1.submap封装维护类ActiveSubmaps2D
2.相关scan-match类RealTimeCorrelativeScanMatcher2D
3.ceres库匹配类CeresScanMatcher2D
4.位置估计器类PoseExtrapolator
5.激光点云预处理后结果类sensor::RangeData
6.多激光数据融合同步类RangeDataCollator
7.运动滤波器类MotionFilter(较为简单,分析流程会插入分析)
以上除第7项外,均已分析。

输出结果MatchingResult结构体

  // 用于存储submap列表数据的结构 
  // 用于存储submap列表数据的结构 
  struct InsertionResult {
    std::shared_ptr<const TrajectoryNode::Data> constant_data;       // 匹配后的点云结果
    std::vector<std::shared_ptr<const Submap2D>> insertion_submaps;  // 更新后的submap列表(仅最新的submap)
  };
  // 用于前端匹配的数据结构
  struct MatchingResult {
    common::Time time;                                               // 时间戳
    transform::Rigid3d local_pose;                                   // 在submap中位置
    sensor::RangeData range_data_in_local;                           // 本帧的点云
    // 'nullptr' if dropped by the motion filter.
    std::unique_ptr<const InsertionResult> insertion_result;         // 匹配后的submap结果
  };

其中rajectoryNode::Data是后续用于记录和拼接的rajectoryNode定义的结构体,主要记录的是submap对应的属性信息包括位置,重力加速度方向,修正后的水平点云数据等;为2d和3dSLAM公共结构体类型。其代码目录在
cartographer/mapping/trajectory_node.h

// 轨迹节点结构体
struct TrajectoryNode {
  // 
### Cartographer 源码解析与工作流程 #### 一、项目结构概述 Cartographer 是一个用于构建二维和三维地图的 SLAM (Simultaneous Localization and Mapping) 系统。该项目采用模块化设计,主要由以下几个部分组成: - **传感器数据处理**:负责接收并预处理来自激光雷达和其他传感器的数据。 - **子图匹配器(Submap Matching)**:通过比较当前扫描结果与已有的子图来估计机器人的位置变化。 - **全局优化器(Global Optimization)**:周期性地调整所有节点的位置关系以减少累积误差。 这些组件共同协作完成实时建图任务[^1]。 #### 二、Bresenham算法的具体应用 尽管之前提到过 Bresenham 算法的应用场景,但在实际源码里其实现细节有所不同。具体来说,在绘制栅格地图时会用到该算法来进行线段填充操作;而在路径规划阶段,则可能利用此方法计算两点间最短直线轨迹上的离散坐标点集。值得注意的是,官方文档并未给出详尽描述,因此对于这部分内容的理解更多依赖于开发者自身的探索。 #### 三、分支定界概念澄清 需要注意,“分支定界”这一术语在此处被误用了——它通常指的是求解组合优化问题的一种策略而非地图创建过程中的某个特定环节。真正的分支定界技术涉及递归分割搜索空间直至找到最优解的过程,并不适用于解释 Cartographer 的工作机制[^2]。 ```cpp // 示例代码片段展示了一个简单的Bresenham算法实现 void drawLine(int x0, int y0, int x1, int y1){ bool steep = abs(y1 - y0) > abs(x1 - x0); if(steep){ std::swap(x0,y0);std::swap(x1,y1); } if(x0>x1){ // Ensure the line is drawn from left to right. std::swap(x0,x1);std::swap(y0,y1); } int deltax=x1-x0; int deltay=abs(y1-y0); int error=-(deltax/2); int y=y0; for(int x=x0;x<=x1;++x){ plotPixel((steep)?y:x,(steep)?x:y); error+=deltay; if(error>0){ y+=(y1>y0?1:-1); error-=deltax; } } } ```
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值