深入分析常规ROS机器人如何使用Navigation导航包实现实时定位

news2024/11/25 3:40:21

   本篇文章主要分析,常规的ROS机器人是如何使用Navigation导航包实现实时定位的,定位精度的决定性因素等内容,结构上分为详细介绍、概括总结、深入思考三大部分。


   注:本文首发于古月居,原文链接如下:

   https://www.guyuehome.com/45110

   本篇文章我首发在古月居,因版权原因,在CSDN不能放全文,只能放一小部分,欢迎大家前往古月居查看完整文章!!!,链接如上↑↑↑


在这里插入图片描述


   一、详细介绍

   常规的ROS机器人一般都会搭载,轮式里程计(编码器),姿态传感器(IMU)、激光雷达等感知传感器。

   rqt_graph是ROS中进行分析的常用工具,下图是航天三院开发的轻舟机器人运行时的节点关系图(点击或拖拽可查看大图),从下图可以看出Navigation导航包的“指挥中心“”move_base节点订阅了/odom_ekf节点发布的/odom_ekf话题,/odom_ekf中的内容是机器人搭载的轮式里程计(编码器)经过推位得到定位信息/odom与姿态传感器(IMU)的定位信息经过/robot_pose_ekf节点,即使用扩展卡尔曼滤波器(EKF)进行融合后的定位信息。

   那么我们的ROS机器人是否就是使用这个定位信息作为机器人的实时位置进行路径规划及其他应用呢?答案是否定的,下面给出解释

   move_base节点中是通过调用getRobotPose函数来获取机器人当前的位姿的

getRobotPose(global_pose, planner_costmap_ros_);

   getRobotPose函数的核心代码如下,可以看出getRobotPose函数实际上是通过监听tf树中的map坐标系与base_link坐标系的关系,从而得到map坐标系下的base_link的坐标,也就是map坐标系下机器人的位姿信息,也就是说机器人的实时定位信息是通过监听tf树中map坐标系与base_link坐标系的变换关系来计算获得的,并非使用了订阅的/odom_ekf话题中的消息。

    tf2::toMsg(tf2::Transform::getIdentity(), global_pose.pose);
    geometry_msgs::PoseStamped robot_pose;
    tf2::toMsg(tf2::Transform::getIdentity(), robot_pose.pose);
    robot_pose.header.frame_id = robot_base_frame_;
    robot_pose.header.stamp = ros::Time(); // latest available
    ros::Time current_time = ros::Time::now();  // save time for checking tf delay later

    // get robot pose on the given costmap frame
    try
    {
       //通过tf获取map到base_link的关系,那么也就是map下base_link的坐标,也就是map下机器人的坐标
      tf_.transform(robot_pose, global_pose, costmap->getGlobalFrameID());
    }

   tf中的transform函数的具体代码如下:(lookupTransform是tf树的监听函数)


  //tf中的transform函数的具体代码如下:

 template <class T>
    T& transform(const T& in, T& out, 
		 const std::string& target_frame, ros::Duration timeout=ros::Duration(0.0)) const
  {
    // do the transform
    tf2::doTransform(in, out, lookupTransform(target_frame, tf2::getFrameId(in), tf2::getTimestamp(in), timeout));
    return out;
  }

   轻舟机器人运行时的tf树如下图所示,可以看出map坐标系与base_link坐标系之间还存在一个odom坐标系,map坐标系与odom的坐标变换关系是由/amcl节点广播出来的,odom坐标系与base_link坐标系的坐标变换关系是/robot_pose_ekf节点广播出来的,所以,我们可以先大胆的推测,机器人的实时定位信息跟/amcl节点与/robot_pose_ekf节点均有关,且/amcl节点给出的定位信息是借助激光雷达的数据,采用粒子滤波算法(PF)估计出来的,而/robot_pose_ekf节点给出的定位信息是里程计信息和IMU信息经过扩展卡尔曼滤波(EKF)融合后得到的。

   那么它们之间的关系又是怎样的呢?下面通过解读amcl包中广播odom与map坐标系的tf关系的过程来进行解释。

   本部分的源码如下:

   geometry_msgs::PoseStamped odom_to_map;
      try
      {
        tf2::Quaternion q;
        q.setRPY(0, 0, hyps[max_weight_hyp].pf_pose_mean.v[2]);
        tf2::Transform tmp_tf(q, tf2::Vector3(hyps[max_weight_hyp].pf_pose_mean.v[0],        
                                              hyps[max_weight_hyp].pf_pose_mean.v[1],
                                              0.0));

        geometry_msgs::PoseStamped tmp_tf_stamped;
        tmp_tf_stamped.header.frame_id = base_frame_id_;
        tmp_tf_stamped.header.stamp = laser_scan->header.stamp;
        tf2::toMsg(tmp_tf.inverse(), tmp_tf_stamped.pose);

        this->tf_->transform(tmp_tf_stamped, odom_to_map, odom_frame_id_);
      }
      catch(const tf2::TransformException&)
      {
        ROS_DEBUG("Failed to subtract base to odom transform");
        return;
      }

      tf2::convert(odom_to_map.pose, latest_tf_);
      latest_tf_valid_ = true;

      if (tf_broadcast_ == true)
      {
        // We want to send a transform that is good up until a
        // tolerance time so that odom can be used
        ros::Time transform_expiration = (laser_scan->header.stamp +
                                          transform_tolerance_);
        geometry_msgs::TransformStamped tmp_tf_stamped;
        tmp_tf_stamped.header.frame_id = global_frame_id_;
        tmp_tf_stamped.header.stamp = transform_expiration;
        tmp_tf_stamped.child_frame_id = odom_frame_id_;
        tf2::convert(latest_tf_.inverse(), tmp_tf_stamped.transform);

        this->tfb_->sendTransform(tmp_tf_stamped);
        sent_first_transform_ = true;
      }

   以上源码可提取关键内容,总结如下:

   (1)获取base_link在世界坐标系map的坐标变换,即base_link在map下的坐标,存放在tmp_tf 中

