ROS TF坐标变换 - 动态坐标变换

目录

  • 一、动态坐标变换(C++实现)
  • 二、动态坐标变换(Python实现)

一、动态坐标变换(C++实现)

所谓动态坐标变换,是指两个坐标系之间的相对位置是变化的。比如机械臂末端执行器与 base_link 之间,移动机器人base_link与world之间。可以理解动态坐标关系是随时间变化的静态坐标关系(即静态是动态对时间的微分)。

我们使用ROS的 turtlesim 模拟一个移动机器人,通过TF发布它相对世界坐标系的坐标。

在创建的 tf2_learning 包路径下的 src 目录中创建 dynamic_frame_broadcast.cppdynamic_frame_listen.cpp ,修改 CMakeLists.txt ,添加如下内容:

add_executable(${PROJECT_NAME}_dynamic_broadcast src/dynamic_frame_broadcast.cpp)
add_executable(${PROJECT_NAME}_dynamic_listen src/dynamic_frame_listen.cpp)

target_link_libraries(${PROJECT_NAME}_dynamic_broadcast
  ${catkin_LIBRARIES}
)

target_link_libraries(${PROJECT_NAME}_dynamic_listen
  ${catkin_LIBRARIES}
)

dynamic_frame_broadcast.cpp 实现广播子坐标系相对于父坐标系的动态坐标关系,内容如下:

/**
 * @file: dynamic_frame_broadcast.cpp
 * @brief: 动态的坐标系相对姿态发布
 * @author: 万俟淋曦(1055311345@qq.com)
 * @date: 2023-12-30 22:47:33
 * @modifier:
 * @date: 2023-12-30 22:47:33
 */

#include "ros/ros.h"
#include "turtlesim/Pose.h"
#include "tf2_ros/transform_broadcaster.h"
#include "geometry_msgs/TransformStamped.h"
#include "tf2/LinearMath/Quaternion.h"

void turtle1PoseCallback(const turtlesim::Pose::ConstPtr &pose)
{
    // 创建 TF 广播器
    static tf2_ros::TransformBroadcaster broadcaster;

    // 创建 广播的数据
    geometry_msgs::TransformStamped tfs;
    // --头设置
    tfs.header.frame_id = "world";
    tfs.header.stamp = ros::Time::now();
    // --坐标系id
    tfs.child_frame_id = "turtle1";
    // --坐标系相对信息设置
    tfs.transform.translation.x = pose->x;
    tfs.transform.translation.y = pose->y;
    tfs.transform.translation.z = 0.0; // 二维, z为0
    //  --欧拉角转四元数
    tf2::Quaternion qtn;
    qtn.setRPY(0, 0, pose->theta); // 二维, 只有偏航角
    tfs.transform.rotation.x = qtn.getX();
    tfs.transform.rotation.y = qtn.getY();
    tfs.transform.rotation.z = qtn.getZ();
    tfs.transform.rotation.w = qtn.getW();

    // 广播器发布数据
    broadcaster.sendTransform(tfs);
}

int main(int argc, char **argv)
{
    // 初始化 ROS 节点
    ros::init(argc, argv, "dynamic_frame_broadcast");

    // 创建 ROS 句柄
    ros::NodeHandle nh;

    // 创建订阅对象,订阅乌龟的世界位姿
    ros::Subscriber sub = nh.subscribe<turtlesim::Pose>("/turtle1/pose", 1000, turtle1PoseCallback);

    ros::spin();

    return 0;
}

dynamic_frame_listen.cpp 订阅动态坐标转换关系,并利用该关系将小乌龟坐标系下的坐标转换到 world 坐标系,编辑内容如下:

/**
 * @file: dynamic_frame_listen.cpp
 * @brief: 订阅动态坐标系并转换相应坐标
 * @author: 万俟淋曦(1055311345@qq.com)
 * @date: 2023-12-31 11:55:40
 * @modifier:
 * @date: 2023-12-31 11:55:40
 */
#include "ros/ros.h"
#include "tf2_ros/transform_listener.h"
#include "tf2_ros/buffer.h"
#include "geometry_msgs/PointStamped.h"
#include "tf2_geometry_msgs/tf2_geometry_msgs.h" // 包含TF坐标转换方法

