Odometry的发布和发布odom到base_link的tf变换
转载自http://www.ncnynl.com/archives/201702/1328.html
ROS发布nav_msgs/Odometry消息,以及通过tf从“odom”坐标系到“base_link”坐标系的转换。
在ROS上发布Odometry信息
导航包使用tf来确定机器人在世界中的位置,并将传感器数据与静态地图相关联。然而,tf不提供关于机器人的速度的任何信息。因此,导航包要求任何odometry源通过ROS发布包含速度信息的transform和nav_msgs/Odometry消息。
nav_msgs/Odometry消息
nav_msgs/Odometry信息存储在自由空间的机器人的位置和速度的估计:
# This represents an estimate of a position and velocity in free space.
# 这表示对自由空间中的位置和速度的估计
# The pose in this message should be specified in the coordinate frame given by header.frame_id.
# 此消息中的姿势应在由header.frame_id给定的坐标系中指定
# The twist in this message should be specified in the coordinate frame given by the child_frame_id
# 这个消息中的twist应该在由child_frame_id给出的坐标系指定
Header header
string child_frame_id
geometry_msgs/PoseWithCovariance pose
geometry_msgs/TwistWithCovariance twist
下面是更详细的消息类型
std_msgs/Header header
string child_frame_id
geometry_msgs/PoseWithCovariance pose
geometry_msgs/TwistWithCovariance twist
该消息中的姿势(pose)对应于机器人在世界坐标系中的估计位置以及该姿势估计特定的可选协方差。
该消息中的速度(twist)对应于机器人在子坐标系的速度,通常是移动基站的坐标系,以及该速度估计特定的可选协方差。
使用tf发布Odometry变换
如变换配置教程中所讨论的,“tf”软件库负责管理与变换树中的机器人相关的坐标系之间的关系。
因此,任何odometry(里程)源都必须发布其管理的坐标系的相关信息。
编写代码
我们将编写一些用于通过ROS发布 nav_msgs/Odometry消息的示例代码,以及使用tf的虚拟机器人的变换。
catkin_create_pkg Odom tf nav_msgs roscpp rospy
#include <ros/ros.h>
#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", );
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 messages
current_time = ros::Time::now(); //compute odometry in a typical way given the velocities of the robot
double 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 yaw
geometry_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 position
odom.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 velocity
odom.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;
- 解释:创建ros::Publisher和tf::TransformBroadcaster实例,以便能够分别使用ROS和tf发送消息。
- 代码:
double x = 0.0;
double y = 0.0;
double th = 0.0;
- 解释:我们假设机器人最初从“odom”坐标系的原点开始
- 代码:
double vx = 0.1;
double vy = -0.1;
double vth = 0.1;
解释:在这里,我们将设置一些速度,其将导致“base_link”坐标系相对于“odom”坐标系,在x方向上以0.1m/s,在y方向上以-0.1m/s的速率和在th方向以0.1rad/s角度移动。这让虚拟机器人走一个圆圈。
代码:
ros::Rate r(1.0);
- 解释:我们将在这个例子中以1Hz的速率发布里程计信息,以使自我检查更容易,大多数系统将希望以更高的速率发布里程消息。
- 代码:
//compute odometry in a typical way given the velocities of the robot
double 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 yaw
geometry_msgs::Quaternion odom_quat = tf::createQuaternionMsgFromYaw(th);
解释:
- 我们通常尝试使用我们系统中所有消息的3D版本,以允许2D和3D组件在适当的时候一起工作,并将我们要创建的消息数量保持在最小。
- 因此,有必要将我们用于里程的偏航值转换为四元数而发送出去。
- 幸运的是,tf提供了允许从偏航值容易地创建四元数并且容易地访问四元数的偏航值的功能。
代码:
//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";
解释:
- 这里我们将创建一个TransformStamped消息,我们将通过tf发送。
- 我们想在“current_time”发布从“odom”坐标系到“base_link”坐标系的转换。
- 因此,我们将相应地设置消息的头部和child_frame_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;
//send the transform
odom_broadcaster.sendTransform(odom_trans);
解释:这里我们从我们的odometry数据填充变换消息,然后使用我们的TransformBroadcaster发送变换。
代码:
//next, we'll publish the odometry message over ROS
nav_msgs::Odometry odom;
odom.header.stamp = current_time;
odom.header.frame_id = "odom";
解释:
- 我们还需要发布nav_msgs/Odometry消息,以便导航堆栈可以从中获取速度信息。
- 我们将消息的头部设置为current_time和“odom”坐标系。
代码:
//set the position
odom.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 velocity
odom.child_frame_id = "base_link";
odom.twist.twist.linear.x = vx;
odom.twist.twist.linear.y = vy;
odom.twist.twist.angular.z = vth;
- 解释:
- 这将使用里程数据填充消息,并通过线路发送。
- 我们将消息的child_frame_id设置为“base_link”坐标系,因为这是我们发送速度信息的坐标系。
CMakeLists.txt
add_executable(Odom_exam src/Odom_exam.cpp)
target_link_libraries(Odom_exam ${catkin_LIBRARIES})


