ROS学习--轻松使用tf

本文介绍了ROS中的tf工具,包括如何广播tf以及如何监听tf。详细解释了如何定义和广播坐标系,以及如何通过监听获取坐标系间的转换关系。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

tf是ROS中建立坐标系,并且使用各个坐标间转换关系的一个很好的工具,对于非导航专业的同学,常常苦恼与各种旋转矩阵的变换,自己经常被搞的头大,最近由于课题实验的需要,尝试使用tf,对此有了一个初步的了解。很多已经接触ROS有一段时间的朋友,可能都没有真正使用过tf,因为从官方给的例程看起来好像非常繁琐,所以也就不想使用,但是最近一段时间的学习发现,其实真正有用程序的就仅仅几句,所以其实非常简单。

一.广播tf

其实就是发布你建立的坐标系。步骤如下:
1.定义一个广播,相当于发布话题时定义一个发布器,还是以官方的小乌龟例程为例:
http://wiki.ros.org/tf/Tutorials/Writing%20a%20tf%20broadcaster%20%28C++%29

static tf::TransformBroadcaster br;

2.定义存放转换信息(平动,转动)的变量

tf::Transform transform;

3.设置坐标原点

transform.setOrigin( tf::Vector3(msg->x, msg->y, 0.0) );

需要注意:
(1)setOrigin()函数的参数类型需要为tf::Vector3类型
(2)假设是要发布一个子坐标系为”turtle1”父坐标系为“world”,那么其中(msg->x,msg->y,0.0)是指“turtle1”的坐标原点在“world”坐标系下的坐标。
4.定义旋转

 tf::Quaternion q;
  q.setRPY(0, 0, msg->theta);
  transform.setRotation(q);

需要注意:
(1)setRPY()函数的参数为”turtle1”在“world”坐标系下的roll(绕X轴),pitch(绕Y轴),yaw(绕Z轴)
(2)为了确保转换正确强烈建议,在转换完后,运行下程序,打开rviz下使用确认下转换是否正确,我在实验中有一次,明明依次填入了三个角度,但是在rviz下发现并不正确,pitch,yaw还存在一个负号的关系(楼主对旋转角的正负号向来头晕,所以都是直接打开rviz,直接试,直观而且准确)。
5.将变换广播出去

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

其中,
(1)transform:存储变换关系的变量;
(2)ros::Time::now():广播tf使的时间戳;
(3)“world”:父坐标系的名字;
(4)turtle_name:子坐标系的名字,这里因为子坐标系有两个,所以它定义了字符串用于存放名字,方便切换,通常我们把名字填到引号里就可以了。
总结一下,假设你在机器人上应用,如果你知道机器人的位置x,y,z,与三个旋转角roll,pitch,yaw就可以广播一个tf了,如果在是平面移动机器人,则只需要知道x,y与yaw即可。

二.监听tf

通过监听tf,我们可以避免繁琐的旋转矩阵的计算,而直接获取我们需要的相关信息。
在监听中我最常用两个函数:
(1)lookupTransform();
(2)transformPoint();
(一)lookupTransform();函数的使用
1.函数功能:可以过得两个坐标系之间转换的关系,包括旋转与平移。
2.使用例程:http://wiki.ros.org/tf/Tutorials/Writing%20a%20tf%20listener%20%28C++%29
3.主要步骤:
(1)定义监听器;

tf::TransformListener listener

(2)定义存放变换关系的变量

 tf::StampedTransform transform;

(3)监听两个坐标系之间的变换

   try{
      listener.lookupTransform("/turtle2", "/turtle1",
                               ros::Time(0), transform);
    }
    catch (tf::TransformException &ex) {
      ROS_ERROR("%s",ex.what());
      ros::Duration(1.0).sleep();
      continue;
    }

注意:
A.由于tf的会把监听的内容存放到一个缓存中,然后再读取相关的内容,而这个过程可能会有几毫秒的延迟,也就是,tf的监听器并不能监听到“现在”的变换,所以如果不使用try,catch函数会导致报错:

“world” passed to lookupTransform argument target_frame does not exist. ”

并且会导致程序挂掉,使用try,catch之后就OK了。

B.lookupTransform()函数参数说明
a.官网的例程说的是实现从“/turtle2”到“/turtle1”的转换,但实际中使用时,我发现转换得出的坐标是在“/turtle2”坐标系下的
b.不可以把ros::Time(0)改成ros::time::now(),因为监听做不到实时,会有几毫秒的延迟。ros::Time(0)指最近时刻存储的数据,ros::time::now()则指当下,如果非要使用ros::time::now,则需要结合waitForTransform()使用,具体见:
http://wiki.ros.org/tf/Tutorials/tf%20and%20Time%20%28C++%29
实际中ros::Time(0)大多数情况下可以满足要求。
c.最后转换关系存入transform中

(4)使用变换
通常我们使用的是点的信息

transform.getOrigin().x()
transform.getOrigin().y(),  

可以得到在turtle1坐标系的原点,在turtle2坐标系下的位置。

transform.getRotation().getW();
transform.getRotation().getX();
transform.getRotation().getY();
transform.getRotation().getZ();

