(02)Cartographer源码无死角解析-(78) ROS数据发布→子图内、子图间、约束与残差发布

news2024/11/18 18:46:47

讲解关于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/736856.html

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

相关文章

requestAnimationFrame(请求动画帧)

前言 在Web应用中&#xff0c;实现动画效果的方法比较多&#xff0c;Javascript 中可以通过定时器 setTimeout 来实现&#xff0c;css3 可以使用 transition 和 animation 来实现&#xff0c;html5 中的 canvas 也可以实现。除此之外&#xff0c;html5 还提供一个专门用于请求…

VS2019使用入门

VS2019使用入门 1.Visual Studio任务栏图标显示变白2.从其他项目拷贝文件到当前项目参考文献 配置VS2019 实现简单的c人脸检测项目&#xff0c;可参考本人blog wndows平台VS2019OpenCVcmake简单应用。 1.Visual Studio任务栏图标显示变白 参考[1]&#xff0c;在命令提示符下输…

Wireshark简单使用教程

混合模式 混合模式就是在抓包的时候不轮是有线网络还是WIFI网络等都会将流量给打印出来 1.打开Wireshark抓包软件 2.点击捕获选择选项按钮 3.选中《在接口上使用混杂模式》 &#xff08;默认是开启的&#xff09; 过滤器 过滤器可以只筛选你要的数据包&#xff0c;下图箭…

JAVA主要日志输出工具

日志工具 SLF4J-JCL LOG4J-CORE LOGBACK SLF4J-SIMPLE JCL-OVER-SLF4J LOGBACK-CORE LOG4J LOG4J-API LOG4J-JUL LOG4J-JCL LOGBACK-ACCESS LOGBACK-CLASSIC SLF4-API SLF4J-LOGJ12 LOGBACK-CLASSIC LOG4J-SLF4J-IMPL JAVA日志体系概述 大家目前正在使用的日志输出工具 日志体…

剑指offer刷题笔记--Num41-50

1--数据流中的中位数&#xff08;41&#xff09; 主要思路&#xff1a; 维护两个优先队列&#xff0c;Q1大数优先&#xff0c;存储比中位数小的数&#xff1b;Q2小数优先&#xff0c;存储比中位数大的数&#xff1b; 当存储的数为偶数时&#xff0c;Q1.size() Q2.size(), 中位…

文献阅读笔记 # SimCSE: Simple Contrastive Learning of Sentence Embeddings

《SimCSE: Simple Contrastive Learning of Sentence Embeddings》EMNLP 2021 (CCF-B) Tianyu Gao†∗ Xingcheng Yao‡∗ Danqi Chen†作者来自普林斯顿计算机系和清华大学交叉信息研究院Code & Pre-trained Models 【研究对象】&#xff1a;Sentence Embedding&#xff…

ubuntu RPM should not be used directly install RPM packages, use Alien instead!

ubuntu RPM should not be used directly install RPM packages, use Alien instead! 所以我们最好下载deb版本的安装包 安装 参考文章

科技资讯|三星智能戒指资料曝光,智能穿戴发展新机遇

根据 Reddit 社区网友反馈&#xff0c;在 Beta 版三星 Health 应用中&#xff0c;发现了全新的“Ring Support”选项&#xff0c;暗示三星未来将会推出智能戒指产品。 三星内部正在开发健康追踪设备 Galaxy Ring&#xff0c;这款智能戒指配备 PPG&#xff08;光电容积脉搏波&…

【视觉SLAM入门】3. 相机模型,内外参,畸变推导

"瑾瑜匿瑕" 1. 相机模型和内参1.1 内参推导1.2 外参推导 2. 畸变2.1 径向畸变2.2 切向畸变 3. 深度信息3.1 算法测距3.2 物理测距 本节讨论围绕这个问题展开 机器人如何观测外部世界&#xff1f; \quad\large\textcolor{red}{机器人如何观测外部世界&#xff1f;} 机…

音频转文字怎么操作?这几个好用的音频转文字方法分享给你

想象一下&#xff0c;你在公交车上听到了一个超搞笑的笑话&#xff0c;想要记下来和朋友分享&#xff0c;但是又怕自己记漏或者记错了关键的部分。这时候&#xff0c;音频转文字的软件就能帮上大忙了&#xff01;你只需将录音导入到软件中&#xff0c;它就会像一位超级厉害的听…

