ROS学习笔记(十一):Learning TF

0.TF初体验

http://wiki.ros.org/tf/Tutorials/Introduction%20to%20tf

1.Writing a tf broadcaster (C++)

在接下来的两个教程中,我们将编写代码来重现tf入门教程中的演示。 之后,以下教程将重点介绍使用更高级的tf功能扩展演示。
在开始之前,您需要为此项目创建一个新的ros软件包。 在沙盒文件夹中,创建一个名为learning_tf的程序包,该程序包取决于tf,roscpp,rospy和turtlesim:

 $ cd %YOUR_CATKIN_WORKSPACE_HOME%/src
 $ catkin_create_pkg learning_tf tf roscpp rospy turtlesim

roscd之前,请构建您的新软件包:

 $ cd %YOUR_CATKIN_WORKSPACE_HOME%/
 $ catkin_make
 $ source ./devel/setup.bash

2.How to broadcast transforms

本教程教您如何将坐标系广播到tf。 在这种情况下,我们要广播海龟移动时不断变化的坐标系。
首先创建源文件。 转到我们刚刚创建的包:

 $ roscd learning_tf

2.1 代码
转到src /文件夹并启动您最喜欢的编辑器,将以下代码粘贴到名为src / turtle_tf_broadcaster.cpp的新文件中。

   1 #include <ros/ros.h>
   2 #include <tf/transform_broadcaster.h>
   3 #include <turtlesim/Pose.h>
   4 
   5 std::string turtle_name;
   6 
   7 
   8 
   9 void poseCallback(const turtlesim::PoseConstPtr& msg){
  10   static tf::TransformBroadcaster br;
  11   tf::Transform transform;
  12   transform.setOrigin( tf::Vector3(msg->x, msg->y, 0.0) );
  13   tf::Quaternion q;
  14   q.setRPY(0, 0, msg->theta);
  15   transform.setRotation(q);
  16   br.sendTransform(tf::StampedTransform(transform, ros::Time::now(), "world", turtle_name));
  17 }
  18 
  19 int main(int argc, char** argv){
  20   ros::init(argc, argv, "my_tf_broadcaster");
  21   if (argc != 2){ROS_ERROR("need turtle name as argument"); return -1;};
  22   turtle_name = argv[1];
  23 
  24   ros::NodeHandle node;
  25   ros::Subscriber sub = node.subscribe(turtle_name+"/pose", 10, &poseCallback);
  26 
  27   ros::spin();
  28   return 0;
  29 };

2.2 代码解释

#include <tf/transform_broadcaster.h>

tf包提供了TransformBroadcaster的实现,以帮助简化发布转换的任务。 要使用TransformBroadcaster,我们需要包含tf / transform_broadcaster.h头文件。

 static tf::TransformBroadcaster br;

在这里,我们创建一个TransformBroadcaster对象,稍后将使用它通过电线发送转换。

tf::Transform transform;
transform.setOrigin( tf::Vector3(msg->x, msg->y, 0.0) );
tf::Quaternion q;
q.setRPY(0, 0, msg->theta);

在这里,我们创建一个Transform对象,并将信息从2D乌龟姿势复制到3D变换中。

  transform.setRotation(q);

在这里,我们设置旋转。

  br.sendTransform(tf::StampedTransform(transform, ros::Time::now(), "world", turtle_name));

这是真正的工作完成的地方。 使用TransformBroadcaster发送转换需要四个参数。
首先,我们传递转换本身。
现在,我们需要给要发布的转换添加时间戳,我们只需将其标记为当前时间ros :: Time :: now()。
然后,我们需要传递所创建链接的父框架的名称,在本例中为“ world”
最后,我们需要传递正在创建的链接的子框架的名称,在这种情况下,这就是乌龟本身的名称。

注意:sendTransform和StampedTransform的父级和子级顺序相反。

2.3Running the broadcaster
现在我们已经创建了代码,让我们首先对其进行编译。 打开CMakeLists.txt文件,并在底部添加以下行:

add_executable(turtle_tf_broadcaster src/turtle_tf_broadcaster.cpp)
target_link_libraries(turtle_tf_broadcaster ${catkin_LIBRARIES})

