【ROS2】中级:tf2- 编写静态广播器 (C++)

目标:学习如何向 tf2 广播静态坐标系。

教程级别:中级

 时间:15 分钟

 目录

  •  背景

  •  先决条件

  •  任务

    • 1. 创建一个包

    • 2. 编写静态广播节点

    •  3. 构建

    •  4. 运行

  • 发布静态变换的正确方法

  •  摘要

 背景

发布静态变换对于定义机器人基座与其传感器或非移动部件之间的关系非常有用。例如,在激光扫描仪中心的框架内推理激光扫描测量数据是最简单的。

这是一个独立的教程,涵盖了静态变换的基础知识,包括两个部分。在第一部分中,我们将编写代码将静态变换发布到 tf2。在第二部分中,我们将解释如何在 tf2_ros 中使用命令行 static_transform_publisher 可执行工具。

在接下来的两个教程中,我们将编写代码以复现《tf2 入门》教程中的演示。之后,后续教程将专注于使用更高级的 tf2 功能扩展演示。

 先决条件

在之前的教程中,您学习了如何创建工作区和创建包。

 任务

1. 创建一个包

首先,我们将创建一个包,用于本教程及后续教程。这个名为 learning_tf2_cpp 的包将依赖于 geometry_msgs 、 rclcpp 、 tf2 、 tf2_ros 和 turtlesim 。本教程的代码存储在这里。

打开一个新的终端并且配置你的 ROS 2 安装,以便 ros2 命令能够工作。导航到工作空间的 src 文件夹并创建一个新的包:

ros2 pkg create --build-type ament_cmake --license Apache-2.0 --dependencies geometry_msgs rclcpp tf2 tf2_ros turtlesim -- learning_tf2_cpp

您的终端将返回一条消息,确认您的包 learning_tf2_cpp 及其所有必要的文件和文件夹已创建。

cxy@ubuntu2404-cxy:~/ros2_ws/src$ ros2 pkg create --build-type ament_cmake --license Apache-2.0 --dependencies geometry_msgs rclcpp tf2 tf2_ros turtlesim -- learning_tf2_cpp
going to create a new package
package name: learning_tf2_cpp
destination directory: /home/cxy/ros2_ws/src
package format: 3
version: 0.0.0
description: TODO: Package description
maintainer: ['cxy <cxy@todo.todo>']
licenses: ['Apache-2.0']
build type: ament_cmake
dependencies: ['geometry_msgs', 'rclcpp', 'tf2', 'tf2_ros', 'turtlesim']
creating folder ./learning_tf2_cpp
creating ./learning_tf2_cpp/package.xml
creating source and include folder
creating folder ./learning_tf2_cpp/src
creating folder ./learning_tf2_cpp/include/learning_tf2_cpp
creating ./learning_tf2_cpp/CMakeLists.txt

2. 编写静态广播器节点

让我们首先创建源文件。在 src/learning_tf2_cpp/src 目录中,通过输入以下命令下载示例静态广播代码:

cxy@ubuntu2404-cxy:~/ros2_ws/src$ cd learning_tf2_cpp/src
cxy@ubuntu2404-cxy:~/ros2_ws/src/learning_tf2_cpp/src$ wget https://raw.githubusercontent.com/ros/geometry_tutorials/ros2/turtle_tf2_cpp/src/static_turtle_tf2_broadcaster.cpp

‍使用您喜欢的文本编辑器打开文件。

// 版权 2021 开源机器人基金会,公司。
//
// 根据Apache许可证版本2.0(“许可证”)许可;
// 除非符合许可证,否则不得使用此文件。
// 您可以在以下位置获取许可证副本:
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// 除非适用法律要求或书面同意,否则在许可证下分发的软件
// 按“原样”基础分发,无任何形式的明示或暗示保证或条件。
// 有关许可证规定的权限和限制的语言,请参阅许可证。


#include <memory>  // 导入内存管理库


