ROS学习(十四)--TF坐标变换

       PS:本节内容完全参考机器人操作系统(ROS)及仿真应用,如有侵权,联系速删

         TF(Transform)坐标变换是一种在机器人学和计算机视觉领域常用的技术,用于在不同坐标系之间进行转换和变换操作。TF是ROS(Robot Operating System)中的一个模块,提供了强大的坐标变换功能。

        在TF中,坐标变换是通过维护一个坐标变换树(Coordinate Frame Tree)来实现的。坐标变换树是由多个坐标系组成的层次结构,每个坐标系都有一个唯一的名称,称为Frame ID。树的根节点是一个固定的全局参考坐标系,通常称为"world"或"map"

        使用TF功能包时,包括两个步骤

(1)广播TF变换,向系统中广播坐标系之间的坐标变换关系。系统中可能会存在多个不同部分的TF变换广播,每个广播都可以直接将坐标变换关系插入到TF树中,不需要再进行同步。

(2)监听TF变换,接受并缓存系统中发布的所有坐标变换数据,并从中查询所需要的坐标变换关系。

1. 广播TF变换

首先,需要新建一个 ROS 功能包。在 Ubunlu 里打开一个终端程序,输人如下指令进人ROS 工作空间。

cd ~/catkin_ws/src/

然后输人如下指令新建一个 ROS 功能包。

catkin_create_pkg tf_test_pkg roscpp tf geometry_msgs

这条指令的具体含义

catkin_create_pkg创建 ROS 源码包(package)的指令
tf_test_pkg新建的 ROS 源码包命名
roscpp  C++语言依赖项
tf使用 TF 功能包
geometry_msgs    ROS 中的一种常用的消息类型


 
       
                

打开 Visual Studio Code,可以看到工作空间里多了一个 tf_test_pkg 文件夹,
在其 src 子文件夹下新建一个名为 tf_broadcaster.cpp 代码文件。其内容如下:

#include <ros/ros.h>
#include <tf/transform_broadcaster.h>

int main(int argc, char **argv)
{
    ros::init(argc, argv, "tf_broadcaster");
    ros::NodeHandle n;
    ros::Rate loop_rate(100);

    tf::TransformBroadcaster broadcaster;
    tf::Transform base_link2base_laser;
    base_link2base_laser.setOrigin( tf::Vector3(0.1, 0.0, 0.2) );
    base_link2base_laser.setRotation( tf::Quaternion(0, 0, 0, 1) );

    while(n.ok())
    {
        broadcaster.sendTransform( tf::StampedTransform( base_link2base_laser, ros::Time::now(), "base_link", "base_laser"));
        //broadcaster.sendTransform(
        //    tf::StampedTransform( tf::Transform(tf::Quaternion(0, 0, 0, 0), tf::Vector3(1, 0.0, 0) ),
        //    ros::Time::now(), "base link", "base laser") );
        loop_rate.sleep();
    }
    return 0;
}

代码详细解读: 

(1)代码的开始部分,先 include 了两个头文件,一个是 ROS 的系统头文件;另一个是transfonn_Bradcaster.h头文件。
TF 功能包提供了 TransformBroadcaster 类的实现,以帮助简化TF 发布转换的任务,
要使用 TransformBroadcaster,就需要包含<tf/transfom_Broadcaster.h>头文件。

(2)ROS 节点的主体函数是 int main(int argc,char** argv),其参数定义和其他 C++语言程序一样。

(3)main()函数里,首先调用 ros::init(argc,argv, “t_broadcaster” ),进行该节点的初始化操作,函数的第三个参数是节点名称。

(4)接下来利用Rate loop_rate(100)控制节点运行的频率,与后面的 looprate.sleep()配合使用。

(5)创建TF 广播器 broadcaster,用于后续发布坐标变换。

(6)创建一个tansform 对象base_link2base_laser,定义存放转换信息(平动,转动的变量)。利用 setOrigin()函数给定平移变换值;
利用setRotation()函数给定旋转变换值,Quaternion()中的前3个参数为 base_laser 子坐标系与 base_link 父坐标系的角度关系,
分别为ro1l(绕x轴)、pitch(绕y轴)、yaw(绕z轴),最后一个参数为角速度。

(7)为了连续不断地发布坐标变换,使用一个 while (n.ok()) 循环,以 n.ok() 返回值作为循环结束条件,可以让循环在程序关闭时正常退出。

(8)利用sendTransform()函数发布坐标变换到主题"/tf"。

(9)调用 loop_rate.sleep()函数,按之前loop_rate(100)设置的 100Hz将程序挂起。

代码编写完毕,需要将文件名添加到编译文件里才能进行编译。
在 CMakeLists.txt 文件末尾,为 tf_broadcaster.cpp 添加新的编译规则。内容如下:

add_executable( tf_broadcaster src/tf_broadcaster.cpp )
add_dependencies( tf_broadcaster ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS} )
target_link_libraries( tf_broadcaster ${catkin_LIBRARIES} )

启动一个终端程序,执行如下指令编译工程。
cd ~/catkin ws/
catkin_make

2. 监听TF变换

打开 Visual Studio Code,在工作空间中 tf_test_pkg 文件夹下的 src 子文件夹下新建一个代码文件。
新建的代码文件命名为 tf_listener.cpp。其内容如下。

#include <ros/ros.h>
#include <tf/transform_listener.h>
#include <geometry_msgs/PointStamped.h>
#include <iostream>

