ROS探索总结(二十一)——如何发布里程计消息

  • 内容
  • 评论
  • 相关

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

1、nav_msgs/Odometry消息格式

       nav_msgs/Odometry消息包含有机器人在自由空间中的位置和速度估算值。

  1. # This represents an estimate of a position and velocity in free space.
  2. # The pose in this message should be specified in the coordinate frame given by header.frame_id.
  3. # The twist in this message should be specified in the coordinate frame given by the child_frame_id
  4. Header header
  5. string child_frame_id
  6. geometry_msgs/PoseWithCovariance pose
  7. geometry_msgs/TwistWithCovariance twist

       pose参数包含机器人在里程计参考系下的位置估算值,同时带有可选的估算协方差。twist参数包含机器人在子参考系(一般是机器人基础参考系)下的速度,同时带有可选的速度估算协方差。

2、使用tf发布里程计变换信息

        参见 Transform Configuration教程。

3、代码例程

        这里使用一个简单的例程,实现 nav_msgs/Odometry消息的发布和tf变换,通过伪造的数据,实现机器人圆周运动。

        首先需要将一些以来添加到manifest.xml中:

  1. <depend package="tf"/>
  2. <depend package="nav_msgs"/>

        实现代码如下:

  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();               // check for incoming messages
  28.     current_time = ros::Time::now();
  29.  
  30.     //compute odometry in a typical way given the velocities of the robot
  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.     //since all odometry is 6DOF we'll need a quaternion created from yaw
  41.     geometry_msgs::Quaternion odom_quat = tf::createQuaternionMsgFromYaw(th);
  42.  
  43.     //first, we'll publish the transform over 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.     //send the transform
  55.     odom_broadcaster.sendTransform(odom_trans);
  56.  
  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";
  61.  
  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;
  73.  
  74.     //publish the message
  75.     odom_pub.publish(odom);
  76.  
  77.     last_time = current_time;
  78.     r.sleep();
  79.   }
  80. }

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

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

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

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

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

  1.   double x = 0.0;
  2.   double y = 0.0;
  3.   double th = 0.0;

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

  1.   double vx = 0.1;
  2.   double vy = -0.1;
  3.   double vth = 0.1;

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

  1.   ros::Rate r(1.0);

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

  1.     //compute odometry in a typical way given the velocities of the robot
  2.     double dt = (current_time - last_time).toSec();
  3.     double delta_x = (vx * cos(th) - vy * sin(th)) * dt;
  4.     double delta_y = (vx * sin(th) + vy * cos(th)) * dt;
  5.     double delta_th = vth * dt;
  6.  
  7.     x += delta_x;
  8.     y += delta_y;
  9.     th += delta_th;

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

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

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

  1.     //first, we'll publish the transform over tf
  2.     geometry_msgs::TransformStamped odom_trans;
  3.     odom_trans.header.stamp = current_time;
  4.     odom_trans.header.frame_id = "odom";
  5.     odom_trans.child_frame_id = "base_link";

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

  1.     odom_trans.transform.translation.x = x;
  2.     odom_trans.transform.translation.y = y;
  3.     odom_trans.transform.translation.z = 0.0;
  4.     odom_trans.transform.rotation = odom_quat;

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

  1.     //next, we'll publish the odometry message over ROS
  2.     nav_msgs::Odometry odom;
  3.     odom.header.stamp = current_time;

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

  1.     //set the position
  2.     odom.pose.pose.position.x = x;
  3.     odom.pose.pose.position.y = y;
  4.     odom.pose.pose.position.z = 0.0;
  5.     odom.pose.pose.orientation = odom_quat;
  6.  
  7.     //set the velocity
  8.     odom.child_frame_id = "base_link";
  9.     odom.twist.twist.linear.x = vx;
  10.     odom.twist.twist.linear.y = vy;
  11.     odom.twist.twist.angular.z = vth;

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

 

        参考链接:http://wiki.ros.org/navigation/Tutorials/RobotSetup/Odom


原创文章,转载请注明: 转载自古月居

本文链接地址: ROS探索总结(二十一)——如何发布里程计消息

微信 OR 支付宝 扫描二维码
为本文作者 打个赏
pay_weixinpay_weixin

评论

4条评论
  1. Gravatar 头像

    acyloin 回复

    您好,我是最近接触tf与导航系统的。我不太清楚这里的协方差是用来表示什么的?是谁与谁的协方差?希望能回答,谢谢。

    • 古月

      古月 回复

      @acyloin 你好,这里协方差的概念目前我也不是很理解,涉及到位置估计方面的算法,我还没有研究过这个方面。

      • Gravatar 头像

        蒋卓 回复

        @古月 您好,我想问下tf的根坐标系是怎么建立的,也就是第一个坐标系是怎么确定的?

发表评论

电子邮件地址不会被公开。 必填项已用*标注