您正在阅读的是旧版本但仍受支持的 ROS 2 文档。 Jazzy.
添加框架 (C++)
目标 学习如何在 tf2 中添加额外帧。
辅导水平: 中级
时间 15 分钟
背景介绍
在之前的教程中,我们通过编写一个 tf2 广播公司 和一个 tf2 监听器.本教程将教您如何在变换树中添加额外的固定帧和动态帧。事实上,在 tf2 中添加帧与创建 tf2 广播器非常相似,但本示例将向您展示 tf2 的一些附加功能。
对于许多与变换相关的任务,在局部框架内进行思考更为容易。例如,在以激光扫描仪为中心的框架内推理激光扫描测量是最简单的。tf2 可以为系统中的每个传感器、链接或关节定义一个局部框架。当从一个框架转换到另一个框架时,tf2 会处理所有引入的隐藏中间框架转换。
tf2 树
tf2 采用树形框架结构,因此不允许在框架结构中形成闭环。这意味着一个框架只有一个父框架,但可以有多个子框架。目前,我们的 tf2 树包含三个帧: 世界
, 乌龟1
和 乌龟2
.这两个乌龟框架是 世界
帧。如果我们想在 tf2 中添加一个新框架,现有的三个框架中必须有一个是父框架,而新框架将成为它的子框架。

