基于ROS发布里程计信息

news2024/11/15 20:01:00

参考文档: navigationTutorialsRobotSetupOdom
参考博客:
(1)ROS机器人里程计模型
(2)ROS里程计消息nav_msgs/Odometry的可视化方法

1 常用坐标系系统模型

世界坐标系是描述机器人全局信息的坐标系;机器人坐标系是描述机器人自身信息的坐标系;传感器坐标系是描述传感器信息的坐标系。如下图所示坐标系之间的关系。
在这里插入图片描述

世界坐标系是固定不变的,机器人坐标系和传感器坐标系是在世界坐标系下描述的。机器人坐标系和传感器坐标系原点重合但是存在一定的角度,不同的机器人坐标系关系是不同的。当我们使用传感器数据时,这些坐标系间的关系就是我们变换矩阵的参数,因为传感器的数据必定是要变换到机器人坐标系或者世界坐标系中使用的

2 移动机器人位姿模型

移动机器人的位姿模型就是机器人在世界坐标系下的状态。常用随机变量 X t = ( x t , y t , θ t ) X_t =(x_t ,y_t ,θ_t ) Xt=(xt,yt,θt)来描述 t t t 时刻的机器人在世界坐标系下的状态,简称位姿
在这里插入图片描述

3 移动机器人里程计的计算

移动机器人的里程计就是机器人每时每刻在世界坐标系下位姿状态
里程计的计算是指以机器人上电时刻为世界坐标系的起点(机器人的航向角是世界坐标系X正方向)开始累积计算任意时刻机器人在世界坐标系下的位姿。通常计算里程计方法是速度积分推算:通过左右电机的编码器测得机器人的左右轮的速度VL和VR,在一个短的时刻△t内,认为机器人是匀速运动,并且根据上一时刻机器人的航向角计算得出机器人在该时刻内世界坐标系上X和Y轴的增量,然后将增量进行累加处理,关于航向角θ采用的IMU的yaw值。然后根据以上描述即可得到机器人的里程计。
在这里插入图片描述
在这里插入图片描述
t a n ( δ ) = L R tan(\delta)=\frac{L}{R} tan(δ)=RL(1)
v = w R v=wR v=wR(2)
由(1)(2)得
w = v R = v t a n ( δ ) L w=\frac{v}{R}=\frac{vtan(\delta)}{L} w=Rv=Lvtan(δ)
θ = w ∗ d t \theta=w*dt θ=wdt

  //在给定机器人速度的情况下,以一种典型的方式计算里程计
  31     double dt = (current_time - last_time).toSec();
  32     double delta_x = v * cos(th) * dt;
  33     double delta_y = v * sin(th) * dt;
  34     double delta_th = (v * tan(steer) / wheel_base) * dt;
  35
  36     x += delta_x;
  37     y += delta_y;
  38     th += delta_th;

4 示例代码