Odometry的发布和发布odom到base_link的tf变换的更多相关文章
- IIS上发布WCF发布服务,访问不到
1 环境是IIS7,发布WCF发布服务,访问不到. 一种原因站点自动生成“程序应用池”和站点的Framwork版本不一致. 解决的办法:新建一个“程序应用池”,然后站点指向这个新建的“程序应用池”
- SQL2005 到 SQL2008R2 发布订阅----发布'xxxxx'的初始快照尚不可用。
步骤略! SQL2005 到 SQL2008R2 发布订阅----发布'xxxxx'的初始快照尚不可用. 发布库快照已经创建完成为什么到订阅就快照不可用呢! 订阅通过日志读取代理解析! 查了下代理安全 ...
- 织梦DedeCMS信息发布员发布文章阅读权限不用审核自动开放亲测试通过!
文章发布员在织梦dedecms后台添加文章时却要超级管理员审核,这无疑是增加了没必要的工作. 登录该账号发布文章你会发现该文章显示的是待审核稿件,且并没有生成静态文件,在前台是看不到这篇文章的,而多数 ...
- 织梦DedeCMS信息发布员发布文章默认自动审核更新并生成HTML页面
织梦DedeCMS信息发布员发布文章默认自动审核更新并生成HTML页面 一直以为DEDECMS的信息发布员在后台发布文章后,非要管理员审核才能显示,今天一哥们问我这个问题.问:“能不能直接发布,并自动 ...
- 蓝绿部署、金丝雀发布(灰度发布)、A/B测试
本文转载自蓝绿部署.金丝雀发布(灰度发布).A/B测试的准确定义 概述 蓝绿部署.A/B测试.金丝雀发布,以及灰度发布.流量切分等,经常被混为一谈,影响沟通效率. 根本原因是这些名词经常出现,人们耳熟 ...
- 使用vs中的发布功能发布asp.net core项目时遇到ERROR_CERTIFICATE_VALIDATION_FAILED错误
今天将VS2015编制的一个asp.net core项目发布到服务器进行测试,使用的是vs中主菜单"生成"中的"发布"功能. 遇到了一个错误,在网上反复检索尝试 ...
- SQL Server 发布订阅 发布类型详解
MicrosoftSQL Server 提供了三种复制类型. 每种复制类型都适合于不同应用程序的要求. 根据应用程序需要,可以在拓扑中使用一种或多种复制类型: 快照复制 事务复制 合并复制 为了帮助您 ...
- web.confgi转换,web发布时发布配置(debug/release)生成不同的配置文件
在web.config下有两个config文件,分比为:web.debug.config和web.replease.config文件,打开之后可以看到demo,根据demo修改后即可在发布时根据选择的 ...
- 【转】如何使用离线博客发布工具发布CSDN的博客文章
目前大部分的博客作者在用Word写博客这件事情上都会遇到以下3个痛点: 1.所有博客平台关闭了文档发布接口,用户无法使用Word,Windows Live Writer等工具来发布博客.使用Word写 ...
随机推荐
- JavaScript 与 Java、PHP 的比较
网站开发的实践从设计方面开始,包括客户端编程语言.大体上说,在网页设计中使用了三种语言:HTML,CSS和Java.自从网站发明以来,HTML和CSS已经成为网页设计的基础,但是Java被用于添加网站 ...
- MyBatis缓存结构
Mybatis Cache结构图: CacheKey(statementId, sql, sqlParams,other). 上图展示了Mybatis Cache的结构: 1)每个Mapper对应一块 ...
- c#实现QQ群成员列表导出及邮件群发开篇
主题已迁移至:http://atiblogs.com/ ITO-神奇的程序员
- 高速PCB设计注意事项
和SERDES应用相关的高速系统PCB设计注意事项如下: (1)微带(Microstrip)和带状线(Stripline)布线. 微带线是用电介质分隔的参考平面(GND或Vcc)的外层信号层上的布线, ...
- 2016女生赛 HDU 5710 Digit-Sum(数学,思维题)
Digit-Sum Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65535/32768 K (Java/Others)Total S ...
- ecshop移动端支付宝支付对接
初始页,提交基本信息到api页面, <?php /* * * 功能:支付宝手机网站支付接口接口调试入口页面 * 版本:3.4 * 修改日期:2016-03-08 * 说明: * 以下代码只是为了 ...
- MFC调试的几个技巧
TCHAR pStr[] = _T("this is a test!"); void* p = (void*)pStr; TRACE(_T("pStr is %s\n&q ...
- java 调用系统外部的某个程序
有时候我们java 调用系统外部的某个程序 可能需要调用系统外部的某个程序,此时就可以用Runtime.getRuntime().exec()来调用,他会生成一个新的进程去运行调用的程序. 此方法返回 ...
- Java之泛型
1. 概述 在引入泛型之前,Java类型分为原始类型.复杂类型,其中复杂类型分为数组和类.引入泛型后,一个复杂类型就可以在细分成更多的类型. 例如原先的类型List ...
- leetcode784
这道题经过独立思考,通过使用二进制编码的方式来进行处理.分几个步骤一层一层的处理,最终解决了,这道题感觉应该属于medimu级别. public class Solution { /// <su ...