TF坐标变换

2023-05-16

文章目录

  • TF坐标变换
    • TF功能包
    • TF工具
    • 乌龟例程中的TF
    • 创建TF广播器
    • 创建TF监听器
    • 实现乌龟跟随运动
  • 存在的问题
  • 总结
  • 参考

TF坐标变换

坐标变换是机器人学中一个非常基础,同时也是非常重要的概念。机器人本体和机器人的工作环境中往往存在大量的组件元素,在机器人设计和机器人应用中都会涉及不同组件的位置和姿态,这就需要引入坐标系和坐标变换的概念。

对于A,B两个坐标系,A坐标系下的位姿可以通过平移和旋转变换成B坐标系下的位姿,这里的平移和旋转可以通过4×4的变换矩阵来描述。

坐标变换是机器人系统中常用的基础功能,ROS中的坐标变换系统由TF功能包维护。

TF功能包

TF是一个让用户随时间跟踪多个坐标系的功能包,它用树形数据结构,根据时间缓冲并维护多个坐标系之间的坐标变换关系,可以帮助开发者在任意时间,在坐标系间完成点,向量等坐标的变换。

一个机器人系统通常有很多三维坐标系,而且会随着时间的推移发生变化,如世界坐标系(Word Frame),基坐标系(Base Frame),机械夹爪坐标系(Gripper Frame),机器人头部坐标系(Head Frame)等。TF可以时间为轴跟踪这些坐标系(默认10s之内),并且允许开发者请求如下类型的数据:

  • 5s之前,机器人头部坐标系相对于全局坐标系的关系是怎样的?
  • 机器人夹取的物体相对于机器人中心坐标系的位置在哪里?
  • 机器人中心坐标系相对于全局坐标系的位置在哪里?

TF可以在分布式系统中进行操作,也就是说,一个机器人系统中的所有坐标变换关系,对于所有的节点组件都是可用的,所有订阅TF消息的节点都会缓冲一份所有坐标系的变换关系数据,这种数据结构不需要中心服务器来存储任何数据。

想要使用TF功能包,总体来说需要以下两个步骤。
(1)监听TF变换
接收并缓存系统中发布的所有坐标变换数据,并从中查询所需要的坐标变化关系。
(2)广播TF变换
向系统中广播坐标系之间的坐标变换关系。系统中可能会存在多个不同部分的TF变换广播,每个广播都可以直接将坐标变换关系插入到TF树中,不需要再进行同步。

TF工具

坐标系统虽然是一个基础理论,但是由于涉及多个空间之间的变换,不容易进行想象,所以TF提供了丰富的终端工具来帮助开发者调试和创建TF变换。

1.tf_monitor
tf_monitor工具的功能是打印TF树中所有坐标系的发布状态,也可以通过输入参数来查看指定坐标系之间的发布状态。

tf_monitor
tf_monitor <source_frame> <target_frame>

2.tf_echo
tf_echo工具的功能是查看指定坐标系之间的变换关系,命令的格式如下:

tf_echo <source_frame> <target_frame>

3.tf_transform_publisher
tf_transform_publisher工具的功能是发布两个坐标系之间的静态坐标变换,这两个坐标系不发生相对位置变化。命令的合适如下:

tf_transform_publisher x y z yaw pitch roll frame_id child_frame_id period_in_ms
tf_transform_publisher x y z qx qy qz qw frame_id child_frame_id period_in_ms

以上两种命令格式,需要设置坐标的偏移参数和旋转参数:偏移参数使用相对于x,y,z三轴的坐标位移;而旋转参数的第一种命令格式使用以弧度为单位的yaw/pitch/roll角度(yaw是围绕z轴旋转的偏航角,pitch是围绕y轴旋转的俯仰角,roll是围绕x轴旋转的翻滚角),第二种命令格式使用四元数表达旋转角度。发布频率以ms为单位。

该命令不仅可以在终端中使用,还可以在launch文件中使用,方法如下:

<launch>
<node pkg="tf" type="tf_transform_publisher" name="link1_broadcaster" 
args="1 0 0 0 0 0 1 link1_parent link1 100" />
</launch>

