Cartographer总览

本文目的大概的讲一下整个代码框架,本来是想尽量这一篇写详细一点,后来时间不够了,而且一篇太长了阅读太难受,后面拆成了好多章去讲,这篇主要是拆官方文档,之后的篇章东西我也没有很透彻,有错误的地方欢迎指正。

从这章开始,借鉴了很多大佬的精华:

1.无处不在的小土

2.南北粉面馆

3.火种源码的机器人

4._Leveon

下面开始解读代码

一、算法概述

官方文档(这里要,基本下面也就是照着官方文档说的,可以把下面当成官方文档的翻译)

Cartographer总览_第1张图片

Cartographer 可以被视为两个独立但相关的子系统。

第一个是局部SLAM(有时也称为前端或局部轨迹构建器)。它的工作是建立一系列的子图。每个子图都应该是局部一致的,但我们接受局部 SLAM 会随着时间的推移而漂移。大多数局部 SLAM 选项可以在install_isolated/share/cartographer/configuration_files/trajectory_builder_2d.lua(对于 2D)和install_isolated/share/cartographer/configuration_files/trajectory_builder_3d.lua(对于 3D)中找到。(对于本页的其余部分,我们将参考TRAJECTORY_BUILDER_nD的常用选项)。

另一个子系统是全局 SLAM(有时称为后端)。它在后台线程中运行,它的主要工作是找到闭环约束。它通过针对子图的扫描匹配扫描(主要是通过节点Node)来做到这一点。将位姿推移类传感器和前端结果按时间顺序进行串联起来形成一条轨迹,寻找闭环关系,最终采用优化求解获取位姿总体误差最小时的位姿轨迹即submap位姿。在 3D 中,它还试图找到重力的方向。它的大部分选项都可以在install_isolated/share/cartographer/configuration_files/pose_graph.lua 中找到。

更抽象的说,局部SLAM的工作是生成更好的子图,全局SLAM的工作是更好的将子图约束到一起。

 

1、输入

1.1 激光、相机等测距传感器

测距传感器(例如:激光雷达)提供多个方向的深度信息。然而,一些测量与 SLAM 无关。如果传感器部分被灰尘覆盖,或者如果它指向机器人的一部分,则某些测量距离可以被视为 SLAM 的噪声。另一方面,一些最远的测量也可能来自不希望的来源(反射、传感​​器噪声),并且与 SLAM 无关。为了解决这些问题,Cartographer 首先应用带通滤波器,并且仅将范围值保持在某个最小和最大范围之间。应根据机器人和传感器的规格选择这些最小值和最大值。

TRAJECTORY_BUILDER_nD.min_range

TRAJECTORY_BUILDER_nD.max_range

在 2D 中,Cartographer 将比 max_range 更远的范围替换为TRAJECTORY_BUILDER_2D.missing_data_ray_length. 它还提供 amax_zmin_z值以将 3D 点云过滤成 2D 切割。

在 Cartographer 配置文件中,每个距离都以米为单位定义。

距离是在机器人实际移动的一段时间内测量的。然而,距离是由传感器在大量 ROS 消息中“批量”传递的。Cartographer 可以独立考虑每条消息的时间戳,以考虑机器人运动引起的畸变。Cartographer 获取测量值越频繁,就越能更好地解卷,即可以立即捕获的单个相干扫描。因此,强烈建议通过扫描提供尽可能多的距离数据(ROS 消息)(一组可以与另一次扫描匹配的距离数据)。

TRAJECTORY_BUILDER_nD.num_accumulated_range_data 这个值写略大就可以将一帧激光分几帧发出来用来处理激光的畸变问题。

距离数据通常是从机器人上的一个点测量的,但是是从多个角度测量的。这意味着靠近的表面(例如道路)反馈的点越多。相反,较远的物体被击中的频率较低并且提供的分数较少。为了减少点处理的计算量,我们通常需要对点云进行二次采样。然而,一个简单的随机抽样会从我们已经有低密度测量的区域中删除点,而高密度区域仍然会有比需要更多的点。为了解决这个密度问题,我们可以使用体素滤波器,将原始点下采样成一个恒定大小的立方体,并且只保留每个立方体的质心。

小的立方体尺寸将导致更密集的数据表示,从而导致更多的计算。大立方体会导致数据丢失,但速度会快得多。

TRAJECTORY_BUILDER_nD.voxel_filter_size   体素滤波器的参数是这个

