cartographer代码流程整理

2023-05-16

代码解析地址
https://zhuanlan.zhihu.com/p/48010119
一、代码目录结构
(1) cartographer_ros
在这里插入图片描述
(2) cartographer
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
二、测试命令
2D:
roslaunch cartographer_ros demo_backpack_2d.launch bag_filename:=
${HOME}/Downloads/cartographer_paper_deutsches_museum.bag
3D:
roslaunch cartographer_ros demo_backpack_3d.launch bag_filename:=
${HOME}/Downloads/b3-2016-04-05-14-14-00.bag
三、cartographer_ros代码分析
a.主函数入口
node_main.cc
main()->Run()
{
Node node(node_options, std::move(map_builder), &tf_buffer,
FLAGS_collect_metrics);
node.StartTrajectoryWithDefaultTopics(trajectory_options);
::ros::spin();
node.FinishAllTrajectories();
node.RunFinalOptimization();
}

Node::Node()
{
1.注册并发布了5个Topic, 并为5个Topic分别设置了定时器函数,在定时器函数中定期向Topic上广播数据:
map_builder_bridge_.GetSubmapList();
map_builder_bridge_.GetTrajectoryNodeList();
map_builder_bridge_.GetLandmarkPoseList();
map_builder_bridge_.GetConstraintList();
map_builder_bridge_.GetTrajectoryStates();
2.发布了4个Service,并为4个Service分别设置了句柄函数,而句柄函数也是通过调用map_builder_bridge_的成员函数来处理的。
map_builder_bridge_.HandleSubmapQuery()
map_builder_bridge_.AddTrajectory()
map_builder_bridge_.FinishTrajectory()
map_builder_bridge_.SerializeState()
}

b.订阅传感器发布的消息
node.cc
LaunchSubscribers()->HandleLaserScanMessage()->
map_builder_bridge_.sensor_bridge(trajectory_id)
->HandleLaserScanMessage(sensor_id, msg)->
sensor_bridge.cc
->HandleLaserScan()->HandleRangefinder()->
trajectory_builder_->AddSensorData()

::cartographer::mapping::TrajectoryBuilderInterface* const
trajectory_builder_;

其它传感器调用方式基本相同:
HandleMultiEchoLaserScanMessage()
HandlePointCloud2Message()
HandleImuMessage()
HandleOdometryMessage()
HandleNavSatFixMessage()
HandleLandmarkMessage()

c.建图入口
node.cc
有两处地方调用了AddTrajectory(),该函数是建图的入口函数。
(1)
Node::Node()
{
service_servers_.push_back(node_handle_.advertiseService(
kStartTrajectoryServiceName, &Node::HandleStartTrajectory, this));
}
HandleStartTrajectory()->AddTrajectory()
{
const int trajectory_id =
map_builder_bridge_.AddTrajectory(expected_sensor_ids, options);
AddExtrapolator(trajectory_id, options);
AddSensorSamplers(trajectory_id, options);
LaunchSubscribers(options, trajectory_id);
}

(2)
node.StartTrajectoryWithDefaultTopics(trajectory_options)
-> AddTrajectory()
{
const int trajectory_id =
map_builder_bridge_.AddTrajectory(expected_sensor_ids, options);
AddExtrapolator(trajectory_id, options);
AddSensorSamplers(trajectory_id, options);
LaunchSubscribers(options, trajectory_id);
}
d.建图过程
node.cc
AddTrajectory()->
map_builder_bridge.cc
AddTrajectory()->map_builder_->AddTrajectoryBuilder()

