网址;http://wiki.ros.org/tf/Tutorials#Learning_tf 许多tf教程同时适用于c++和Python。教程是流线型的,以完成c++轨道或Python轨道。如果你想学习c++和Python,你应该浏览一次c++教程和一次Python教程。注意,一般概念本身在tf包中直接解释。

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 从这个例子开始

在本教程中,我们设置了一个有许多问题的演示应用程序。本教程的目标是应用系统的方法来发现这些问题。

首先,让我们运行一个示例,看看会发生什么

  1. $ roslaunch turtle_tf start_debug_demo.launch

你会看到海龟浮出水面。如果您选择启动演示的终端窗口,则可以使用方向键来驱动其中一个机器人。在左上角有第二个机器人。


如果演示程序能够正常工作,那么第二个机器人应该跟随您可以使用方向键命令的机器人。显然,它不……因为我们必须先解决一些问题。你看到的是下面的错误信息:
  1. [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监听器

让我们首先创建源文件。转到我们在上一教程中创建的包

  1. $ roscd learning_tf

启动您喜欢的编辑器,并将以下代码粘贴到一个名为src/turtle_tf_listener_debug.cpp的新文件中。

  1. #include <ros/ros.h>
  2. #include <tf/transform_listener.h>
  3. #include <geometry_msgs/Twist.h>
  4. #include <turtlesim/Spawn.h>
  5. int main(int argc, char** argv){
  6. ros::init(argc, argv, "my_tf_listener");
  7. ros::NodeHandle node;
  8. ros::service::waitForService("spawn");
  9. ros::ServiceClient add_turtle =
  10. node.serviceClient<turtlesim::Spawn>("spawn");
  11. turtlesim::Spawn srv;
  12. add_turtle.call(srv);
  13. ros::Publisher turtle_vel =
  14. node.advertise<geometry_msgs::Twist>("turtle2/cmd_vel", 10);
  15. tf::TransformListener listener;
  16. ros::Rate rate(10.0);
  17. while (node.ok()){
  18. tf::StampedTransform transform;
  19. try{
  20. listener.lookupTransform("/turtle3", "/turtle1",
  21. ros::Time::now(), transform);
  22. }
  23. catch (tf::TransformException &ex) {
  24. ROS_ERROR("%s",ex.what());
  25. ros::Duration(1.0).sleep();
  26. continue;
  27. }
  28. geometry_msgs::Twist vel_msg;
  29. vel_msg.angular.z = 4.0 * atan2(transform.getOrigin().y(),
  30. transform.getOrigin().x());
  31. vel_msg.linear.x = 0.5 * sqrt(pow(transform.getOrigin().x(), 2) +
  32. pow(transform.getOrigin().y(), 2));
  33. turtle_vel.publish(vel_msg);
  34. rate.sleep();
  35. }
  36. return 0;
  37. };

看一下第25-28行:这里我们执行对tf的实际请求。前三个参数直接告诉我们要问tf的是什么:在time“now”时从frame /turtle1转换到frame /turtle3。

现在,让我们看看为什么这个对tf的请求会失败。

3.4 检查坐标系 首先,我们想知道tf是否知道/turtle3和/turtle1之间的转换:

  1. $ rosrun tf tf_echo turtle3 turtle1

输出告诉我们frame turtle3不存在

  1. Exception thrown:Frame id /turtle3 does not exist! When trying to transform between /turtle1 and /turtle3.
  2. The current list of frames is:
  3. Frame /turtle1 exists with parent /world.
  4. Frame /world exists with parent NO_PARENT.
  5. Frame /turtle2 exists with parent /world.

上面消息的最后三行告诉我们什么帧存在。如果您想要得到这个的图形表示,请输入

  1. $ rosrun tf view_frames
  2. $ evince frames.pdf

您将得到如下输出。

tf基本教程 - 图1
显然,问题是我们正在请求turtle3,它不存在。要修复此错误,请在第25-28行中将turtle3替换为turtle2

  1. try{
  2. listener.lookupTransform("/turtle2", "/turtle1",
  3. ros::Time::now(), transform);
  4. }

现在停止正在运行的演示(Ctrl-c),构建它,并再次运行它

  1. $ make
  2. $ roslaunch learning_tf start_debug_demo.launch

我们马上就会遇到下一个问题

  1. [ERROR] 1254264620.183142000: You requested a transform that is 0.116 miliseconds in the past,
  2. but the most recent transform in the tf buffer is 3.565 miliseconds old.
  3. When trying to transform between /turtle1 and /turtle2.

3.5 检查时间戳 既然我们已经解决了框架名称问题,就该看看时间戳了。记住,我们试图在“now”时间点获得turtle2和turtle1之间的转换。要获取关于时间的统计信息,请运行:

  1. $ rosrun tf tf_monitor turtle2 turtle1

结果应该是这样的:

  1. RESULTS: for /turtle2 to /turtle1
  2. Chain currently is: /turtle1 -> /turtle2
  3. Net delay avg = 0.008562: max = 0.05632
  4. Frames:
  5. Broadcasters:
  6. Node: /broadcaster1 40.01641 Hz, Average Delay: 0.0001178 Max Delay: 0.000528
  7. Node: /broadcaster2 40.01641 Hz, Average Delay: 0.0001258 Max Delay: 0.000309

这里的关键部分是从turtle2到turtle1的链条的延迟。输出显示平均延迟为8毫秒。这意味着tf只能在经过8毫秒后在海龟之间转换。所以,如果我们问tf关于海龟之间的转换是8毫秒前而不是现在,tf有时会给我们一个答案。让我们通过将第25-28行更改为:

  1. try{
  2. listener.lookupTransform("/turtle2", "/turtle1",
  3. ros::Time::now()-ros::Duration(0.1), transform);
  4. }

所以在新代码中,我们要求100毫秒之前的海龟之间的转换(为什么不是8毫秒?为了安全起见……)。停止演示(Ctrl-c),构建并运行

在你的catkin工作区的顶部文件夹重新构建你的包:**
**

  1. $ catkin_make

然后再次运行该示例

你终于可以看到乌龟在动了!

tf基本教程 - 图2

我们做的最后一个修正并不是你想要的,它只是为了确保这是我们的问题。真正的解决方案是这样的:

  1. try{
  2. listener.lookupTransform("/turtle2", "/turtle1",
  3. ros::Time(0), transform);
  4. }

或者是像这样:

  1. try{
  2. ros::Time now = ros::Time::now();
  3. listener.waitForTransform("/turtle2", "/turtle1",
  4. now, ros::Duration(1.0));
  5. listener.lookupTransform("/turtle2", "/turtle1",
  6. now, transform);
  7. }

4、使用tf的传感器消息

Using Stamped datatypes with tf::MessageFilter

This tutorial describes how to use tf::MessageFIlter to process Stamped datatypes.

5、和tf一起设置你的机器人

5.1 使用tf设置你的机器人Setting up your robot using tf

本教程将指导您如何设置机器人来开始使用tf。

5.2 在您自己的机器人上使用机器人状态发布器Using the robot state publisher on your own robot

本教程解释了如何使用机器人状态发布器将机器人的状态发布到tf。

5.3 使用urdf与robot_state_publisher Using urdf with robot_state_publisher

本教程给出了使用robot_state_publisher的URDF机器人模型的完整示例。首先,我们创建包含所有必要部件的URDF模型。然后我们编写一个节点来发布JointState并进行转换。最后,我们把所有零件组装在一起。

6、视频教程(PR2测试工作坊)