4. Springboot快速回顾(@Mapper和@MapperScan)

springboot最重要的注解之一&#xff0c;因为有了Mapper注解&#xff0c;省去了在xml文件繁琐的配置 本文将对比介绍有无注解的区别&#xff0c;加深认识 目录 1 初次使用Mapper1.1 创建实体类Stu1.2 创建dao层接口1.3 创建service层接口和实现1.5 创建StuMapper.xml文件1.4 创…

岛屿数量 (力扣) dfs + bfs JAVA

给你一个由 ‘1’&#xff08;陆地&#xff09;和 ‘0’&#xff08;水&#xff09;组成的的二维网格&#xff0c;请你计算网格中岛屿的数量。 岛屿总是被水包围&#xff0c;并且每座岛屿只能由水平方向和/或竖直方向上相邻的陆地连接形成。 此外&#xff0c;你可以假设该网格的…

【Linux】进程间通信 -- 命名管道 | mkfifo调用

小实验1.我们首先创建两个文件2.我们使用创建命名管道的命令mkfifo3.开始执行 在语言层面使用命名管道实现通信1.创建命名管道-函数mkfifo2.写入端代码3.读取端代码小细节&#xff1a; 小实验 1.我们首先创建两个文件 client.cpp: #include <iostream> using namespac…

《向量数据库指南》——什么是比较 Embedding?

目录 比较 Embedding 准备工作 示例 0:Marlon Brando 示例 1:国王与王后 示例 2:Apple,水果还是公司 欢迎回到向量数据库 101 系列教程。 之前的教程中,我们介绍了非结构化数据、向量数据库和 Milvus——全球最受欢迎的开源向量数据库。我们还简单介绍了 Embedding …

windows提权总结

文章目录 windows基础知识提权总结基础知识提权思路metasplit 提权反弹shell提权windows系统配置错误提权系统服务权限配置错误不带引号的服务路径提权注册键 AlwaysInstallElevated 本地dll劫持提权第三方提权sqlserver提权mysql提权MOF提权G6FTP提权 绕过UAC bypassuac远程终…

前端|HTML

参考视频&#xff1a;黑马程序员前端Html5教程&#xff0c;前端必备基础 目录 &#x1f4da;web标准 &#x1f4da;HTML认识 &#x1f407;HTML的概念 &#x1f407;开发工具 &#x1f407;HTML标签基础 ⭐️HTML骨架标签 ⭐️HTML元素标签分类 ⭐️HTML标签关系&#…

一款可源码交付的Java可视化建模规则引擎

规则引擎是一种根据规则中包含的指定过滤条件&#xff0c;判断其能否匹配运行时刻的实时条件来执行规则中所规定的动作的引擎。 规则引擎由推理引擎发展而来&#xff0c;是一种嵌入在应用程序中的组件&#xff0c;实现了将业务决策从应用程序代码中分离出来&#xff0c;并使用预…

FPGA纯verilog实现10G UDP协议栈,XGMII接口UltraScale GTY驱动,提供工程源码和技术支持

目录 1、前言2、我这里已有的UDP方案3、该UDP协议栈性能4、详细设计方案SFPSGMII收发接口模块AXIS FIFOUDP协议栈UltraScale FPGAs Transceivers Wizard GTY 5、vivado工程6、上板调试验证并演示准备工作查看ARPUDP数据回环测试 7、福利&#xff1a;工程代码的获取 1、前言 目…

SpringBoot原理分析 | 安全框架:Security

&#x1f497;wei_shuo的个人主页 &#x1f4ab;wei_shuo的学习社区 &#x1f310;Hello World &#xff01; Security Spring Security是一个能够为基于Spring的企业应用系统提供声明式的安全访问控制解决方案的安全框架&#xff1b;提供一组可以在Spring应用上下文中配置的Be…

使用作用域函数简化您的 Kotlin 代码:释放对象的力量

使用作用域函数简化您的 Kotlin 代码&#xff1a;释放对象的力量 欢迎&#xff0c;亲爱的 Kotlin 爱好者&#xff01;厌倦了在处理对象时反复书写相同的代码吗&#xff1f;别担心&#xff01;Kotlin 的作用域函数来拯救你&#xff0c;为你的编码之旅增添优雅的色彩。在这篇有趣…