当前位置: 首页 > news >正文

ros2 tf2详解

tf2工具

ros2 run tf2_tools view_frames

使用tf2_echo

ros2 run tf2_ros tf2_echo [source_frame] [target_frame]

静态坐标变换

静态坐标变换用于定义机器人基座与传感器或固定部件之间的相对关系,这在机器人系统中非常实用。例如,激光雷达的扫描数据在其自身坐标系下处理会更直观,而静态坐标变换能明确该坐标系与机器人基座坐标系的关联。

Python

import math
import sysfrom geometry_msgs.msg import TransformStampedimport numpy as npimport rclpy
from rclpy.node import Nodefrom tf2_ros.static_transform_broadcaster import StaticTransformBroadcasterdef quaternion_from_euler(ai, aj, ak):ai /= 2.0aj /= 2.0ak /= 2.0ci = 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*ckcs = ci*sksc = si*ckss = si*skq = np.empty((4, ))q[0] = cj*sc - sj*csq[1] = cj*ss + sj*ccq[2] = cj*cs - sj*scq[3] = cj*cc + sj*ssreturn qclass 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 alltime."""def __init__(self, transformation):super().__init__('static_turtle_tf2_broadcaster')self.tf_static_broadcaster = StaticTransformBroadcaster(self)# Publish static transforms once at startupself.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 argumentsif 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 noderclpy.init()node = StaticFramePublisher(sys.argv)try:rclpy.spin(node)except KeyboardInterrupt:passrclpy.shutdown()

这段代码是一个 ROS 2 节点,用于广播静态坐标变换(TF 变换)。它的主要功能是将一个子坐标系相对于父坐标系(这里是 "world" 坐标系)的变换关系发布出去,供系统中的其他节点使用。

解释: 

主函数先检查命令行参数个数是不是8个,然后检查子坐标系名字是否与父坐标系重名

package.xml 

<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>

setup.py

'static_turtle_tf2_broadcaster = learning_tf2_py.static_turtle_tf2_broadcaster:main',

运行

ros2 run learning_tf2_py static_turtle_tf2_broadcaster mystaticturtle 0 0 1 0 0 0

其他方式发布静态坐标系

1.
ros2 run tf2_ros static_transform_publisher --x 0 --y 0 --z 1 --yaw 0 --pitch 0 --roll 0 --frame-id world --child-frame-id mystaticturtle
2.
ros2 run tf2_ros static_transform_publisher --x 0 --y 0 --z 1 --qx 0 --qy 0 --qz 0 --qw 1 --frame-id world --child-frame-id mystaticturtle
3.
from launch import LaunchDescription
from launch_ros.actions import Nodedef 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']),])

C++

推荐使用命令行和launch文件来发送(见目录   静态坐标变换/Python/其他方式发布静态坐标系)

动态坐标变换

Python

import mathfrom geometry_msgs.msg import TransformStampedimport numpy as npimport rclpy
from rclpy.node import Nodefrom tf2_ros import TransformBroadcasterfrom turtlesim.msg import Posedef quaternion_from_euler(ai, aj, ak):ai /= 2.0aj /= 2.0ak /= 2.0ci = 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*ckcs = ci*sksc = si*ckss = si*skq = np.empty((4, ))q[0] = cj*sc - sj*csq[1] = cj*ss + sj*ccq[2] = cj*cs - sj*scq[3] = cj*cc + sj*ssreturn qclass FramePublisher(Node):def __init__(self):super().__init__('turtle_tf2_frame_publisher')# Declare and acquire `turtlename` parameterself.turtlename = self.declare_parameter('turtlename', 'turtle').get_parameter_value().string_value# Initialize the transform broadcasterself.tf_broadcaster = TransformBroadcaster(self)# Subscribe to a turtle{1}{2}/pose topic and call handle_turtle_pose# callback function on each messageself.subscription = self.create_subscription(Pose,f'/{self.turtlename}/pose',self.handle_turtle_pose,1)self.subscription  # prevent unused variable warningdef handle_turtle_pose(self, msg):t = TransformStamped()# Read message content and assign it to# corresponding tf variablest.header.stamp = self.get_clock().now().to_msg()t.header.frame_id = 'world't.child_frame_id = self.turtlename# Turtle only exists in 2D, thus we get x and y translation# coordinates from the message and set the z coordinate to 0t.transform.translation.x = msg.xt.transform.translation.y = msg.yt.transform.translation.z = 0.0# For the same reason, turtle can only rotate around one axis# and this why we set rotation in x and y to 0 and obtain# rotation in z axis from the messageq = quaternion_from_euler(0, 0, msg.theta)t.transform.rotation.x = q[0]t.transform.rotation.y = q[1]t.transform.rotation.z = q[2]t.transform.rotation.w = q[3]# Send the transformationself.tf_broadcaster.sendTransform(t)def main():rclpy.init()node = FramePublisher()try:rclpy.spin(node)except KeyboardInterrupt:passrclpy.shutdown()

这段代码是一个 ROS 2 节点,用于发布动态坐标系变换,特别是针对 turtlesim 仿真中的海龟位置和姿态。与之前的静态坐标系发布器不同,这个节点会实时跟踪海龟的运动并更新其坐标系变换。

setup.py 

data_files=[...(os.path.join('share', package_name, 'launch'), glob('launch/*')),
],
'turtle_tf2_broadcaster = learning_tf2_py.turtle_tf2_broadcaster:main',

launch

from launch import LaunchDescription
from launch_ros.actions import Nodedef generate_launch_description():return LaunchDescription([Node(package='turtlesim',executable='turtlesim_node',name='sim'),Node(package='learning_tf2_py',executable='turtle_tf2_broadcaster',name='broadcaster1',parameters=[{'turtlename': 'turtle1'}]),])

package.xml

<exec_depend>launch</exec_depend>
<exec_depend>launch_ros</exec_depend>

C++

#include <functional>
#include <memory>
#include <sstream>
#include <string>#include "geometry_msgs/msg/transform_stamped.hpp"
#include "rclcpp/rclcpp.hpp"
#include "tf2/LinearMath/Quaternion.h"
#include "tf2_ros/transform_broadcaster.h"
#include "turtlesim/msg/pose.hpp"class FramePublisher : public rclcpp::Node
{
public:FramePublisher(): Node("turtle_tf2_frame_publisher"){// Declare and acquire `turtlename` parameterturtlename_ = this->declare_parameter<std::string>("turtlename", "turtle");// Initialize the transform broadcastertf_broadcaster_ =std::make_unique<tf2_ros::TransformBroadcaster>(*this);// Subscribe to a turtle{1}{2}/pose topic and call handle_turtle_pose// callback function on each messagestd::ostringstream stream;stream << "/" << turtlename_.c_str() << "/pose";std::string topic_name = stream.str();subscription_ = this->create_subscription<turtlesim::msg::Pose>(topic_name, 10,std::bind(&FramePublisher::handle_turtle_pose, this, std::placeholders::_1));}private:void handle_turtle_pose(const std::shared_ptr<turtlesim::msg::Pose> msg){geometry_msgs::msg::TransformStamped t;// Read message content and assign it to// corresponding tf variablest.header.stamp = this->get_clock()->now();t.header.frame_id = "world";t.child_frame_id = turtlename_.c_str();// Turtle only exists in 2D, thus we get x and y translation// coordinates from the message and set the z coordinate to 0t.transform.translation.x = msg->x;t.transform.translation.y = msg->y;t.transform.translation.z = 0.0;// For the same reason, turtle can only rotate around one axis// and this why we set rotation in x and y to 0 and obtain// rotation in z axis from the messagetf2::Quaternion q;q.setRPY(0, 0, msg->theta);t.transform.rotation.x = q.x();t.transform.rotation.y = q.y();t.transform.rotation.z = q.z();t.transform.rotation.w = q.w();// Send the transformationtf_broadcaster_->sendTransform(t);}rclcpp::Subscription<turtlesim::msg::Pose>::SharedPtr subscription_;std::unique_ptr<tf2_ros::TransformBroadcaster> tf_broadcaster_;std::string turtlename_;
};int main(int argc, char * argv[])
{rclcpp::init(argc, argv);rclcpp::spin(std::make_shared<FramePublisher>());rclcpp::shutdown();return 0;
}

CMakeList

add_executable(turtle_tf2_broadcaster src/turtle_tf2_broadcaster.cpp)
ament_target_dependencies(turtle_tf2_broadcastergeometry_msgsrclcpptf2tf2_rosturtlesim
)
install(TARGETSturtle_tf2_broadcasterDESTINATION lib/${PROJECT_NAME})

launch

from launch import LaunchDescription
from launch_ros.actions import NodeNode(package='turtlesim',executable='turtlesim_node',name='sim'),Node(package='learning_tf2_cpp',executable='turtle_tf2_broadcaster',name='broadcaster1',parameters=[{'turtlename': 'turtle1'}]),

package

<exec_depend>launch</exec_depend>
<exec_depend>launch_ros</exec_depend>

CMakeList

install(DIRECTORY launchDESTINATION share/${PROJECT_NAME})

 

wrighting a listener

待定

using time

核心内容:将教授如何获取特定时间点的变换。这一功能在很多场景下非常有用,比如需要分析过去某个时刻的系统状态、同步不同传感器在同一时间点的数据等。

t = tf_buffer_->lookupTransform(toFrameRel,fromFrameRel,tf2::TimePointZero);

具体作用是获取两个坐标系在 “最早可用时间点” 的变换关系。

rclcpp::Time now = this->get_clock()->now();
t = tf_buffer_->lookupTransform(toFrameRel,fromFrameRel,now);

这段代码的功能是:获取 “当前时刻” 下,从 fromFrameRel 到 toFrameRel 的坐标变换关系

Traveling in time 

rclcpp::Time when = this->get_clock()->now() - rclcpp::Duration(5, 0);
t = tf_buffer_->lookupTransform(toFrameRel,fromFrameRel,when,50ms);

这段代码是 ROS 2 中使用 tf2 库查询过去特定时间点的坐标系变换,并设置了超时等待机制,具体解释如下:

1. this->get_clock()->now() 获取当前时间 ,rclcpp::Duration(5, 0) 表示 5 秒的时间间隔。两者相减得到 “5 秒前” 的时间点,存储在 when 变量中。

  • 这是 tf2 查询变换的扩展用法,四个参数的含义:
    • toFrameRel:目标坐标系(变换终点)
    • fromFrameRel:源坐标系(变换起点)
    • when:要查询的时间点(这里是 5 秒前)
    • 50ms:超时等待时间(50 毫秒)
rclcpp::Time now = this->get_clock()->now();
rclcpp::Time when = now - rclcpp::Duration(5, 0);
t = tf_buffer_->lookupTransform(toFrameRel,now,fromFrameRel,when,"world",50ms);

这段代码是 ROS 2 中 tf2 库的一种更灵活的坐标变换查询方式,允许为源坐标系和目标坐标系指定不同的时间点,并指定一个参考坐标系。

http://www.lryc.cn/news/605853.html

相关文章:

  • 从 0 到 1:PHP 基础到就业教程指南(附教程资料)
  • ceph sc 设置文件系统格式化参数
  • Python 程序设计讲义(48):组合数据类型——字典类型:字典的常用操作
  • 商旅平台怎么选?如何规避商旅流程中的违规风险?
  • 云原生技术创新中的安全和合规问题有哪些解决方案?
  • Java客户端连接Redis
  • 《计算机“十万个为什么”》之 [特殊字符] 字符集:数字世界的文字密码本 [特殊字符]️
  • OpenCV 中的「通道」(Channel)详解
  • Windows 安全中心是什么?如何关闭 Windows 11 的安全中心
  • centos下安装anaconda
  • Traccar:开源GPS追踪系统的核心价值与技术全景
  • VuePress 使用详解
  • 【Coze Studio代码分析】开源多智能体应用开发平台原理与实践
  • 技术分享 | 悬镜亮相于“2025开放原子开源生态大会软件物料清单(SBOM)”分论坛
  • 「源力觉醒 创作者计划」开源大模型重构数智文明新范式
  • 前端 vue 第三方工具包详解-小白版
  • 「源力觉醒 创作者计划」破局与重构:文心大模型开源的产业变革密码
  • 前端开发(HTML,CSS,VUE,JS)从入门到精通!第一天(HTML5)
  • [论文阅读] 人工智能 + 软件工程 | KnowledgeMind:基于MCTS的微服务故障定位新方案——告别LLM幻觉,提升根因分析准确率
  • MLIR TableGen
  • SpringAI:AI工程应用框架新选择
  • 第三十篇:AI的“思考引擎”:神经网络、损失与优化器的核心机制【总结前面2】
  • 嵌入式系统常用架构
  • 使用iptables封禁恶意ip异常请求
  • Kubernetes架构概览
  • tlias智能学习辅助系统--SpringAOP-基础-核心概念
  • 联通元景万悟 开源,抢先体验!!!
  • sqoop从pg导出数据到hadoop上
  • Linux: network:netlink是否可以一次性request多加几个IP地址?
  • 社区团购系统 vs 传统电商系统:业务逻辑差异与技术适配