nav_msgs/Odometry消息的发布和tf变换

article/2025/7/22 21:00:09

一。ROS使用tf来决定机器人的位置和静态地图中的传感器数据,但是tf中没有机器人的速度信息,所以导航功能包要求机器人 能够通过里程计信息源发布包含速度信息的里程计nav_msgs/Odometry 消息。 本篇将介绍nav_msgs/Odometry消息,并且通过代码实现消息的发布,以及tf树的变换。这里使用一个简单的例程,实现 nav_msgs/Odometry消息的发布和tf变换,通过伪造的数据,实现机器人圆周运动。

1.1.首先在你得ros工作空间中新建功能包,包含以下库:

catkin_create_pkg odom_tf_package std_msgs rospy roscpp sensor_msgs tf nav_msgs

1.2.在odom_tf_package/src下创建TF变换的代码文件:

touch odom_tf_node.cpp

源码如下:(建议初学者必须认真阅读代码,搞清所发布的话题上的消息和消息类型等)

复制代码
#include <tf/transform_broadcaster.h>
#include <nav_msgs/Odometry.h>int main(int argc, char** argv)
{ros::init(argc, argv, "odometry_publisher");ros::NodeHandle n;ros::Publisher odom_pub = n.advertise<nav_msgs::Odometry>("odom", 50);tf::TransformBroadcaster odom_broadcaster;double x = 0.0;double y = 0.0;double th = 0.0;double vx = 0.1;double vy = -0.1;double vth = 0.1;ros::Time current_time, last_time;current_time = ros::Time::now();last_time = ros::Time::now();ros::Rate r(1.0);while(n.ok()){ros::spinOnce();               // check for incoming messagescurrent_time = ros::Time::now();//compute odometry in a typical way given the velocities of the robotdouble dt = (current_time - last_time).toSec();double delta_x = (vx * cos(th) - vy * sin(th)) * dt;double delta_y = (vx * sin(th) + vy * cos(th)) * dt;double delta_th = vth * dt;x += delta_x;y += delta_y;th += delta_th;//since all odometry is 6DOF we'll need a quaternion created from yawgeometry_msgs::Quaternion odom_quat = tf::createQuaternionMsgFromYaw(th);//first, we'll publish the transform over tf
        geometry_msgs::TransformStamped odom_trans;odom_trans.header.stamp = current_time;odom_trans.header.frame_id = "odom";odom_trans.child_frame_id = "base_link";odom_trans.transform.translation.x = x;odom_trans.transform.translation.y = y;odom_trans.transform.translation.z = 0.0;odom_trans.transform.rotation = odom_quat;//send the transform
        odom_broadcaster.sendTransform(odom_trans);//next, we'll publish the odometry message over ROS
        nav_msgs::Odometry odom;odom.header.stamp = current_time;odom.header.frame_id = "odom";//set the positionodom.pose.pose.position.x = x;odom.pose.pose.position.y = y;odom.pose.pose.position.z = 0.0;odom.pose.pose.orientation = odom_quat;//set the velocityodom.child_frame_id = "base_link";odom.twist.twist.linear.x = vx;odom.twist.twist.linear.y = vy;odom.twist.twist.angular.z = vth;//publish the message
        odom_pub.publish(odom);last_time = current_time;r.sleep();}
}
复制代码

下面来剖析代码进行分析:

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

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

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

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

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

 默认机器人的起始位置是odom参考系下的0点。

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

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

  ros::Rate r(1.0);

使用1Hz的频率发布odom消息,当然,在实际系统中,往往需要更快的速度进行发布。

复制代码
        //compute odometry in a typical way given the velocities of the robotdouble dt = (current_time - last_time).toSec();double delta_x = (vx * cos(th) - vy * sin(th)) * dt;double delta_y = (vx * sin(th) + vy * cos(th)) * dt;double delta_th = vth * dt;x += delta_x;y += delta_y;th += delta_th;
复制代码

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

       //since all odometry is 6DOF we'll need a quaternion created from yawgeometry_msgs::Quaternion odom_quat = tf::createQuaternionMsgFromYaw(th);

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

        //first, we'll publish the transform over tfgeometry_msgs::TransformStamped odom_trans;odom_trans.header.stamp = current_time;odom_trans.header.frame_id = "odom";odom_trans.child_frame_id = "base_link";

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

        odom_trans.transform.translation.x = x;odom_trans.transform.translation.y = y;odom_trans.transform.translation.z = 0.0;odom_trans.transform.rotation = odom_quat;

 填充里程计信息,然后发布tf变换的消息。

        //next, we'll publish the odometry message over ROSnav_msgs::Odometry odom;odom.header.stamp = current_time;

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