示例代码,用于通过ROS发布。nav_msgs/Odometry消息,并使用tf对只在圆圈中行驶的机器人进行转换。我们将首先展示整个代码,并在下面进行逐段解释。

   1 #include <ros/ros.h>
   2 #include <tf/transform_broadcaster.h>
   3 #include <nav_msgs/Odometry.h>
   4 
   5 int main(int argc, char** argv){
   6   ros::init(argc, argv, "odometry_publisher");
   7 
   8   ros::NodeHandle n;
   9   ros::Publisher odom_pub = n.advertise<nav_msgs::Odometry>("odom", 50);
  10   tf::TransformBroadcaster odom_broadcaster;
  11 
  12   double x = 0.0;
  13   double y = 0.0;
  14   double th = 0.0;
  15 
  16   double vx = 0.1;
  17   double vy = -0.1;
  18   double vth = 0.1;
  19 
  20   ros::Time current_time, last_time;
  21   current_time = ros::Time::now();
  22   last_time = ros::Time::now();
  23 
  24   ros::Rate r(1.0);
  25   while(n.ok()){
  26 
  27     ros::spinOnce();               // 检查传入消息
  28     current_time = ros::Time::now();
  29 
  30     //在给定机器人速度的情况下,以一种典型的方式计算里程计
  31     double dt = (current_time - last_time).toSec();
  32     double delta_x = (vx * cos(th) - vy * sin(th)) * dt;
  33     double delta_y = (vx * sin(th) + vy * cos(th)) * dt;
  34     double delta_th = vth * dt;
  35 
  36     x += delta_x;
  37     y += delta_y;
  38     th += delta_th;
  39 
  40     //由于所有里程计都是6DOF,我们需要一个由yaw创建的四元数
  41     geometry_msgs::Quaternion odom_quat = tf::createQuaternionMsgFromYaw(th);
  42 
  43     // 首先,我们将通过tf发布转换
  44     geometry_msgs::TransformStamped odom_trans;
  45     odom_trans.header.stamp = current_time;
  46     odom_trans.header.frame_id = "odom";
  47     odom_trans.child_frame_id = "base_link";
  48 
  49     odom_trans.transform.translation.x = x;
  50     odom_trans.transform.translation.y = y;
  51     odom_trans.transform.translation.z = 0.0;
  52     odom_trans.transform.rotation = odom_quat;
  53 
  54     // 发送转换
  55     odom_broadcaster.sendTransform(odom_trans);
  56 
  57     //接下来,我们将通过ROS发布里程计消息
  58     nav_msgs::Odometry odom;
  59     odom.header.stamp = current_time;
  60     odom.header.frame_id = "odom";
  61 
  62     //设置位置
  63     odom.pose.pose.position.x = x;
  64     odom.pose.pose.position.y = y;
  65     odom.pose.pose.position.z = 0.0;
  66     odom.pose.pose.orientation = odom_quat;
  67 
  68     //设置速度
  69     odom.child_frame_id = "base_link";
  70     odom.twist.twist.linear.x = vx;
  71     odom.twist.twist.linear.y = vy;
  72     odom.twist.twist.angular.z = vth;
  73 
  74     //发布消息
  75     odom_pub.publish(odom);
  76 
  77     last_time = current_time;
  78     r.sleep();
  79   }
  80 }

(1)我们需要实现“odom”参考系到“base_link”参考系的变换,以及nav_msgs/Odometry消息的发布,所以首先需要包含相关的头文件。

#include <tf/transform_broadcaster.h>
#include <nav_msgs/Odometry>

(2)定义一个消息发布者来发布“odom”消息,在定义一个tf广播,来发布tf变换信息

ros::Publisher odom_pub = n.advertise<nav_msgs::Odometry>("odom", 50);
tf::TransformBroadcaster odom_broadcaster;

(3)我们假设机器人最初从“odom”坐标系的原点开始。

double x = 0.0;
double y = 0.0;
double th = 0.0;

(4)我们设置机器人的默认前进速度,让机器人的base_link参考系在odom参考系下以x轴方向0.1m/s,Y轴速度-0.1m/s,角速度0.1rad/s的状态移动,这种状态下,可以让机器人保持圆周运动。

double vx = 0.1;
double vy = -0.1;
double vth = 0.1;

(5)在这个例子中,我们将以1Hz的速率发布里程计信息,以便于内省,大多数系统都希望以更高的速率发布里程计。

ros::Rate r(1.0);

(6)使用我们设置的速度信息,来计算并更新里程计的信息,包括单位时间内机器人在x轴、y轴的坐标变化和角度的变化。在实际系统中,需要更具里程计的实际信息进行更新。

  31     double dt = (current_time - last_time).toSec();
  32     double delta_x = (vx * cos(th) - vy * sin(th)) * dt;
  33     double delta_y = (vx * sin(th) + vy * cos(th)) * dt;
  34     double delta_th = vth * dt;
  35 
  36     x += delta_x;
  37     y += delta_y;
  38     th += delta_th;

(7)为了兼容二维和三维的功能包,让消息结构更加通用,里程计的偏航角需要转换成四元数才能发布,辛运的是,ROS为我们提供了偏航角与四元数相互转换的功能。

  41    geometry_msgs::Quaternion odom_quat = tf::createQuaternionMsgFromYaw(th)

(8) 创建一个tf发布需要使用的TransformStamped类型消息,然后根据消息结构填充当前的时间戳、参考系id、子参考系id,注意两个参考系的id必须要是“odom”和“base_link”。

  44     geometry_msgs::TransformStamped odom_trans;
  45     odom_trans.header.stamp = current_time;
  46     odom_trans.header.frame_id = "odom";
  47     odom_trans.child_frame_id = "base_link";