在应用了固定大小的体素滤波器后,Cartographer 还应用了自适应体素滤波器。此过滤器尝试确定最佳体素大小(在最大长度下)以实现目标点数。在 3D 中,两个自适应体素滤波器用于生成高分辨率和低分辨率点云,它们的用法将在Local SLAM中阐明。

TRAJECTORY_BUILDER_nD.*adaptive_voxel_filter.max_length

TRAJECTORY_BUILDER_nD.*adaptive_voxel_filter.min_num_points

1.2 惯性测量单元

 惯性测量单元可以成为 SLAM 的有用信息来源,因为它提供了准确的重力方向(因此是地面),以及全面但有噪声的旋转信息为了过滤 IMU 噪声,在一定时间内观察重力。如果您使用 2D SLAM,则可以实时处理距离数据,而无需额外的信息源,因此您可以选择是否希望 Cartographer 使用 IMU。使用 3D SLAM,您需要提供 IMU,因为它用作扫描方向的初始猜测,大大降低了扫描匹配的复杂性。

TRAJECTORY_BUILDER_2D.use_imu_data

TRAJECTORY_BUILDER_nD.imu_gravity_time_constant

1.3 landmark部分

Cartographer可以将二维码、反光板等作为landmark,也就是当作特征点,这里先不讲,放在后续文档里讲解。

1.4 绝对位置输入

Cartographer可以将GPS当作fixed pose输入,目前没有条件,还没有测试过。

2、Local SLam 

一旦从多个距离数据中组合并过滤了扫描,就可以使用局部SLAM算法了。局部 SLAM 通过使用来自姿态推测器的初始猜测进行扫描匹配,将新的扫描插入到其当前的子图构造中。姿势推测器背后的想法是使用除测距仪之外的其他传感器的传感器数据来预测下一次扫描应该插入到子图中的位置。(以前版本的位姿推测器是通过UKF去做的,后来应该是觉得UKF的输入太慢了,简化了这部分传感器融合的工作,位姿推测器部分后续会单独开一篇讲解)

有两种扫描匹配策略可用:

  • 1)CeresScanMatcher初始猜测作为先验,并找到扫描匹配适合子图的最佳位置。它通过插入子图和子像素对齐扫描来做到这一点。这很快,但无法修复明显大于子图分辨率的错误。如果您的传感器设置和时序合理,则仅使用CeresScanMatcher通常是最好的选择。
  • 2)RealTimeCorrelativeScanMatcher如果您没有其他传感器或您不信任它们,则可以启用。它使用类似于在闭环(稍后描述)中如何将扫描与子图匹配的方法,但它与当前子图匹配。然后将最佳匹配用作CeresScanMatcher. 这种扫描匹配器代价大,基本上会覆盖除测距仪以外的其他传感器的任何信号,但它在功能丰富的环境中非常强大。

无论哪种方式,CeresScanMatcher都可以配置为为其每个输入赋予一定的权重。权重是对数据信任度的衡量标准,这可以看作是静态协方差。权重参数的单位是无量纲量,不能相互比较。一个数据源的权重越大,Cartographer 在进行扫描匹配时就会越重视这个数据源。数据源包括占用空间(来自扫描的点)、来自姿态推器(或RealTimeCorrelativeScanMatcher)的平移和旋转

下面三个参数是激光反馈点的权重

TRAJECTORY_BUILDER_nD.ceres_scan_matcher.occupied_space_weight

TRAJECTORY_BUILDER_3D.ceres_scan_matcher.occupied_space_weight_0

TRAJECTORY_BUILDER_3D.ceres_scan_matcher.occupied_space_weight_1

下面两个参数是位姿推测器的权重

TRAJECTORY_BUILDER_nD.ceres_scan_matcher.translation_weight

TRAJECTORY_BUILDER_nD.ceres_scan_matcher.rotation_weight

在 3D 中,occupied_space_weight_0occupied_space_weight_1参数分别与高分辨率和低分辨率滤波点云相关。

当然也会有参数去配置 后端里odom 和前端 imu 的权重

扫描匹配问题被建模为此问题的最小化,其中两次扫描之间的运动(变换矩阵)是要确定的参数。Ceres 使用下降算法针对给定的迭代次数优化运动。Ceres 可以配置为根据您自己的需要调整收敛速度。

TRAJECTORY_BUILDER_nD.ceres_scan_matcher.ceres_solver_options.use_nonmonotonic_steps

TRAJECTORY_BUILDER_nD.ceres_scan_matcher.ceres_solver_options.max_num_iterations