int main(int argc, char **argv)
{
    // 初始化 ROS 节点
    ros::init(argc, argv, "dynamic_frame_listen");
    ros::NodeHandle nh;

    // 创建 TF 订阅节点
    tf2_ros::Buffer buffer;
    tf2_ros::TransformListener listener(buffer);

    ros::Rate r(1);
    while (ros::ok())
    {
        // 生成一个坐标点, 模拟末端执行器坐标系下的点坐标(小乌龟坐标系下的坐标)
        geometry_msgs::PointStamped point_turtle1;
        point_turtle1.header.frame_id = "turtle1";
        point_turtle1.header.stamp = ros::Time();
        point_turtle1.point.x = 1;
        point_turtle1.point.y = 1;
        point_turtle1.point.z = 0;

        // 转换坐标点, 计算小乌龟坐标系下的坐标点在 world 下的坐标
        try
        {
            geometry_msgs::PointStamped point_base;
            point_base = buffer.transform(point_turtle1, "world");
            ROS_INFO("point_base: (%.2f, %.2f, %.2f), frame: %s", 
                point_base.point.x, 
                point_base.point.y, 
                point_base.point.z,
                point_base.header.frame_id.c_str());
        }
        catch (const std::exception &e)
        {
            ROS_ERROR("%s", e.what());
        }

        r.sleep();
        ros::spinOnce();
    }

    return 0;
}

编译后,

  • 首先开启小乌龟 rosrun turtlesim turtlesim_node

  • 执行 rosrun tf2_learning tf2_learning_dynamic_broadcast 开始广播坐标,此时打开rviz订阅TF看到TF树模型

  • 输入命令:rviz

  • 在启动的 rviz 中设置 Fixed Frameworld

  • 点击左下的 Add 按钮,在弹出的窗口中选择 TF 组件,即可显示坐标关系,如下:

在这里插入图片描述

继续执行命令rosrun tf2_learning tf2_learning_listen可以看到转换后的坐标,以及所属父坐标系

在这里插入图片描述

执行命令 rosrun turtlesim turtle_teleop_key 使用键盘控制小乌龟移动,可以看到 rviz以及转换后的坐标都在同步动态变化。

在这里插入图片描述

二、动态坐标变换(Python实现)

在创建的 tf2_learning 包路径下 src 目录的同级,创建一个 scripts 目录,在这里存储脚本(如python脚本),我们创建 dynamic_frame_broadcast.py 以实现坐标广播,编辑内容如下:

#! /usr/bin/env python

import rospy
import tf2_ros
import tf
from turtlesim.msg import Pose
from geometry_msgs.msg import TransformStamped

# 回调函数处理
def turtle1PoseCallback(pose):
    # 创建 TF 广播器
    broadcaster = tf2_ros.TransformBroadcaster()
    # 创建 广播的数据(通过 pose 设置)
    tfs = TransformStamped()
    tfs.header.frame_id = "world"
    tfs.header.stamp = rospy.Time.now()
    tfs.child_frame_id = "turtle1"
    tfs.transform.translation.x = pose.x
    tfs.transform.translation.y = pose.y
    tfs.transform.translation.z = 0.0
    qtn = tf.transformations.quaternion_from_euler(0,0,pose.theta)
    tfs.transform.rotation.x = qtn[0]
    tfs.transform.rotation.y = qtn[1]
    tfs.transform.rotation.z = qtn[2]
    tfs.transform.rotation.w = qtn[3]
    # 广播器发布数据
    broadcaster.sendTransform(tfs)

if __name__ == "__main__":
    # 初始化 ROS 节点
    rospy.init_node("dynamic_frame_broadcast_py")
    # 订阅 /turtle1/pose 话题消息
    sub = rospy.Subscriber("/turtle1/pose", Pose, turtle1PoseCallback)
    rospy.spin()

创建 dynamic_frame_listen.py 以订阅静态坐标转换关系,并利用该关系将雷达坐标系的点转换到 world 坐标系,编辑内容如下:

#! /usr/bin/env python

import rospy
import tf2_ros
# 不要使用 geometry_msgs,需要使用 tf2 内置的消息类型
from tf2_geometry_msgs import PointStamped
# from geometry_msgs.msg import PointStamped

