(02)Cartographer源码无死角解析-(78) ROS数据发布→2D点云数据、tf、机器人tracking frame轨迹发布

news2024/11/24 7:36:10

讲解关于slam一系列文章汇总链接:史上最全slam从零开始,针对于本栏目讲解(02)Cartographer源码无死角解析-链接如下:
(02)Cartographer源码无死角解析- (00)目录_最新无死角讲解:https://blog.csdn.net/weixin_43013761/article/details/127350885
 
文末正下方中心提供了本人 联系方式, 点击本人照片即可显示 W X → 官方认证 {\color{blue}{文末正下方中心}提供了本人 \color{red} 联系方式,\color{blue}点击本人照片即可显示WX→官方认证} 文末正下方中心提供了本人联系方式,点击本人照片即可显示WX官方认证
 

一、前言

通过前面一系列博客的分析,到目前为止,node.cc 文件中有关于数据发布的函数,只有 Node::PublishConstraintList() 函数没有讲解了。

// 每0.5s发布一次约束数据
void Node::PublishConstraintList(
    const ::ros::WallTimerEvent& unused_timer_event) {
  if (constraint_list_publisher_.getNumSubscribers() > 0) {
    absl::MutexLock lock(&mutex_);
    constraint_list_publisher_.publish(map_builder_bridge_.GetConstraintList());
  }
}

这里就不用多说了,其核心函数就是 MapBuilderBridge::GetConstraintList(),该函数返回的又是一个人 visualization_msgs::MarkerArray() 类型的数据。就开始进入主题吧。

二、多种marker声明

源码中首先创建了一个 visualization_msgs::MarkerArray 对象 constraint_list,且让 marker_id 从零开始,接着声名了六种 marker。

第一种 : \color{blue}第一种: 第一种: 为子图内约束 constraint_intra_marker,非全局约束, rviz中显示的最多的约束。marker_id = 1,命名空间为 “Intra constraints”。constraint_intra_marker.header.frame_id = node_options_.map_frame 可知其是基于gloabal 系的。kConstraintMarkerScale 是设置线段缩放大小,且位姿设置为单位旋转。注意 constraint_intra_marker.type = visualization_msgs::Marker::LINE_LIST 这个设置,其表示可以存储多条线段,每条线段进行连接。

第二种 : \color{blue}第二种: 第二种: 源码中的 residual_intra_marker,其在 constraint_intra_marker 的基础上进行修改, marker_id=2,命名空间为 “Intra residuals”,该 marker 先对于的其他的数量比较少,为了其容易被观察到,将该标记和其他数量较少的标记设置z为略高于帧内约束标记, 对应于源码中的 residual_intra_marker.pose.position.z = 0.1,主要体现的是一个残差关系,后续进行分析。

第三种 : \color{blue}第三种: 第三种: Inter constraints, 同1轨迹的外部约束 ,rviz 显示的第二多的约束,命名空间为 “Inter constraints, same trajectory”,同样 pose.position.z = 0.1。

第四种 : \color{blue}第四种: 第四种: 基于第一种,marker_id=4,命名空间为 “Inter residuals, same trajectory”,也是用来显示残差的。

第五种 : \color{blue}第五种: 第五种: 基于第一种,marker_id=5,命名空间为 “Inter constraints, different trajectories” 用来描述不同轨迹间的残差。

第六种 : \color{blue}第六种: 第六种: 基于第一种,marker_id=5,命名空间为 “Inter constraints, different trajectories” 用来描述不同轨迹间的子图内约束。这六种可以归为3类,

1.第一种与第二种表示不区分轨迹的子图间约束及残差
2.第三种与第四种表示相同轨迹的子图间约束及残差
3.第四种与第五种表示不同轨迹的子图间约束及残差

相关代码注释如下:

/**
 * @brief 获取位姿图中所有的约束,分成6种类型,放入不同类型的marker中
 * 
 * @return visualization_msgs::MarkerArray 返回6种marker的集合
 */