4.view_frames
view_frames是可视化的调试工具,可以生成pdf文件,显示整棵TF树的信息。执行方式为:

rosrun tf view_frames

然后使用以下命令,或者pdf阅读器查看生成的pdf文件。

evince frames.pdf

此外,rviz中也提供了TF可视化的插件。

乌龟例程中的TF

接下来我们在乌龟仿真器中通过一个例程(turtle_tf)来理解TF的作用,并且熟悉以上学到的TF工具。该例程的功能包turtle_tf可以使用如下命令进行安装:

sudo apt-get install ros-kinetic-turtle-tf

安装完成后使用如下命令运行例程:

roslaunch turtle_tf turtle_tf_demo.launch

乌龟仿真器打开后会出现两只小乌龟,并且下方的小乌龟会自动向中心位置的小乌龟移动。

打开键盘控制节点,控制中心位置的小乌龟运行:

rosrun turtlesim turtle_teleop_key

另外一只乌龟总会跟随我们控制的那只乌龟运行。在这个例程中,TF是如何运用的呢?首先,使用TF工具来查看以下这个例程中的TF树是什么样的:

rosrun tf view_frames

在当前系统中存在3个坐标系:word,turtle1,tutle2。word是世界坐标系,作为系统的基础坐标系,其他坐标系都相对该坐标系建立,所以word是TF树的根节点。相对于word坐标系,有分别针对两只乌龟创建了两个乌龟坐标系,这两个坐标系的原点就是乌龟在世界坐标系下的坐标位置。

现在要让turtle2跟随turtle1运动,等价于turtle2坐标系要向turtle1坐标系移动,这就需要知道turtle2与turtle1之间的坐标变换。三个坐标系之间的变换关系为:

T t u r t l e 1 _ t u r t l e 2 = T t u r t l e 1 _ w o r d × T w o r d _ t u r t l e 2 T_{turtle1\_turtle2}=T_{turtle1\_word}×T_{word\_turtle2} Tturtle1_turtle2=Tturtle1_word×Tword_turtle2

使用tf_echo工具在TF树中查找乌龟坐标系之间的变换关系:

rosrun tf tf_echo turtle1 turtle2

也可以通过rviz的图形界面更加形象地看到这三者之间的坐标关系:

rosrun rviz rviz -d 'rospack find turtle_tf'/rviz/turtle_rviz.rviz

得到turtle2与turtle1之间的坐标变换后,就可以通过计算两只乌龟间的距离和角度,即可控制turtle2向turtle1运动了。

接下来我i们以这个例程为目标,学习如何实现TF的广播和监听功能。

创建TF广播器

首选,我们需要创建一个发布乌龟坐标系与世界坐标系之间TF变换的节点,源码如下:

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

std::string turtle_name;

void poseCallback(const turtlesim::PoseConstPtr& msg)
{
    // tf广播器
    static tf::TransformBroadcaster br;

    // 根据乌龟当前的位姿,设置相对于世界坐标系的坐标变换
    tf::Transform transform;
    transform.setOrigin( tf::Vector3(msg->x, msg->y, 0.0) );
    tf::Quaternion q;
    q.setRPY(0, 0, msg->theta);
    transform.setRotation(q);

    // 发布坐标变换
    br.sendTransform(tf::StampedTransform(transform, ros::Time::now(), "world", turtle_name));
}

int main(int argc, char** argv)
{
    // 初始化节点
    ros::init(argc, argv, "my_tf_broadcaster");
    if (argc != 2)
    {
        ROS_ERROR("need turtle name as argument"); 
        return -1;
    };
    turtle_name = argv[1];

    // 订阅乌龟的pose信息
    ros::NodeHandle node;
    ros::Subscriber sub = node.subscribe(turtle_name+"/pose", 10, &poseCallback);

    ros::spin();

    return 0;
};

以上代码的关键部分是处理乌龟pose消息的回调函数poseCallback,在广播TF消息之前要定义tf::TransformBroadcaster广播器,然后根据乌龟当前的位姿设置tf::Transform类型的坐标变换,包含setOrigin设置的平移变换,setRotation设置的旋转变换。

