ROS2学习(15)------ROS2 TF2 机器人坐标系管理器

news2025/7/16 2:01:46
  • 操作系统:ubuntu22.04
  • IDE:Visual Studio Code
  • 编程语言:C++11
  • ROS版本:2

在 ROS 2 中,TF2(Transform Library, v2) 是一个非常核心的工具库,用于管理多个坐标系之间的 变换关系(translation + rotation)。它广泛应用于机器人导航、SLAM、机械臂控制等场景。

什么是 TF2?

简单来说:

TF2 是 ROS 2 中用于实时跟踪和转换多个坐标系之间位置与姿态关系的系统。

你可以把它想象成机器人的“空间感知大脑”,帮助你在不同坐标系之间进行数据对齐和坐标变换。

常见应用场景

  • 将激光雷达的数据从传感器坐标系转换到机器人底盘坐标系。
  • 在导航中将地图坐标系(map)下的路径点转换为机器人当前坐标系(base_link)下的相对位置。
  • 控制机械臂末端执行器时,需要知道其相对于世界坐标系的位置。
  • 多传感器融合(如 IMU + 激光雷达 + 视觉)时,需要统一坐标系。

核心概念

名称含义
frame_id坐标系名称,例如 /base_link, /laser, /map
transform描述两个坐标系之间的平移和旋转(translation + rotation)
tf2_ros::TransformBroadcaster发布坐标变换信息
tf2_ros::TransformListener订阅并缓存坐标变换信息
tf2::Buffer存储所有已知的坐标变换,支持查询历史数据
static_transform_publisher用于发布静态坐标变换(常用于 URDF)

使用 TF2 的基本流程

  1. 安装依赖包(Ubuntu)
 sudo apt install ros-humble-tf2-tools ros-humble-tf2-ros ros-humble-geometry-msgs
  1. 示例:发布和监听坐标变换

下面是一个简单的 C++ 示例,演示如何使用 TF2 来广播和监听坐标变换。
创建 my_tf2_demo 包的命令如下:

ros2 pkg  create --build-type ament_cmake my_tf2_demo

文件结构:
在这里插入图片描述
3.示例一:发布坐标变换(tf2_broadcaster.cpp)

#include <rclcpp/rclcpp.hpp>
#include <tf2_ros/transform_broadcaster.h>
#include <geometry_msgs/msg/transform_stamped.hpp>

class FramePublisher : public rclcpp::Node {
public:
    FramePublisher() : Node("tf2_broadcaster") {
        broadcaster_ = std::make_shared<tf2_ros::TransformBroadcaster>(this);

        timer_ = this->create_wall_timer(
            std::chrono::milliseconds(100),
            [this]() { this->timer_callback(); });
    }

private:
    void timer_callback() {
        geometry_msgs::msg::TransformStamped t;

        t.header.stamp = this->now();
        t.header.frame_id = "world";
        t.child_frame_id = "robot_base";

        // 位置
        t.transform.translation.x = 1.0;
        t.transform.translation.y = 0.5;
        t.transform.translation.z = 0.0;

        // 四元数(绕 Z 轴旋转 30 度)
        double angle = 0.5236; // radians
        t.transform.rotation.w = cos(angle / 2);
        t.transform.rotation.x = 0.0;
        t.transform.rotation.y = 0.0;
        t.transform.rotation.z = sin(angle / 2);

        broadcaster_->sendTransform(t);
    }

    rclcpp::TimerBase::SharedPtr timer_;
    std::shared_ptr<tf2_ros::TransformBroadcaster> broadcaster_;
};

int main(int argc, char * argv[]) {
    rclcpp::init(argc, argv);
    auto node = std::make_shared<FramePublisher>();
    rclcpp::spin(node);
    rclcpp::shutdown();
    return 0;
}

4.示例二:监听并查询坐标变换(tf2_listener.cpp)

#include <rclcpp/rclcpp.hpp>
#include <tf2_ros/buffer.h>
#include <tf2_ros/transform_listener.h>
#include <geometry_msgs/msg/transform_stamped.hpp>

class FrameListener : public rclcpp::Node {
public:
    FrameListener() : Node("tf2_listener") {
        tf_buffer_ = std::make_unique<tf2_ros::Buffer>(this->get_clock());
        listener_ = std::make_shared<tf2_ros::TransformListener>(*tf_buffer_);

        timer_ = this->create_wall_timer(
            std::chrono::seconds(1),
            [this]() { this->timer_callback(); });
    }

private:
    void timer_callback() {
        try {
            geometry_msgs::msg::TransformStamped transform =
                tf_buffer_->lookupTransform("world", "robot_base", tf2::TimePointZero);

            RCLCPP_INFO(this->get_logger(), "Translation: (%.2f, %.2f, %.2f)",
                        transform.transform.translation.x,
                        transform.transform.translation.y,
                        transform.transform.translation.z);

            RCLCPP_INFO(this->get_logger(), "Rotation (quat): (%.2f, %.2f, %.2f, %.2f)",
                        transform.transform.rotation.x,
                        transform.transform.rotation.y,
                        transform.transform.rotation.z,
                        transform.transform.rotation.w);

        } catch (const tf2::TransformException & ex) {
            RCLCPP_WARN(this->get_logger(), "Could not get transform: %s", ex.what());
        }
    }