建立你的包; 在catkin工作区的顶部文件夹中:

 $ catkin_make

如果一切顺利,则在devel / lib / learning_tf文件夹中应该有一个名为turtle_tf_broadcaster的二进制文件。
如果是这样,我们准备为该演示创建启动文件。 使用您的文本编辑器,创建一个名为start_demo.launch的新文件,并添加以下行:

  <launch>
    <!-- Turtlesim Node-->
    <node pkg="turtlesim" type="turtlesim_node" name="sim"/>

    <node pkg="turtlesim" type="turtle_teleop_key" name="teleop" output="screen"/>
    <!-- Axes -->
    <param name="scale_linear" value="2" type="double"/>
    <param name="scale_angular" value="2" type="double"/>

    <node pkg="learning_tf" type="turtle_tf_broadcaster"
          args="/turtle1" name="turtle1_tf_broadcaster" />
    <node pkg="learning_tf" type="turtle_tf_broadcaster"
          args="/turtle2" name="turtle2_tf_broadcaster" />

  </launch>

首先,请确保您已停止上一教程中的启动文件(使用ctrl-c)。 现在,您可以开始自己的乌龟 broadcaster 演示了:

 $ roslaunch learning_tf start_demo.launch

2.4 Checking the results
现在,使用tf_echo工具检查乌龟姿势是否确实正在广播到tf:

 $ rosrun tf tf_echo /world /turtle1

这应该向您显示第一只乌龟的姿势。 使用箭头键在乌龟周围行驶(确保您的终端窗口处于活动状态,而不是模拟器窗口处于活动状态)。 如果运行tf_echo进行世界和乌龟2之间的转换,则不会看到转换,因为第二只乌龟还没有出现。 但是,一旦我们在下一个教程中添加了第二个乌龟,乌龟2的姿势将被广播到tf。

3.Writing a tf listener (C++)

在先前的教程中,我们创建了一个tf广播器,将乌龟的姿势发布到tf。 在本教程中,我们将创建一个tf侦听器以开始使用tf。
3.1如何创建一个TF监听器
首先创建源文件:

 $ roscd learning_tf

3.2代码
启动您喜欢的编辑器,然后将以下代码粘贴到名为src / turtle_tf_listener.cpp的新文件中。

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

3.3代码解释

#include <tf/transform_listener.h>

tf包提供了TransformListener的实现,以帮助简化接收转换的任务。 要使用TransformListener,我们需要包含tf / transform_listener.h头文件。

tf::TransformListener listener;

在这里,我们创建一个TransformListener对象。 创建侦听器后,它将开始通过网络接收tf转换,并将其最多缓冲10秒钟。 TransformListener对象的作用域应为持久性,否则它的缓存将无法填充,并且几乎每个查询都会失败。 一种常见的方法是使TransformListener对象成为类的成员变量。

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

在这里,实际的工作已经完成,我们向侦听器查询特定的转换。 让我们看一下四个参数:
1.我们希望从框架/ turtle1转换到框架/ turtle2。
2.我们想要转变的时间。 提供ros :: Time(0)只会为我们提供最新的可用转换。
3.我们存储结果转换的对象。
所有这些都包装在try-catch块中,以捕获可能的异常。

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));

在这里,该变换用于根据乌龟2与乌龟1的距离和角度计算新的线性和角速度。 新的速度将在主题“ turtle2 / cmd_vel”中发布,并且模拟器将使用该速度来更新turtle2的运动。
3.4Running the listener
现在我们已经创建了代码,让我们首先对其进行编译。 打开CMakeLists.txt文件,并在底部添加以下行:

add_executable(turtle_tf_listener src/turtle_tf_listener.cpp)
target_link_libraries(turtle_tf_listener ${catkin_LIBRARIES})

在catkin工作区的顶部文件夹中构建软件包:

  $ catkin_make

如果一切顺利,则在devel / lib / learning_tf文件夹中应该有一个名为turtle_tf_listener的二进制文件。

如果是这样,我们已经准备好为该演示添加启动文件。 使用文本编辑器,打开名为start_demo.launch的启动文件,并将下面的节点块合并到块内:

<launch>
    ...
    <node pkg="learning_tf" type="turtle_tf_listener"
          name="listener" />
  </launch>