然后使用广播器将坐标变换插入TF树并进行发布,这里发布的TF消息类型是tf::StampedTransform,不仅包含tf::Transform类型的坐标变换、时间戳,而且需要指定坐标变换的源坐标系(parent)和目标坐标系(child)。

创建TF监听器

TF消息广播之后,其他节点就可以监听该TF消息,从而获取需要的坐标变换了。目前我们已经将乌龟相对于world坐标系的TF变换进行了广播,接下来需要监听TF消息,并从中获取turtle2相对于turtle1坐标系的变换,从而控制turtle2移动。源码如下:

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

    // 通过服务调用,产生第二只乌龟turtle2
    ros::service::waitForService("spawn");
    ros::ServiceClient add_turtle =
    node.serviceClient<turtlesim::Spawn>("spawn");
    turtlesim::Spawn srv;
    add_turtle.call(srv);

    // 定义turtle2的速度控制发布器
    ros::Publisher turtle_vel =
    node.advertise<geometry_msgs::Twist>("turtle2/cmd_vel", 10);

    // tf监听器
    tf::TransformListener listener;

    ros::Rate rate(10.0);
    while (node.ok())
    {
        tf::StampedTransform transform;
        try
        {
            // 查找turtle2与turtle1的坐标变换
            listener.waitForTransform("/turtle2", "/turtle1", ros::Time(0), ros::Duration(3.0));
            listener.lookupTransform("/turtle2", "/turtle1", ros::Time(0), transform);
        }
        catch (tf::TransformException &ex) 
        {
            ROS_ERROR("%s",ex.what());
            ros::Duration(1.0).sleep();
            continue;
        }

        // 根据turtle1和turtle2之间的坐标变换,计算turtle2需要运动的线速度和角速度
        // 并发布速度控制指令,使turtle2向turtle1移动
        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;
};

该节点首先通过服务调用产生乌龟turtle2,然后声明控制turtle2速度的Publisher。在监听TF消息之前,需要创建一个tf::TransformListener类型的监听器,创建成功后监听器会自动接收TF树的消息,并且缓存10s。然后在循环中就可以实时查找TF树中的坐标变换了,这里需要调用的是tf::TransformListener中的两个接口:

  • waitForTransform(const std::string &target_frame, const std::string &source_frame, const ros::Time &time, const ros::Duration &timeout):给定源坐标系和目标坐标系,等待两个坐标系指定时间的变换关系,该函数会阻塞程序运行,因此要设置超时时间。
  • lookupTransform(const std::string &target_frame, const std::string &source_frame, const ros::Time &time, StampedTransform &transform):给定源坐标系和目标坐标系,得到两个坐标系指定时间的变换关系,ros::Time(0)表示我们想要得到最新一次的坐标变换。

通过以上两个接口的调用,就可以获取turtle2相对于turtle1的坐标变换了。然后根据坐标系之间的位置关系,计算得到turtle2需要运动的线速度和角速度,并发布速度控制指令使turtle2向turtle1移动。

实现乌龟跟随运动

编写一个launch文件,使所有节点运行起来,源码:

 <launch>
    <!-- 海龟仿真器 -->
    <node pkg="turtlesim" type="turtlesim_node" name="sim"/>

    <!-- 键盘控制 -->
    <node pkg="turtlesim" type="turtle_teleop_key" name="teleop" output="screen"/>

    <!-- 两只海龟的tf广播 -->
    <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" />

    <!-- 监听tf广播,并且控制turtle2移动 -->
    <node pkg="learning_tf" type="turtle_tf_listener"
          name="listener" />

 </launch>

存在的问题

看理论基本都能明白,但是通过实践,我发现程序并不能实现想要的效果。

启动launch文件后,打开了一个乌龟仿真器,但里面只有一个乌龟,在终端按方向键能控制其运动,但是没有另一只乌龟做跟随运动。

而且在上一个乌龟例程中,乌龟2没有一直跟随乌龟1运动。

这里先记录以下,不知道是程序问题,还是我操作的问题。

总结

通过这个例程的实现,我们学习了TF广播与监听的实现方法,在实际使用中会产生更多的坐标系,TF树的结构也会更加复杂,但是基本的使用方法依然相同。