#include "geometry_msgs/msg/transform_stamped.hpp"  // 导入geometry_msgs包中的TransformStamped消息类型
#include "rclcpp/rclcpp.hpp"  // 导入ROS客户端库C++包
#include "tf2/LinearMath/Quaternion.h"  // 导入tf2库中的Quaternion类
#include "tf2_ros/static_transform_broadcaster.h"  // 导入tf2_ros库中的StaticTransformBroadcaster类


class StaticFramePublisher : public rclcpp::Node  // 定义一个名为StaticFramePublisher的类,继承自rclcpp::Node类
{
public:
  explicit StaticFramePublisher(char * transformation[])  // 定义类的构造函数,参数为transformation数组
  : Node("static_turtle_tf2_broadcaster")  // 调用父类的构造函数,设置节点名为'static_turtle_tf2_broadcaster'
  {
    tf_static_broadcaster_ = std::make_shared<tf2_ros::StaticTransformBroadcaster>(this);  // 创建一个StaticTransformBroadcaster对象


    // 在启动时发布一次静态转换
    this->make_transforms(transformation);  // 调用make_transforms函数,参数为transformation
  }


private:
  void make_transforms(char * transformation[])  // 定义make_transforms函数,参数为transformation数组
  {
    geometry_msgs::msg::TransformStamped t;  // 创建一个TransformStamped对象t


    t.header.stamp = this->get_clock()->now();  // 设置t的时间戳为当前时间
    t.header.frame_id = "world";  // 设置t的frame_id为'world'
    t.child_frame_id = transformation[1];  // 设置t的child_frame_id为transformation的第二个元素


    t.transform.translation.x = atof(transformation[2]);  // 设置t的x平移为transformation的第三个元素
    t.transform.translation.y = atof(transformation[3]);  // 设置t的y平移为transformation的第四个元素
    t.transform.translation.z = atof(transformation[4]);  // 设置t的z平移为transformation的第五个元素
    tf2::Quaternion q;  // 创建一个Quaternion对象q
    q.setRPY(
      atof(transformation[5]),
      atof(transformation[6]),
      atof(transformation[7]));  // 调用q的setRPY函数,参数为transformation的第六、七、八个元素
    t.transform.rotation.x = q.x();  // 设置t的x旋转为q的x值
    t.transform.rotation.y = q.y();  // 设置t的y旋转为q的y值
    t.transform.rotation.z = q.z();  // 设置t的z旋转为q的z值
    t.transform.rotation.w = q.w();  // 设置t的w旋转为q的w值


    tf_static_broadcaster_->sendTransform(t);  // 调用tf_static_broadcaster_的sendTransform函数,参数为t
  }


  std::shared_ptr<tf2_ros::StaticTransformBroadcaster> tf_static_broadcaster_;  // 定义一个StaticTransformBroadcaster的智能指针tf_static_broadcaster_
};


int main(int argc, char * argv[])  // 定义主函数
{
  auto logger = rclcpp::get_logger("logger");  // 获取一个名为'logger'的日志记录器


  // 从命令行参数获取参数
  if (argc != 8) {  // 如果命令行参数的数量不等于8
    RCLCPP_INFO(
      logger, "Invalid number of parameters\nusage: "
      "$ ros2 run learning_tf2_cpp static_turtle_tf2_broadcaster "
      "child_frame_name x y z roll pitch yaw");  // 输出错误信息
    return 1;  // 返回1
  }


  // As the parent frame of the transform is `world`, it is
  // necessary to check that the frame name passed is different
  if (strcmp(argv[1], "world") == 0) {  // 如果命令行参数的第二个元素等于'world'
    RCLCPP_INFO(logger, "Your static turtle name cannot be 'world'");  // 输出错误信息
    return 2;  // 返回2
  }


  // 传递参数并初始化节点
  rclcpp::init(argc, argv);  // 初始化ROS客户端库
  rclcpp::spin(std::make_shared<StaticFramePublisher>(argv));  // 开始处理来自节点的所有回调
  rclcpp::shutdown();  // 关闭ROS客户端库
  return 0;  // 返回0
}
2.1 检查代码 