(9)在这里,我们从里程计数据中填写转换消息,然后使用TransformBroadcaster发送转换。

  49     odom_trans.transform.translation.x = x;
  50     odom_trans.transform.translation.y = y;
  51     odom_trans.transform.translation.z = 0.0;
  52     odom_trans.transform.rotation = odom_quat;
  53 
  54     //send the transform
  55     odom_broadcaster.sendTransform(odom_trans);

(10)别忘了,我们还要发布nav_msgs/Odometry消息,让导航包获取机器人的速度。创建消息变量,然后填充时间戳。

  57     //next, we'll publish the odometry message over ROS
  58     nav_msgs::Odometry odom;
  59     odom.header.stamp = current_time;
  60     odom.header.frame_id = "odom";

(11) 填充机器人的位置、速度,然后发布消息。我们将消息的child_frame_id设置为“base_link”帧,因为这是我们发送速度信息的坐标帧。

  62     //set the position
  63     odom.pose.pose.position.x = x;
  64     odom.pose.pose.position.y = y;
  65     odom.pose.pose.position.z = 0.0;
  66     odom.pose.pose.orientation = odom_quat;
  67 
  68     //set the velocity
  69     odom.child_frame_id = "base_link";
  70     odom.twist.twist.linear.x = vx;
  71     odom.twist.twist.linear.y = vy;
  72     odom.twist.twist.angular.z = vth;

5 ROS里程计消息nav_msgs/Odometry的可视化方法

参考大佬的博客:ROS里程计消息nav_msgs/Odometry的可视化方法
在这里插入图片描述里程计消息中的pose包含了位置pose.position和姿态pose.orientation
可视化方法
(1)在一个节点中订阅发布的里程计话题消息nav_msgs/Odometry
(2)创建geometry_msgs::PoseStamped对象接收里程计的位姿。
(3)创建nav_msgs/Path对象作为容器,将赋值后的对象push_back进nav_msgs/Path中并发布。
然后即可在rviz中订阅包含nav_msgs/Path的话题并可视化轨迹。
(1)编写消息收发节点文件path_3d.cpp

#include <ros/ros.h>
#include <nav_msgs/Path.h>
#include <std_msgs/String.h>
#include <nav_msgs/Odometry.h>
#include <geometry_msgs/PoseStamped.h>
#include <tf/transform_broadcaster.h>
#include <tf/tf.h>
 
nav_msgs::Path  path; // 创建nav_msgs/Path对象
ros::Publisher  path_pub;
 
void pathCallback(const nav_msgs::Odometry::ConstPtr& odom_3d)
{
    geometry_msgs::PoseStamped position_3d;
    position_3d.pose.position.x = odom_3d->pose.pose.position.x; 
    position_3d.pose.position.y = odom_3d->pose.pose.position.y; 
    position_3d.pose.position.z = odom_3d->pose.pose.position.z;
    position_3d.pose.orientation = odom_3d->pose.pose.orientation;
 
 
    position_3d.header.stamp = odom_3d->header.stamp;
    position_3d.header.frame_id = "map";
 
    path.poses.push_back(position_3d);
    path.header.stamp = position_3d.header.stamp;
    path.header.frame_id = "map";
    path_pub.publish(path);
  
    std::cout << odom_3d -> header.stamp << ' ' << odom_3d->pose.pose.position.x << ' ' << odom_3d->pose.pose.position.y << ' ' << odom_3d->pose.pose.position.z << std::endl;
}
 
int main (int argc, char **argv)
{
    ros::init (argc, argv, "showpath");
    ros::NodeHandle ph;
 
    path_pub = ph.advertise<nav_msgs::Path>("odom3d_path", 10, true);
    ros::Subscriber odomSub = ph.subscribe<nav_msgs::Odometry>("/odometry_3d", 10, pathCallback);  //订阅里程计话题信息,其中"/odometry_3d"是自己发布的里程计话题名,别忘了修改
    
    ros::Rate loop_rate(1000);
    while(ros::ok())
    {
        ros::spinOnce();
        loop_rate.sleep();
    }
    return 0;
}

(2)CMakeLists.txt如下

cmake_minimum_required(VERSION 2.8.3)
project(path_3d)
 
## Compile as C++11, supported in ROS Kinetic and newer
# add_compile_options(-std=c++11)
 
find_package(catkin REQUIRED COMPONENTS
  geometry_msgs
  roscpp
  rospy
  std_msgs
  message_generation
)
 
## Generate added messages and services with any dependencies listed here
 generate_messages(
   DEPENDENCIES
   geometry_msgs   std_msgs
 )
 