可以得到旋转的四元数,如何直接得到偏航角因为没有用到这个功能所以也就没有去找相关的函数。

(二)transformPoint()函数使用
在实际应用中我们肯定会需要把在一个坐标系下的点转换到另一个坐标系下,这就需要transformPoint()函数。
1.函数功能:将一个坐标系下的点的坐标转换到另一个坐标系下。
2.函数参数:如

listener_.transformPoint("PTAM_world",m_normal_pose,pose_PTAM_world);

(1)其中m_normal_pose数据类型为 geometry_msgs::PointStamped,
其中需要定义m_normal_pose.header.frame_id即该点所属的坐标系
(2)而“PTAM_world”则指,我要将m_normal_pose转换到“PTAM_world”坐标系下。
pose_PTAM_world是转换的结果,数据类型同样为geometry_msgs::PointStamped。
3.使用例程:

geometry_msgs::PointStamped turtle1;
turtle1.header.stamp=ros::Time();
turtle1.header.frame_id="turtle1";
turtle1.point.x=1;
turtle1.point.y=2;
turtle1.point.z=3;
geometry_msgs::PointStamped turtle1_world;
        try{
listener_.transformPoint("PTAM_world",turtle1,turtle1_world);
         }
             catch (tf::TransformException &ex) {
          ROS_ERROR("%s",ex.what());
          ros::Duration(1.0).sleep();
    }

说明:同样因为延时,turtle1.header.stamp不能为ros::Time::now();否则会出类似错误

[ERROR] [1456669076.279804500]: Lookup would require extrapolation into the future. Requested time 1456669076.279616253 but the latest data is at time 1456669076.159341977, when looking up transform from frame …

总结,只有开始使用tf了才能逐渐感受到它的方便,另外再建议tf一定要与rviz结合使用,真的直观又方便。
### 使用 TF 进行坐标变换的教程 在 ROS 中,`tf` 或 `tf2` 提供了一种方便的方式来处理多个坐标系之间的转换关系。以下是关于如何使用 `tf2` 来实现坐标变换的相关内容。 #### 创建支持 `tf2` 的 ROS 软件包 为了构建一个基于 `tf2` 的项目,首先需要创建一个新的 ROS 包,并指定其依赖项。例如,在引用中提到的一个名为 `learning_tf2_cpp` 的软件包被用来展示 `tf2` 的功能[^1]。此包通常会依赖以下几个核心组件: - **geometry_msgs**: 用于描述几何数据结构的消息类型。 - **rclcpp**: C++ 版本的 ROS 客户端库。 - **tf2**: 基础库,提供时间同步和坐标变换的核心逻辑。 - **tf2_ros**: 高级接口层,简化了 `tf2` 的日常操作。 - **turtlesim**: 教学工具模拟器,常用于演示 `tf2` 功能。 可以通过以下命令来创建这个包(假设使用的是 Catkin 工作区): ```bash cd ~/ros_ws/src catkin_create_pkg learning_tf2_cpp rclcpp tf2 tf2_ros geometry_msgs turtlesim ``` --- #### 启动示例程序 一旦安装好所需的包之后,可以直接运行官方提供的 `turtle_tf2_py` 示例程序来进行学习[^2]。启动方法如下所示: 1. 打开新的终端窗口; 2. 初始化环境变量以确保可以调用 `ros2` 命令; 3. 输入下面这条指令执行演示脚本: ```bash source /opt/ros/<distro>/setup.bash ros2 launch turtle_tf2_py turtle_tf2_demo.launch.py ``` 这里 `<distro>` 应替换为你当前使用ROS 发布版本名称(比如 foxy、humble 等)。通过观察这些节点的行为模式,你可以直观理解到 `tf2` 如何管理不同的坐标帧以及它们之间的时间关联性。 --- #### 实现点的空间坐标变换 当涉及到具体的应用场景时,可能需要用到某些高级函数完成更复杂的任务。例如,如果想把某个点从一个参照系映射至另一个,则可借助模块 `tf2_geometry_msgs` 下面的方法——即 `do_transform_point()` 函数[^3]。下面是它的基本用法片段: ```python from geometry_msgs.msg import PointStamped from tf2_geometry_msgs import do_transform_point def transform_point(point_stamped, buffer): try: # 获取目标坐标系中的变换矩阵 target_frame = 'target_frame_name' transformed_point = buffer.transform(point_stamped, target_frame) # 对原始点应用该变换 result = do_transform_point(transformed_point.point, transformed_point) return result except Exception as e: print(f"Error during transformation: {e}") return None ``` 在这个例子当中,传入参数是一个包含了初始坐标的 `PointStamped` 类型对象还有负责查询历史记录的服务缓存实例 (`buffer`) 。最终返回的结果便是经过相应调整后的三维向量形式的新位置。 --- ### 总结 综上所述,利用 ROS 平台上的 `tf2` 可以为开发者带来极大的便利去解决涉及多参考框架的实际工程难题。无论是简单的静态偏移还是动态追踪物体轨迹都可以依靠这套体系轻松达成目的。
评论 16
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值