一、Time 与 Duration
roscpp里有两种时间的表示方法,一种是时刻(ros::Time),一种是时长(ros::Duration)。
基本使用
ros::Time begin = ros::Time::now(); //获取当前时间
ros::Time at_some_time1(5,20000000); //5.2s
ros::Time at_some_time2(5.2); //同上,重载了float类型和两个uint类型的构造函数
ros::Duration one_hour(60*60,0); //1h
double secs1 = at_some_time1.toSec();//将Time转为double型时间
double secs2 = one_hour.toSec();//将Duration转为double型时间
时间的加减
ros::Time t1 = ros::Time::now() - ros::Duration(5.5); //t1是5.5s前的时刻,Time加减Duration返回都是Time
ros::Time t2 = ros::Time::now() + ros::Duration(3.3);//t2是当前时刻往后推3.3s的时刻
ros::Duration d1 = t2 - t1;//从t1到t2的时长,两个Time相减返回Duration类型
ros::Duration d2 = d1 -ros::Duration(0,300);//两个Duration相减,还是Duration
sleep相关
ros::Duration(0.5).sleep(); //用Duration对象的sleep方法休眠
ros::Rate r(10); //10HZ
while(ros::ok())
{
r.sleep();
//定义好sleep的频率,Rate对象会自动让整个循环以10hz休眠,即使有任务执行占用了时间
}
定时器
ros::Timer timer1 = n.createTimer(ros::Duration(0.1), callback1); //timer1每0.1s触发一次callback1函数
ros::Timer timer2 = n.createTimer(ros::Duration(1.0), callback2); //timer2每1.0s触发一次callback2函数
ros::spin(); //千万别忘了spin,只有spin了才能真正去触发回调函数
二、TF中的时间
关于TF的详解:可以参考我之前的教程:
本部分基于上文中实现的小乌龟跟随程序,继续做相关实验。
1. TF2中的time(0)
time1 = ros::Time(0);
time2 = ros::Time::now();
注意:
For tf2, time 0 means “the latest available” transform in the buffer.
所以,如果我们将下方代码中的ros::Time(0)
更改为ros::Time::now()
try{
transformStamped = tfBuffer.lookupTransform("turtle2", "turtle1", ros::Time(0));
} catch (tf2::TransformException &ex) {
ROS_WARN("Could NOT transform turtle2 to turtle1: %s", ex.what());
}
会出现如下报错:
[ WARN] [1610786104.938774121]: wait tf tree: Lookup would require extrapolation into the future. Requested time 1610786104.938642617 but the latest data is at time 1610786104.933745945, when looking up transform from frame [turtle1] to frame [turtle2]
大意就是,我们在试图获取未来的tf变换。
2. wait for transforms
上述出现报错,原因很简单,tf_listener
有个buffer来存储所有坐标变换。tf_tree
是由很多的broadcaster
来维护的,每个broadcaster
进入buffer的时候都要花费个几ms,所以如果请求now当前时刻的tf变换的话,我们应该等个几个ms再来获取信息。
由此,引出参数wait_for_transform
try{
transformStamped = tfBuffer.lookupTransform("turtle2", "turtle1", ros::Time::now(),
ros::Duration(3.0));
} catch (tf2::TransformException &ex) {
ROS_WARN("Could NOT transform turtle2 to turtle1: %s", ex.what());
}
在lookupTransform()
最后加入参数wait_for_transform
之后, 有两种结果:
- 在等待时间内,要查询的两个/frame搭上线,退出等待。
- 两个/frame没有连接上,就等到timeout结束
3. 带时间间隔的TF变换
本部分想要实现,second_turtle 跟着 first_turtle的5s前的状态来行动。
很直观的想法:
try{
ros::Time past = ros::Time::now() - ros::Duration(5.0);
transformStamped = tfBuffer.lookupTransform("turtle2", "turtle1",
past, ros::Duration(1.0));
如上所示,我们将now减去5s
查看结果:
乌龟就像喝醉了酒一样,完全没有follow第一只乌龟。
究其原因,在求now的turtle1
to turtle2
时候,我们却使用 turtle1(5s前)
to world
乘 world
toturtle2(5s前)
来求解。
这导致turtle2
一直被自己5s前的状态所牵扯。
正确的做法应该是:
now的turtle1
to turtle2
= turtle1(5s前)
to world
* world
to turtle2(now)
为了实现上述目标,tf2为我们提供如下接口:
try{
ros::Time now = ros::Time::now();
ros::Time past = now - ros::Duration(5.0);
transformStamped = tfBuffer.lookupTransform("turtle2", now,
"turtle1", past,
"world", ros::Duration(1.0));
其中6个参数的lookupTransform()
版本参数含义:
- target frame,
- at this time …
- source frame,
- at past time.
- Specify the frame that does not change over time, in this case the “/world” frame
- the time-out
查看实验结果:
没有问题!!