catkin_package(
  INCLUDE_DIRS include
  LIBRARIES path_3d
  CATKIN_DEPENDS geometry_msgs roscpp rospy std_msgs
  DEPENDS system_lib
)
 
include_directories(
  include
  ${catkin_INCLUDE_DIRS}
)
 
add_executable(path_3d src/path_3d.cpp) #${PROJECT_NAME}_node
target_link_libraries(path_3d ${catkin_LIBRARIES}) # ${PROJECT_NAME}_node
add_dependencies(path_3d beginner_tutorials_generate_messages_cpp) #path_3d_node

不足之处望指正

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

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

相关文章

洛谷 B2004 对齐输出 C++代码

目录 推荐专栏 题目描述 AC Code 切记 推荐专栏 http://t.csdnimg.cn/Z1tCAhttp://t.csdnimg.cn/Z1tCA 题目描述 题目网址&#xff1a;对齐输出 - 洛谷 AC Code #include<bits/stdc.h> using namespace std; typedef long long ll; int main() { int a,b,c;cin&g…

【仙逆】尸阴宗始祖现身,王林修得黄泉生窍诀,阿呆惊险逃生

【侵权联系删除】【文/郑尔巴金】 深度爆料最新集&#xff0c;王林终于成功筑基&#xff0c;这一集的《仙逆》动漫真是让人热血沸腾啊&#xff01;在这个阶段&#xff0c;王林展现出了他的决心和毅力&#xff0c;成功地击杀了藤厉&#xff0c;并采取了夺基大法&#xff0c;从藤…

Hive On Spark 概述、安装配置、计算引擎更换、应用、异常解决

文章目录 Hadoop 安装Hive 安装Hive On Spark 与 Spark On Hive 区别Hive On SparkSpark On Hive 部署 Hive On Spark查询 Hive 对应的 Spark 版本号下载 Spark解压 Spark配置环境变量指定 Hadoop 路径在 Hive 配置 Spark 参数上传 Jar 包并更换引擎 测试 Hive On Spark解决依赖…

Springboot的Container Images,docker加springboot

Spring Boot应用程序可以使用Dockerfiles容器化&#xff0c;或者使用Cloud Native Buildpacks来创建优化的docker兼容的容器映像&#xff0c;您可以在任何地方运行。 1. Efficient Container Images 很容易将Spring Boot fat jar打包为docker映像。然而&#xff0c;像在docke…

智能分析视频平台EasyCVR地图功能出现异常该如何解决?

安防视频监控/视频集中存储/云存储/磁盘阵列EasyCVR平台可拓展性强、视频能力灵活、部署轻快&#xff0c;可支持的主流标准协议有国标GB28181、RTSP/Onvif、RTMP等&#xff0c;以及支持厂家私有协议与SDK接入&#xff0c;包括海康Ehome、海大宇等设备的SDK等。平台既具备传统安…

41基于matlab的CNN的图像边缘提取,程序已调通,可直接运行。

基于matlab的CNN的图像边缘提取&#xff0c;程序已调通&#xff0c;可直接运行。 41matlabCNN图像边缘提取 (xiaohongshu.com)

《AI时代架构师修炼之道:ChatGPT让架构师插上翅膀》

本专注于帮助架构师在AI时代 实现晋级、提高效率的图书 书中介绍了如何使用 ChatGPT 来完成架构设计的各个环节 并通过实战案例展示了ChatGPT在实际架构设计中的应用方法 关键点 1.架构设计新模式&#xff1a;让架构设计更高效、更快捷、更完美。 2.全流程解析&#xff1a;涵盖…

List的add(int index,E element)陷阱,不得不防

项目场景&#xff1a; 项目中有两个List列表&#xff0c;一个是List1用来存储一个标识&#xff0c;后续会根据这个标识去重。 一个List2是用来返回对象的&#xff0c;其中对象里也有一个属性List3。现需要将重复的标识数据追加到List3 我想到的两个方案&#xff1a; 尽量不动…

设计工位卡片

因为今天接到了一个设计工位卡片的任务&#xff0c;于是在这里记录一哈&#xff0c;设计卡片所需要的一些东西&#xff1a; 设计卡片的网站有以下几个可供参考&#xff1a; Canva&#xff1a;提供各种类型的卡片设计模板&#xff0c;包括圣诞贺卡、生日贺卡、感谢卡等&#x…