    rclcpp::TimerBase::SharedPtr timer_;
    std::unique_ptr<tf2_ros::Buffer> tf_buffer_;
    std::shared_ptr<tf2_ros::TransformListener> listener_;
};

int main(int argc, char * argv[]) {
    rclcpp::init(argc, argv);
    auto node = std::make_shared<FrameListener>();
    rclcpp::spin(node);
    rclcpp::shutdown();
    return 0;
}
  1. 配置 CMakeLists.txt
cmake_minimum_required(VERSION 3.8)
project(my_tf2_demo)

if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
  add_compile_options(-Wall -Wextra -Wpedantic)
endif()

# find dependencies
find_package(ament_cmake REQUIRED)
# uncomment the following section in order to fill in
# further dependencies manually.
# find_package(<dependency> REQUIRED)

find_package(tf2_ros REQUIRED)
find_package(geometry_msgs REQUIRED)
find_package(rclcpp REQUIRED)

message(STATUS "rclcpp include dirs: ${rclcpp_INCLUDE_DIRS}")
message(STATUS "rclcpp libraries: ${rclcpp_LIBRARIES}")

if(BUILD_TESTING)
  find_package(ament_lint_auto REQUIRED)
  # the following line skips the linter which checks for copyrights
  # comment the line when a copyright and license is added to all source files
  set(ament_cmake_copyright_FOUND TRUE)
  # the following line skips cpplint (only works in a git repo)
  # comment the line when this package is in a git repo and when
  # a copyright and license is added to all source files
  set(ament_cmake_cpplint_FOUND TRUE)
  ament_lint_auto_find_test_dependencies()
endif()


add_executable(tf2_broadcaster src/tf2_broadcaster.cpp)
target_link_libraries(tf2_broadcaster PRIVATE rclcpp::rclcpp tf2_ros::tf2_ros)

add_executable(tf2_listener src/tf2_listener.cpp)
target_link_libraries(tf2_listener PRIVATE rclcpp::rclcpp tf2_ros::tf2_ros)

install(TARGETS
  tf2_broadcaster
  tf2_listener
  DESTINATION lib/${PROJECT_NAME})

ament_package()

  1. 编译并运行
colcon build --packages-select my_tf2_demo
source install/setup.bash

# 启动广播节点
ros2 run my_tf2_demo tf2_broadcaster

# 另开终端启动监听节点
ros2 run my_tf2_demo tf2_listener

7.运行结果

ros2 run my_tf2_demo tf2_listener
[INFO] [1748338687.574480189] [tf2_listener]: Translation: (1.00, 0.50, 0.00)
[INFO] [1748338687.574690392] [tf2_listener]: Rotation (quat): (0.00, 0.00, 0.26, 0.97)
[INFO] [1748338688.574395561] [tf2_listener]: Translation: (1.00, 0.50, 0.00)
[INFO] [1748338688.574498723] [tf2_listener]: Rotation (quat): (0.00, 0.00, 0.26, 0.97)
[INFO] [1748338689.574409312] [tf2_listener]: Translation: (1.00, 0.50, 0.00)
[INFO] [1748338689.574533292] [tf2_listener]: Rotation (quat): (0.00, 0.00, 0.26, 0.97)
[INFO] [1748338690.574416727] [tf2_listener]: Translation: (1.00, 0.50, 0.00)
[INFO] [1748338690.574547269] [tf2_listener]: Rotation (quat): (0.00, 0.00, 0.26, 0.97)
[INFO] [1748338691.574411090] [tf2_listener]: Translation: (1.00, 0.50, 0.00)
[INFO] [1748338691.574529887] [tf2_listener]: Rotation (quat): (0.00, 0.00, 0.26, 0.97)
[INFO] [1748338692.574424152] [tf2_listener]: Translation: (1.00, 0.50, 0.00)
[INFO] [1748338692.574548332] [tf2_listener]: Rotation (quat): (0.00, 0.00, 0.26, 0.97)
[INFO] [1748338693.574500531] [tf2_listener]: Translation: (1.00, 0.50, 0.00)
[INFO] [1748338693.574624469] [tf2_listener]: Rotation (quat): (0.00, 0.00, 0.26, 0.97)

