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

news2024/11/28 1:35:20

目录

  • 一、动态坐标变换(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()

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

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

相关文章

在Go中使用Goroutines和Channels发送电子邮件

学习如何使用Goroutines和Channels在Go中发送电子邮件 在现代软件开发的世界中&#xff0c;通信是一个关键元素。发送电子邮件是各种目的的常见实践&#xff0c;例如用户通知、报告等。Go是一种静态类型和编译语言&#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:…

【力扣题解】P106-从中序与后序遍历序列构造二叉树-Java题解

&#x1f468;‍&#x1f4bb;博客主页&#xff1a;花无缺 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! 本文由 花无缺 原创 收录于专栏 【力扣题解】 文章目录 【力扣题解】P106-从中序与后序遍历序列构造二叉树-Java题解&#x1f30f;题目描述&#x1f4a1;题…

2023结婚成家,2024借势起飞

您好&#xff0c;我是码农飞哥&#xff08;wei158556&#xff09;&#xff0c;感谢您阅读本文&#xff0c;欢迎一键三连哦。 &#x1f4aa;&#x1f3fb; 1. Python基础专栏&#xff0c;基础知识一网打尽&#xff0c;9.9元买不了吃亏&#xff0c;买不了上当。 Python从入门到精…

OpenGL FXAA抗锯齿算法(Qt)

文章目录 一、简介二、实现代码三、实现效果参考资料一、简介 之前已经提供了使用VCG读取Mesh的方式,接下来就需要针对读取的网格数据进行一些渲染操作了。在绘制Mesh数据时总会遇到图形的抗锯齿问题,OpenGL本身已经为我们提供了一种MSAA技术,但该技术对于一些实时渲染性能有…

看了好多烟花,自己也来了段

<!DOCTYPE html> <!DOCTYPE html> <html lang"zh-CN"> <meta charset"UTF-8"> <title>烟花动画</title> <style>body, html { height: 100%; margin: 0; }canvas { position: absolute; } </style> </…

基于C#的机械臂欧拉角与旋转矩阵转换

欧拉角概述 机器人末端执行器姿态描述方法主要有四种&#xff1a;旋转矩阵法、欧拉角法、等效轴角法和四元数法。所以&#xff0c;欧拉角是描述机械臂末端姿态的重要方法之一。 关于欧拉角的历史&#xff0c;由来已久&#xff0c;莱昂哈德欧拉用欧拉角来描述刚体在三维欧几里…

Python+Django 构建实验室药品管理和预警系统【源码】

人生苦短&#xff0c;我用 Python。 今天给大家分享一个完整的实战案例&#xff1a;Python实现实验室药品管理和预警系统&#xff0c;文末附完整代码! 在线演示环境 项目演示地址&#xff1a;http://101.34.18.118:8002/ &#xff08;图片未压缩&#xff0c;所以加载有点慢&…

c++_09_继承

1 继承 C的继承是弱继承 继承的语法&#xff1a; class 子类 : 继承方式1 基类1, 继承方式2 基类2, ... { ... }; 继承方式&#xff1a; 共有继承 public 保护继承 protected 私有继承 private 2 继承的基本属性&#xff08;3种继承方式均有&#xff09; 继承所…

MySql——1146 - Table‘mysql.proc‘doesn‘t exit是这个

项目场景&#xff1a; 做自己的小项目需要连接mysql数据库 问题描述 点击数据库时报错 1146 - Table’mysql.proc’doesn’t exit 原因分析&#xff1a; 误删原生的mysql数据库 解决方案&#xff1a; 重新安装装部署mysql就好了 注意不要轻易删除原生的东西

基于Java在线商城系统设计实现(源码+部署文档+讲解视频)

博主介绍&#xff1a; ✌至今服务客户已经1000、专注于Java技术领域、项目定制、技术答疑、开发工具、毕业项目实战 ✌&#x1f345; 文末获取源码联系 &#x1f345;&#x1f447;&#x1f3fb; 精彩专栏 推荐订阅 &#x1f447;&#x1f3fb; 不然下次找不到 Java项目精品实…

qt图像绘制QPainter

QPainter 以下是一些常用的 Qt::PenStyle 枚举值&#xff1a; Qt::NoPen&#xff1a;无线条。Qt::SolidLine&#xff1a;实线。Qt::DashLine&#xff1a;虚线&#xff0c;由短划线组成。Qt::DotLine&#xff1a;点线&#xff0c;由点组成。Qt::DashDotLine&#xff1a;点划线&…

HarmonyOS 实践之应用状态变量共享

平时在开发的过程中&#xff0c;我们会在应用中共享数据&#xff0c;在不同的页面间共享信息。虽然常用的共享信息&#xff0c;也可以通过不同页面中组件间信息共享的方式&#xff0c;但有时使用应用级别的状态管理会让开发工作变得简单。 根据不同的使用场景&#xff0c;ArkTS…

SELinux 基本原理

本文讲述 SELinux 保护安全的基本原理 首发公号&#xff1a;Rand_cs 安全检查顺序 不废话&#xff0c;直接先来看张图 当我们执行系统调用的时候&#xff0c;会首先对某些错误情况进行检查&#xff0c;如果失败通常会得到一些 error 信息&#xff0c;通过查看全局变量 errno …

多模态大模型的前世今生

1 引言 前段时间 ChatGPT 进行了一轮重大更新&#xff1a;多模态上线&#xff0c;能说话&#xff0c;会看图&#xff01;微软发了一篇长达 166 页的 GPT-4V 测评论文&#xff0c;一时间又带起了一阵多模态的热议&#xff0c;随后像是 LLaVA-1.5、CogVLM、MiniGPT-5 等研究工作…

matalb实践(十二):减肥

1.题目 2.解答 2.1模型假设 1.体重增加正比于吸收的热量&#xff0c;平均每8000kcal增加体重1kg 2.身体正常代谢引起的体重减少正比于体重&#xff0c;每周每千克体重消耗热量一般在200kcal至320kcal之间&#xff0c;且因人而异&#xff0c;这相当于体重70kg的人每天消耗2000k…

Feign远程调用丢失请求头问题处理--异步任务执行远程请求线程丢失请求属性问题处理

在关于Feign远程调用丢失请求头问题处理中解决了远程调用发送请求丢失老请求中请求头的问题。A方法接收浏览器中的请求&#xff0c;B方法是A方法中嵌套方法用来发送Feign远程调用。如果B方法是在异步任务CompletableFuture.runAsync(()->{},Executor)中执行并启用线程池分配…

C++多态性——(1)初识多态

归纳编程学习的感悟&#xff0c; 记录奋斗路上的点滴&#xff0c; 希望能帮到一样刻苦的你&#xff01; 如有不足欢迎指正&#xff01; 共同学习交流&#xff01; &#x1f30e;欢迎各位→点赞 &#x1f44d; 收藏⭐ 留言​&#x1f4dd; 苦难和幸福一样&#xff0c;都是生命盛…

HarmonyOS 组件通用属性之通用事件 文档参数讲解(触摸事件)

好 本文 我们来说说触摸事件 字面意思也非常好理解 就是我们手机手指触摸物体触发 我们先在编辑器组件介绍中 找到这个东西的基本用法 Button("跳转").onTouch((event: TouchEvent) > {})最明显的就是 event 的类型变了 点击事件的是 ClickEvent 而这里是 Touc…

【教3妹学编程-算法题】经营摩天轮的最大利润

3妹&#xff1a;“打个中国结&#xff0c;再系个红腰带&#xff0c; 愿善良的人们天天好运来, 你勤劳生活美, 你健康春常在, 你一生的忙碌为了笑逐颜开。” 2哥 : 3妹&#xff0c;元旦快乐啊。 3妹&#xff1a;2哥元旦快乐~。 2哥&#xff1a;祝新的一年&#xff0c;3妹技术突飞…