编写静态广播器 (Python)

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

教程级别: 中级

时间: 15 分钟

背景

发布静态变换对于定义机器人底座与其传感器或非移动部件之间的关系很有用。 例如,在激光扫描仪中心的框架中推断激光扫描测量是最容易的。

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

在接下来的两个教程中,我们将编写代码来重现 Introduction to tf2 教程中的演示。 之后,以下教程将重点介绍如何使用更高级的 tf2 功能扩展演示。

先决条件

在之前的教程中,您学习了如何:doc:创建工作区

任务

1 创建包

首先,我们将创建一个将用于本教程和后续教程的包。

名为“learning_tf2_py”的包将依赖于“geometry_msgs”、“python3-numpy”、“rclpy”、“tf2_ros_py”和“turtlesim”。 本教程的代码存储在`此处<https://raw.githubusercontent.com/ros/geometry_tutorials/ros2/turtle_tf2_py/turtle_tf2_py/static_turtle_tf2_broadcaster.py>`_。

打开一个新终端并:doc:source your ROS 2 installation,以便``ros2``命令可以正常工作。 导航到工作区的``src``文件夹并创建一个新包:

ros2 pkg create --build-type ament_python --license Apache-2.0 -- learning_tf2_py

您的终端将返回一条消息,验证您的包“learning_tf2_py”及其所有必要的文件和文件夹的创建。

2 编写静态广播节点

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

wget https://raw.githubusercontent.com/ros/geometry_tutorials/ros2/turtle_tf2_py/turtle_tf2_py/static_turtle_tf2_broadcaster.py

现在使用您喜欢的文本编辑器打开名为“static_turtle_tf2_broadcaster.py”的文件。

import math
import sys

from geometry_msgs.msg import TransformStamped

import numpy as np

import rclpy
from rclpy.node import Node

from tf2_ros.static_transform_broadcaster import StaticTransformBroadcaster


def quaternion_from_euler(ai, aj, ak):
    ai /= 2.0
    aj /= 2.0
    ak /= 2.0
    ci = math.cos(ai)
    si = math.sin(ai)
    cj = math.cos(aj)
    sj = math.sin(aj)
    ck = math.cos(ak)
    sk = math.sin(ak)
    cc = ci*ck
    cs = ci*sk
    sc = si*ck
    ss = si*sk

    q = np.empty((4, ))
    q[0] = cj*sc - sj*cs
    q[1] = cj*ss + sj*cc
    q[2] = cj*cs - sj*sc
    q[3] = cj*cc + sj*ss

    return q


class StaticFramePublisher(Node):
    """
    Broadcast transforms that never change.

    This example publishes transforms from `world` to a static turtle frame.
    The transforms are only published once at startup, and are constant for all
    time.
    """

    def __init__(self, transformation):
        super().__init__('static_turtle_tf2_broadcaster')

        self.tf_static_broadcaster = StaticTransformBroadcaster(self)

        # Publish static transforms once at startup
        self.make_transforms(transformation)

    def make_transforms(self, transformation):
        t = TransformStamped()

        t.header.stamp = self.get_clock().now().to_msg()
        t.header.frame_id = 'world'
        t.child_frame_id = transformation[1]

        t.transform.translation.x = float(transformation[2])
        t.transform.translation.y = float(transformation[3])
        t.transform.translation.z = float(transformation[4])
        quat = quaternion_from_euler(
            float(transformation[5]), float(transformation[6]), float(transformation[7]))
        t.transform.rotation.x = quat[0]
        t.transform.rotation.y = quat[1]
        t.transform.rotation.z = quat[2]
        t.transform.rotation.w = quat[3]

        self.tf_static_broadcaster.sendTransform(t)


def main():
    logger = rclpy.logging.get_logger('logger')

    # obtain parameters from command line arguments
    if len(sys.argv) != 8:
        logger.info('Invalid number of parameters. Usage: \n'
                    '$ ros2 run learning_tf2_py static_turtle_tf2_broadcaster'
                    'child_frame_name x y z roll pitch yaw')
        sys.exit(1)

    if sys.argv[1] == 'world':
        logger.info('Your static turtle name cannot be "world"')
        sys.exit(2)

    # pass parameters and initialize node
    rclpy.init()
    node = StaticFramePublisher(sys.argv)
    try:
        rclpy.spin(node)
    except KeyboardInterrupt:
        pass

    rclpy.shutdown()