8.查看 TF 变换树

你也可以使用命令行工具查看整个变换树:

ros2 run tf2_tools view_frames

输出:

[INFO] [1748338803.412777413] [view_frames]: Listening to tf data for 5.0 seconds...
[INFO] [1748338808.497500320] [view_frames]: Generating graph in frames.pdf file...
[INFO] [1748338808.499981451] [view_frames]: Result:tf2_msgs.srv.FrameGraph_Response(frame_yaml="robot_base: \n  parent: 'world'\n  broadcaster: 'default_authority'\n  rate: 10.200\n  most_recent_transform: 1748338808.495811\n  oldest_transform: 1748338803.495771\n  buffer_length: 5.000\n")

总结

功能工具
广播坐标变换tf2_ros::TransformBroadcaster
监听坐标变换tf2_ros::TransformListener + tf2::Buffer
查询坐标变换lookupTransform()
静态变换static_transform_publisher
查看变换树view_frames
与 URDF 结合robot_state_publisher

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2387818.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Vue+css实现扫描动画效果(使用@keyframes scan)

实现效果 扫描效果 参考链接 MDN Web Docs: CSS Animations 关键代码 示例代码 <div class"scanner-container"><div class"scanner-line"></div><div class"scanner-icon">&#x1f4f7;</div><p>Scan m…

PHP学习笔记(九)

箭头函数 箭头函数是 PHP 7.4的新语法。是一种更简洁的匿名函数的写法&#xff0c;它们都是closure类的实现。 箭头函数的基本语法为fn&#xff08;argument_list&#xff09; > expr 箭头函数支持与匿名函数相同的功能&#xff0c;只是其父作用域的变量总是自动的。 当表…

信创 CDC 实战 | OGG、Attunity……之后,信创数据库实时同步链路如何构建?(以 GaussDB 数据入仓为例)

国产数据库加速进入核心系统&#xff0c;传统同步工具却频频“掉链子”。本系列文章聚焦 OceanBase、GaussDB、TDSQL、达梦等主流信创数据库&#xff0c;逐一拆解其日志机制与同步难点&#xff0c;结合 TapData 的实践经验&#xff0c;系统讲解从 CDC 捕获到实时入仓&#xff0…

微服务(SpringCloud)的简单介绍

一.什么是微服务&#xff1f; 微服务是一种软件架构风格&#xff0c;核心思想是用职责单一的小型项目&#xff0c;组合出复杂的大型项目。 二.举例 1.单体架构&#xff08;SpringBoot&#xff09; 无论项目中有多少功能&#xff0c;都是放在一个项目中。 如下图所示&#xff1…

Python 爬虫开发

文章目录 1. 常用库安装2. 基础爬虫开发2.1. 使用 requests 获取网页内容2.2. 使用 BeautifulSoup 解析 HTML2.3. 处理登录与会话 3. 进阶爬虫开发3.1. 处理动态加载内容&#xff08;Selenium&#xff09;3.2. 使用Scrapy框架3.3. 分布式爬虫&#xff08;Scrapy-Redis&#xff…

第十一周作业

一、实现bluecms旁注&#xff0c;并解释为什么旁站攻击可以拿下主站&#xff1f;跨库的意思是什么&#xff1f; 1、为什么旁站攻击可以拿下主站 因为主站业务和旁站业务共处于同一个服务器上面&#xff0c;当我们无法攻破主站业务时&#xff0c;可以通过攻破旁站业务&#xf…

猿大师办公助手网页编辑Office/wps支持服务器文件多线程下载吗?

浏览器兼容性割裂、信创替代迫切的2025年&#xff0c;传统WebOffice控件因依赖NPAPI/PPAPI插件已无法适配Chrome 107等高版本浏览器。猿大师办公助手通过系统级窗口嵌入技术&#xff0c;直接调用本地Office/WPS内核&#xff0c;实现&#xff1a; 真内嵌非弹窗&#xff1a;将Of…

英码科技携带 “无感知AI数字课堂”解决方案,亮相第22届广东教育装备展

5月23日至25日&#xff0c;第22届广东教育装备展览会在广州国际采购中心盛大举行。作为华为生态重要合作伙伴&#xff0c;英码科技携“无感知AI数字课堂解决方案”重磅登场&#xff0c;聚焦教学提质增效&#xff0c;为教育数字化转型注入新动能。 聚焦课堂真实场景&#xff0c;…

【R语言科研绘图】

R语言在绘制SCI期刊图像时具有显著优势&#xff0c;以下从功能、灵活性和学术适配性三个方面分析其适用性&#xff1a; 数据可视化库丰富 R语言拥有ggplot2、lattice、ggpubr等专业绘图包&#xff0c;支持生成符合SCI期刊要求的高分辨率图像&#xff08;如TIFF/PDF格式&#…