复制代码
        //set the positionodom.pose.pose.position.x = x;odom.pose.pose.position.y = y;odom.pose.pose.position.z = 0.0;odom.pose.pose.orientation = odom_quat;//set the velocityodom.child_frame_id = "base_link";odom.twist.twist.linear.x = vx;odom.twist.twist.linear.y = vy;odom.twist.twist.angular.z = vth;
复制代码

  填充机器人的位置、速度,然后发布消息。注意,我们发布的是机器人本体的信息,所以参考系需要填"base_link"。

1.3.编译源码:在odom_tf_package/CMakeLists.txt添加编译选项:

add_executable(odom_tf_node src/odom_tf_node.cpp)
target_link_libraries(odom_tf_node ${catkin_LIBRARIES}

返回到你的工作空间的顶层目录下:

catkin_make 

二。  在导航过程中,传感器的信息至关重要,这些传感器可以是激光雷达、摄像机、声纳、红外线、碰撞开关,但是归根结底,导航功能包要求机器人必须发布sensor_msgs/LaserScan或sensor_msgs/PointCloud格式的传感器信息,本篇将详细介绍如何使用代码发布所需要的消息。无论是 sensor_msgs/LaserScan,还是sensor_msgs/PointCloud ,都和ROS中tf帧信息等时间相关的消息一样,带标准格式的头信息。

复制代码
    #Standard metadata for higher-level flow data types#sequence ID: consecutively increasing ID uint32 seq    #Two-integer timestamp that is expressed as:# * stamp.secs: seconds (stamp_secs) since epoch# * stamp.nsecs: nanoseconds since stamp_secs# time-handling sugar is provided by the client librarytime stamp     #Frame this data is associated with# 0: no frame# 1: global framestring frame_id
复制代码

以上是标准头信息的主要部分。seq是消息的顺序标识,不需要手动设置,发布节点在发布消息时,会自动累加。stamp 是消息中与数据相关联的时间戳,例如激光数据中,时间戳对应激光数据的采集时间点。frame_id 是消息中与数据相关联的参考系id,例如在在激光数据中,frame_id对应激光数据采集的参考系。

2.1.如何发布点云数据

点云消息的结构

复制代码
    #This message holds a collection of 3d points, plus optional additional information about each point.   #Each Point32 should be interpreted as a 3d point in the frame given in the header          Header header     geometry_msgs/Point32[] points  #Array of 3d points  ChannelFloat32[] channels       #Each channel should have the same number of elements as points array, and the data in each channel should correspond 1:1 with each point
复制代码

如上所示,点云消息的结构支持存储三维环境的点阵列,而且channels参数中,可以设置这些点云相关的数据,例如可以设置一个强度通道,存储每个点的数据强度,还可以设置一个系数通道,存储每个点的反射系数,等等。

2.2.通过代码发布点云数据

.在odom_tf_package/src下创建TF变换的代码文件:

touch point_kinect_node.cpp

源代码如下:

复制代码
#include "ros/ros.h"
#include <sensor_msgs/PointCloud.h>int main(int argc, char** argv)
{ros::init(argc, argv, "point_cloud_publisher");ros::NodeHandle n;ros::Publisher cloud_pub = n.advertise<sensor_msgs::PointCloud>("cloud", 50);unsigned int num_points = 100;int count = 0;ros::Rate r(1.0);while(n.ok()){sensor_msgs::PointCloud cloud;cloud.header.stamp = ros::Time::now();cloud.header.frame_id = "sensor_frame";cloud.points.resize(num_points);//we'll also add an intensity channel to the cloudcloud.channels.resize(1);cloud.channels[0].name = "intensities";cloud.channels[0].values.resize(num_points);//generate some fake data for our point cloudfor(unsigned int i = 0; i < num_points; ++i){cloud.points[i].x = 1 + count;cloud.points[i].y = 2 + count;cloud.points[i].z = 3 + count;cloud.channels[0].values[i] = 100 + count;}cloud_pub.publish(cloud);++count;r.sleep();}}
复制代码

分解代码来分析:

#include <sensor_msgs/PointCloud.h>

 首先也是要包含sensor_msgs/PointCloud消息结构。

    ros::Publisher cloud_pub = n.advertise<sensor_msgs::PointCloud>("cloud", 50);

定义一个发布点云消息的发布者。

        sensor_msgs::PointCloud cloud;cloud.header.stamp = ros::Time::now();cloud.header.frame_id = "sensor_frame";

 为点云消息填充头信息,包括时间戳和相关的参考系id。

    cloud.points.resize(num_points);

设置存储点云数据的空间大小。

        //we'll also add an intensity channel to the cloudcloud.channels.resize(1);cloud.channels[0].name = "intensities";cloud.channels[0].values.resize(num_points);

设置一个名为“intensity“的强度通道,并且设置存储每个点强度信息的空间大小。

        //generate some fake data for our point cloudfor(unsigned int i = 0; i < num_points; ++i){cloud.points[i].x = 1 + count;cloud.points[i].y = 2 + count;cloud.points[i].z = 3 + count;cloud.channels[0].values[i] = 100 + count;   

 将我们伪造的数据填充到点云消息结构当中。

   cloud_pub.publish(cloud);

 最后,发布点云数据。

2.3.编译源码:在odom_tf_package/CMakeLists.txt添加编译选项:

add_executable(point_kinect_node src/point_kinect_node.cpp)
target_link_libraries(point_kinect_node ${catkin_LIBRARIES}

返回到你的工作空间的顶层目录下:

catkin_make

三:测试代码:

roscore
rosrun odom_tf_package odom_tf_node
rosrun odom_tf_package point_kinect_node 
rviz

3.1.rviz视图如下:tf变换和机器人移动信息.

3.2.查看发布的点云数据。

rostopic echo /cloud 


http://chatgpt.dhexx.cn/article/Mi44RgRG.shtml

相关文章

【视觉SLAM】DM-VIO: Delayed Marginalization Visual-Inertial Odometry

L. v. Stumberg and D. Cremers, “DM-VIO: Delayed Marginalization Visual-Inertial Odometry,” in IEEE Robotics and Automation Letters, vol. 7, no. 2, pp. 1408-1415, April 2022, doi: 10.1109/LRA.2021.3140129. 论文阅读方法&#xff1a;Title&#xff0c;Abstract…

DSO(Direct Sparse Odometry)

DSO&#xff08;Direct Sparse Odometry&#xff09; 文章目录 1. 简述2. 概述3. 框架流程3.1 代码框架与数据表示3.2 VO流程 4. DSO详细介绍4.1 残差的构成与雅可比4.2 滑动窗口的维护与边缘化4.3 零空间&#xff0c;FEJ4.4 其他零散的模块和算法 5. 光度标定6. 评述7. 资料与…

SVO(SVO: fast semi-direct monocular visual odometry)

SVO2系列之深度滤波DepthFiltersvo_noteSVO&#xff08;SVO: fast semi-direct monocular visual odometry&#xff09;SVO 半直接视觉里程计【DepthFilter】深度滤波器【svopro】代码梳理 SVO&#xff08;SVO: fast semi-direct monocular visual odometry&#xff09;翻译 1、…

航迹推演

​ 做机器人底层程序的时候&#xff0c;经常用到航迹推演&#xff08;Odometry&#xff09;&#xff0c;无论是定位导航还是普通的方向控制。航迹推演中除了对机器人位姿进行估计&#xff0c;另一个很重要的关系是移动机器人前进速度、转向角速度与左轮速度、右轮速度之间的转换…

航迹推演(Odometry)

做机器人底层程序的时候&#xff0c;经常用到航迹推演&#xff08;Odometry&#xff09;&#xff0c;无论是定位导航还是普通的方向控制。航迹推演中除了对机器人位姿进行估计&#xff0c;另一个很重要的关系是移动机器人前进速度、转向角速度与左轮速度、右轮速度之间的转换。…

大数据基础编程+实验

本文仅用于分析和记录在校期间学习大数据分析课程的一点心得体会。 1、Ubuntu系统的安装和使用 本文采用Ubuntu16.04系统&#xff0c;安装系统省略&#xff0c;选择镜像后一直点击下一步 1.1 进入系统后&#xff0c;调整输入法&#xff0c;将输入法切换至中英文切换 1.2为…

Python数据库编程pymysql

Python数据库编程pymysql 一、数据库编程介绍 数据库编程就是针对数据库的操作&#xff0c;通过编写程序的方式&#xff0c;让程序做为数据库的客户端进行数据库操作。 对于MySQL的操作我们可以通过SQL语句&#xff0c;但是有很多情况下我们需要写入MySQL的数据非常多&#…

过程化SQL数据库编程

一、过程化SQL的块结构 基本的SQL是高度非过程化的语言。嵌入式SQL将SQL语句嵌入程序设计语言&#xff0c;借助高级语言的控制功能实现过程化。过程化SQL是对SQL的扩展&#xff0c;使其增加了过程化语句功能。 过程化SQL程序的基本结构是块。所有的过程化SQL程序都是由块组成的…

Java的数据库编程:JDBC

目录 一、JDBC是什么&#xff1f; 二、使用步骤 1.首先将JDBC的包引进java中 2.创建新的类来写代码 3.描述你的服务器 4.设置你的数据库地址,数据库用户名,数据库密码 5.连接数据库 6.书写你所要执行的SQL语句 7.把字符串风格的sql转化成一个对象 8.执行语句 9.回收资…

Python数据库编程

操作SQLite3数据库 从Python3.x版本开始&#xff0c;在标准库中已经内置了SQLlite3模块&#xff0c;它可以支持SQLite3数据库的访问和相关的数据库操作。在需要操作SQLite3数据库数据时&#xff0c;只须在程序中导入SQLite3模块即可。Python语言操作SQLite3数据库的基本流程如…

实验7 数据库编程

第1关 定义一个名为PROC_COUNT的无参数存储过程 任务描述 定义一个名为PROC_COUNT的无参数存储过程&#xff0c;查询工程名称中含有“厂”字的工程数量&#xff0c;并调用该存储过程。 相关知识 1、工程项目表J由工程项目代码(JNO)、工程项目名(JNAME)、工程项目所在城市(CITY)…

C++数据库编程简介

C数据库编程简介 C数据库编程 ODBC简介 C数据库编程 ODBC连接SQL Server数据库 C数据库编程 ODBC插入数据 C数据库编程 ODBC查询数据 C数据库编程 ODBC删除数据 C数据库编程 ODBC修改数据 C数据库编程 ODBC连接MySQL增删改查数据 C数据库编程 MySQL Connecttor C简介 …

游戏设计模式——面向数据编程(转)

作者&#xff1a;KillerAery 出处&#xff1a;http://www.cnblogs.com/KillerAery/ 随着软件需求的日益复杂发展&#xff0c;远古时期面的向过程编程思想才渐渐萌生了面向对象编程思想。 当人们发现面向对象在应对高层软件的种种好处时&#xff0c;越来越沉醉于面向对象&…

数据科学必备Python编程基础

前言 对于Python复杂的编程语言中提取了数据分析常用的数据处理以及数据可视化等数据分析师常用的内容&#xff0c;区别与其他的Python编程教程&#xff0c;如果是纯开发的小伙伴&#xff0c;看完本系列的文章仅仅只能掌握数据相关处理的内容&#xff0c;并不能完全掌握开发方…

数据结构中的C语言编程基础

​ 在学习数据结构时&#xff0c;需要我们编写许多的程序&#xff0c;对于一些变量的定义、结构体的声明、指针的使用&#xff0c;需要有一个统一的标准&#xff0c;这样才能方便我们使用、并简化记忆难度。 ​ 本文结合自身的编程经验和高分笔记中对考研数据结构编程的一些建…

面向数据编程 Data-Oriented Programming [1]

RuntimeMapMaker3D-Pro 面向数据的编程原则 0.1 简介 面向数据的编程是一种编程范式&#xff0c;旨在简化以信息为中心的软件系统的设计和实施。而不是围绕着将数据和代码结合在一起的实体&#xff08;如实例化的对象&#xff09;来设计信息系统&#xff08;例如&#xff0c;从…

ODBC API开发教程

ODBC API开发教程 作者&#xff1a;闻怡洋 未得到作者允许请勿转载 http://wyy.vchelp.net/ 目录 第 1 章 介绍... 2 第 2 章 ODBC API访问数据库... 2 2.1 ODBC简要介绍... 2 2.1.1 在没有ODBC以前... 2 2.1.2 ODBC介绍... 3 2.1.3 ODBC结构…

Microsoft Server 2008 空间存储 应用方法详解

2019独角兽企业重金招聘Python工程师标准>>> 刚接触2008的空间存储&#xff0c;资料比较少&#xff0c;在数据库操作中有很多不懂的地方&#xff0c;包括用法&#xff0c;语法等&#xff0c;经过同事的指点明白了一些&#xff0c;感谢大腿同事~这里只记录数据库操作…

ODBC 数据类型和API(VC)

目录 ODBC 数据类型API 函数 ODBC 数据类型 ODBC SQL 数据类型 展开显示 ODBC SQL 数据类型SQL类型标识SQL 92类型标识类型描述SQL_CHARCHAR(n)定长字符串,其长度为nSQL_VARCHARVARCHAR(n)变长字符串,最大长度为nSQL_LONGVARCHARLONG VARCHAR变长字符串,最大长度取决于数据源S…

Sql的decimal、float、double类型的区别

三者的区别介绍 float:浮点型&#xff0c;含字节数为4&#xff0c;32bit&#xff0c;数值范围为-3.4E38~3.4E38&#xff08;7个有效位&#xff09; double:双精度实型&#xff0c;含字节数为8&#xff0c;64bit数值范围-1.7E308~1.7E308&#xff08;15个有效位&#xff09; d…