Skip to content

ROS2入门:常用工具

一、坐标变换TF

在机器人系统中,坐标变换是必不可少的,比如把相机坐标系下的点转换到机器人底盘坐标系下。ROS2 提供了tf2tf2_ros用于管理和广播坐标变换。

1. 基本概念

Frame(坐标系):如 base_link(机器人底盘)、camera_link(相机)、odom(里程计)。

Transform(变换):描述两个坐标系之间的位置和平移关系,包括平移(x, y, z)和旋转(roll, pitch, yaw)或四元数 (x, y, z, w)

2.命令行使用TF 坐标变换工具

示例:已知机器人基坐标系 base_link 和 雷达坐标系 base_laser 之间的坐标关系,求 base_laser 系下的障碍,在 base_link 系的坐标。

发布静态坐标变换,使用 static_transform_publisher 可以快速创建一个固定的坐标变换:

bash
ros2 run tf2_ros static_transform_publisher \
    --x 0.1 --y 0.0 --z 0.2 \
    --roll 0.0 --pitch 0.0 --yaw 0.0 \
    --frame-id base_link \
    --child-frame-id base_laser

如下

说明:

  • --x/--y/--z:平移向量,子坐标系相对于父坐标系的位置(单位:米)。
  • --roll/--pitch/--yaw:旋转角度(欧拉角,单位:弧度)。
  • --frame-id:父坐标系名称。
  • --child-frame-id:子坐标系名称。

例子中:base_laser 相对于 base_link 有 (0.1, 0.0, 0.2) 的平移,没有旋转。

发布多个静态变换,如果系统中有多个传感器,可以连续启动多个 static_transform_publisher

bash
# 发布 base_laser 到 wall_point 的变换
ros2 run tf2_ros static_transform_publisher \
    --x 0.3 --y 0.0 --z 0.0 \
    --roll 0.0 --pitch 0.0 --yaw 0.0 \
    --frame-id base_laser \
    --child-frame-id wall_point

如下:

这样,系统中就有以下层级关系:

base_link → base_laser → wall_point

使用tf2_echo可以实时查看任意两个坐标系间的平移和旋转:

bash
ros2 run tf2_ros tf2_echo base_link wall_point
  • 第一个参数是父坐标系(frame_id)。
  • 第二个参数是子坐标系(child_frame_id)。

使用 view_frames 可以生成系统中所有坐标系的拓扑图:

3.使用 MRPT 可视化 TF2 坐标系

除了 view_frames 生成的静态 PDF 图,MRPT(Mobile Robot Programming Toolkit)提供了动态、交互式的 3D 坐标系可视化工具,非常适合调试多传感器机器人系统。

安装 MRPT ROS2:

bash
sudo apt install mrpt-apps

启动可视化,可以简单体验坐标选择

3d-rotation-converter

4、实践:手眼坐标转换(python)

1.机械臂底座到相机(静态TF发布)

在 ROS2 中,我们通常通过发布 tf2 的静态坐标变换,描述相机在机械臂坐标系中的位置和姿态,为后续手眼标定、目标识别和机械臂运动规划提供参考。

安装依赖包:

bash
sudo apt install ros-${ROS_DISTRO}-tf-transformations
sudo pip3 install transforms3d

tf-transformationstransforms3d用于坐标变换的计算和调试。

在节点中创建静态 TF 广播器:

先创建 ROS2 工作区(以自己的实际目录为准):

mkdir -p ~/ros2_ws/src
cd ~/ros2_ws/src

在 src 下创建一个 Python 功能包,比如叫 handeye_tf:

bash
cd ~/ros2_ws/src
ros2 pkg create --build-type ament_python handeye_tf --dependencies rclpy geometry_msgs tf2_ros

新建静态TF节点 handeye_tf/handeye_tf/static_tf_broadcaster.py

python
import math
import rclpy
from rclpy.node import Node
from tf2_ros import StaticTransformBroadcaster
from geometry_msgs.msg import TransformStamped
from tf_transformations import quaternion_from_euler