首先,请确保您已停止上一教程中的启动文件(使用ctrl-c)。 现在,您可以开始完整的乌龟演示了:

 $ roslaunch learning_tf start_demo.launch

如图效果:
在这里插入图片描述

4.Adding a frame (C++)

在之前的教程中,我们通过添加tf广播器和tf监听器来重新创建了乌龟演示。 本教程将教您如何向tf树添加额外的帧。 这与创建tf广播器非常相似,并且将显示tf的某些功能。

4.1 Why adding frames
对于许多任务,更容易在本地框架内进行思考,例如 在激光扫描仪中心的框架中更容易推断出激光扫描的原因。 tf允许您为系统中的每个传感器,链接等定义本地框架。 并且,tf将处理所有引入的额外帧转换。

4.2 Where to add frames
tf建立框架的树结构; 它不允许框架结构中存在闭环。 这意味着一帧只有一个单亲,但可以有多个孩子。 当前,我们的tf树包含三个框架:world,turtle1和turtle2。 两只乌龟是世界的孩子。 如果要向tf添加新帧,则需要将三个现有帧之一作为父帧,并且新帧将成为子帧。

4.3 How to add a frame
在我们的乌龟示例中,我们将向第一只乌龟添加一个新框架。 该框架将成为第二只乌龟的“胡萝卜”。首先创建源文件。 转到我们为之前的教程创建的包:

 $ roscd learning_tf

4.3.1 代码
启动您喜欢的编辑器,然后将以下代码粘贴到名为src / frame_tf_broadcaster.cpp的新文件中。

   1 #include <ros/ros.h>
   2 #include <tf/transform_broadcaster.h>
   3 
   4 int main(int argc, char** argv){
   5   ros::init(argc, argv, "my_tf_broadcaster");
   6   ros::NodeHandle node;
   7 
   8   tf::TransformBroadcaster br;
   9   tf::Transform transform;
  10 
  11   ros::Rate rate(10.0);
  12   while (node.ok()){
  13     transform.setOrigin( tf::Vector3(0.0, 2.0, 0.0) );
  14     transform.setRotation( tf::Quaternion(0, 0, 0, 1) );
  15     br.sendTransform(tf::StampedTransform(transform, ros::Time::now(), "turtle1", "carrot1"));
  16     rate.sleep();
  17   }
  18   return 0;
  19 };

4.3.2 代码解释

transform.setOrigin( tf::Vector3(0.0, 2.0, 0.0) );
transform.setRotation( tf::Quaternion(0, 0, 0, 1) );
br.sendTransform(tf::StampedTransform(transform, ros::Time::now(), "turtle1", "carrot1"));

在这里,我们创建了一个新的转换,从父级turtle1到新子级carrot1。 carrot1框架从turtle1框架向左偏移2米。

4.4 Running the frame broadcaster
现在我们已经创建了代码,让我们首先对其进行编译。 打开CMakeLists.txt文件,并在底部添加以下行:

add_executable(frame_tf_broadcaster src/frame_tf_broadcaster.cpp)
target_link_libraries(frame_tf_broadcaster ${catkin_LIBRARIES})

在catkin工作区的顶部文件夹中构建软件包:

 $ catkin_make

如果一切顺利,则bin文件夹中应该有一个名为frame_tf_broadcaster的二进制文件。 如果是这样,我们已经准备好编辑start_demo.launch启动文件。 只需将下面的节点块合并到启动块中:

  <launch>
    ...
    <node pkg="learning_tf" type="frame_tf_broadcaster"
          name="broadcaster_frame" />
  </launch>

首先,请确保您已停止上一教程中的启动文件(使用Ctrl-c)。 现在您可以开始乌龟broadcaster演示了:

 $ roslaunch learning_tf start_demo.launch

5.Checking the results
因此,如果您开着第一只乌龟,即使我们添加了新的框架,您也会注意到其行为与上一教程并没有改变。 这是因为添加额外的帧不会影响其他帧,并且我们的侦听器仍在使用先前定义的帧。 因此,让我们更改侦听器的行为。打开src / turtle_tf_listener.cpp文件,并在第26-27行中将“ / turtle1”替换为“ / carrot1”:

listener.lookupTransform("/turtle2", "/carrot1",
                       ros::Time(0), transform);

现在好了:重新构建并重新启动乌龟演示,您将看到carrot1后面的第二只乌龟而不是第一只乌龟! 请记住,carrot1在turtle1的左侧2米处。 胡萝卜没有视觉上的表现,但是您应该看到第二只乌龟正在移动到该点。

 $ catkin_make
 $ roslaunch learning_tf start_demo.launch

6.Broadcasting a moving frame
我们在本教程中发布的额外框架是固定框架,相对于父框架,框架不会随时间变化。 但是,如果要发布移动框架,则可以将广播者更改为随时间变化。 让我们修改/ carrot1框架,使其相对于/ turtle1随时间变化。

transform.setOrigin( tf::Vector3(2.0*sin(ros::Time::now().toSec()), 2.0*cos(ros::Time::now().toSec()), 0.0) );
transform.setRotation( tf::Quaternion(0, 0, 0, 1) );

现在好了:重建并重新启动乌龟演示,然后您将看到第二只乌龟紧随carrot1移动。

 $ catkin_make
 $ roslaunch learning_tf start_demo.launch

5.Learning about tf and time (C++)

5.1 tf and Time
在先前的教程中,我们了解了tf如何跟踪坐标系树。 该树随时间变化,并且tf为每个转换存储时间快照(默认情况下最多10秒)。 到目前为止,我们一直使用lookupTransform()函数来访问该tf树中的最新可用转换,而无需知道该转换在何时记录。 本教程将教您如何在特定时间进行转换。

  $ roscd learning_tf

并打开文件src / turtle_tf_listener.cpp。 看一下第25-27行:

try{
      listener.lookupTransform("/turtle2", "/carrot1",  
                               ros::Time(0), transform);

让我们让turtle2跟随turtle1,而不是carrot1。 将您的代码更改为以下内容:

   try{
      listener.lookupTransform("/turtle2", "/turtle1",  
                               ros::Time(0), transform);

您还可以看到我们指定的时间等于0。对于tf,时间0表示缓冲区中的“最新可用”转换。 现在,更改此行以获取当前时间“ now()”的转换:

  try{
    listener.lookupTransform("/turtle2", "/turtle1",  
                             ros::Time::now(), transform);

首先,请确保您已停止上一教程中的启动文件(使用ctrl-c)。 编译代码,然后再次运行:

  $ catkin_make
 $ roslaunch learning_tf start_demo.launch

所有突然的lookupTransform()都失败了,反复告诉您:

[ERROR] [1593244295.486927555]: "turtle2" passed to lookupTransform argument target_frame does not exist. 
[ERROR] [1593244296.487242916]: Lookup would require extrapolation into the future.  Requested time 1593244296.487054225 but the latest data is at time 1593244296.485805631, when looking up transform from frame [turtle1] to frame [turtle2]
[ERROR] [1593244297.487563375]: Lookup would require extrapolation into the future.  Requested time 1593244297.487426933 but the latest data is at time 1593244297.478300250, when looking up transform from frame [turtle1] to frame [turtle2]
[ERROR] [1593244298.487860078]: Lookup would require extrapolation into the future.  Requested time 1593244298.487720426 but the latest data is at time 1593244298.485835624, when looking up transform from frame [turtle1] to frame [turtle2]
[ERROR] [1593244299.488147246]: Lookup would require extrapolation into the future.  Requested time 1593244299.488011257 but the latest data is at time 1593244299.477407355, when looking up transform from frame [turtle1] to frame [turtle2]
[ERROR] [1593244300.488496293]: Lookup would require extrapolation into the future.  Requested time 1593244300.488357332 but the latest data is at time 1593244300.485526880, when looking up transform from frame [turtle1] to frame [turtle2]
[ERROR] [1593244301.488840603]: Lookup would require extrapolation into the future.  Requested time 1593244301.488703340 but the latest data is at time 1593244301.477613318, when looking up transform from frame [turtle1] to frame [turtle2]
[ERROR] [1593244302.489149021]: Lookup would require extrapolation into the future.  Requested time 1593244302.489012258 but the latest data is at time 1593244302.485458383, when looking up transform from frame [turtle1] to frame [turtle2]
[ERROR] [1593244303.489441347]: Lookup would require extrapolation into the future.  Requested time 1593244303.489303431 but the latest data is at time 1593244303.477806639, when looking up transform from frame [turtle1] to frame [turtle2]
[ERROR] [1593244304.489796913]: Lookup would require extrapolation into the future.  Requested time 1593244304.489661855 but the latest data is at time 1593244304.486178673, when looking up transform from frame [turtle1] to frame [turtle2]
[ERROR] [1593244305.490139069]: Lookup would require extrapolation into the future.  Requested time 1593244305.490001497 but the latest data is at time 1593244305.477462519, when looking up transform from frame [turtle1] to frame [turtle2]
[ERROR] [1593244306.490485060]: Lookup would require extrapolation into the future.  Requested time 1593244306.490348978 but the latest data is at time 1593244306.485902964, when looking up transform from frame [turtle1] to frame [turtle2]
[ERROR] [1593244307.490828487]: Lookup would require extrapolation into the future.  Requested time 1593244307.490691740 but the latest data is at time 1593244307.478282159, when looking up transform from frame [turtle1] to frame [turtle2]
[ERROR] [1593244308.491162073]: Lookup would require extrapolation into the future.  Requested time 1593244308.491025430 but the latest data is at time 1593244308.485529031, when looking up transform from frame [turtle1] to frame [turtle2]
[ERROR] [1593244309.491495458]: Lookup would require extrapolation into the future.  Requested time 1593244309.491358303 but the latest data is at time 1593244309.477552194, when looking up transform from frame [turtle1] to frame [turtle2]
[ERROR] [1593244310.491841852]: Lookup would require extrapolation into the future.  Requested time 1593244310.491705238 but the latest data is at time 1593244310.485717984, when looking up transform from frame [turtle1] to frame [turtle2]
[ERROR] [1593244311.492188325]: Lookup would require extrapolation into the future.  Requested time 1593244311.492052189 but the latest data is at time 1593244311.477919606, when looking up transform from frame [turtle1] to frame [turtle2]
[ERROR] [1593244312.492513610]: Lookup would require extrapolation into the future.  Requested time 1593244312.492388180 but the latest data is at time 1593244312.486240605, when looking up transform from frame [turtle1] to frame [turtle2]
[ERROR] [1593244313.492850102]: Lookup would require extrapolation into the future.  Requested time 1593244313.492712866 but the latest data is at time 1593244313.477790258, when looking up transform from frame [turtle1] to frame [turtle2]
[ERROR] [1593244314.493177217]: Lookup would require extrapolation into the future.  Requested time 1593244314.493035425 but the latest data is at time 1593244314.486245855, when looking up transform from frame [turtle1] to frame [turtle2]
[ERROR] [1593244315.493459044]: Lookup would require extrapolation into the future.  Requested time 1593244315.493327800 but the latest data is at time 1593244315.478292697, when looking up transform from frame [turtle1] to frame [turtle2]
[ERROR] [1593244316.493780943]: Lookup would require extrapolation into the future.  Requested time 1593244316.493655831 but the latest data is at time 1593244316.485756488, when looking up transform from frame [turtle1] to frame [turtle2]
[broadcaster_frame-7] killing on exit

这是为什么?每个侦听器都有一个缓冲区,在其中存储来自不同tf广播器的所有坐标转换。 当广播公司发出转换时,该转换进入缓冲区要花费一些时间(通常为几毫秒)。 因此,当您在“现在”时间请求帧转换时,您应该等待几毫秒,以便该信息到达。

5.2Wait for transforms
tf提供了一个不错的工具,它将等到转换可用为止。 让我们看看代码是什么样的:

  try{
    ros::Time now = ros::Time::now();
    listener.waitForTransform("/turtle2", "/turtle1",
                              now, ros::Duration(3.0));
    listener.lookupTransform("/turtle2", "/turtle1",
                             now, transform);

waitForTransform()接受四个参数:
1.等待此帧的转换…
2. …到这个框架,
3. 在这个时候
4.超时:请勿等待超过此最大持续时间

注意:本示例使用ros :: Time :: now()。 通常,这将是希望转换的数据的时间戳。

因此,waitForTransform()实际上会阻塞,直到两个乌龟之间的转换可用(这通常需要几毫秒),或者-如果转换不可用-直到达到超时为止。首先,请确保您已停止上一教程中的启动文件(使用ctrl-c)。 现在编译网络代码,然后再次运行:

  $ catkin_make

  $ roslaunch learning_tf start_demo.launch

您可能仍然会看到一次错误(错误消息可能有所不同):

[ERROR] [1593245004.532610719]: Lookup would require extrapolation into the past. 
 Requested time 1593245001.519151647 but the earliest data is at time 1593245002.121131312, 
 when looking up transform from frame [turtle1] to frame [turtle2]

发生这种情况是因为turtle2花费了一个非零的时间来生成并开始发布tf帧。 因此,您现在第一次请求/ turtle2帧可能不存在,当请求转换时,转换可能还不存在,并且第一次失败。 在第一个变换之后,所有变换都存在,并且乌龟的行为符合预期。

在这里插入图片描述
5.3Checking the results
现在,您应该再次能够使用箭头键绕过第一只乌龟(确保您的终端窗口处于活动状态,而不是模拟器窗口),然后您将看到第二只乌龟在第二只乌龟之后!

因此,您注意到乌龟的行为没有明显的不同。 那是因为实际的时间差只有几毫秒。 但是,为什么我们要从Time(0)更改为now()? 只是要教您有关tf缓冲区及其相关的时间延迟。 对于实际的tf用例,通常最好使用Time(0)。

6.Time travel with tf (C++)

6.1Time travel
因此,让我们回到上一教程的结尾。 转到您的软件包以获取本教程:

  $ roscd learning_tf

并打开文件src / turtle_tf_listener.cpp。 看一下25-30行:

  try{
    ros::Time now = ros::Time::now();
    listener.waitForTransform("/turtle2", "/turtle1",
                              now, ros::Duration(1.0));
    listener.lookupTransform("/turtle2", "/turtle1",
                             now, transform);

现在,不要让第二只乌龟移到第一只乌龟所在的位置,而是让第二只乌龟移到5秒钟前第一只乌龟所在的位置:

  try{
    ros::Time past = ros::Time::now() - ros::Duration(5.0);
    listener.waitForTransform("/turtle2", "/turtle1",
                              past, ros::Duration(1.0));
    listener.lookupTransform("/turtle2", "/turtle1",
                             past, transform);

所以现在,如果运行此命令,您希望看到什么? 肯定在最初的5秒钟内,第二只乌龟不知道要去哪里,因为我们还没有第一只乌龟5秒钟的历史。 但是这5秒钟后会怎样? 让我们尝试一下:

  $ make  or  catkin_make
  $ roslaunch learning_tf start_demo.launch

在这里插入图片描述您的乌龟是否像这张屏幕截图一样不受控制地行驶? 那么发生了什么事?

6.2 Advanced API for lookupTransform
那么我们怎样才能问这样的问题呢? 该API使我们能够明确地说出每帧转换的时间。 代码如下所示:

  try{
    ros::Time now = ros::Time::now();
    ros::Time past = now - ros::Duration(5.0);
    listener.waitForTransform("/turtle2", now,
                              "/turtle1", past,
                              "/world", ros::Duration(1.0));
    listener.lookupTransform("/turtle2", now,
                             "/turtle1", past,
                             "/world", transform);

lookupTransform()的高级API带有六个参数:

1.从此帧进行转换,
2.此时 …
3…到这个框架,
4. 此时。
5. 指定不随时间变化的框架,在这种情况下为“ / world”框架,并
6. 用于存储结果的变量。

请注意,就像lookupTransform()一样,waitForTransform()也具有基本和高级API。
在这里插入图片描述该图显示了tf在后台执行的操作。 过去,它计算从第一个乌龟到世界的转变。 在世界范围内,时间从过去到现在。 现在,tf会计算从世界到第二只乌龟的转换。

6.3Checking the results

  $ make  or catkin_make
  $ roslaunch learning_tf start_demo.launch

猜你喜欢

转载自blog.csdn.net/qq_42910179/article/details/106972236