TRAJECTORY_BUILDER_nD.ceres_scan_matcher.ceres_solver_options.num_threads

可以根据您对传感器的信任进行切换RealTimeCorrelativeScanMatcher它通过在由最大距离半径和最大角度半径定义的搜索窗口中搜索类似扫描来工作。当与在此窗口中找到的扫描执行扫描匹配时,可以为平移和旋转分量选择不同的权重。例如,如果您知道您的机器人不经常旋转,您可以使用这些权重

TRAJECTORY_BUILDER_nD.use_online_correlative_scan_matching

TRAJECTORY_BUILDER_nD.real_time_correlative_scan_matcher.linear_search_window

TRAJECTORY_BUILDER_nD.real_time_correlative_scan_matcher.angular_search_window

TRAJECTORY_BUILDER_nD.real_time_correlative_scan_matcher.translation_delta_cost_weight

TRAJECTORY_BUILDER_nD.real_time_correlative_scan_matcher.rotation_delta_cost_weight

为了避免每个子图插入过多的扫描,一旦扫描匹配器发现两次扫描之间的运动,它就会通过运动滤波。如果导致扫描的运动被认为不够重要,则放弃扫描。只有当扫描的运动超过某个距离、角度或时间阈值时,才会将扫描插入到当前子图中。

TRAJECTORY_BUILDER_nD.motion_filter.max_time_seconds

TRAJECTORY_BUILDER_nD.motion_filter.max_distance_meters

TRAJECTORY_BUILDER_nD.motion_filter.max_angle_radians

这里可见代码中的IsSimilar

局部 SLAM 接收到给定数量的范围数据时,子图被认为是完整的。局部 SLAM 随时间漂移,全局 SLAM 用于修复这种漂移。子图必须足够小,以便它们内部的漂移低于分辨率,以便它们在局部正确。另一方面,它们应该足够大以区分闭环以正常工作。

TRAJECTORY_BUILDER_nD.submaps.num_range_data

这里需要根据你使用的激光长度去修改每个submap的大小。

子图可以将其范围数据存储在几种不同的数据结构中:最广泛使用的表示称为概率网格。但是,在 2D 中,也可以选择使用截断有符号距离场 (TSDF)。

TRAJECTORY_BUILDER_2D.submaps.grid_options_2d.grid_type

概率网格将空间切割成 2D 或 3D 表格,其中每个单元格具有固定大小并包含被阻挡的几率。率根据“命中”(测量范围数据的位置)和“未命中”(传感器和测量点之间的自由空间)更新。命中未命中都可以在占用概率计算中具有不同的权重,从而或多或少地信任占用或空闲空间测量。

TRAJECTORY_BUILDER_2D.submaps.range_data_inserter.probability_grid_range_data_inserter.hit_probability

TRAJECTORY_BUILDER_2D.submaps.range_data_inserter.probability_grid_range_data_inserter.miss_probability

TRAJECTORY_BUILDER_3D.submaps.range_data_inserter.hit_probability

TRAJECTORY_BUILDER_3D.submaps.range_data_inserter.miss_probability

在 2D 中,每个子图只存储一个概率网格。在 3D 中,出于扫描匹配性能的原因,使用了两个混合概率网格。(术语“混合”仅指内部树状数据表示,并抽象给用户)

  • 1)用于远距离测量的低分辨率混合网格
  • 2)用于近距离测量的高分辨率混合网格

扫描匹配首先将低分辨率点云的远点与低分辨率混合网格对齐,然后通过将靠近的高分辨率点与高分辨率混合网格对齐来细化位姿。

TRAJECTORY_BUILDER_2D.submaps.grid_options_2d.resolution

TRAJECTORY_BUILDER_3D.submaps.high_resolution

TRAJECTORY_BUILDER_3D.submaps.low_resolution

TRAJECTORY_BUILDER_3D.high_resolution_adaptive_voxel_filter.max_range

TRAJECTORY_BUILDER_3D.low_resolution_adaptive_voxel_filter.max_range

3、Global Slam

当局部 SLAM 生成其连续的子图时,全局优化(通常称为“优化问题”或“稀疏姿态调整”)任务在后台运行。它的作用是重新排列彼此之间的子图,使它们形成一个连贯的全局图。例如,此优化负责更改当前构建的轨迹以正确对齐子图与闭环有关。

一旦插入一定数量的轨迹节点,优化就会分批运行。根据您需要运行它的频率,您可以调整这些批次的大小。