现在让我们来看看与发布静态乌龟姿态到 tf2 相关的代码。前几行包括所需的头文件。首先我们包含 geometry_msgs/msg/transform_stamped.hpp 以访问 TransformStamped 消息类型,我们将发布到变换树。

#include "geometry_msgs/msg/transform_stamped.hpp"

之后,包含 rclcpp 以便可以使用其 rclcpp::Node 类。

#include "rclcpp/rclcpp.hpp"

tf2::Quaternion 是一个四元数类,它提供了方便的函数用于将欧拉角转换为四元数,反之亦然。我们还包括了 tf2_ros/static_transform_broadcaster.h ,以便使用 StaticTransformBroadcaster 来轻松发布静态变换。

#include "tf2/LinearMath/Quaternion.h"
#include "tf2_ros/static_transform_broadcaster.h"

StaticFramePublisher 类构造函数使用名称 static_turtle_tf2_broadcaster 初始化节点。然后,创建 StaticTransformBroadcaster ,它将在启动时发送一个静态变换。

tf_static_broadcaster_ = std::make_shared<tf2_ros::StaticTransformBroadcaster>(this);


this->make_transforms(transformation);

在这里,我们创建一个 TransformStamped 对象,这将是我们一旦填充后将发送的消息。在传递实际的变换值之前,我们需要为其提供适当的元数据。

  1. 我们需要为正在发布的转换提供一个时间戳,我们将用当前时间来标记它, this->get_clock()->now()

  2. 然后我们需要设置我们正在创建的链接的父框架的名称,在这种情况下是 world

  3. 最后,我们需要设置我们正在创建的链接的子框架的名称

geometry_msgs::msg::TransformStamped t;


t.header.stamp = this->get_clock()->now();
t.header.frame_id = "world";
t.child_frame_id = transformation[1];

在这里,我们填充乌龟的 6D 姿态(平移和旋转)。

t.transform.translation.x = atof(transformation[2]);
t.transform.translation.y = atof(transformation[3]);
t.transform.translation.z = atof(transformation[4]);
tf2::Quaternion q;
q.setRPY(
  atof(transformation[5]),
  atof(transformation[6]),
  atof(transformation[7]));
t.transform.rotation.x = q.x();
t.transform.rotation.y = q.y();
t.transform.rotation.z = q.z();
t.transform.rotation.w = q.w();

最后,我们使用 sendTransform() 函数广播静态变换。

tf_static_broadcaster_->sendTransform(t);
 2.2 更新 package.xml

返回到 src/learning_tf2_cpp 目录, CMakeLists.txt 和 package.xml 文件已为您创建。

使用您的文本编辑器打开 package.xml 。

在创建包教程中提到的,确保填写 <description> 、 <maintainer> 和 <license> 标签:

<description>Learning tf2 with rclcpp</description>
  <maintainer email="cxy@126.com">cxy</maintainer>
  <license>Apache-2.0</license>

确保保存文件。

2.3 CMakeLists.txt

将可执行文件添加到 CMakeLists.txt 并命名为 static_turtle_tf2_broadcaster ,稍后您将与 ros2 run 一起使用。

add_executable(static_turtle_tf2_broadcaster src/static_turtle_tf2_broadcaster.cpp)
ament_target_dependencies(
   static_turtle_tf2_broadcaster
   geometry_msgs
   rclcpp
   tf2
   tf2_ros
)

最后,添加 install(TARGETS…) 部分,以便 ros2 run 可以找到你的可执行文件:

install(TARGETS
   static_turtle_tf2_broadcaster
   DESTINATION lib/${PROJECT_NAME})

3. 构建

在工作区的根目录运行 rosdep 以检查构建前缺失的依赖项是一个好习惯:

rosdep install -i --from-path src --rosdistro jazzy -y