最近遇到TF的问题,发现对其理解不够深入,所以重温了古月居编写的《ROS机器人开发实践》,这部分内容写得通俗易懂,在此向古月居大佬致敬。

参考

[1]《ROS机器人开发实践》胡春旭 编著
[2] https://github.com/huchunxu/ros_exploring

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

TF坐标变换 的相关文章

  • OpenStreetMap:对抗谷歌帝国的共享开源地图

    OpenStreetMap xff1a 对抗谷歌帝国的共享免费地图 讲在前面 在步入自动驾驶的学习之后 xff0c 不可避免地我需要去了解在驾驶领域中选择的地图格式 xff0c 而随着研究的进一步深入 xff0c 我逐步了解到两种较为主流的
  • Copilot:AI自动写代码,人工智能究竟还能取代什么?

    Copilot xff1a AI自动写代码 xff0c 人工智能究竟还能取代什么 xff1f 前言 在AI绘画掀起一阵热潮之后 xff0c AI写代码又逐渐进入了我们的视野 xff0c 似乎这一步我们还没想到就迅速到来了 xff0c 难道说
  • 关于SubSonic3.0生成的表名自动加复数(s)的“用户代码未处理SqlException,对象名‘xxxs‘无效”异常处理

    关于SubSonic3 0生成的表名自动加复数 xff08 s xff09 的 用户代码未处理SqlException xff0c 对象名 39 xxxs 39 无效 异常处理 参考文章 xff1a xff08 1 xff09 关于SubS
  • 互联网金融风控面试算法知识(三)

    资料来源于网络搜集和汇总 xff0c 把算法知识的总结放在业务知识后面也是为了说明实际工作业务落地应用的重要性大于算法创新 面试题依然是适用于3年经验以内的初学者 xff0c 希望大家在学习算法的同时不要一心只研究算法而脱离了业务 xff0
  • wiki树莓派安装ubuntu mate 和 ros

    两大步骤 1 安装ubuntu mate 2 安装ros 一 安装ubuntu mate 下载ubuntu mate 18 04 img 并制作系统盘 首先要说的就是树莓派支持的系统是很多样的 xff0c 但是针对ros xff0c 我们只
  • npm 的工作原理

    包 Package 和模块 Module 如何定义一个Package 满足如下条件都可以称为一个包 xff1a 一个文件夹包含应用程序 xff0c 使用package json来描述它 a 一个用gzip压缩的文件夹 xff0c 满足 a
  • 2023最全Postman安装使用详解

    一 Postman背景介绍 用户在开发或者调试网络程序或者是网页B S模式的程序的时候是需要一些方法来跟踪网页请求的 xff0c 用户可以使用一些网络的监视工具比如著名的Firebug等网页调试工具 今天给大家介绍的这款网页调试工具不仅可以
  • cmake 设置 debug release模式

    1 通过命令行的方式 cmake DCMAKE BUILD TYPE 61 Debug 2 set CMAKE BUILD TYPE Debug CACHE STRING 34 set build type to debug 34 或者 s
  • 华为笔试题(4)

    一 计算n x m的棋盘格子 xff08 n为横向的格子数 xff0c m为竖向的格子数 xff09 沿着各自边缘线从左上角走到右下角 xff0c 总共有多少种走法 xff0c 要求不能走回头路 xff0c 即 xff1a 只能往右和往下走
  • 安装RedisBloom插件

    前言 安装RedisBloom模块会遇到很多坑 xff0c 希望你不要和我一样踩的这么全 x1f60f 如果觉得编译麻烦 xff0c 我也上传了我编译的so文件 xff0c 可以直接加载使用 https download csdn net
  • ROS Catkin 教程之 CMakeLists.txt

    1 概览 CMakeLists txt 是用 CMake 构建系统构建 ROS 程序包的输入文件 任何兼容 CMake 的包都包含一个或多个 CMakeLists txt 文件 xff0c 用以描述怎样构建和安装代码 catkin 项目采用
  • Xsens Mti-g-710 IMU driver在Ubuntu18.04 ROS melodic中的安装使用

    Ubuntu18 04下安装的ROS melodic 如何使用Xsens Mti g 710 IMU driver xff1f 这里给出一个详细步骤说明 这里的IMU是USB接口 1安装 首先插入IMU的USB口 命令行运行 gt lsus
  • PYTHON -MYSQLDB安装遇到的问题和解决办法

    PYTHON MYSQLDB安装遇到的问题和解决办法 参考文章 xff1a xff08 1 xff09 PYTHON MYSQLDB安装遇到的问题和解决办法 xff08 2 xff09 https www cnblogs com gaosh
  • 位姿估计Robot_pose_efk的配置和使用

    Robot pose efk 用于融合里程计 xff0c 惯性测量单元和视觉里程计的传感器输出 xff0c 从而减少测量中的总体误差 了解ROS的robot pose ekf软件包中扩展卡尔曼滤波器的用法 xff1a robot pose
  • linux录屏和截图软件

    linux下的录屏和截图软件有很多 xff0c kazam集成了录屏和截图两个功能 xff0c 而且十分轻量级 xff0c 比较好用 如果是在VirtualBox虚拟机中跑linux的话 xff0c virtualbox本身就提供录屏和截图
  • APM 学习 6 --- ArduPilot 线程

    ArduPilot 学习之路 6 xff0c 线程 英文原文地址 xff1a https ardupilot org dev docs learning ardupilot threading html 理解 ArduPilot 线程 线程
  • nginx 配置多个vue,环境部署

    1 最近项目要上线 xff0c 需要通过nginx作为代理 xff0c 要发布2个VUE前端项目 xff0c 记录一下nginx conf配置文件 亲自验证 xff0c 特此记录一下 xff0c 希望能帮助向我一样 小白的人 user ro
  • freertos源码分析(1)--初始篇

    代码下载地址 xff1a https www freertos org 部分转载参考 FreeRTOS基础知识 xff1a RTOS全称为 xff1a Real Time OS xff0c 就是实时操作系统 xff0c 强调的是 xff1a
  • nginx服务占用百分之百

    一 当nginx达到100 时 xff0c 也就是服务器负载突然上升 1 利用top命令查看cpu使用率较高的php cgi进程 PID USER PR NI VIRT RES SHR S CPU MEM TIME 43 COMMAND 1
  • Gazebo教程(使用roslaunch 启动Gazebo,world以及urdf模型)

    Gazebo教程 xff08 使用roslaunch 启动Gazebo xff0c world以及urdf模型 xff09 关于如何学习ROS可以参考古月居的这篇文章 1 https www zhihu com question 35788