if __name__ == "__main__":
    # 初始化 ROS 节点
    rospy.init_node("dynamic_frame_listen_py")
    # 创建 TF 订阅对象
    buffer = tf2_ros.Buffer()
    listener = tf2_ros.TransformListener(buffer)

    rate = rospy.Rate(1)
    while not rospy.is_shutdown():    
    # 创建一个 radar 坐标系中的坐标点
        point_source = PointStamped()
        point_source.header.frame_id = "turtle1"
        point_source.header.stamp = rospy.Time.now()
        point_source.point.x = 10
        point_source.point.y = 2
        point_source.point.z = 3

        try:
            # 转换坐标点, 计算小乌龟坐标系下的坐标点在 world 下的坐标
            point_target = buffer.transform(point_source,"world",rospy.Duration(1))
            rospy.loginfo("point_target: (%.2f, %.2f, %.2f), frame: %s",
                            point_target.point.x,
                            point_target.point.y,
                            point_target.point.z,
                            point_target.header.frame_id)
        except Exception as e:
            rospy.logerr("%s", e)

        rate.sleep()

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

考pmp有用么?

PMP考出来究竟有什么用&#xff0c;这个问题一直是站在边缘的朋友经常思考的问题&#xff0c;其实我想说的是&#xff0c;当能力和经验都充足的时候&#xff0c;可能这单单的一张证书就能有莫大的作用&#xff0c;帮助你实现目前所追求的东西。 当我利用这张证书达到我的目的之…

Vue3 的 emit 该怎么写, vue2 对比

Vue3 的 emit 该怎么写&#xff0c; vue2 对比 这是个新手问题&#xff0c;从 vue2 转到 vue3 之后&#xff0c;一时间不知道该怎么用它了。 vue2 用法 vue2 在 template 中 和 在方法中的用法如下&#xff1a; <template><button click"$emit(clicked, 要传…

数据结构期末复习(3)栈和队列

堆栈&#xff08;stack&#xff09; 堆栈&#xff08;stack&#xff09;是一种基于后进先出&#xff08;LIFO&#xff0c;Last In First Out&#xff09;原则的数据结构。它模拟了现实生活中的堆栈&#xff0c;类似于一摞盘子或一堆书。 堆栈有两个基本操作&#xff1a;入栈&a…

移除链表元素

description 给你一个链表的头节点 head 和一个整数 val &#xff0c;请你删除链表中所有满足 Node.val val 的节点&#xff0c;并返回 新的头节点 。 示例 1&#xff1a; 输入&#xff1a;head [1,2,6,3,4,5,6], val 6 输出&#xff1a;[1,2,3,4,5] 示例 2&#xff1a; …

开放原子训练营(第四季)TobudOS——TobudOS内核移植(keil版)

前言 12月份参加了开放原第四季线下活动&#xff0c;觉得很有意义。通过这篇博文&#xff0c;记录一下这次活动进行的移植TobudOS内核的过程&#xff0c;下面就让我们开始吧。 开发板介绍 本次使用的开发板型号为STM32H750&#xff0c;当然了&#xff0c;其他型号的开发版也…

OSG读取和添加节点学习

之前加载了一个模型&#xff0c;代码是&#xff0c; osg::Group* root new osg::Group(); osg::Node* node new osg::Node(); node osgDB::readNodeFile("tree.osg"); root->addChild(node); root是指向osg::Group的指针&#xff1b; node是 osg:…

240101-5步MacOS自带软件无损快速导出iPhone照片

硬件准备&#xff1a; iphone手机Mac电脑数据线 操作步骤&#xff1a; Step 1: 找到并打开MacOS自带的图像捕捉 Step 2: 通过数据线将iphone与电脑连接Step 3&#xff1a;iphone与电脑提示“是否授权“&#xff1f; >>> “是“Step 4&#xff1a;左上角选择自己的设…

软件工程期末总复习

第一章 软件工程概述 什么是软件&#xff1a;程序数据配套文档 软件危机&#xff1a;计算机软件开发过程中所遇到的一系列严重问题 软件危机的背景&#xff1a;20世纪60年代中后期软件开发需求急剧增长 软件工程的定义&#xff1a;指导计算机软件开发和维护的工程学科&…