任务
1 编写固定框架广播节目
在我们的乌龟示例中,我们将添加一个新框架 胡萝卜1
的子代。 乌龟1
.这个框架将作为第二只乌龟的目标。
首先创建源文件。转到 learning_tf2_cpp
软件包。在 来源
目录,输入以下命令下载固定框架广播代码:
wget https://raw.githubusercontent.com/ros/geometry_tutorials/ros2/turtle_tf2_cpp/src/fixed_frame_tf2_broadcaster.cpp
wget https://raw.githubusercontent.com/ros/geometry_tutorials/ros2/turtle_tf2_cpp/src/fixed_frame_tf2_broadcaster.cpp
在 Windows 命令行提示符下:
curl -sk https://raw.githubusercontent.com/ros/geometry_tutorials/ros2/turtle_tf2_cpp/src/fixed_frame_tf2_broadcaster.cpp -o fixed_frame_tf2_broadcaster.cpp
或使用 powershell:
curl https://raw.githubusercontent.com/ros/geometry_tutorials/ros2/turtle_tf2_cpp/src/fixed_frame_tf2_broadcaster.cpp -o fixed_frame_tf2_broadcaster.cpp
现在打开名为 fixed_frame_tf2_broadcaster.cpp
.
#include 时间顺序<chrono>;
#include 功能强大;
#include <内存>;
#include "geometry_msgs/msg/transform_stamped.hpp";
#include "rclcpp/rclcpp.hpp";
#include "tf2_ros/transform_broadcaster.h";
使用 命名空间 标准::计时器;
类 固定框架广播 : 公 rclcpp::节点
{
公:
固定框架广播()
: 节点("fixed_frame_tf2_broadcaster";)
{
tf_broadcaster_ = 标准::共享<;tf2_ros::改造播音员>;(此);
定时器 = 此->;创建隔离墙计时器(
100毫秒, 标准::约束(及样品;固定框架广播::广播定时器回调, 此));
}
私人:
空白 广播定时器回调()
{
几何参数::信息::TransformStamped t;
t.页眉.盖章 = 此->;获取时钟()->;现在();
t.页眉.frame_id = "turtle1";;
t.子帧标识 = "carrot1";;
t.变.译文.x = 0.0;
t.变.译文.y = 2.0;
t.变.译文.z = 0.0;
t.变.自转.x = 0.0;
t.变.自转.y = 0.0;
t.变.自转.z = 0.0;
t.变.自转.w = 1.0;
tf_broadcaster_->;发送变换(t);
}
rclcpp::定时器基数::SharedPtr 定时器;
标准::共享_ptr<;tf2_ros::改造播音员>; tf_broadcaster_;
};
int 主要(int 参数, 烧焦 * 参数[])
{
rclcpp::启动(参数, 参数);
rclcpp::后旋(标准::共享<;固定框架广播>;());
rclcpp::关闭();
返回 0;
}
代码与 tf2 广播员教程示例非常相似,唯一的区别是这里的变换不会随时间而改变。
1.1 检查代码
让我们来看看这段代码中的关键行。在这里,我们从父代的 乌龟1
给新孩子 胡萝卜1
.......。 胡萝卜1
帧的 Y 轴偏移 2 米。 乌龟1
镜框
几何参数::信息::TransformStamped t;
t.页眉.盖章 = 此->;获取时钟()->;现在();
t.页眉.frame_id = "turtle1";;
t.子帧标识 = "carrot1";;
t.变.译文.x = 0.0;
t.变.译文.y = 2.0;
t.变.译文.z = 0.0;
1.2 CMakeLists.txt
导航一级回到 learning_tf2_cpp
目录中的 CMakeLists.txt
和 package.xml
文件的位置。
现在打开 CMakeLists.txt
添加可执行文件并命名 固定框架_TF2_播音员
.
add_executable(fixed_frame_tf2_broadcaster src/fixed_frame_tf2_broadcaster.cpp)
ament_target_dependencies(
固定框架_TF2_播音员
几何参数
rclcpp
tf2_ros
)
最后,添加 install(TARGETS...)
因此 玫瑰2 运行
能找到你的可执行文件:
install(TARGETS
固定框架_TF2_播音员
DESTINATION lib/${PROJECT_NAME})
1.3 编写启动文件
现在,让我们为这个示例创建一个启动文件。用文本编辑器新建一个名为 turtle_tf2_fixed_frame_demo_launch.py
在 src/learning_tf2_cpp/launch
目录,并添加以下几行:
舶来品 os
从 ament_index_python.packages 舶来品 获取软件包共享目录
从 启动 舶来品 启动说明
从 launch.actions 舶来品 包含启动描述
从 launch.launch_description_sources 舶来品 PythonLaunchDescriptionSource
从 launch_ros.actions 舶来品 节点
捍卫 生成发射描述():
演示节点 = 包含启动描述(
PythonLaunchDescriptionSource([os.路.加入(
获取软件包共享目录('learning_tf2_cpp'), 'launch';),
'/turtle_tf2_demo_launch.py';]),
)
返回 启动说明([
演示节点,
节点(
包装='learning_tf2_cpp',
可执行='fixed_frame_tf2_broadcaster';,
名字='fixed_broadcaster';,
),
])
这个启动文件会导入所需的软件包,然后创建一个 演示节点
变量,该变量将存储我们在上一教程的启动文件中创建的节点。
代码的最后一部分将添加我们固定的 胡萝卜1
使用我们的 固定框架_TF2_播音员
节点。
节点(
包装='learning_tf2_cpp',
可执行='fixed_frame_tf2_broadcaster';,
名字='fixed_broadcaster';,
),
1.4 建设
运行 rosdep
在工作区的根目录下,检查是否存在缺失的依赖项。
rosdep install -i --from-path src --rosdistro iron -y
rosdep 只能在 Linux 上运行,因此您需要安装 几何参数
和 海龟
自己的依赖
rosdep 只能在 Linux 上运行,因此您需要安装 几何参数
和 海龟
自己的依赖
还是在工作区的根目录下,构建你的软件包:
colcon build --packages-select learning_tf2_cpp
colcon build --packages-select learning_tf2_cpp
colcon build --merge-install --packages-select learning_tf2_cpp
打开一个新终端,导航到工作区的根目录,然后获取设置文件:
install/setup.bash
install/setup.bash
# CMD
调用 install\setup.bat
# Powershell
.install\setup.ps1
1.5 运行
现在可以开始海龟广播员演示了:
ros2 launch learning_tf2_cpp turtle_tf2_fixed_frame_demo_launch.py
您应该注意到,新的 胡萝卜1
帧出现在转换树中。