int main(int argc, char **argv)
{
    ros::init(argc, argv, "tf_listener");
    ros::NodeHandle n;
    ros::Rate loop_rate(100);

    tf::TransformListener listener;
    geometry_msgs::PointStamped laser_pos;
    laser_pos.header.frame_id = "base_laser";
    laser_pos.header.stamp    = ros::Time() ;

    laser_pos.point.x = 0.3 ;
    laser_pos.point.y = 0 ;
    laser_pos.point.z = 0 ;

    geometry_msgs::PointStamped base_pos;

    while (n.ok())
    {
        if (listener.waitForTransform("base_link", "base_laser", ros::Time(0), ros::Duration(3)) )
        {
            listener.transformPoint("base_link", laser_pos, base_pos);
            ROS_INFO( "pointpos in base_laser:(%.2f,%.2f,%.2f)", laser_pos.point.x, laser_pos.point.y, laser_pos.point.z);
            ROS_INFO( "pointpos in base_link:(%.2f,%.2f,%.2f)", base_pos.point.x, base_pos.point.y, base_pos.point.z);

            tf::StampedTransform laserTransform;
            listener.lookupTransform( "base_link", "base_laser", ros::Time(0), laserTransform );

            std::cout << "laserTransform.getOrigin().getX():" << laserTransform.getOrigin().getX() << std::endl;
            std::cout << "laserTransform.getOrigin().getY():" << laserTransform.getOrigin().getY() << std::endl;
            std::cout << "laserTransform.getOrigin().getZ():" << laserTransform.getOrigin().getZ() << std::endl;
        }
        loop_rate. sleep();
    }
}

代码注释: 

(1)代码的开始部分,先 include 4个头文件。在 u_listener.cpp 中使用一个新的类型叫作geometry_msgs/PointStamped,
在ROS 终端命令行使用 rosmsg show geometry_msgs/PointStamped 命令可以查看该类型,所以包含<geometry_msgs/PointStamped.h>头文件。

(2)ROS节点的主体函数是 int main(int argc,char** argv),其参数定义和其他 C++语言程序一样。

(3)main()函数里,首先调用ros::init(argc,argv, "t_listener" ),进行该节点的初始化操作,函数的第三个参数是节点名称。

(4)接下来利用 Rate loop_rate(100) 控制节点运行的频率,与后面的 loop_rate.sleep()配合使用。

(5)创建一个监听器 listener,监听所有 TF 变换。

(6)在 base_laser 上声明一个点 laser_pos,用来转换得到 base_link上的点坐标。
首先创建一个 PointStamped类型的点 laser_pos,将这个点绑定到 base_laser 坐标系下,定义点的坐标为(0.3,0,0)。

(7)创建一个 PointStamped 类型的点 base_pos,用于存储转换到 base_link 上的点坐标

(8)为了连续不断地发送速度,使用一个 while(n.ok())循环,以 n.ok()返回值作为循环结束条件,可以让循环在程序关闭时正常退出。

(9)为了进行坐标变换,需要首先利用 waitForTransform()函数监听两个坐标之间的变换关系,
其中的第三个参数ros::Time(0)表示使用缓冲区中最新的 TF 数据。

(10)利用tansfommPoint()函数将 base_laser 坐标系中的 laser_pos 点变换到 base_link 坐标系中的 base_pos 点。

(11)输出坐标变换前 laser_pos 点的坐标值和坐标变换后 base_pos 点的坐标值。

(12)利用 lookupTransform() 函数获取最新的坐标变换关系,并输出 base_laser 坐标系原点在 base_link 坐标系中的坐标值。

(13)调用 loop_rate.sleep()兩数,按之前 loop_rate(100)设置的 100Hz将程序挂起。

代码编写完毕,需要将文件名添加到编译文件里才能进行编译。编译文件在tf_test-pkg的目录下,文件名为CMakeLisis.txt
CMakeLists.txt文件末尾,为 tf_listener.cpp 添加新的编译规则。内容如下:

add_executable( tf_listener src/tf_listener.cpp)
add_dependencies( tf_listener ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS} )
target_link_libraries( tf_listener ${catkin_LIBRARIES} )

启动一个终端程序,执行如下指令编译工程。

cd ~/catkin_ws/
catkin_make

在新终端程序中输人以下指令,启动节点管理器

roscore

新建终端窗口,在终端程序中输人以下指令,运行 tf_test_pkg 功能包中的 tf_broadcaster 节点。

rosrun tf_test_pkg tf_broadcaster

新建终端窗口,在终端程序中输人以下指令:

rosrun tf_test_pkg tf_listener

可以看到在 base_laser 坐标系中的位置为(0.3,0,0)的点,在经过坐标变换后,在 base_link 坐标系中的位置为(0.4,0,0.2),
base.laser 坐标系原点在 base_link 坐标系中的位置为( 0.1, 0,0.2 )。

可以通过指令査看 ROS 的节点网络情况。

rqt_graph

可以看到,编写的 tf_broadcaster 节点通过主题 "/tf" 向 tf_listener 节点发送消息包。
tf_listener 节点获得消息后,将坐标变换前后的结果发送到终端界面显示。

可以通过指令査看 TF 树状结构图。

rosrun rqt_tf_tree rqt_tf_tree

可以通过指令输出两个坐标系的坐标变换。

rosrun tf tf_echo /base_link base_laser

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值