tf2 简介
tf2是转换库的第二代,可让用户随时间跟踪多个坐标系。tf2保持时间缓冲的树结构中的坐标系之间的关系,并允许用户在任意所需的时间点在任意两个坐标系之间转换点,向量等,任何用户基本上都会使用tf2进行两项任务,即监听转换和广播转换。

编写tf2广播器
广播转换:将坐标系的相对姿势发送到系统的其余部分。一个系统可以有许多广播公司,每个广播公司都提供有关机器人不同部分的信息
- 创建一个learning_tf2软件包 
 首先,我们将创建一个catkin包,该包将用于本教程和后续教程。这个名为learning_tf2的软件包将取决于 tf2,tf2_ros,roscpp,rospy和turtlesim- catkin_create_pkg learning_tf2 tf tf2_ros roscpp rospy turtlesim 
 roscd learning_tf2 t
- 将乌龟姿势发布到tf2的相关代码 
 创建一个名为src /的文件夹,并启动您喜欢的编辑器,将以下代码粘贴到一个名为src / turtle_tf2_broadcaster.cpp的新文件中- 1 
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 //tf2软件包提供了TransformBroadcaster的实现,以帮助简化发布转换的任务
 std::string turtle_name;
 void poseCallbacks(const turtlesim::PoseConstPtr &msg)
 {
 //在这里,我们创建一个TransformBroadcaster对象br,稍后将使用它通过网络发送转换
 static tf2_ros::TransformBroadcaster br;
 //在这里,我们创建一个Transform对象,并稍后为其提供适当的元数据
 geometry_msgs::TransformStamped transformStamped;
 //我们需要给要发布的转换加上时间戳,我们只需将其标记为当前时间ros::Time::now()即可
 transformStamped.header.stamp = ros::Time::now();
 //然后,我们需要设置要创建的链接的父框架的名称,在本例中为“ world”
 transformStamped.header.frame_id = "world";
 //最后,我们需要设置要创建的链接的子节点的名称,在这种情况下,这就是乌龟本身的名称
 transformStamped.child_frame_id = turtle_name;
 //在这里,我们将信息从3D乌龟姿势复制到3D变换中
 transformStamped.transform.translation.x = msg->x;
 transformStamped.transform.translation.y = msg->y;
 transformStamped.transform.translation.z = 0.0;
 tf2::Quaternion q;
 q.setRPY(0, 0, msg->theta);
 transformStamped.transform.rotation.x = q.x();
 transformStamped.transform.rotation.y = q.y();
 transformStamped.transform.rotation.z = q.z();
 transformStamped.transform.rotation.w = q.w();
 //这是完成实际工作的地方。使用TransformBroadcaster发送转换仅需要传递转换本身
 //sendTransform和StampedTransform的父级和子级顺序相反
 br.sendTrannsform(transformStamped);
 }
 int main(int argc, char **argv)
 {
 ros::intt(argc, argv, "my_tf2_broadcaster");
 ros::NodeHandle private_node("~");
 if (!private_node.hasParam("turtle"))
 {
 if (argc != 2)
 {
 ROS_ERROR("need turtle name as argument");
 return -1;
 };
 turtle_name = argv[1];
 }
 else
 {
 private_node.getParam("turtle", turtle_name);
 }
 ros::NodeHandle node;
 ros::Subscriber sub = node.subscribe(turtle_name + "/pose", 10, $poseCallbacks);
 ros::spin();
 return 0;
 }
- 修改CMakeList.txt文件 
 打开CMakeLists.txt文件,并添加以下内容:- 1 
 2
 3
 4- add_executable(turtle_tf2_broadcaster src / turtle_tf2_broadcaster.cpp) 
 target_link_libraries(turtle_tf2_broadcaster
 $ {catkin_LIBRARIES}
 )
- 编译 - catkin_make 
成功后可以在lib 文件中获得名为 turtle_tf2_broadcaster 的二进制文件
- 创建启动文件start_demo.launch
| 1 | <启动> | 
- 修改CMakeList.txt文件 
 在文件中的install 区域内将启动文件标记为安装文件- 1 
 2
 3
 4- start_demo.launch 
 #myfile2
 DESTINATION $ {CATKIN_PACKAGE_SHARE_DESTINATION}
 )