visualization_msgs::MarkerArray MapBuilderBridge::GetConstraintList() {
  visualization_msgs::MarkerArray constraint_list;
  int marker_id = 0;

  // 6种marker的声明

  // 1 内部子图约束, 非全局约束, rviz中显示的最多的约束
  visualization_msgs::Marker constraint_intra_marker;
  constraint_intra_marker.id = marker_id++;
  constraint_intra_marker.ns = "Intra constraints";
  // note: Marker::LINE_LIST: 每对点之间画一条线, eg: 0-1, 2-3, 4-5
  constraint_intra_marker.type = visualization_msgs::Marker::LINE_LIST;
  constraint_intra_marker.header.stamp = ros::Time::now();
  constraint_intra_marker.header.frame_id = node_options_.map_frame;
  constraint_intra_marker.scale.x = kConstraintMarkerScale;
  constraint_intra_marker.pose.orientation.w = 1.0;

  // 2 Intra residuals
  visualization_msgs::Marker residual_intra_marker = constraint_intra_marker;
  residual_intra_marker.id = marker_id++;
  residual_intra_marker.ns = "Intra residuals";
  // This and other markers which are less numerous are set to be slightly
  // above the intra constraints marker in order to ensure that they are
  // visible.
  // 将该标记和其他数量较少的标记设置z为略高于帧内约束标记, 以确保它们可见.
  residual_intra_marker.pose.position.z = 0.1;

  // 3 Inter constraints, same trajectory, rviz中显示的第二多的约束
  // 外部子图约束, 回环约束, 全局约束
  visualization_msgs::Marker constraint_inter_same_trajectory_marker =
      constraint_intra_marker;
  constraint_inter_same_trajectory_marker.id = marker_id++;
  constraint_inter_same_trajectory_marker.ns =
      "Inter constraints, same trajectory";
  constraint_inter_same_trajectory_marker.pose.position.z = 0.1;

  // 4 Inter residuals, same trajectory
  visualization_msgs::Marker residual_inter_same_trajectory_marker =
      constraint_intra_marker;
  residual_inter_same_trajectory_marker.id = marker_id++;
  residual_inter_same_trajectory_marker.ns = "Inter residuals, same trajectory";
  residual_inter_same_trajectory_marker.pose.position.z = 0.1;

  // 5 Inter constraints, different trajectories
  visualization_msgs::Marker constraint_inter_diff_trajectory_marker =
      constraint_intra_marker;
  constraint_inter_diff_trajectory_marker.id = marker_id++;
  constraint_inter_diff_trajectory_marker.ns =
      "Inter constraints, different trajectories";
  constraint_inter_diff_trajectory_marker.pose.position.z = 0.1;

  // 6 Inter residuals, different trajectories
  visualization_msgs::Marker residual_inter_diff_trajectory_marker =
      constraint_intra_marker;
  residual_inter_diff_trajectory_marker.id = marker_id++;
  residual_inter_diff_trajectory_marker.ns =
      "Inter residuals, different trajectories";
  residual_inter_diff_trajectory_marker.pose.position.z = 0.1;

三、后端数据获取

	const auto trajectory_node_poses =map_builder_->pose_graph()->GetTrajectoryNodePoses();
	const auto submap_poses = map_builder_->pose_graph()->GetAllSubmapPoses();
	const auto constraints = map_builder_->pose_graph()->constraints();
	
	 // 将约束信息填充到6种marker里
	for (const auto& constraint : constraints) {
		visualization_msgs::Marker *constraint_marker, *residual_marker;
		std_msgs::ColorRGBA color_constraint, color_residual;
		......
	}

在定义好6种 marker 之后,其首先获得基于 global 系下轨迹节点位姿、子图位姿。以及约束。随后进入到一个for循环,该循环主要就是把约束残差的数据添加到 6种 marker 之中。每次遍历之前,都会先创建两个 visualization_msgs::Marker 类型的指针,一个用于存储约束,一个用于存储残差。以及两个 std_msgs::ColorRGBA 实例,分别用于描述 *constraint_marker, *residual_marker 的颜色信息。

四、颜色透明度设置

( 1 ) : \color{blue}(1): (1): 循环遍历每一个越苏,先判断约束的类型,如果为子图内约束,也就是条件 onstraint.tag ==cartographer::mapping::PoseGraphInterface::Constraint::INTRA_SUBMAP 成立,
首先把 constraint_marker、residual_marker 赋值成第1类 marker,接着设置颜色与透明图,color_residual.a = 1.0 与 color_residual.r = 1.0 表示不透明,红色。

( 2 ) : \color{blue}(2): (2): 如果为子图间约束,且子图与节点轨迹相同,则设置为 Bright yellow 亮黄色,对应前面的第2类。

( 3 ) : \color{blue}(3): (3): 如果为子图间约束,且子图与节点轨迹不相同,则设置为 Bright cyan 亮青色,对应前面的第3类。

( 4 ) : \color{blue}(4): (4): 设置颜色信息,使用for循环添加了两次,因为一条线段有两个点。

源码注释如下:

    // 根据不同情况,将constraint_marker与residual_marker 指到到不同的maker类型上

    // 子图内部的constraint,对应第一种与第二种marker
    if (constraint.tag ==
        cartographer::mapping::PoseGraphInterface::Constraint::INTRA_SUBMAP) {
      constraint_marker = &constraint_intra_marker;
      residual_marker = &residual_intra_marker;
      // Color mapping for submaps of various trajectories - add trajectory id
      // to ensure different starting colors. Also add a fixed offset of 25
      // to avoid having identical colors as trajectories.
      // 各种轨迹的子图的颜色映射-添加轨迹ID以确保不同的起始颜色 还要添加25的固定偏移量, 以避免与轨迹具有相同的颜色. 
      color_constraint = ToMessage(
          cartographer::io::GetColor(constraint.submap_id.submap_index +
                                     constraint.submap_id.trajectory_id + 25));
      color_residual.a = 1.0;
      color_residual.r = 1.0;
    } 
    else {
      // 相同轨迹内,子图外部约束, 对应第三种与第四种marker
      if (constraint.node_id.trajectory_id ==
          constraint.submap_id.trajectory_id) {
        constraint_marker = &constraint_inter_same_trajectory_marker;
        residual_marker = &residual_inter_same_trajectory_marker;
        // Bright yellow 亮黄色
        color_constraint.a = 1.0;
        color_constraint.r = color_constraint.g = 1.0;
      } 
      // 不同轨迹间的constraint,对应第五种与第六种marker
      else {
        constraint_marker = &constraint_inter_diff_trajectory_marker;
        residual_marker = &residual_inter_diff_trajectory_marker;
        // Bright orange
        color_constraint.a = 1.0;
        color_constraint.r = 1.0;
        color_constraint.g = 165. / 255.;
      }
      // Bright cyan 亮青色
      color_residual.a = 1.0;
      color_residual.b = color_residual.g = 1.0;
    }

    // 设置颜色信息
    for (int i = 0; i < 2; ++i) {
      constraint_marker->colors.push_back(color_constraint);
      residual_marker->colors.push_back(color_residual);
    }

五、构建marker

无论那种情况,都会对应一个 constraint_marker 以及 一个 residual_marker。现在颜色信息已经设置好了,下面就是设置线段的起始点与结束点了。

( 1 ) : \color{blue}(1): (1): 先获得约束对应的子图迭代器 submap_it,然后获得子图的 global 位姿,submap_pose。

( 2 ) : \color{blue}(2): (2): 获得约束对应节点的迭代器 node_it,再获得该节点 global 系下的位姿 trajectory_node_pose。

( 3 ) : \color{blue}(3): (3): 根据子图的global位姿,结合约束(分支定界扫描匹配得到节点相对子图的位姿),求得越苏的另一头坐标,constraint_pose。

( 4 ) : \color{blue}(4): (4): 将global系下子图原点(约束起点),以及约束的结束点连接起来,把这两个点的global系下的位姿添加到 constraint_marker 之中。

( 5 ) : \color{blue}(5): (5): 将global系下子图原点(约束起点),以及约束的结束点连接起来,把这两个点的global系下位置添加到 constraint_marker->points 之中。

( 6 ) : \color{blue}(6): (6): constraint_pose.translation() 表示节点相对于子图的位置,未进行后端优化,但是却显示再global系。trajectory_node_pose 表示经过后端优化时候的节点位姿。把两者的位置都添加到 residual_marker->points 之中。两种方式计算出的节点坐标不会完全相同, 将这个差值作为残差发布出来。

( 7 ) : \color{blue}(7): (7): 最后就是把六种构建好的 marker 全部添加到 constraint_list 之中,返回进行话题发不。

六、结语

通过前面的讲解,关于 Node::PublishConstraintList() 函数已经讲解完成了。关于约束的再 rviz 的查看这里就不说了,但是提及以及残差的查,如本人设置如下在在rviz的设置如下:
在这里插入图片描述
上面青色与红色的线段就是约束残差的可视化。通过连续几篇的博客对源码的分析,对于ROS话题的发布基本都比较清楚了,但是缺少一个很重要的东西,那就是地图,地图并不是以话题的形式发布的,而是通过服务的方式,具体过程下篇博客继续为大家分析。

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

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

相关文章

【MTK】ES7210、ES7243E Driver调试

文章目录 1.概要2.整体架构流程3. ES7210、ES7243E Driver4. 调试过程中的问题点小结1.概要 由于项目需要实现 4 路MIC 以及 2 路Speaker回采输入android系统,硬件是一个ES7210用来采集4路MIC,一个ES7243E用来采集2路Speaker回采,组成类似6路麦克风输入系统。系统SoC无法支持…

“前端刘德华”Pink老师送签名图书啦

就算成功的概率为1%又如何呢&#xff0c;如太阳系般波澜壮阔&#xff0c;也只有0.14%产生了生命&#xff0c;平凡的我们绝大多数也终将如整个太阳系的99.86%一般化作死寂。 但这不重要朋友&#xff0c;今天是黑马疯狂星期四&#xff0c;Pink老师开讲了&#xff01;&#xff01…

跑马灯实验(stm32)

目录 LED的功能代码led.cled.h硬件相关说明 main.c代码的一些介绍BSRR和BRR 实验结果 说明&#xff1a;以下内容参考正点原子的相关资料 LED的功能代码 led.c void LED_Init(void) {GPIO_InitTypeDef GPIO_InitStructure;RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB|RCC_A…

单月涨粉30w,小红书涨粉秘诀是什么?

6月&#xff0c;小红书平台又涌现出哪些优质博主&#xff1f;品牌在投放种草方面有何亮眼表现&#xff1f; 为洞察小红书平台的内容创作趋势及品牌营销策略&#xff0c;新红推出6月月度榜单&#xff0c;从创作者、品牌、品类多方面入手&#xff0c;解析月榜数据&#xff0c;为从…

耗时半月,终于把牛客网上的软件测试面试八股文整理成PDF合集!

大家好&#xff0c;最近收到不少小伙伴的留言&#xff0c;反映现在的面试难度越来越高&#xff0c;要背的八股文越来越多了&#xff0c;考察的知识点也越来越细致&#xff0c;明摆着就是想让我们“徒手造航母”嘛&#xff01;对程序员们来说确实是一大挑战。 因此&#xff0c;…

Win10快捷方式添加到开始菜单或磁贴

打开Windows文件夹&#xff0c;进入该目录(用户名替换为当前用户)&#xff1a;C:\Users\[你的用户名]\AppData\Roaming\Microsoft\Windows\Start Menu\Programs 将应用的快捷方式复制到此目录下&#xff0c;即可展示在开始菜单中可以右键在将快捷方式固定到磁贴

【UnityDOTS 小知识】在DOTS中实例化Prefab的方法

在DOTS中实例化Prefab的方法 前言 实例化Prefab的方法常规方法&#xff1a; 1.利用Baker的方式&#xff0c;以及getEntity方法&#xff0c;将prefab转化为一个对应的Entity原型&#xff0c;再利用EntityManager或ECB的Instantiate方法实例化这个Entity原型得到对应Prefab的实…

LSTD: A Low-Shot Transfer Detector for Object Detection论文阅读笔记

LSTD: A Low-Shot Transfer Detector for Object Detection论文阅读笔记 提出low-shot Transfer detector&#xff0c;来解决标注样本数据不足的情况。利用source domain知识&#xff0c;来构建高效的target-domain检测器&#xff0c;仅需要很少的训练样本。 提出了一个高效的…

第一课:Figma 软件安装与汉化

Figma软件基本介绍 Figma 是一款可在线协作的UI设计软件&#xff0c;最大的亮点在于它基于 WEB 平台研发&#xff0c;能支撑全平台操作系统的运行&#xff0c;适合各种场景下的使用。Figma软件特色功能&#xff1a; 在线编辑&#xff0c;适配全平台&#xff1a;使用软件 Figma…

Python 利用opencv实现识别最大面积验证码

此篇文章解决的是某象的最大验证码,这个最大面积验证码,就是识别图中划线区域中最大面积的验证码,我一开始打算用深度学习去做,但是结合了网上的资料以及自己的想法来看,还是用opencv处理又快又较为准确,而且还不用准备深度学习的一些环境 我准备了大概几十张验证码,经过…

Linux运维-修改密码报错提示:authentication token manipulation error

背景 今天在Linux Centos7环境的使用中,突然遇到了这个问题,为了以后再次遇到后可以快速的解决问题&#xff0c;特此记录。 首先是无法进入系统,然后通过单用户进入系统后修改密码,然后遇到该问题。 单用户进入系统 在如下位置添加init/bin/bash 后按Ctrl X 重启系统 进入b…

联想凌拓数据管理平台—— MagnaScale 分布式存储软件

MagnaScale 分布式存储软件 MagnaScale 数据管理平台是联想凌拓自研 ThinkSystem DXN 非结构化存储的核心软件&#xff0c;具有高性能、高可用性、横向扩展的特点。 MagnaScale 解决方案 是 DXN 非结构化存储的核心软件&#xff0c;能够真正实现软硬件解耦&#xff0c;为客户提…

在 VS Code 的 User Snippets 中使用美元符号 $ 失败的问题

在 VS Code 的 User Snippets 中使用美元符号 $ 失败的问题 在日常工作里经常会用到一些常用的代码片段, 比如创建一个 .vue 文件的初始结构, 所以我会选择在 VS Code 的 User Snippet 中创建一个代码片段, 如下 {"Print to console": {"scope": "v…

异常处理在开源SpringBoot/SpringCloud微服务框架的最佳实践

目录导读 异常处理在开源SpringBoot/SpringCloud微服务框架的最佳实践1. Java为什么要有异常2 Java异常分类3. JDK异常处理4. 模块化异常处理5. 服务化异常处理5.1 web服务化异常处理5.1.1 web参数校验服务化异常最佳实践5.1.1.1 web参数校验服务化异常分析5.1.1.2 web参数校验…

七、html表格

1、表格 表格 组织内容&#xff1a;推荐 布局&#xff1a;不推荐&#xff0c;嵌套非常复杂&#xff0c;影响SEO&#xff0c;采用列表代替布局 2、表格的结构 标题 头部 列标题 主体 行、列、单元格 脚部 在JavaScript默认提供主体 3、表格的使用 4、列与行合并&#xff1…

Kubernetes(k8s)实战:使用k8s+jenkins实现CICD

文章目录 一、什么是CICD二、准备k8s环境三、jenkins环境准备&#xff08;选择一台服务器&#xff09;1、安装java&#xff08;最新版jenkins只支持jdk11以上&#xff09;&#xff08;1&#xff09;找到jdk资源上传到指定机器&#xff08;2&#xff09;配置环境变量 2、安装mav…

stm32 freertos多任务状态迁移,中断临界段,任务延时

freertos的作用 处理器运行模式 arm中SP&#xff0c;LR&#xff0c;PC寄存器以及其它所有寄存器以及处理器运行模式介绍 一、xTaskCreate&#xff08;&#xff09; 1、prvInitialiseNewTask&#xff08;&#xff09; 2、prvAddNewTaskToReadyList&#xff08;&#xff09; (1…

mysql迁移

目录 1.SQLyog 2.阿里云DTS 1.创建任务 2.配置源库及目标库信息 3.配置任务对象及高级配置 4.高级配置 1.SQLyog 适用于离线迁移&#xff0c;全量迁移&#xff0c;少量迁移&#xff0c;数据库大小在几百兆范围内&#xff0c;时间需要几十秒到几分钟不等。 选择源库&…

java网络编程、TCP和UDP、三次握手和四次挥手

一、网络编程入门 &#xff08;1&#xff09;概念 在网络通信协议下&#xff0c;不同计算机上运行的程序&#xff0c;可以进行数据传输 &#xff08;2&#xff09;网络编程三要素&#xff1a; IP地址 设备在网络中的地址&#xff0c;是唯一的标识。全称”互联网协议地址”&a…

value_counts()方法--Pandas

1. 函数功能 返回DataFrame中值与计算组成的Series 2. 函数语法 DataFrame.value_counts(subsetNone, normalizeFalse, sortTrue, ascendingFalse, dropnaTrue)3.函数参数 参数含义subset可选参数&#xff0c;标签或者标签列表 &#xff0c;计算时要用的列normalize布尔值&…