tf2::Transform tmp_tf(q, tf2::Vector3(hyps[max_weight_hyp].pf_pose_mean.v[0], hyps[max_weight_hyp].pf_pose_mean.v[1]0.0));

   (2)将tmp_tf通过求逆变换inverse()表示为世界坐标系map到base_link的坐标变换,即map在base_link下的坐标,存放在tmp_tf_stamped.pose中

tf2::toMsg(tmp_tf.inverse(), tmp_tf_stamped.pose);

   (3)使用transform变换获取map到odom的变换,即map原点在odom坐标系下的坐标,存放在odom_to_map中,并进行了格式转换存放在latest_tf_中。

this->tf_->transform(tmp_tf_stamped, odom_to_map, odom_frame_id_);

   这里的具体实现过程如下:tmp_tf_stamped中存放的是世界坐标系map到base_link的坐标变换,根据此处传入的参数可知transform函数中监听了base_link到odom坐标系的坐标变换,因此,可以看成将世界坐标系map到base_link的坐标变换再进行了一次从base_link到odom的变换,进而得到了map到odom的坐标变换,即map原点在odom坐标系下的坐标,存放在odom_to_map中

在这里插入图片描述


   (4)最后,对latest_tf_求逆,得到odom–>map的变换,即odom在map坐标系下的坐标。

tf2::convert(latest_tf_.inverse(), tmp_tf_stamped.transform);

   (5)广播odom–>map的坐标变换关系,即可实现对EKF的修正。


   二、概括总结

   本部分内容欢迎前往古月居查看,链接如下:


   三、深入思考

   本部分内容欢迎前往古月居查看,链接如下:


   https://www.guyuehome.com/45110

   本篇文章我首发在古月居,因版权原因,在CSDN不能放全文,只能放一小部分,欢迎大家前往古月居查看完整文章!!!,链接如上↑↑↑


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

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

相关文章

Visual Studio Code将中文写入变量时,中文老是乱码问题