std::unique_ptrcartographer::mapping::MapBuilderInterface map_builder_;
四、cartographer代码分析
通过MapBuilderBridge和SensorBridge这两个类的过渡,我们正式从cartographer_ros转到了cartographer部分了。从之前部分我们可知,MapBuilderBridge和SensorBridge主要调用了MapBuilderInterface和TrajectoryBuilderInterface这两个类的成员函数来处理。所以,在cartographer部分,我们将从这两个部分入手。
a.建图逻辑
1.当刚初始化一个TrajectoryBuilder时:
初始时,用处发出AddTrajectory的服务请求–>cartographer_node调用MapBuilderBridge::AddTrajectory函数,同时订阅传感器的Topic–>该函数调用MapBuilder::AddTrajectory–>该函数中生成一个LocalTrajectoryBuilder2D,然后通过CreateGlobalTrajectoryBuilder把LocalTrajectoryBuilder2D的参数和回调函数传给GlobalTrajectoryBuilder–>以GlobalTrajectoryBuilder为参数构造了CollatedTrajectoryBuilder,并给CollatorInterface中的AddTrajectory函数注册回调函数的实际地址。
2. 在初始化完成后,每次传感器往Topic上广播Message后:
传感器的ROS节点/Playbag广播到Topic上相关数据的Message---->cartographer_node中启动的StartTrajectory这个服务会订阅传感器数据---->接收到该数据由相应的处理函数处理,比如Node::HandleImuMessage---->该处理函数实际调用是MapBuilderBridge中的一个SensorBridge变量进行处理---->调用了TrajectoryBuilder的虚函数AddSensorData()---->CollatedTrajectoryBuilder或GlobalTrajectoryBuilder继承TrajectoryBuilder并具体实现AddSensorData()函数–>这些函数通过LocalTrajectoryBuilder2D将传感器数据压入相应的数据队列–>LocalTrajectoryBuilder2D通过AddRangeData等函数处理传感器数据。
3. 被动地等待调用AddRangeData来处理队列中的数据
上面这个流程中有传感器数据来临时的做法都是调用了AddSensorData()函数把数据压入了数据队列,那么程序是在什么地方调用AddRangeData来处理队列中的数据的呢?
对于IMU数据和里程计数据都是压入数据队列后被动地等待AddRangeData函数调用PoseExtrapolator来做处理,但当RangeData数据发布时,最后在处理RangeData的AddSensorData中,并不是把数据压入队列,而是在这其中调用了LocalTrajectoryBuilder2D的AddRangeData函数处理。所以,cartographer算法是以激光、点云数据为核心的,IMU、里程计等数据都是起到一定的辅助作用。这就是整个程序的逻辑思路。
b.建图入口
map_builder.cc
std::unique_ptrcartographer::mapping::MapBuilderInterface map_builder_;
map_builder_->AddTrajectoryBuilder()
{
std::unique_ptr local_trajectory_builder;
if (trajectory_options.has_trajectory_builder_2d_options()) {
local_trajectory_builder = absl::make_unique(
trajectory_options.trajectory_builder_2d_options(),
SelectRangeSensorIds(expected_sensor_ids));
}
DCHECK(dynamic_cast<PoseGraph2D*>(pose_graph_.get()));
trajectory_builders_.push_back(absl::make_unique(
trajectory_options, sensor_collator_.get(), trajectory_id,
expected_sensor_ids,
CreateGlobalTrajectoryBuilder2D(
std::move(local_trajectory_builder), trajectory_id,
static_cast<PoseGraph2D*>(pose_graph_.get()),
local_slam_result_callback, pose_graph_odometry_motion_filter)));
}

c.MapBuilder 类
class MapBuilder : public MapBuilderInterface
map_builder_interface.cc
map_builder.cc
MapBuilder中定义了3个重要的变量:
std::unique_ptr pose_graph_;
std::unique_ptrsensor::CollatorInterface sensor_collator_;
std::vector<std::unique_ptrmapping::TrajectoryBuilderInterface>
trajectory_builders_;
其中,trajectory_builders_是一个TrajectoryBuilder的向量列表,每一个TrajectoryBuilder对应了机器人运行了一圈。这个向量列表就管理了整个图中的所有submap。pose_graph_是一个PoseGraph的智能指针,该指针用来做Loop Closure。
sensor_collator_接受传感器数据。
d. CollatedTrajectoryBuilder和GlobalTrajectoryBuilder类
collated_trajectory_builder.cc
global_trajectory_builder.cc
class CollatedTrajectoryBuilder : public TrajectoryBuilderInterface
class GlobalTrajectoryBuilder : public mapping::TrajectoryBuilderInterf
ace
e. LocalTrajectoryBuilder2D类
local_trajectory_builder_2d.cc
这个类的主要作用就是局部轨迹的构建。
定义了如下变量:
scan_matching::RealTimeCorrelativeScanMatcher2D
real_time_correlative_scan_matcher_;
scan_matching::CeresScanMatcher2D ceres_scan_matcher_;
std::unique_ptr extrapolator_;