POSE_GRAPH.optimize_every_n_nodes

将 POSE_GRAPH.optimize_every_n_nodes 设置为 0 是禁用全局 SLAM 并专注于局部 SLAM 行为的便捷方法。这通常是调整 Cartographer 时要做的第一件事。

全局 SLAM 是一种“ GraphSLAM ”,它本质上是一种位姿图优化,通过在节点和子图之间建立约束,然后优化得到的约束图来工作。约束可以直观地被认为是把所有节点绑在一起的小绳索。稀疏的姿势调整将这些绳索完全固定。生成的网络称为“姿势图”。

  • 1)非全局约束(也称为子图内约束)在轨迹上彼此紧密跟随的节点之间自动构建。直观地说,那些“非全局绳索”使轨迹的局部结构保持一致。
  • 2)全局约束(也称为闭环约束或子图间约束)在新的子图和被认为在空间中“足够接近”(某个搜索窗口的一部分)和强拟合(良好匹配)的先前节点之间定期搜索运行扫描匹配时)。直观地说,这些“全球绳索”在结构中引入了结,并牢固地将两股绳拉近了。

POSE_GRAPH.constraint_builder.max_constraint_distance

POSE_GRAPH.fast_correlative_scan_matcher.linear_search_window

POSE_GRAPH.fast_correlative_scan_matcher_3d.linear_xy_search_window

POSE_GRAPH.fast_correlative_scan_matcher_3d.linear_z_search_window

POSE_GRAPH.fast_correlative_scan_matcher*.angular_search_window

为了限制约束(和计算)的数量,Cartographer 仅考虑所有冻结节点的子采样集来构建约束。这由采样率常数控制。采样过少的节点可能会导致遗漏约束和无效的闭环。采样太多节点会减慢全局 SLAM 并阻止实时闭环。

POSE_GRAPH.constraint_builder.sampling_ratio   

POSE_GRAPH.global_sampling_ratio 重点,这俩是算力不够处理的主要对象

当一个节点和一个子图被考虑用于约束构建时,它们会通过第一个扫描匹配器,称为FastCorrelativeScanMatcher. 该扫描匹配器专为 Cartographer 设计,可实现实时闭环扫描匹配。它FastCorrelativeScanMatcher依靠“分支定界”机制在不同的网格分辨率下工作并有效地消除不正确的匹配。此机制在本文档前面介绍的 Cartographer 论文中得到了广泛介绍。它适用于深度可以控制的探索树。

POSE_GRAPH.constraint_builder.fast_correlative_scan_matcher.branch_and_bound_depth

POSE_GRAPH.constraint_builder.fast_correlative_scan_matcher_3d.branch_and_bound_depth

POSE_GRAPH.constraint_builder.fast_correlative_scan_matcher_3d.full_resolution_depth

一旦FastCorrelativeScanMatcher有足够好的得分(高于匹配的最低分数),它就会被送入 Ceres Scan Matcher 以优化姿势。

POSE_GRAPH.constraint_builder.min_score

POSE_GRAPH.constraint_builder.ceres_scan_matcher_3d

POSE_GRAPH.constraint_builder.ceres_scan_matcher

当 Cartographer 运行优化问题时,Ceres 用于根据多个残差重新排列子图。残差是使用加权成本函数计算的。全局优化具有考虑大量数据源的成本函数:全局(闭环)约束、非全局(匹配器)约束、IMU 加速度和旋转测量、局部 SLAM 粗略姿态估计、里程计源或固定框架(如 GPS 系统)。权重和 Ceres 选项可以按照局部 SLAM部分中的说明进行配置。

POSE_GRAPH.constraint_builder.loop_closure_translation_weight

POSE_GRAPH.constraint_builder.loop_closure_rotation_weight

POSE_GRAPH.matcher_translation_weightPOSE_GRAPH.matcher_rotation_weight

POSE_GRAPH.optimization_problem.*_weight

POSE_GRAPH.optimization_problem.ceres_solver_options

这里是用来降低错误约束的

通过切换POSE_GRAPH.max_num_final_iterations

,可以找到有关优化问题中使用的残差的有用信息

作为 IMU 残差的一部分,优化问题为 IMU 姿势提供了一些灵活性,默认情况下,Ceres 可以自由优化 IMU 和跟踪框架之间的外部校准。如果您不信任您的 IMU 姿势,可以记录 Ceres 全局优化的结果并用于改进您的外部校准。如果 Ceres 没有正确优化你的 IMU 姿势并且你足够信任你的外部校准,你可以使这个姿势保持不变。

 POSE_GRAPH.optimization_problem.log_solver_summary