对于这个问题&#xff0c;我也是弄了很久才知道&#xff0c;编码格式的问题 在此之前我们要先下载个插件 照这以上步骤&#xff0c;最后按F6运行即可&#xff0c;按F6是利用我们刚刚下载的插件进行编译&#xff0c;唯一有一点不好就是&#xff0c;用这种插件运行的话&#xff…

Mean-Shift聚类方法

刘玉琪 跟随 出版于 台湾人工智能学院 一、说明 上一篇介绍了基于密度的分群方法——DBSCAN&#xff0c;本篇会介绍另一个分群方法——Mean Shift&#xff0c;与DBSCAN一样不需要预先知道欲分群的数量&#xff0c;而对于分群的形状也没有限制。 然而&#xff0c;这个方法是基…

使用Scrapy的调试工具和日志系统定位并解决爬虫问题

目录 摘要 一、Scrapy简介 二、Scrapy的调试工具 1、Shell调试工具 2、断点调试 三、Scrapy的日志系统 四、实例解析 1、启用详细日志 2、断点调试 3、分析日志 4、解决问题 五、代码示例 总结 摘要 本文详细介绍了如何使用Scrapy的调试工具和日志系统来定位并解…

0基础学习VR全景平台篇第118篇:利用动作录制器功能避免重复操作 - PS教程

上课&#xff01;全体起立~ 大家好&#xff0c;欢迎观看蛙色官方系列全景摄影课程&#xff01; 嗨&#xff0c;大家好。欢迎收看蛙色VR系列教程之PS利用动作记录器节约补地时间。 大家拍摄在补地的时候&#xff0c;利用插件选择输入输出选项的时候&#xff0c;每次重复操作…

完美解决RuntimeError: expected scalar type Long but found Float

文章目录 一、错误解释RuntimeError: expected scalar type Long but found Float二、错误分析三、解决办法总结 一、错误解释RuntimeError: expected scalar type Long but found Float RuntimeError&#xff1a;应为标量类型Long&#xff0c;但找到了Float 二、错误分析 我…

开源一个房屋租赁平台

前言 哈喽兄弟们&#xff0c;好久不见哦&#xff5e; 最近整理了一下之前写过的一些小项目/毕业设计。发现还是有很多存货的&#xff0c;虽然这些项目普遍都写的比较简单&#xff0c;但想一想既然放在电脑里面也吃灰&#xff0c;那么还不如开源分享出去&#xff0c;没准还可以…

每日一题 --- 力扣318----最大单词长度乘积

这道题时间复杂度我感觉设置的不是很好&#xff0c;应该最好是有一个1000变成10000就行。 因为我在做这道题的时候被误导了&#xff0c;以为双重循环暴力判断一下也能过&#xff0c;因为1000*1000 *26的时间复杂度没有到1亿&#xff0c;那么我刚开始认为是能过的&#xff0c;结…

用Rust和Scraper库编写图像爬虫的建议

本文提供一些有关如何使用Rust和Scraper库编写图像爬虫的一般建议&#xff1a; 1、首先&#xff0c;你需要安装Rust和Scraper库。你可以通过Rustup或Cargo来安装Rust&#xff0c;然后使用Cargo来安装Scraper库。 2、然后&#xff0c;你可以使用Scraper库的Crawler类来创建一个…

海洋专用cmocean颜色包_共22种--全平台可用

海洋专用cmocean颜色包_共22种–全平台可用 往期推荐&#xff1a; Python语言_matplotlib包_共80种–全平台可用 Python语言_single_color_共140种–全平台可用 R语言_RColorBrewer包–全平台可用 R语言gplots包的颜色索引表–全平台可用 R语言中的自带的调色板–五种–全平台…

Python Tkinter快速入门

一、背景 接了一个小活&#xff0c;需要做一个比特币走势分析小工具&#xff0c;客户希望能安装在Mac上&#xff0c;考虑后决定采用Python的Tkintermatplotlib来做&#xff0c;今天我们就来了快速了解一下Tkinter。 二、Tkinter介绍 Tkinter是Tk的Python版本&#xff0c;而T…

Spring boot集成sentinel限流服务