随机推荐

  • dispatch_queue_create---创建队列

    dispatch queue create span class hljs keyword const span span class hljs keyword char span label dispatch queue attr t a
  • Java多种方式解决生产者消费者问题(十分详细)

    一 问题描述 生产者消费者问题 xff08 Producer consumer problem xff09 xff0c 也称有限缓冲问题 xff08 Bounded buffer problem xff09 xff0c 是一个多线程同步问题
  • Http协议WWW-Authenticate

    HTTP协议有一个叫WWW Authenticate的头字段 xff0c 可以用于实现登录验证 它是在RFC 2617中定义的 当服务器接收到一个request xff0c 并在实现下面的代码 xff1a br http response
  • Android 运行时注解

    编译时注解点击此处 xff5e xff5e xff5e 运行时注解 以 64 BindView 为例 下面是实现步骤 新建一个 apt annotation 的 java library xff0c 然后在库中新建一个注解 xff0c 传入
  • 使用k-近邻算法识别手写数字。

    在之前的文章中介绍了k 近邻算法的原理知识并且用Python实现了一个分类器 xff0c 而且完成了一个简单的优化约会网站配对效果的实例 在 机器学习实战 中有关kNN的后一部分内容就是一个手写识别系统 xff0c 可以识别手写的0 9的数
  • 在Ubuntu14.04不能添加PPA源到apt源的问题解决方法

    在Ubuntu14 04使用apt get 更新Git 时 xff0c 需要更新apt源 xff0c 添加一个带有最新Git的源 xff0c 如下命令 xff1a sudo add apt repository ppa git core p
  • 单片机的操作本质【以STM32系列为例】

    单片机的操作本质 摘要寄存器的本质单片机的操作本质操作寄存器的方法 摘要 本文档是笔者学习野火F103视频 课时5 至 课时7 的总结 视频链接 xff1a https study 163 com course introduction 1
  • 《视觉SLAM进阶:从零开始手写VIO》第一讲作业

    目录 1 视觉与IMU融合之后有何优势 xff1f 2 有哪些常见的视觉 43 IMU融合方案 xff1f 有没有工业界应用的例子 xff1f 3 在学术界 xff0c VIO研究有哪些新进展 xff1f 有没有将学习方法应用到VIO的例子
  • GPS坐标与UTM坐标的转换

    1 简介 1 1 消息 gps common定义了两个通用消息 xff0c 供GPS驱动程序输出 xff1a gps common GPSFix和gps common GPSStatus 在大多数情况下 xff0c 这些消息应同时发布 xf
  • NVIDIA Jetson TX2使用笔记(一):开机设置

    0 写在前面 由于项目需要 xff0c 使用 NVIDIA Jetson TX2作为硬件开发平台 xff0c 在此记录使用方法和遇到的问题 NVIDIA Jetson TX2是英伟达的嵌入式开发套件 xff0c 可以进行视觉计算 xff0c
  • ORB-SLAM2的安装与运行

    0 背景简介 ORB SLAM是西班牙Zaragoza大学的Raul Mur Artal编写的视觉SLAM系统 他的论文 ORB SLAM a versatile andaccurate monocular SLAM system 34 发
  • Ubuntu14.04升级cmake版本的方法

    在Ubuntu14 04用以下命令默认安装的cmake版本为2 8 x xff0c 有时我们需要更高版本的cmake xff0c 所以需要升级 span class hljs built in sudo span apt get insta
  • 在TX2上配置ORB-SLAM2错误总结

    Pangolin 错误描述 usr lib gcc aarch64 linux gnu 5 aarch64 linux gnu libGL so undefined reference to 96 drmFreeDevice 解决方法 cd
  • docker镜像迁移/移植

    docker镜像迁移 移植 或者docker save 镜像名 版本号 o 路径 保存的包名 tar 通过这两个命令保存保存镜像 xff0c 下载到本地再上传到其他服务器 然后通过docker load lt 保存的包名 tar 完成镜像移
  • 安装并运行VINS-Mono

    0 A Robust and Versatile Monocular Visual Inertial State Estimator VINS Mono是单目视觉惯性系统的实时SLAM框架 它使用基于优化的滑动窗口配方来提供高精度的视觉惯性
  • 使用小觅双目-惯性相机运行VINS-Mono

    步骤 1 下载相机驱动MYNT EYE SDK 2 xff0c 然后make ros xff08 注意 xff1a 前面的Ubuntu安装也要操作 xff09 xff1b 2 安装VINS Mono xff1b 3 在MYNT EYE VI
  • 在TX2上安装VIINS-Mono——问题总结

    1 ceres solver 我们一般通过以下命令安装Eigen xff1a sudo apt get install libeigen3 dev 默认安装在 usr include eigen3 在CMakeList txt中一般用以下语
  • LeGO-LOAM初探:原理,安装和测试

    前言 最近要搞3D激光SLAM xff0c 先后测试了Autoware xff0c cartographer xff0c loam和LeGO LOAM 今天就带来LeGO LOAM的使用体验 Github xff1a https githu
  • IMU噪声标定——加速度计和陀螺仪的白噪声和零偏不稳定性

    前言 imu utils是一个用于分析IMU性能的ROS工具包 参考资料 Allan Variance Noise Analysis for Gyroscopesvectornav gyroscopeAn introduction to i
  • TF坐标变换

    文章目录 TF坐标变换TF功能包TF工具乌龟例程中的TF创建TF广播器创建TF监听器实现乌龟跟随运动 存在的问题总结参考 TF坐标变换 坐标变换是机器人学中一个非常基础 xff0c 同时也是非常重要的概念 机器人本体和机器人的工作环境中往往