f. PoseExtrapolator类
pose_extrapolator.cc
class PoseExtrapolator : public PoseExtrapolatorInterface
该类主要作用是对IMU、里程计数据进行融合,估计机器人的实时位姿。
AdvanceImuTracker()
该函数的主要作用就是取出IMU数据队列中的数据,更新ImuTracker。
g. PoseGraph类
pose_graph.cc
class PoseGraph : public PoseGraphInterface
我们在Local Slam部分已经可过通过一帧帧的Laser Scan建立起了一个个的Submap,接下来,需要Loop Closure来进行回环检测,来消除累积误差。
PoseGraph要优化的就是所有的Submap和所有的TrajectoryNode的绝对位姿。

五.算法流程总结
node_main.cc
main()->Run()
node.cc
HandleStartTrajectory()->AddTrajectory()
map_builder_bridge.cc
AddTrajectory()
map_builder.cc
AddTrajectoryBuilder()
collated_trajectory_builder.cc
global_trajectory_builder.cc
local_trajectory_builder_2d.cc
pose_extrapolator.cc
pose_graph.cc
六、代码阅读笔记

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述七、cartographer和ROS的坐标系关系
https://www.cnblogs.com/long5683/p/11562823.html
https://blog.csdn.net/xiekaikaibing/article/details/80118134
https://www.cnblogs.com/jiangxinyu1/p/12458699.html
https://www.ncnynl.com/archives/201810/2778.html
https://www.jianshu.com/p/d18096c66d3a

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

cartographer代码流程整理 的相关文章