class StaticTFBroadcaster(Node):

    def __init__(self):
        super().__init__('static_tf2_broadcaster')
        self.static_broadcaster_ = StaticTransformBroadcaster(self)
        self.publish_static_tf()

    def publish_static_tf(self):
        transform = TransformStamped()
        transform.header.stamp = self.get_clock().now().to_msg()
        transform.header.frame_id = "base_link"
        transform.child_frame_id = "camera_link"
        transform.transform.translation.x = 0.5
        transform.transform.translation.y = 0.3
        transform.transform.translation.z = 0.6
        # 欧拉角转四元数
        rotation_quat = quaternion_from_euler(math.radians(180), 0, 0)
        transform.transform.rotation.x = rotation_quat[0]
        transform.transform.rotation.y = rotation_quat[1]
        transform.transform.rotation.z = rotation_quat[2]
        transform.transform.rotation.w = rotation_quat[3]
      	# 发布静态坐标变换
        self.static_broadcaster_.sendTransform(transform)
        self.get_logger().info(f"发布 TF:{transform}")


def main():
    rclpy.init()
    static_tf_broadcaster = StaticTFBroadcaster()
    rclpy.spin(static_tf_broadcaster)
    rclpy.shutdown()

修改 setup.py,添加节点入口:

python
entry_points={
    'console_scripts': [
        'static_tf_broadcaster = handeye_tf.static_tf_broadcaster:main',
    ],
},

编译 & 运行

colcon build
source install/setup.bash
ros2 run handeye_tf static_tf_broadcaster

如下:

查看坐标话题

ros2 topic echo /tf_static

2.动态发布目标相对相机的坐标

在实际场景中,目标(如瓶子、零件)相对于相机的位置会随着机械臂运动或相机视角的变化不断更新,所以我们需要动态发布 TF,让 TF 树实时反映目标的位置和姿态。

新建动态 TF 节点,在 handeye_tf/handeye_tf/ 下新建 dynamic_tf_broadcaster.py 文件:

python
import rclpy
from rclpy.node import Node
from tf2_ros import TransformBroadcaster
from geometry_msgs.msg import TransformStamped
from tf_transformations import quaternion_from_euler


class DynamicTFBroadcaster(Node):
    def __init__(self):
        super().__init__("tf_node")
        self.tf_broadcaster_ = TransformBroadcaster(self)
        # 动态TF需要持续发布,这里发布频率设置为 100HZ
        self.timer_ = self.create_timer(0.01, self.publish_transform)

    def publish_transform(self):
        transform = TransformStamped()
        transform.header.stamp = self.get_clock().now().to_msg()
        transform.header.frame_id = "camera_link"
        transform.child_frame_id = "bottle_link"
        transform.transform.translation.x = 0.2
        transform.transform.translation.y = 0.0
        transform.transform.translation.z = 0.5
        rotation_quat = quaternion_from_euler(0, 0, 0)
        transform.transform.rotation.x = rotation_quat[0]
        transform.transform.rotation.y = rotation_quat[1]
        transform.transform.rotation.z = rotation_quat[2]
        transform.transform.rotation.w = rotation_quat[3]
        self.tf_broadcaster_.sendTransform(transform)


def main():
    rclpy.init()
    tf_node = DynamicTFBroadcaster()
    rclpy.spin(tf_node)
    rclpy.shutdown()

修改 setup.py

entry_points={
    'console_scripts': [
        'static_tf_broadcaster = handeye_tf.static_tf_broadcaster:main',
        'dynamic_tf_broadcaster = handeye_tf.dynamic_tf_broadcaster:main',
    ],
},

编译并运行

colcon build
source install/setup.bash

先运行静态TF节点

ros2 run handeye_tf static_tf_broadcaster

再开一个终端运行动态TF节点

ros2 run handeye_tf dynamic_tf_broadcaster

3. 查看坐标间的变换关系

可以使用命令查看:

ros2 run tf2_ros tf2_echo base_link  bottle_link

下面使用python来查询TF关系:

新建 TF 监听节点,在 handeye_tf/handeye_tf/ 下新建 tf_listener.py 文件:

python
import rclpy
from rclpy.node import Node
from tf2_ros import TransformListener, Buffer
from tf_transformations import euler_from_quaternion


class TFListener(Node):

    def __init__(self):
        super().__init__("tf2_listener")
        self.buffer_ = Buffer()
        self.listener_ = TransformListener(self.buffer_, self)
        self.timer_ = self.create_timer(1, self.get_transform)

    def get_transform(self):
        try:
            result = self.buffer_.lookup_transform("base_link", "bottle_link", rclpy.time.Time(seconds=0), rclpy.time.Duration(seconds=1))
            transform = result.transform
            rotation_euler = euler_from_quaternion([
                transform.rotation.x,
                transform.rotation.y,
                transform.rotation.z,
                transform.rotation.w
            ])
            self.get_logger().info(f"平移:{transform.translation},旋转四元数:{transform.rotation}:旋转欧拉角:{rotation_euler}")
        except Exception as e:
            self.get_logger().warn(f"不能够获取坐标变换,原因: {str(e)}")


def main():
    rclpy.init()
    node = TFListener()
    rclpy.spin(node)
    rclpy.shutdown()

修改 setup.py

    entry_points={
        'console_scripts': [
            'static_tf_broadcaster = handeye_tf.static_tf_broadcaster:main',
            'dynamic_tf_broadcaster = handeye_tf.dynamic_tf_broadcaster:main',
            'tf_listener = handeye_tf.tf_listener:main',
        ],
    },

编译并运行

colcon build
source install/setup.bash
ros2 run handeye_tf  tf_listener

如下:

5、实践:地图坐标系变换(C++)

1. C++发布静态TF

待补充: https://www.bilibili.com/video/BV1h8xnePEMe

2. C++发布动态TF

待补充: https://www.bilibili.com/video/BV1h8xnePEMe

3. C++查询TF关系

待补充: https://www.bilibili.com/video/BV1h8xnePEMe

二、可视化工具rqt

rqt 是 ROS 提供的一个基于 Qt 的图形化调试工具,可以用插件方式查看话题、参数、节点关系图等,非常适合开发调试。

安装:

sudo apt install ros-${ROS_DISTRO}-rqt ros-${ROS_DISTRO}-rqt-common-plugins

启动:

bash
rqt

常用插件:

  • Node Graph:查看节点之间的话题关系图,直观展示 TF 节点的发布关系。
  • Topic Monitor:订阅并查看某个话题的实时消息内容,比如 /tf_static 和 /tf。
  • TF Tree:可视化 TF 树,展示各坐标系之间的父子关系。

三、数据可视化工具Rviz

Rviz 是 ROS 中最常用的 3D 可视化工具,适合展示机器人模型、传感器数据和 TF 树。

安装:

sudo apt install ros-${ROS_DISTRO}-rviz2

启动:

rviz2

操作步骤:

  1. 打开 Rviz2,添加 TF 显示项,可以看到发布的 base_link、camera_link、bottle_link 坐标系。
  2. 可以添加 RobotModel 查看机械臂模型,结合 URDF 文件更直观。
  3. 可以添加点云、图像等可视化显示项,辅助调试手眼标定和感知功能。

比如说使用TF工具,查看1.4 实例里面的坐标关系:

四、数据记录工具bag

rosbag 是 ROS 提供的数据录制和回放工具,方便调试时记录话题消息,之后离线回放分析。

安装:

sudo apt install ros-${ROS_DISTRO}-ros2bag ros-${ROS_DISTRO}-rosbag2-storage-default-plugins

录制:

ros2 bag record /tf /tf_static

这会将 /tf 和 /tf_static 两个话题数据保存到一个 bag 文件夹中。

回放:

ros2 bag play <bag文件夹>

查看 bag 文件:

ros2 bag info <bag文件夹>

典型用途:

保存机器人运行时的数据,便于分析。离线调试算法(比如视觉识别、路径规划)时无需真实硬件。