ProfiNet转Ethernet/IP网关选型策略适配西门子S7-1500与罗克韦尔ControlLogix5580的关键指标对比

一、行业背景 新能源汽车电池制造是当前工业自动化领域增长最快的细分市场之一。随着动力电池产能扩张与技术迭代&#xff0c;产线对高精度装配、实时数据交互和系统兼容性提出了更高要求。在某头部电池企业的模组装配线中&#xff0c;面临着不同品牌设备通信协议不兼容的问题&…

[图文]图6.3会计事项-Fowler分析模式的剖析和实现

1 00:00:02,090 --> 00:00:05,160 Fowler在书里面也说了&#xff0c;6.4 2 00:00:05,290 --> 00:00:07,540 这里也说了 3 00:00:08,030 --> 00:00:11,340 不是常用的 4 00:00:12,520 --> 00:00:15,060 更倾向用6.2&#xff0c;实际上就是6.3了 5 00:00:15,760 …

【Linux】shell脚本的常用命令

目录 简介 一.设置主机名称 1.1通过文件修改 1.2通过命令修改 二.网络管理命令nmcli 2.1查看网卡 2.2设置网卡 三.简单处理字符 3.1seq打印连续字符 3.2printf,echo打印字符 3.3sort排序 3.4uniq冗余处理 3.5cut对字符的截取 四.xargs输入转参 简介 以下命令都是…

Pycharm and Flask 的学习心得(9)

request对象&#xff1a; 1. request包含前端发送过来的所有请求数据 将from表单里的内容CV到request里面&#xff0c;可以添加if语句来做判断出请求类型后的操作 在网页上的表单上input的数据&#xff0c;后端如何获取呢&#xff1f; request对象获取前端发送来的数据 // …

Linux初始-环境安装(2)

文章目录 安装问题&#xff08;1-1.51.39&#xff09;xshell的下载和登录步骤xshell创建多用户与删除用户xshell免密码登录 简介&#xff1a;这篇文章我认为对于初学Linux还是非常重要的&#xff0c;正所谓磨刀不误砍柴工&#xff0c;工具环境准备好了&#xff0c;后面的学习才…

Python Day34 学习

今日内容 通过“心脏病数据集”对之前的内容进行复习&#xff0c;再进行新内容“元组和OS模块”的学习。 机器学习模型建模和评估&#xff08;先不考虑调参&#xff09; 基于之前已经预处理过的心脏病数据集 划分数据值 模型训练与模型评估 # 随机森林 rf_model RandomFo…

【ASR】基于分块非自回归模型的流式端到端语音识别

论文地址:https://arxiv.org/abs/2107.09428 摘要 非自回归 (NAR) 模型在语音处理中越来越受到关注。 凭借最新的基于注意力的自动语音识别 (ASR) 结构,与自回归 (AR) 模型相比,NAR 可以在仅精度略有下降的情况下实现有前景的实时因子 (RTF) 提升。 然而,识别推理需要等待…

国芯思辰|国产FRAM SF25C128助力监控系统高效低功耗解决方案,对标MB85RS128/FM25V01

监控系统已成为保障公共安全、维护社会秩序的重要工具。随着监控系统的不断发展&#xff0c;对数据存储的要求也越来越高&#xff0c;不仅需要大容量、高速度的存储设备&#xff0c;还要求其具备高可靠性和低功耗等特性。国产铁电存储器 SF25C128作为一种新型非易失性存储器&am…

攻防世界逆向刷题笔记(新手模式9-1?)

bad_python 看样子是pyc文件损坏了。利用工具打开&#xff0c;发现是MAGIC坏了。搜下也没有头绪。 攻防世界-难度1- bad_python - _rainyday - 博客园 python Magic Number对照表以及pyc修复方法 - iPlayForSG - 博客园 看WP才知道36已经提示了pyc版本了。参考第二个文章&am…

制作一款打飞机游戏58:子弹模式组合

今天我们将继续深入探讨子弹模式系统&#xff0c;并在我们的模式编辑器上做一些收尾工作。 子弹模式系统的乐趣 首先&#xff0c;我想说&#xff0c;这个子弹模式系统真的非常有趣。看着屏幕上不断喷射的子弹&#xff0c;感觉真是太棒了&#xff01; 合并修饰符 今天&#…

使用新一代达梦管理工具SQLark,高效处理 JSON/XML 数据!

在应用开发领域&#xff0c;JSON/XML数据结构因其灵活性和通用性&#xff0c;成为开发者存储和交换数据的首选。然而&#xff0c;传统管理工具在处理这些半结构化数据时&#xff0c;往往存在可视化效果差、编辑效率低等问题&#xff0c;严重影响开发者的工作效率。 现在&#…