CL_MVSNet复现可能会出现的问题汇总

1.最好按照说明文档要求配好python3.7和pytorch1.0 2. 【已解决】 FutureWarning: The module torch.distributed.launch is deprecated and will be removed in future. torch.distributed.launch被弃用&#xff0c;考虑使用torchrun模块进行替换。 解决方案&#xff1a; 将…

pyzed.sl 突然 ImportError: DLL load failed while importing sl: 找不到指定的程序。 的解决

问题描述 >>> import pyzed.sl Traceback (most recent call last):File "<stdin>", line 1, in <module> ImportError: DLL load failed while importing sl: 找不到指定的程序。解决 1 按网上说的&#xff0c;将bin文件夹下所有dll复制到py…

2023年美团外卖商家数据(含销量)

2023年美团外卖商家、地址、电话、经纬度、起送价、月销量、评分、经营许可

前端移动web高级详细解析三

模拟移动设备&#xff0c;方便查看页面效果 屏幕分辨率 分类&#xff1a; 物理分辨率&#xff1a;硬件分辨率&#xff08;出厂设置&#xff09; 逻辑分辨率&#xff1a;软件 / 驱动设置 结论&#xff1a;制作网页参考 逻辑分辨率 视口 作用&#xff1a;显示 HTML 网页的区…

数据结构:绪论(数据对象,逻辑结构,数据的运算,物理结构)

目录 1.数据2.数据元素、数据项3.数据对象4.数据结构1.逻辑结构1.集合结构2.线性结构3.树形结构4.图状结构 2.数据的运算3.物理结构&#xff08;存储结构&#xff09;1.顺序存储2.链式存储3.索引存储4.散列存储 4.数据结构的选择 5.数据类型 1.数据 数据是信息的载体&#xff0…

返利管理系统建设指南

目录 引子 一、返利业务模式介绍 1. 什么是返利 2. 返利与优惠区别 3. 业务模式 4. 返利管理业务流程 二、返利系统建设说明 1. 业务架构 2. 主要功能 三、结束语 引子 返利作为供应商在商业活动开拓新市场、挖掘存量市场潜力&#xff0c;激励经销商的销售策略&…

Mac/Linux类虚拟机_CrossOver虚拟机CrossOver 23.6正式发布2024全新功能解析

CodeWeivers 公司于今年 10 月发布了 CrossOver 23.6 测试版&#xff0c;重点添加了对 DirectX 12 支持&#xff0c;从而在 Mac 上更好地模拟运行 Windows 游戏。 该公司今天发布新闻稿&#xff0c;表示正式发布 CrossOver 23 稳定版&#xff0c;在诸多新增功能中&#xff0c;最…

HackerOne支付给道德黑客超过3亿美元的漏洞赏金

导语 近日&#xff0c;HackerOne宣布自其成立以来&#xff0c;其漏洞赏金计划已经支付给道德黑客和漏洞研究人员超过3亿美元的奖励。这个数字令人瞠目结舌&#xff0c;展示了道德黑客在网络安全中的重要性。让我们一起来看看HackerOne的成就和一些有趣的数据。 道德黑客的奖励 …

项目资源管理-考试重点

1. 冲突管理的5种方法 ① 撤退/回避 ② 缓和/包容 ③ 妥协/调解 ④ 强迫/命令 ⑤ 合作/解决问题 2. 虚拟团队的优缺点 优点&#xff1a; ① 能够利用不在同一地理区域的专家的专业技术 ② 将在家办公的员工纳入团队 ③ 以及将行动不便者或残疾人纳入团队 缺点&#…

codeforces (C++ Doremy‘s Paint 3)

题目&#xff1a; 翻译&#xff1a; 思路&#xff1a; 1、题目意思&#xff1a;将数组中的数进行排列&#xff0c;任意相邻两个数的和都相等&#xff0c;才能说这个数组为好。一下分三种情况讨论。 2、当数组中有三种及三种以上的数字&#xff0c;那任意相邻两个数的和都相等必…

【算法】染色法判定二分图

题目 给定一个 n 个点 m 条边的无向图&#xff0c;图中可能存在重边和自环。 请你判断这个图是否是二分图。 输入格式 第一行包含两个整数 n 和 m。 接下来 m 行&#xff0c;每行包含两个整数 u 和 v&#xff0c;表示点 u 和点 v 之间存在一条边。 输出格式 如果给定图是二分…