POSE_GRAPH.optimization_problem.use_online_imu_extrinsics_in_3d

在残差中,异常值的影响由配置有特定 Huber 尺度的Huber 损失函数处理。

Huber 越大,异常值(离群值)的影响就越大。

POSE_GRAPH.optimization_problem.huber_scale

这里可以用来应对对称环境

轨迹完成后,Cartographer 会运行新的全局优化,其迭代次数通常比以前的全局优化多得多。这样做是为了完善 Cartographer 的最终结果,通常不需要实时,因此大量迭代通常是正确的选择。

POSE_GRAPH.max_num_final_iterations

4、Local SLam 处理整体思想

slam前端主要采用scan-match的相关技术,计算相邻激光点相对位姿,若已知初始位姿,则可获取所有scan节点全局绝对位姿。因其原理原因,会引入累计误差,但由于相对位姿较为精确,短时间内累加误差较小,cartographer采用一段时间内的scan构建submap而非整个map;每个submap相互独立,并且采用scan-to-map思想进行scan-match进一步降低累计误差,其结构如下所示。

Cartographer总览_第2张图片

从图中可看出N个连续scan构建submap0,然后接下来的N个连续scan构建新的submap1,以此类推。前端可获取每个scan的相对于对应submap的位姿,和submap在全局的位姿。

前端的处理流程如下:

1)传感器预处理:主要将测距传感器即激光传感器信息转换为cartographer内部定义的统一类型:点云类型,而非ros中的range类型;

2)激光点云数据经过两次体素滤波处理,即降采样;

3)odometry、imu及其scan-match精确位姿作为历史轨迹,构建位置估计器实时估计下刻位置;其中odometry和imu在2d slam非必要输入,可完全采用纯激光激光匹配估计。

4)根据预估位置作为初始位姿,经相关匹配(可配置使能或禁能)和ceres优化匹配获取精确的位置;

5)经过运动滤波器(即降采样)的scan节点作为slam轨迹节点;

6)将轨迹节点scan即及其对应的点云进行维护和更新submap;

7)当submap满足一定数量的激光帧时,完成此submap的创建,输出MatchingResult类型结果;

重新累计新的scan node构建新的submap,以此循环;

5.Global Slam 处理整体思想

slam后端主要解决slam前端带来的累计误差,主要包括闭环检测和图优化求解两大部分。其中图优化求解目前一般都采用开源优化库,如google的ceres库和国内常用的g2o库;故目前slam质量的好坏,闭环检测成为了重中之重。如下给出cartographer进行后端优化位姿图结构拓扑,后端给出了相邻scan node的位姿约束,同时也给出了submap与组成其的scan node的约束关系,如下图在前端基础上增加了黑色线即为闭环检测提供的闭环约束。

Cartographer总览_第3张图片

在进行图优化求解前,构建位姿图。其中红色和绿色约束由前端匹配结果获取;而黑色线约束,即scannode 与历史的submap(非自身构建的submap)之间的相对位姿;或submap与历史的scan node(除构建自身的其他scannode)之间的相对位姿。

闭环检测

闭环检测本质仍为scan-match,只是采用当前scannode匹配历史的submap而非当前时刻的submap;或者采用当前完成的submap去匹配历史的scannode。当匹配的置信度(即可认为是吻合度)达到一定值时,可认为闭环存在,增加其约束关系。

由于闭环检测是检测当前位姿是否经过曾经经历的地方,和前端匹配本质区别,无法获取较为准确的初始位姿。因其搜索范围较大,则不适合直接使用real_time_correlative_scan_match。cartographer优化了相关匹配算法,提出了FastCorrelativeScanMatcher(即分支限界加速匹配法),实现快速搜索闭环可能性。

如果存在闭环,即匹配可信度超过设置阈值,将匹配出的位姿作为初始值送入ceres库获取更加精细的匹配结果,其结果即为闭环约束。

后端处理的流程如下:

1)将scannode和submap作为优化节点,加入位姿图;

2)从前端匹配结果中获取相邻scannode节点间约束和submap内部的submp与scannode约束;

3)创建闭环匹配器进行闭环检测,增加闭环约束;

4)构建位姿图优化器进行优化;

5)将优化结果将旧值进行更新;