随机推荐

  • 什么是DOM对象?如何获取DOM对象?

    今天的web前端培训环节 xff0c 我们讲解一下DOM对象 DOM对象本质上是浏览器根据html标签生成的 JS对象 xff0c 它的所有的标签属性都可以在这个 JS对象上面找到 xff0c 修改这个对象的属性会自动映射到标签身上 DOM
  • 2021电赛F题数字识别和巡线部分

    文章之前12月发了一次 xff0c 但是我后来申请的免毕设后 xff0c 用到了一些文字 xff0c 所以删了这篇文章 xff0c 但是还是查重了 xff0c 于是我把一些程序讲解先删了 xff0c 等毕设结束后再编辑加上 这次电赛我没有准
  • STM32CubeMX的使用,配置DMA串口

    这次记录下最近调用的外设 DMA以前用得很少 xff0c 只通过ADC采集使用 xff0c 开启后就直接读那个数组就可以了 我对dma的理解就是不占用cpu xff0c 数据传输速度快 xff0c 可以直接从外设和内存间相互读取 目前的工作
  • Freertos利用队列传递变量值

    记录部分代码 void CreateTask xTaskCreate LED Task 34 LED 34 30 NULL 0 amp LED Handle xTaskCreate OLED Task 34 OLED 34 150 NULL
  • CubeMX调用DSP库

    踩坑记录 先下载库 下载安装完记得勾选 生成工程 把Cube库中对应型号文件夹中的DSP头文件复制到自己工程目录下 Middlewares ST ARM DSP Inc 编译 xff0c 此时会报错 xff0c 定位 复制对应系列芯片至 再
  • FreeRTOS 事件组

    实现功能 xff0c 当任务A B完成后执行串口任务 不同任务用不同的位表示 configUSE 16 BIT TICKS 61 1 bitx 0 7 xff1b configUSE 16 BIT TICKS设置为0 xff0c bitx
  • STM32 硬件SPI应用 WK2124串口扩展芯片

    根据官方实例代码修改而成 xff0c 具体寄存器参数请参考手册 全双工模式 xff0c 8位数据 xff0c 有效位为最高位 预分频结果最终在芯片工作范围内即可 SPI模式0 CS片选信号是自己用软件设定 仅列出读写寄存器修改 xff0c
  • Mapreduce单词计数的例子

    WordCount类 package org apache hadoop examples span class hljs comment 著名源文件存放的地方 span import java io IOException import
  • LCD12864驱动显示程序

    这里是用的并行写法 xff0c 后续会更新串行 并行缺点就是太占用io口了 xff0c 我其实更喜欢用串行 不过不是比赛要求都不怎么用了 lcd12864 c include 34 lcd12864 h 34 uchar code num
  • HC-SR501人体红外感应模块程序

    当感应到时 xff0c 输出一个高电平 这里我对io口进行了配置 xff0c 要不然读不到高电平 建议大家以后养成习惯 xff0c 对io口进行配置 xff0c stm32中会用到 在这里我额外提下 xff0c 一般在stm32中 浮空输入
  • HX711称重模块程序

    以上部分截自于厂家数据手册 include lt STC12C5A60S2 H gt include 34 lcd12864 h 34 unsigned long HX711 Buffer 61 0 unsigned long Weight
  • SYN6288中文语音合成 程序

    用到串口2 xff0c 因为串口1的话会影响下载 xff0c 需每次拔插线 include lt STC12C5A60S2 H gt include lt stdio h gt include lt math h gt include lt
  • STC单片机超声波程序

    SR05 xff0c 这款只能用定时器计算 include lt intrins h gt include 34 12864 h 34 include lt stdio h gt sbit RX 61 P1 6 sbit TX 61 P1
  • VTK学习-坐标系统

    写在前面 xff1a 这篇博客简要根据书本内容简要介绍一些VTK中要用到的坐标系统与空间变换 这一部分与计算机图形学结合更加紧密 想起之前也 被迫 研究过一段时间的计算机图形学中三维显示部分 xff0c 现在简单学一学 xff0c 提示一下
  • python出现进程已结束,退出代码为-1066598274 (0xC06D007E)的含义

    python出现进程已结束 xff0c 退出代码为 1066598274 0xC06D007E 的含义为你当前使用的某个包与其他相关的包版本之间发生冲突 解决方法 xff1a 卸载正在使用的当前包与相关包 重新安装一遍即可解决
  • lxc

    LXC为Linux Container的简写 Linux Container容器是一种内核 虚拟化技术 xff0c 可以提供轻量级的虚拟化 xff0c 以便隔离进程和资源 xff0c 而且不需要提供指令解释机制以及全虚拟化的其他复杂性 相当
  • 关于ros2、turtlebot3和nav2的应用总结

    关于ros2 turtlebot3和nav2的应用总结 一 资源简介 1 版本要求 ros2 foxy turtlebot3 waffle nav2 0 4 1 gazebo gazebo ros pkgs 3 5 0 zip 2 资源链接
  • motion_primitive_library导航源码阅读笔记

    一 motion primitive library导航源码阅读笔记 二 路径规划文献 Search based Motion Planning for Quadrotors using Linear Quadratic Minimum T
  • Linux下安装cmake步骤详解(图文)

    1 查看Linux位数 getconf LONG BIT 2 获cmake源码包 这里我先新建一个文件夹来存放cmake mkdir app cd app wget https cmake org files v3 3 cmake 3 3
  • cartographer代码流程整理

    代码解析地址 https zhuanlan zhihu com p 48010119 一 代码目录结构 1 cartographer ros 2 cartographer 二 测试命令 2D xff1a roslaunch cartogra