如果你驾驶第一只乌龟四处走动,你应该会注意到,尽管我们添加了一个新的帧,但其行为与之前的教程相比并没有变化。这是因为增加一帧并不会影响其他帧,我们的监听器仍在使用之前定义的帧。
因此,如果我们想让第二只乌龟跟随胡萝卜而不是第一只乌龟,就需要更改 target_frame
.这有两种方法。一种方法是通过 target_frame
参数直接从控制台调入启动文件:
ros2 launch learning_tf2_cpp turtle_tf2_fixed_frame_demo_launch.py target_frame:=carrot1
第二种方法是更新启动文件。为此,打开 turtle_tf2_fixed_frame_demo_launch.py
文件,并添加 target_frame': 胡萝卜1
参数通过 启动参数
争论。
捍卫 生成发射描述():
演示节点 = 包含启动描述(
...,
启动参数={'target_frame': 'carrot1';}.项目(),
)
现在重建软件包,重启 turtle_tf2_fixed_frame_demo_launch.py
然后,你会看到第二只乌龟跟着胡萝卜走,而不是第一只乌龟!

2 编写动态帧广播器
我们在本教程中发布的额外帧是一个固定帧,与父帧相比不会随时间变化。不过,如果您想发布一个移动帧,可以编写代码使广播器随时间改变帧。让我们改变我们的 胡萝卜1
帧,使其相对于 乌龟1
帧。转到 learning_tf2_cpp
软件包。在 来源
输入以下命令,下载动态帧广播器代码:
wget https://raw.githubusercontent.com/ros/geometry_tutorials/ros2/turtle_tf2_cpp/src/dynamic_frame_tf2_broadcaster.cpp
wget https://raw.githubusercontent.com/ros/geometry_tutorials/ros2/turtle_tf2_cpp/src/dynamic_frame_tf2_broadcaster.cpp
在 Windows 命令行提示符下:
curl -sk https://raw.githubusercontent.com/ros/geometry_tutorials/ros2/turtle_tf2_cpp/src/dynamic_frame_tf2_broadcaster.cpp -o dynamic_frame_tf2_broadcaster.cpp
或使用 powershell:
curl https://raw.githubusercontent.com/ros/geometry_tutorials/ros2/turtle_tf2_cpp/src/dynamic_frame_tf2_broadcaster.cpp -o dynamic_frame_tf2_broadcaster.cpp
现在打开名为 dynamic_frame_tf2_broadcaster.cpp
:
#include 时间顺序<chrono>;
#include 功能强大;
#include <内存>;
#include "geometry_msgs/msg/transform_stamped.hpp";
#include "rclcpp/rclcpp.hpp";
#include "tf2_ros/transform_broadcaster.h";
使用 命名空间 标准::计时器;
缢 双人 PI = 3.141592653589793238463;
类 动态帧广播 : 公 rclcpp::节点
{
公:
动态帧广播()
: 节点("dynamic_frame_tf2_broadcaster";)
{
tf_broadcaster_ = 标准::共享<;tf2_ros::改造播音员>;(此);
定时器 = 此->;创建隔离墙计时器(
100毫秒, 标准::约束(及样品;动态帧广播::广播定时器回调, 此));
}
私人:
空白 广播定时器回调()
{
rclcpp::时间 现在 = 此->;获取时钟()->;现在();
双人 x = 现在.秒钟() * PI;
几何参数::信息::TransformStamped t;
t.页眉.盖章 = 现在;
t.页眉.frame_id = "turtle1";;
t.子帧标识 = "carrot1";;
t.变.译文.x = 10 * 罪过(x);
t.变.译文.y = 10 * 系数(x);
t.变.译文.z = 0.0;
t.变.自转.x = 0.0;
t.变.自转.y = 0.0;
t.变.自转.z = 0.0;
t.变.自转.w = 1.0;
tf_broadcaster_->;发送变换(t);
}
rclcpp::定时器基数::SharedPtr 定时器;
标准::共享_ptr<;tf2_ros::改造播音员>; tf_broadcaster_;
};
int 主要(int 参数, 烧焦 * 参数[])
{
rclcpp::启动(参数, 参数);
rclcpp::后旋(标准::共享<;动态帧广播>;());
rclcpp::关闭();
返回 0;
}
2.1 检查代码
我们没有固定定义 x 和 y 偏移量,而是使用了 sin()
和 cos()
在当前时间上的功能,使 胡萝卜1
是不断变化的。
双人 x = 现在.秒钟() * PI;
...
t.变.译文.x = 10 * 罪过(x);
t.变.译文.y = 10 * 系数(x);
2.2 CMakeLists.txt
导航一级回到 learning_tf2_cpp
目录中的 CMakeLists.txt
和 package.xml
文件的位置。
现在打开 CMakeLists.txt
添加可执行文件并命名 dynamic_frame_tf2_broadcaster
.
add_executable(dynamic_frame_tf2_broadcaster src/dynamic_frame_tf2_broadcaster.cpp)
ament_target_dependencies(
dynamic_frame_tf2_broadcaster
几何参数
rclcpp
tf2_ros
)
最后,添加 install(TARGETS...)
因此 玫瑰2 运行
能找到你的可执行文件:
install(TARGETS
dynamic_frame_tf2_broadcaster
DESTINATION lib/${PROJECT_NAME})
2.3 编写启动文件
要测试这段代码,请创建一个新的启动文件 turtle_tf2_dynamic_frame_demo_launch.py
在 src/learning_tf2_cpp/launch
目录,并粘贴以下代码:
舶来品 os
从 ament_index_python.packages 舶来品 获取软件包共享目录
从 启动 舶来品 启动说明
从 launch.actions 舶来品 包含启动描述
从 launch.launch_description_sources 舶来品 PythonLaunchDescriptionSource
从 launch_ros.actions 舶来品 节点
捍卫 生成发射描述():
演示节点 = 包含启动描述(
PythonLaunchDescriptionSource([os.路.加入(
获取软件包共享目录('learning_tf2_cpp'), 'launch';),
'/turtle_tf2_demo_launch.py';]),
启动参数={'target_frame': 'carrot1';}.项目(),
)
返回 启动说明([
演示节点,
节点(
包装='learning_tf2_cpp',
可执行='dynamic_frame_tf2_broadcaster';,
名字='dynamic_broadcaster';,
),
])
2.4 建设
运行 rosdep
在工作区的根目录下,检查是否存在缺失的依赖项。
rosdep install -i --from-path src --rosdistro iron -y
rosdep 只能在 Linux 上运行,因此您需要安装 几何参数
和 海龟
自己的依赖
rosdep 只能在 Linux 上运行,因此您需要安装 几何参数
和 海龟
自己的依赖
还是在工作区的根目录下,构建你的软件包:
colcon build --packages-select learning_tf2_cpp
colcon build --packages-select learning_tf2_cpp
colcon build --merge-install --packages-select learning_tf2_cpp
打开一个新终端,导航到工作区的根目录,然后获取设置文件:
install/setup.bash
install/setup.bash
# CMD
调用 install\setup.bat
# Powershell
.install\setup.ps1
2.5 运行
现在可以开始动态帧演示了:
ros2 launch learning_tf2_cpp turtle_tf2_dynamic_frame_demo_launch.py
你应该看到,第二只乌龟正跟着胡萝卜的位置不断变化。

摘要
在本教程中,您了解了 tf2 变换树、其结构和功能。您还了解到在局部框架内思考是最简单的方法,并学会了为局部框架添加额外的固定框架和动态框架。