- 1、工作空间设置
- 2、学习TF
- C++版本:
- Writing a tf broadcaster (C++)">编写tf广播器(c++)Writing a tf broadcaster (C++)
- Writing a tf listener (C++)">编写tf监听器(c++)Writing a tf listener (C++)
- Adding a frame (C++)">添加框架(c++)Adding a frame (C++)
- Learning about tf and time (C++)">学习tf和时间(c++)Learning about tf and time (C++)
- Time travel with tf (C++)">使用tf (c++)进行时间旅行Time travel with tf (C++)
- Python版本:
- Writing a tf broadcaster (Python)">编写tf广播器(Python)Writing a tf broadcaster (Python)
- Writing a tf listener (Python)">编写tf监听器(Python)Writing a tf listener (Python)
- Adding a frame (Python)">添加框架(Python)Adding a frame (Python)
- Learning about tf and time (Python)">学习tf和time (Python)Learning about tf and time (Python)
- 使用tf (Python)进行时间旅行
- C++版本:
- 3、调试TF
- 4、使用tf的传感器消息
- 5、和tf一起设置你的机器人
- Setting up your robot using tf">5.1 使用tf设置你的机器人Setting up your robot using tf
- Using the robot state publisher on your own robot">5.2 在您自己的机器人上使用机器人状态发布器Using the robot state publisher on your own robot
- Using urdf with robot_state_publisher">5.3 使用urdf与robot_state_publisher Using urdf with robot_state_publisher
- 6、视频教程(PR2测试工作坊)
Tf已被弃用,取而代之的是tf2。Tf2提供了tf功能的超集,实际上现在是底层的实现。如果你现在正在学习,强烈建议你使用tf2/教程。
1、工作空间设置
如果您还没有创建一个工作区来完成教程,请单击这里获取一些简短的说明。click here for some brief instructions.
2、学习TF
C++版本:
编写tf广播器(c++)Writing a tf broadcaster (C++)
本教程教你如何向tf广播机器人的坐标系。编写tf监听器(c++)Writing a tf listener (C++)
本教程教你如何使用tf来访问帧转换。添加框架(c++)Adding a frame (C++)
本教程教你如何添加一个额外的固定帧到tf。学习tf和时间(c++)Learning about tf and time (C++)
本教程教你使用waitForTransform函数来等待tf树中的转换可用。使用tf (c++)进行时间旅行Time travel with tf (C++)
本教程教你关于tf的高级时间旅行特性Python版本:
编写tf广播器(Python)Writing a tf broadcaster (Python)
本教程教你如何向tf广播机器人的状态。编写tf监听器(Python)Writing a tf listener (Python)
本教程教你如何使用tf来访问帧转换。添加框架(Python)Adding a frame (Python)
本教程教你如何添加一个额外的固定帧到tf。学习tf和time (Python)Learning about tf and time (Python)
本教程教你使用waitForTransform函数来等待tf树中的转换可用。使用tf (Python)进行时间旅行
本教程教你关于tf的高级时间旅行特性3、调试TF
网址:http://wiki.ros.org/tf/Tutorials/Debugging%20tf%20problems
注意:本教程假设你已经完成了中级教程。本教程为调试tf相关问题提供了一种系统的方法。
教程级别:高级
本教程将带领您完成调试一个典型tf问题的步骤。它将对使用turtlesim的示例应用tf故障排除指南中解释的步骤。它还将使用许多tf调试工具。
3.1 从这个例子开始
在本教程中,我们设置了一个有许多问题的演示应用程序。本教程的目标是应用系统的方法来发现这些问题。首先,让我们运行一个示例,看看会发生什么
$ roslaunch turtle_tf start_debug_demo.launch
你会看到海龟浮出水面。如果您选择启动演示的终端窗口,则可以使用方向键来驱动其中一个机器人。在左上角有第二个机器人。
如果演示程序能够正常工作,那么第二个机器人应该跟随您可以使用方向键命令的机器人。显然,它不……因为我们必须先解决一些问题。你看到的是下面的错误信息:
[ERROR] 1254263539.785016000: Frame id /turtle3 does not exist! When trying to transform between /turtle1 and /turtle3.
3.2 查找tf请求
如果你看一下调试tf问题指南,你会发现我们首先需要弄清楚我们到底要求tf做什么。因此,我们进入使用tf的代码部分。
在之前的教程中,我们创建了一个tf广播器来发布海龟的姿势到tf。在本教程中,我们将创建一个tf监听器来开始使用tf。
3.3 如何创建一个tf监听器
让我们首先创建源文件。转到我们在上一教程中创建的包
$ roscd learning_tf
启动您喜欢的编辑器,并将以下代码粘贴到一个名为src/turtle_tf_listener_debug.cpp的新文件中。
#include <ros/ros.h>
#include <tf/transform_listener.h>
#include <geometry_msgs/Twist.h>
#include <turtlesim/Spawn.h>
int main(int argc, char** argv){
ros::init(argc, argv, "my_tf_listener");
ros::NodeHandle node;
ros::service::waitForService("spawn");
ros::ServiceClient add_turtle =
node.serviceClient<turtlesim::Spawn>("spawn");
turtlesim::Spawn srv;
add_turtle.call(srv);
ros::Publisher turtle_vel =
node.advertise<geometry_msgs::Twist>("turtle2/cmd_vel", 10);
tf::TransformListener listener;
ros::Rate rate(10.0);
while (node.ok()){
tf::StampedTransform transform;
try{
listener.lookupTransform("/turtle3", "/turtle1",
ros::Time::now(), transform);
}
catch (tf::TransformException &ex) {
ROS_ERROR("%s",ex.what());
ros::Duration(1.0).sleep();
continue;
}
geometry_msgs::Twist vel_msg;
vel_msg.angular.z = 4.0 * atan2(transform.getOrigin().y(),
transform.getOrigin().x());
vel_msg.linear.x = 0.5 * sqrt(pow(transform.getOrigin().x(), 2) +
pow(transform.getOrigin().y(), 2));
turtle_vel.publish(vel_msg);
rate.sleep();
}
return 0;
};
看一下第25-28行:这里我们执行对tf的实际请求。前三个参数直接告诉我们要问tf的是什么:在time“now”时从frame /turtle1转换到frame /turtle3。
现在,让我们看看为什么这个对tf的请求会失败。
3.4 检查坐标系 首先,我们想知道tf是否知道/turtle3和/turtle1之间的转换:
$ rosrun tf tf_echo turtle3 turtle1
输出告诉我们frame turtle3不存在
Exception thrown:Frame id /turtle3 does not exist! When trying to transform between /turtle1 and /turtle3.
The current list of frames is:
Frame /turtle1 exists with parent /world.
Frame /world exists with parent NO_PARENT.
Frame /turtle2 exists with parent /world.
上面消息的最后三行告诉我们什么帧存在。如果您想要得到这个的图形表示,请输入
$ rosrun tf view_frames
$ evince frames.pdf
您将得到如下输出。
显然,问题是我们正在请求turtle3,它不存在。要修复此错误,请在第25-28行中将turtle3替换为turtle2
try{
listener.lookupTransform("/turtle2", "/turtle1",
ros::Time::now(), transform);
}
现在停止正在运行的演示(Ctrl-c),构建它,并再次运行它
$ make
$ roslaunch learning_tf start_debug_demo.launch
我们马上就会遇到下一个问题
[ERROR] 1254264620.183142000: You requested a transform that is 0.116 miliseconds in the past,
but the most recent transform in the tf buffer is 3.565 miliseconds old.
When trying to transform between /turtle1 and /turtle2.
3.5 检查时间戳 既然我们已经解决了框架名称问题,就该看看时间戳了。记住,我们试图在“now”时间点获得turtle2和turtle1之间的转换。要获取关于时间的统计信息,请运行:
$ rosrun tf tf_monitor turtle2 turtle1
结果应该是这样的:
RESULTS: for /turtle2 to /turtle1
Chain currently is: /turtle1 -> /turtle2
Net delay avg = 0.008562: max = 0.05632
Frames:
Broadcasters:
Node: /broadcaster1 40.01641 Hz, Average Delay: 0.0001178 Max Delay: 0.000528
Node: /broadcaster2 40.01641 Hz, Average Delay: 0.0001258 Max Delay: 0.000309
这里的关键部分是从turtle2到turtle1的链条的延迟。输出显示平均延迟为8毫秒。这意味着tf只能在经过8毫秒后在海龟之间转换。所以,如果我们问tf关于海龟之间的转换是8毫秒前而不是现在,tf有时会给我们一个答案。让我们通过将第25-28行更改为:
try{
listener.lookupTransform("/turtle2", "/turtle1",
ros::Time::now()-ros::Duration(0.1), transform);
}
所以在新代码中,我们要求100毫秒之前的海龟之间的转换(为什么不是8毫秒?为了安全起见……)。停止演示(Ctrl-c),构建并运行
在你的catkin工作区的顶部文件夹重新构建你的包:**
**
$ catkin_make
然后再次运行该示例
你终于可以看到乌龟在动了!
我们做的最后一个修正并不是你想要的,它只是为了确保这是我们的问题。真正的解决方案是这样的:
try{
listener.lookupTransform("/turtle2", "/turtle1",
ros::Time(0), transform);
}
或者是像这样:
try{
ros::Time now = ros::Time::now();
listener.waitForTransform("/turtle2", "/turtle1",
now, ros::Duration(1.0));
listener.lookupTransform("/turtle2", "/turtle1",
now, transform);
}
4、使用tf的传感器消息
Using Stamped datatypes with tf::MessageFilter
This tutorial describes how to use tf::MessageFIlter to process Stamped datatypes.