安装Typora

链接&#xff1a;https://pan.baidu.com/s/1OLHtUTziKdB0sW0UIOnBBw?pwd6666 提取码&#xff1a;6666

spring创建与使用

spring创建与使用 创建 Spring 项⽬创建⼀个 Maven 项⽬添加 Spring 框架⽀持添加启动类 存储 Bean 对象创建 Bean将 Bean 注册到容器 获取并使⽤ Bean 对象创建 Spring 上下⽂获取指定的 Bean 对象获取bean对象的方法 使⽤ Bean 总结 创建 Spring 项⽬ 接下来使⽤ Maven ⽅式…

css 实现圆角渐变色效果

效果图 代码 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8" /><meta name"viewport" content"widthdevice-width, initial-scale1.0" /><title>css 实现圆角渐变色效果</title…

字母简化(UPC练习)

题目描述 给出一串全部为小写英文字母的字符串&#xff0c;要求把这串字母简化。简化规则是&#xff1a;统计连续出现的字母数&#xff0c;输出时先输出个数&#xff0c;再输出字母。比如&#xff1a;aaabbbaa&#xff0c;则简化为3a3b2a&#xff1b;而zzzzeeeeea&#xff0c;…

渗透线上下料控制(SCL源代码)

有关渗透线的其它详细介绍请参考下面链接文章&#xff1a; https://rxxw-control.blog.csdn.net/article/details/133611151https://rxxw-control.blog.csdn.net/article/details/133611151这里的渗透线上下料属于整个渗透线流程里的最前端和最后端&#xff0c;分别负责待处理…

机器学习的分类与经典算法

机器学习算法按照学习方式分类&#xff0c;可以分为有监督学习&#xff08;Supervised Learning&#xff09;、无监督学习&#xff08;Unsupervised Learning&#xff09;、半监督学习&#xff08;Semi-supervised Learning&#xff09;、强化学习&#xff08;Reinforcement Le…

C++面向对象基础-构造函数

1、构造函数 1.1 基本使用 构造函数是一种特殊的成员函数&#xff0c;用于创建对象&#xff0c;写法上有以下要求&#xff1a; 函数名必须与类名完全相同构造函数不写返回值如果程序员不手动编写构造函数&#xff0c;编译器就会自动添加一个无参的构造函数 手动添加构造函数&am…

小白学 PyTorch 系列:54个超强 pytorch 操作

最近观察到一个有趣的趋势&#xff0c;越来越多的人在学术界热衷于学习和应用PyTorch。在工业界&#xff0c;虽然仍有一些项目在延续使用之前的深度学习框架&#xff0c;但 PyTorch 的影响力也在逐渐渗透。 对于昨天为什么没发文&#xff0c;原因很心酸。把 PyTorch 的这篇文章…

亚马逊鲲鹏系统一款自动化全能软件

亚马逊鲲鹏系统是一款专为亚马逊买家提供全方位功能的自动化软件。它不仅可以轻松实现自动注册、养号、测评、QA等一系列操作&#xff0c;更在用户关心的账号关联问题上做出了创新性的解决方案。有的朋友可能对全自动化操作心存疑虑&#xff0c;担心可能引起关联从而导致封号&a…

24届春招实习必备技能(一)之MyBatis Plus入门实践详解

MyBatis Plus入门实践详解 一、什么是MyBatis Plus? MyBatis Plus简称MP&#xff0c;是mybatis的增强工具&#xff0c;旨在增强&#xff0c;不做改变。MyBatis Plus内置了内置通用 Mapper、通用 Service&#xff0c;仅仅通过少量配置即可实现单表大部分 CRUD 操作&#xff0…

FileZilla的使用,主动模式和被动模式思维导图

注&#xff1a;图片 &#xff08;与上面的思维导图文字配图看&#xff09;

PAT乙级1045 快速排序

著名的快速排序算法里有一个经典的划分过程&#xff1a;我们通常采用某种方法取一个元素作为主元&#xff0c;通过交换&#xff0c;把比主元小的元素放到它的左边&#xff0c;比主元大的元素放到它的右边。 给定划分后的 N 个互不相同的正整数的排列&#xff0c;请问有多少个元…