仍在您的工作区根目录下,构建您的新包:

colcon build --packages-select learning_tf2_cpp

打开一个新的终端,导航到工作区的根目录,然后加载设置文件:

. install/setup.bash

4. 运行

现在运行 static_turtle_tf2_broadcaster 节点:

cxy@ubuntu2404-cxy:~$ cd ~/ros2_ws
cxy@ubuntu2404-cxy:~/ros2_ws$ colcon build --packages-select learning_tf2_cpp
Starting >>> learning_tf2_cpp
Finished <<< learning_tf2_cpp [0.33s]                     


Summary: 1 package finished [0.57s]
cxy@ubuntu2404-cxy:~/ros2_ws$ . install/setup.bash
cxy@ubuntu2404-cxy:~/ros2_ws$ ros2 run learning_tf2_cpp static_turtle_tf2_broadcaster mystaticturtle 0 0 1 0 0 0

‍这将为 mystaticturtle 设置一个海龟姿势广播,使其在地面上方 1 米处浮动。

我们现在可以通过回显 tf_static 主题来检查静态变换是否已经发布

ros2 topic echo /tf_static

如果一切顺利,您应该会看到一个单一的静态变换

cxy@ubuntu2404-cxy:~$ ros2 topic echo /tf_static
transforms:
- header:
    stamp:
      sec: 1720669722
      nanosec: 668744038
    frame_id: world
  child_frame_id: mystaticturtle
  transform:
    translation:
      x: 0.0
      y: 0.0
      z: 1.0
    rotation:
      x: 0.0
      y: 0.0
      z: 0.0
      w: 1.0

bbd250531ecf25cd30cede292e225a0f.png

发布静态变换的正确方法

本教程旨在展示如何使用 StaticTransformBroadcaster 发布静态变换。在您的实际开发过程中,您不应该自己编写这段代码,而应该使用专用的 tf2_ros 工具来完成。 tf2_ros 提供了一个名为 static_transform_publisher 的可执行文件,可以作为命令行工具或添加到启动文件中的节点使用。

以下命令使用米和弧度中的 x/y/z 偏移量以及滚动/俯仰/偏航发布到 tf2 的静态坐标变换。在 ROS 2 中,滚动/俯仰/偏航分别指的是绕 x/y/z 轴的旋转。

ros2 run tf2_ros static_transform_publisher --x x --y y --z z --yaw yaw --pitch pitch --roll roll --frame-id frame_id --child-frame-id child_frame_id

以下命令使用米为单位的 x/y/z 偏移量和四元数表示的滚转/俯仰/偏航,向 tf2 发布一个静态坐标变换。

ros2 run tf2_ros static_transform_publisher --x x --y y --z z --qx qx --qy qy --qz qz --qw qw --frame-id frame_id --child-frame-id child_frame_id

static_transform_publisher 旨在作为命令行工具供手动使用,也可在 launch 文件中使用,用于设置静态变换。例如:

from launch import LaunchDescription
from launch_ros.actions import Node


def generate_launch_description():
    return LaunchDescription([
        Node(
             package='tf2_ros',
             executable='static_transform_publisher',
             arguments = ['--x', '0', '--y', '0', '--z', '1', '--yaw', '0', '--pitch', '0', '--roll', '0', '--frame-id', 'world', '--child-frame-id', 'mystaticturtle']
        ),
    ])

请注意,除 --frame-id 和 --child-frame-id 外的所有参数都是可选的;如果没有指定某个选项,则将假定为身份。

 摘要

在本教程中,您学习了静态变换如何用于定义帧之间的静态关系,例如 mystaticturtle 相对于 world 帧。此外,您还了解了静态变换如何有助于理解传感器数据,例如来自激光扫描仪的数据,通过将数据与公共坐标帧相关联。最后,您编写了自己的节点以将静态变换发布到 tf2,并学习了如何使用 static_transform_publisher 可执行文件和启动文件发布所需的静态变换。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值