Sentinel集成文档 Sentinel控制台 Sentinel本身不支持持久化&#xff0c;项目通过下载源码改造后&#xff0c;将规则配置持久化进nacos中&#xff0c;sentinel重启后&#xff0c;配置不会丢失。 架构图&#xff1a; 改造步骤&#xff1a; 接着我们就要改造Sentinel的源码。…

灵活多样的流水号生成方式:JVS低代码表单满足你的各种需求

在数字化时代&#xff0c;表单成为了我们日常生活和工作中不可或缺的一部分。无论是在线申请、数据录入还是业务流程管理&#xff0c;表单都发挥着重要的作用。为了确保数据的准确性和可追溯性&#xff0c;流水号的概念应运而生。流水号作为表单数据记录的唯一标识&#xff0c;…

2022ICPC,济南站(M,E,D

初见安~好久好久没写博客了……感觉还是有必要写的。 拿去年济南的题目训练了一下&#xff0c;状态还不错&#xff0c;写一下自己写过了的题目的题解。 M Best Carry Player 题意&#xff1a;给你n个数&#xff0c;交换他们的顺序使依次相加后总的进位次数最少&#xff08;十…

代码随想录打卡第62天|● 503.下一个更大元素II ● 42. 接雨水

● 503.下一个更大元素II 题目&#xff1a;给定一个循环数组 nums &#xff08; nums[nums.length - 1] 的下一个元素是 nums[0] &#xff09;&#xff0c;返回 nums 中每个元素的 下一个更大元素 。 数字 x 的 下一个更大的元素 是按数组遍历顺序&#xff0c;这个数字之后的第…

蓝桥杯每日一题2023.11.6

取位数 - 蓝桥云课 (lanqiao.cn) 题目描述 题目分析 由题意我们知道len中为现阶段长度&#xff0c;如果其与k相等也就是找到了正确的位数&#xff0c;否则就调用递归来进行搜索&#xff0c;每次搜索一位数。 #include <stdio.h> // 求x用10进制表示时的数位长度 int …

你的停机真的优雅么?第二弹来袭 | 京东云技术团队

1. 前言 之前总结了一篇基于现有业务线在停机重启时会产生RPC和MQ调用强杀导致业务数据不一致文章&#xff0c;文中通过优雅停机改造对RPC服务进行反注册和MQ进行暂停消费&#xff0c;进而可以解决在停机时强制kill掉RPC线程或者MQ线程导致数据不一致现象&#xff0c;具体的原…

DolphinDB 流计算优化实践:时延统计与性能调优

在实时计算中&#xff0c;端到端的响应延迟是衡量计算性能时最重要的指标。DolphinDB 内置的流数据框架支持流数据的发布与订阅、流式增量计算、实时关联等&#xff0c;用户能够快速实现复杂的实时计算任务&#xff0c;达到毫秒级甚至亚毫秒级的效果&#xff0c;而无需编写大量…

这8个Wireshark使用技巧,网工屡试屡爽!

你们好&#xff0c;我的网工朋友。 都懂哈&#xff0c;wireshark是个啥我就不多赘述了&#xff0c;软件功能十分强大就对了。 想安装的可以戳这里&#xff1a;《wireshark下载&安装 》 wireshark作为网工的得力助手&#xff0c;你知道哪些使用技巧&#xff1f; 今天就给…

2023软考-系统架构师一日游

上周六&#xff08;11月4号&#xff09;参见了软考&#xff0c;报的系统架构师&#xff0c;今年下半年是第一次推行机考&#xff0c;简单来分享下大致流程&#xff0c;至于考试难度、考点什么的&#xff0c;这个网上有很多专门研究这些的机构&#xff0c;本人无权发言。考试的经…

每日一题 318. 最大单词长度乘积(中等)

暴力求解没超时&#xff0c;那就这样吧 class Solution:def maxProduct(self, words: List[str]) -> int:ans 0for i in range(len(words)):for j in range(i 1, len(words)):if len(words[i]) * len(words[j]) < ans:continuet 0for k in range(26):ch chr(k ord(…