6)由于后端处理不需要实时处理,为保证前端的实时,后端采用线程池在后台处理;因此优化的结果替换时无法替换所有scan node值,需将优化后最新的位姿转移矩阵,去估计前端中未被优化的节点。否则可能会导致在地图最新时间附近错位。



6、最后捋一下代码

6.1 初始化阶段

main

Run

XXX::RunPureLocalization,

Node::StartTrajectoryWithDefaultTopics

Node::AddTrajectory

MapBuilderBridge::AddTrajectory

MapBuilder::AddTrajectoryBuilder

CollatedTrajectoryBuilder::CollatedTrajectoryBuilder

TrajectoryCollator::AddTrajectory

其中,

XXX继承于Node,XXX的MapBuilder采用了CreateMapBuilder函数生成为MapBuilder类型,而XXX的MapBuilderBridge则引用了该MapBuilder,也就是说XXX中的MapBuilderBridge包含了MapBuilder的实例。而最终由MapBuilder的AddTrajectoryBuilder完成计算。

6.2 运行阶段

Node::HandlePointCloud2Message:

    map_builder_bridge_->sensor_bridge(trajectory_id)->HandlePoint

Cloud2Message(sensor_id, msg);

SensorBridge::HandlePointCloud2Message:

    this->HandleRangefinder(sensor_id, subdivision_time, frame_id, subdivision);

SensorBridge::HandleRangefinder      

    trajectory_builder_->AddSensorData(

        sensor_id, carto::sensor::TimedPointCloudData{

                       time, sensor_to_tracking->translation().cast(),

                       carto::sensor::TransformTimedPointCloud(

                           ranges, sensor_to_tracking->cast())});

CollatedTrajectoryBuilder::::AddSensorData

CollatedTrajectoryBuilder::HandleCollatedSensorData

CollatedTrajectoryBuilder::AddData

TrajectoryCollator::AddSensorData

LocalSlamResult2D::AddToPoseGraph

PoseGraph2D::AddNode

6.3 前端的调用顺序为

SensorBridge::HandleLaserScanMessage()

SensorBridge::HandleRangefinder()  trajectory_builder_->AddSensorData

CollatedTrajectoryBuilder::AddSensorData()

CollatedTrajectoryBuilder::AddData()

Collator::AddSensorData()

Collator::AddTrajectory()

CollatedTrajectoryBuilder::CollatedTrajectoryBuilder()

CollatedTrajectoryBuilder::HandleCollatedSensorData()

Dispatchable::AddToTrajectoryBuilder()

GlobalTrajectoryBuilder::AddSensorData()

LocalTrajectoryBuilder2D::AddRangeData()

LocalTrajectoryBuilder2D::AddAccumulatedRangeData()

LocalTrajectoryBuilder2D::ScanMatch()

如果用real_time_correlative_scan_matcher_,就进real_time_correlative_scan_matcher_.Match 否则不进

ceres_scan_matcher_.Match

6.4 后端的调用流程为: 

AddNode()

PoseGraph2D::ComputeConstraintsForNode()

PoseGraph2D::ComputeConstraint()

ConstraintBuilder2D::MaybeAddGlobalConstraint()

ConstraintBuilder2D::ComputeConstraint()

接下来做各个scanmatch和分支定界

下面摘部分简要说明,没来得及写完的,先贴上来了

前端代码解析

  • pose_extrapolator.cc部分解析,这里是位姿预估器,具体说明见 Cartographer前端之位姿推测器pose ,cartographer 0.2.0版本使用ukf做的,后用这个来做,这里没有去管 covariance,因为前端负责submap,而前端的相对位移一般不会偏的离谱,所以不用ukf也好,ukf太慢了,这里和我们原来的odom_combined或robot_pose_ekf做的是一样的事。
  • local_trajectory_builder_2d.cc部分解析 这里主要是前端的匹配(前端不带闭环,可以通过关闭后端只用前端去检测、调整底盘odom、imu、laser),具体说明见 Cartographer前端之local_trajectory
  • real_time_correlative_scan_matcher.cc部分解析,具体说明见Cartographer前端之real_time_correlative_scan_matcher,这里是通过概率地图、初始位置、激光数据在指定范围内计算socre,将最大score的位置当作位置输出。当ODOM、IMU不准时一定要打开这里,在我们重新做底盘的时候可以依赖这里来看ODOM、IMU的准确程度。

后端代码解析

一、ceres_scan_matcher.cc这里前端和后端都会用到,我们统一说明,具体见:

你可能感兴趣的:(Cartographer入门,人工智能)