2.1 检查代码

现在让我们看看与将静态海龟姿势发布到 tf2 相关的代码。 第一行导入所需的包。 首先,我们从“geometry_msgs”导入“TransformStamped”,它为我们提供了将发布到转换树的消息模板。

from geometry_msgs.msg import TransformStamped

随后,导入“rclpy”,以便可以使用其“Node”类。

import rclpy
from rclpy.node import Node

“tf2_ros” 包提供了一个“StaticTransformBroadcaster”,使静态转换的发布变得简单。 要使用“StaticTransformBroadcaster”,我们需要从“tf2_ros”模块导入它。

from tf2_ros.static_transform_broadcaster import StaticTransformBroadcaster

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

self.tf_static_broadcaster = StaticTransformBroadcaster(self)
self.make_transforms(transformation)

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

#。我们需要为发布的转换提供一个时间戳,我们将用当前时间标记它,self.get_clock().now()

#。然后我们需要设置我们正在创建的链接的父框架的名称,在本例中为``world``

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

t = TransformStamped()

t.header.stamp = self.get_clock().now().to_msg()
t.header.frame_id = 'world'
t.child_frame_id = transformation[1]

这里我们填充了海龟的 6D 姿势(平移和旋转)。

t.transform.translation.x = float(transformation[2])
t.transform.translation.y = float(transformation[3])
t.transform.translation.z = float(transformation[4])
quat = quaternion_from_euler(
    float(transformation[5]), float(transformation[6]), float(transformation[7]))
t.transform.rotation.x = quat[0]
t.transform.rotation.y = quat[1]
t.transform.rotation.z = quat[2]
t.transform.rotation.w = quat[3]

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

self.tf_static_broadcaster.sendTransform(t)

2.2 更新 package.xml

返回上一级到 src/learning_tf2_py 目录,其中已为您创建了 setup.pysetup.cfgpackage.xml 文件。

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

创建包 教程中所述,请确保填写 <description><maintainer><license> 标签:

<description>Learning tf2 with rclpy</description>
<maintainer email="you@email.com">Your Name</maintainer>
<license>Apache-2.0</license>

在上述行之后,添加与节点的导入语句相对应的以下依赖项:

<exec_depend>geometry_msgs</exec_depend>
<exec_depend>python3-numpy</exec_depend>
<exec_depend>rclpy</exec_depend>
<exec_depend>tf2_ros_py</exec_depend>
<exec_depend>turtlesim</exec_depend>

这声明了执行代码时所需的 geometry_msgspython3-numpyrclpytf2_ros_pyturtlesim 依赖项。

确保保存文件。

2.3 添加入口点

要允许 ros2 run 命令运行您的节点,您必须将入口点添加到 setup.py``(位于 ``src/learning_tf2_py 目录中)。

'console_scripts': 括号之间添加以下行:

'static_turtle_tf2_broadcaster = learning_tf2_py.static_turtle_tf2_broadcaster:main',

3 构建

在构建之前,最好在工作区的根目录中运行“rosdep”来检查是否缺少依赖项:

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

仍然在工作区的根目录中,构建新包:

colcon build --packages-select learning_tf2_py

打开一个新终端,导航到工作区的根目录,然后获取安装文件:

. install/setup.bash

4 运行

现在运行“static_turtle_tf2_broadcaster”节点:

ros2 run learning_tf2_py static_turtle_tf2_broadcaster mystaticturtle 0 0 1 0 0 0

这将为“mystaticturtle”设置一个海龟姿势广播,使其漂浮在离地面 1 米的地方。

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

ros2 topic echo /tf_static

如果一切顺利你应该看到一个静态变换

transforms:
- header:
   stamp:
      sec: 1622908754
      nanosec: 208515730
   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

发布静态变换的正确方法

本教程旨在展示如何使用“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”之外的所有参数都是可选的;如果未指定特定选项,则将假定身份。

摘要

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