关于在ROS中会出现的两种ROS Time

做实验的过程中发现,ROS里面其实会计算两个ROS Time

  • 一个是仿真的时间:sim time,这个时间应该是从启动程序时才开始计算的
  • 一个是系统的时间:system time,这个应该跟系统的时间是match的

这两个时间,很多人可能没有怎么了解,应该确实很多地方也需要考虑到它,系统内部会帮忙我们处理好它们的关系。

同时,读取它们的函数都是同一个,ros::Time::now()
如果有留意的话,会发现,有些节点的更新频率是一串很长的时间数字,有些节点的更新频率却是很多的一个时间,这就说明一个节点读取的是系统时间,另一个读取的是SIM time,所以这两个节点的时间其实可以说是不一样的。
在一般应用过程中,只是通过topic发布信息,和接收信息,不考虑ros time的情况下,可以忽略上面的问题,但是有些节点要做规划的时候,需要根据ros time对应的数据来做计算,这时这个ros time如果一个是系统时间另一个是sim time就会出问题,因为系统时间是很大的一组数,它会认为sim time是属于它很久远的一个数值,然后就会报错,如1631889779.809762075是系统时间,42.56851是sim time,这种情况下,即使我们知道它们两个时间在含义上是对等的,但在数值上,这两个数就相差很远很远了。

要处理这个问题,可以看看自己是否启动了gazebo的simi time,如下:

  <include file="$(find gazebo_ros)/launch/empty_world.launch">
    <arg name="world_name" value="worlds/empty.world"/>
    <arg name="debug" value="$(arg debug)" />
    <arg name="gui" value="$(arg gui)" />
    <arg name="paused" value="false"/>
    <arg name="use_sim_time" value="true"/>
    <arg name="headless" value="$(arg headless)"/>
  </include>

里面有一个参数名叫use_sim_time,如果选true,它就会发布sim time,不使用系统时间,选false就会变为系统时间。

知道这个选项后就可以解决部分问题。

但是如果有人使用的是ROS自带的关节控制器,如下所示。

 <node name="controller_spawner" pkg="controller_manager" type="spawner" respawn="false"
	output="screen" ns="/mobile_manipulator" args="joint_state_controller
					  first_joint_position_controller second_joint_position_controller third_joint_position_controller"/> 

这时会发现,不管是关闭或选择开启gazebo的sim time,对它都是没有影响的,它一直使用SIM time,这就很恶心了,如果gazebo中选了系统时间,而这个关节控制器使用的是sim time,就会导致机器人状态发布器robot_state_publisher发布的TF关系也是sim time,这就导致TF报错,无法连接上,TF就会出错,导致机器人无法正常显示,如下图所示
关于在ROS中会出现的两种ROS Time

从图中可以看出,最顶端用的是系统时间,一串很长的数,但是下方的关节处,first link 和 second link以及arm link那里更新的时间是sim time,很小的值,这两个时间完全match不上,就会导致机器人没办法正常在rivz中显示。

所以要解决的方案是,不使用ROS自带的这个关节控制器,而是自己写一个关节状态发布器,来维护关节的状态信息。当然这个关节状态发布器也要考虑到,不能只是收到指令才发布状态,在没有收到指令时也要一直发布关节信息。

以下是一个简易版的关节状态发布器,下周再更新完整版的:

#include <string>
#include <ros/ros.h>
#include <sensor_msgs/JointState.h>
#include <tf/transform_broadcaster.h>

int main(int argc, char** argv) {
    ros::init(argc, argv, "state_publisher");
    ros::NodeHandle n;
    ros::Publisher joint_pub = n.advertise<sensor_msgs::JointState>("/mobile_manipulator/joint_states", 1);
    tf::TransformBroadcaster broadcaster;
    ros::Rate loop_rate(100);

    // message declarations
    sensor_msgs::JointState joint_state;

/**/
    double num = 1;
    while (ros::ok()) {
        //update joint_state
        num++;

        joint_state.header.stamp = ros::Time::now();
        joint_state.name.resize(3);
        joint_state.position.resize(3);
        joint_state.name[0] ="first_joint";
        joint_state.position[0] = num*0.01;
        joint_state.name[1] ="second_joint";
        joint_state.position[1] = num*0.01;
        joint_state.name[2] ="third_joint";
        joint_state.position[2] = num*0.01;
 
        joint_pub.publish(joint_state);


        // This will adjust as needed per iteration
         loop_rate.sleep();
    }

    // joint_state.header.stamp = ros::Time::now();
    // joint_state.name.resize(3);
    // joint_state.position.resize(3);
    // joint_state.name[0] ="first_joint";
    // joint_state.position[0] = 0;
    // joint_state.name[1] ="second_joint";
    // joint_state.position[1] = 0;
    // joint_state.name[2] ="third_joint";
    // joint_state.position[2] = 0;

    // joint_pub.publish(joint_state);
    // ros::spin();
 
    return 0;
}


以上都是个人的理解,如果有大神有更准确的信息,请评论留言指正。

Reference

  1. ROS学习笔记(十六):Using urdf with robot_state_publisher:
    https://blog.csdn.net/qq_42910179/article/details/107038351
上一篇:复杂逻辑控制与智能规则引擎zz


下一篇:什么是aop,由哪些部分组成