- 开始乌龟广播演示 - roslaunch learning_tf2 start_demo.launch 
 结果显示一直乌龟在turtlesim窗口中
- 检查结果 
 使用ts_echo 工具检查乌龟姿势是否确实正在广播到tf2,在启动广播界面使用键盘方向键更改乌龟位置,查看乌龟姿势中个位置是否发生变化 
编写tf2侦听器
监听装换:接收和缓冲系统中广播的所有坐标帧,并查询帧之间的特定变换
接下来就是实现如何使用tf2访问帧转换  
- 创建tf2帧听器 
 首先在之前创建的learning_tf2 文件中创建 /src/ turtle_tf2_listener.cpp 文件,并完成将乌龟姿势发布到tf2中的代码:- 1 
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 //tf2软件包提供了TransformListener的实现,以帮助简化接收转换的任务。
 //要使用TransformListener,我们需要包括tf2 / transform_listener.h头文件
 int main(int argc, char **argv)
 {
 ros::init(argc, argv, "my_tf2_listener");
 ros::NodeHandle node;
 ros::service::waitForServer("spawn");
 ros::ServiceClient spawner = node.serviceClient<turtlesim::Spawn>("spawn");
 turtlesim::Spawn turtle;
 turtle.request.x = 4;
 turtle.request.y = 2;
 turtle.request.thera = 0;
 turtle.request.name = "turtle2";
 spawner.call(turtle);
 //发布话题Turtle2/cmd_vel
 ros::Publisher turtle_vel = node.advertise<geometry_msgs::Twist>("turtle2/cmd_vel", 10);
 //在这里,我们创建一个TransformListener类监听对象,创建侦听器后,它将开始通过网络接收tf2转换
 tf2_ros::Buffer tfBuffer;
 tf2_ros::TransformListener tfListener(tfBuffer);
 ros::Rate rate(10.0);
 while (node.ok())
 {
 //创建一个StampedTransform对象存储变换结果数据
 geometry_msgs::TransformStamped transformStamped;
 //监听包装在一个try-catch块中以捕获可能的异常
 try
 {
 //向侦听器查询特定的转换,(想得到/turtle1到/turtle2的变换),想要转换的时间ros::Time(0)提供了最新的可用转换
 transformStamped = tfBuffer.lookupTransform("turtle2", "turtle1", ros::Time(0));
 }
 catch (tf2::TransformException &ex)
 {
 ROS_WARN("%s", ex.what());
 ros::Duration(1.0).sleep();
 continue;
 }
 //在这里,变换使用turtle1的距离和角度,为turtle2计算新的线和角速度
 geometry_msgs::Twist vel_msg;
 vel_msg.angular.z = 4.0 * atan2(transformStamped.transform.translation.y, transformStamped.transform.translation.x);
 vel_msg.angular.x = 0.5 * sqrt(pow(transformStamped.transform.translation.x, 2) + pow(transformStamped.transform.translation.y, 2));
 //发布话题Turtle2/cmd_vel,用来更新Turtle2的运动
 turtle_vel.publish(vel_msg);
 rate.sleep();
 }
 return 0;
 }
- 运行监听器 
 现在我们已经创建了代码,让我们首先对其进行编译。打开CMakeLists.txt文件并插入:- 1 
 2
 3
 4- add_executable(turtle_tf2_listener src / turtle_tf2_listener.cpp) 
 target_link_libraries(turtle_tf2_listener
 $ {catkin_LIBRARIES}
 )
在已经创建的 /launch/start_demo.launch 的启动文件中添加下面的节点快信息到< launch >块内:
| 1 | <node pkg =“ learning_tf2” type =“ turtle_tf2_listener” | 
- 检查结果
编译并开始乌龟演示
catkin_make
roslaunch learning_tf2 start_demo.launch
如果一切顺利则会有两个乌龟在turtlesim界面,在启动终端界面使用键盘方向键移动turtle1,会发现turtle2始终看行turtle1,并随着turtle1的移动二改变方向

