ROS中如何实现将一个基于A坐标系下的三维向量变换到基于B坐标系下?

news2025/1/16 1:38:02

摘要

ROS中通过tf.TransformListener.lookupTransform方法获取从A坐标系到B坐标系的旋转四元数rot,通过quaternion_multiply函数实现 p ′ = q p q − 1 p' = qpq^{-1} p=qpq1中的矩阵乘法,从而可以方便获取在新坐标系下的四元数坐标表示 p ′ p' p. 基于此,本博客提供将一个基于A坐标系下的三维向量变换到基于B坐标系下的标准代码模板和应用实例,便于后续查阅。

关键词

ROS, TF, 坐标系变换,四元数,向量表示

前言

在构建海洋机器人动力学模型的时候,其中的外力项通常要包含进风和海流的作用力,这些力在全局大地坐标系(静态坐标系)下的向量表示通常是放方向和大小都较为固定的常量,但是如果要将这些作用引入动力学模型,一般都是基于船体坐标系下的,而随着船体的位置和朝向的变化,风海流在船体坐标系下的向量表示,连同其三轴分量,一定时刻变化着。在得到风海流在大地坐标系下的向量表示后,如何快速求得其在船体坐标系下的分量?

在机器人执行目标跟踪任务的时候,由于机器人的路径规划算法中,目标的坐标表示一般是要基于以机器人中心为原点的坐标系下的,而目标检测的视觉算法得到的距离与偏角却是目标和机载摄像头之间的相对位置关系;如果还要知道目标在全局地图中的坐标,还需要进一步求得在大地坐标系下的位置表示。

这些问题的背后是同一个问题:基于A坐标系下的三维向量在另一个坐标系下的坐标是什么,如何快速求得? 本博客提供了在ROS中快速实现实现将一个基于A坐标系下的三维向量变换到基于B坐标系下的标准代码模板和应用实例,便于后续查阅。

方法

基本思路

抽象为数学问题后可以表述为:假设一个三维实数空间向量 v v v在坐标系A中表示为 v = [ x , y , z ] ∈ R 3 v=[x,y,z] \in \mathbb {R^3} v=[x,y,z]R3,坐标系A到坐标系B的旋转可以用四元数向量为 q q q,求空间向量 v v v在坐标系B中的坐标表示。

求法是:首先将向量 v v v用四元数 p p p表达,用四元数表达的实数向量一定是一个虚四元数,即 p = [ 0 , x , y , z ] = [ 0 , v ] p=[0,x,y,z]=[0,v] p=[0,x,y,z]=[0,v],然后,向量 v v v在B系中的四元数表示(设此四元数为 p ′ p' p)即为 p ′ = q p q − 1 p' = qpq^{-1} p=qpq1 p ′ p' p也是一个虚四元数。

出处见下图:
用四元数表示旋转
(出处来源链接:https://www.cnblogs.com/gaoxiang12/p/5120175.html)

因此,在ROS中为了实现将一个基于A坐标系下的三维向量变换到基于B坐标系下,需要知道:

  1. 向量在A系下的坐标;
  2. 向量在A系下的坐标的四元数表示;
  3. A系到B系变换的旋转四元数

最后,在ROS中实现矩阵乘法

p ′ = q p q − 1 p' = qpq^{-1} p=qpq1

并取 p ′ p' p四元数中的虚部部分即得到变换到基于B坐标系下后的坐标表示。

基本模板

# Neccessary library
import rospy
import tf
import tf2_ros

x,y,z = 1,2,3
vector = [x,y,z]
v_base_link= [vector[0], vector[1],vector[2], 0] 
listener = tf.TransformListener()
try:
	(trans,rot) = listener.lookupTransform('parent_frame', 'child_frame', rospy.Time(0))
    v_map = tf.transformations.quaternion_multiply(tf.transformations.quaternion_multiply(rot, v_base_link), tf.transformations.quaternion_conjugate(rot))
    v_new = v_map[0:3]
except Exception as e:
	print(f"Exception:{e}")
# ...

部分代码解释

# 假设向量v在A系中的坐标和对应的四元数表示
x,y,z = 1,2,3
vector = [x,y,z]
v_base_link= [vector[0], vector[1],vector[2], 0] 
# 实例化tf库中TransformListener类,利用该类的lookupTransform方法获取从A坐标系到B坐标系的旋转四元数rot
# 注意在本问题中,坐标系平移变换向量trans是不需要用到的
listener = tf.TransformListener()
try:
	(trans,rot) = listener.lookupTransform('parent_frame', 'child_frame', rospy.Time(0))
# 利用quaternion_multiply函数实现p' = qpq^{-1}中的矩阵乘法
v_map = tf.transformations.quaternion_multiply(tf.transformations.quaternion_multiply(rot, v_base_link), tf.transformations.quaternion_conjugate(rot))

细节问题

Q:四元数是一个向量,为什么会有逆?

四元数取逆其实是取共轭

出处见下图:
四元数取逆其实就是取共轭
(出处来源链接:https://zhuanlan.zhihu.com/p/52565002)

tf.transformations.quaternion_conjugate(quaternion)的作用就是返回四元数的共轭。

出处见下图:
的作用就是返回四元数的共轭。(出处来源链接:https://blog.csdn.net/weixin_44682965/article/details/107818474)

Q:listener.lookupTransform的前两个参数中哪个是父系(源坐标系)哪个是子系(目标坐标系)?

第一个参数是源坐标系,第二个参数是目标坐标系。

在这里插入图片描述

Q:rospy.Time(0)的作用?

获取距离调用listener.lookupTransform函数时间最近的rot变量。

出处见下图:
rospy.Time(0)的作用是获取距离调用listener.lookupTransform函数时间最近的rot变量。
(出处来源链接:http://wiki.ros.org/tf/Tutorials/Writing%20a%20tf%20listener%20%28Python%29)

这也意味着,在程序执行一开始的时候,可能是没有可用的rot供获取的,这是需要使用try-except语句的原因。

Q:四元数在ROS中的排布?

  • 数学上,一个四元数的表达若设置为[w,x,y,z],则其中的w为实部,x,y,z为虚部。

出处见下图:
四元数在数学上实部与虚部的向量结构
(出处来源链接:https://www.cnblogs.com/gaoxiang12/p/5120175.html)

  • 在ROS中,这样的四元数是按如下顺序排布的:[x,y,z,w],即将实部放在了最后。

出处见下图:
四元数在ROS中实部与虚部的向量结构
(来源链接:http://wiki.ros.org/tf2/Tutorials/Quaternions)。

应用

目标

承接在ROS中如何实现两个固定坐标系之间的变换?中应用部分的问题。在该问题中,新静态坐标系为NED(东北地坐标系),设向量 v v v在world坐标系中的坐标表示是 v = [ 1 , 2 , 3 ] v=[1,2,3] v=[1,2,3],求该向量在NED坐标系中的坐标表示。

操作过程

  1. 打开ROS中如何实现两个固定坐标系之间的变换?中应用部分的脚本
  2. 修改代码。脚本代码如下(代码已经过测试):
#!/usr/bin/env python3
# coding: utf-8

# Neccessary library
import rospy
import tf
import tf2_ros
import geometry_msgs.msg
import numpy as np

rospy.init_node("static_transformation")

static_transformStamped = geometry_msgs.msg.TransformStamped()

broadcaster = tf2_ros.StaticTransformBroadcaster()
static_transformStamped.header.stamp = rospy.Time.now()
static_transformStamped.header.frame_id = "world"
static_transformStamped.child_frame_id = "NED"
# ...此处省略
broadcaster.sendTransform(static_transformStamped)

#重点是以下部分
x,y,z = 1,2,3
vector = [x,y,z]
v_base_link= [vector[0], vector[1],vector[2], 0] 
listener = tf.TransformListener()
loop_counter = 0
r = rospy.Rate(100)
while not rospy.is_shutdown():
    try:
        (trans,rot) = listener.lookupTransform('world', 'NED', rospy.Time(0))
        v_map = tf.transformations.quaternion_multiply(tf.transformations.quaternion_multiply(rot, v_base_link), tf.transformations.quaternion_conjugate(rot))
        v_new = v_map[0:3]
        if loop_counter > 100: #控制打印频率
            print(f"v_new:{v_new}")
            loop_counter = 0
        loop_counter += 1
    except Exception as e:
        print(f"Exception:{e}")
    r.sleep()
  1. 启动roscore,source Python文件,rosrun 对应Python节点。
    具体过程略

结果

东北天坐标系下的[1,2,3]放在东北地坐标系下来表示就是[2,1,-3]。

打印结果如下图所示:
东北天坐标系下的[1,2,3]放在东北地坐标系下来表示就是[2,1,-3]

延伸阅读

四元数相比欧拉角的优势到底在哪里?

参考文献

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

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

相关文章

app广告变现,开发者如何提高用户的参与度?

越来越多的开发者已经认识到用户参与移动应用内广告的价值。 1、人性化的消息传递 人性化的消息传递在情感层面上与用户产生共鸣的方式进行沟通,让他们感到被理解、被重视和参与。它在用户之间建立了一种信任感和可信度。对话式和相关的语气建立了超越交易关系的联…

OBS直播软件使用NDI协议输入输出

OBS(Open Broadcaster Software)是一个免费的开源的视频录制和视频推流软件。其功能强大并广泛使用在视频导播、录制及直播等领域。 OBS可以导入多种素材,除了本地音频、视频、图像外,还支持硬件采集设备,更能支持各种…

致远OA wpsAssistServlet任意文件读取漏洞复现 [附POC]

文章目录 致远OA wpsAssistServlet任意文件读取漏洞复现 [附POC]0x01 前言0x02 漏洞描述0x03 影响版本0x04 漏洞环境0x05 漏洞复现1.访问漏洞环境2.构造POC3.复现 0x06 修复建议 致远OA wpsAssistServlet任意文件读取漏洞复现 [附POC] 0x01 前言 免责声明:请勿利用…

多测师肖sir_高级金牌讲师_性能测试之badboy录制脚本02

性能测试之badboy录制脚本 一、下载安装包,点击安装 二、点击我同意 三、选择路径,点击install 打开以下界面,表示安装成功 第二步:录制流程 界面视图,模拟浏览器,能够进行操作 需要录制脚本的URL 点…

英语小作文模板(06求助+描述;07描述+建议)

06 求助描述: 题目背景及要求 第一段 第二段 第三段 翻译成中文 07 描述+建议: 题目背景及要求 第一段 第二段

记一次 AWD 比赛中曲折的 Linux 提权

前提背景: 今天一场 AWD 比赛中,遇到一个场景:PHP网站存在SQL注入和文件上传漏洞, MYSQL当前用户为ROOT,文件上传蚁剑连接SHELL是权限很低的用户。我需要想办法进行提权,才能读取到 /root 目录下的 flag。 一、sqlmap …

通过外网客户端远程到内部区域网环境

拓扑 项目需要远程内部区域网德服务器,可以提供一台双网卡的电脑来实现

面向制造企业的持续发展,2023数字化工单管理系统创新篇章-亿发

面向制造企业的持续发展,2023数字化工单管理系统开创新篇章-亿发 随着制造业的持续发展,运维工单管理日益成为关键环节,它设计客户管理、设备维护、服务商合作等多个业务领域,对运营效率和服务质量有着重要影响。然而&#xff0c…

MySQL用户管理和授权

用户管理和授权是属于MySQL当中的DCL语句 创建用户以及一些相关操作 明文创建用户 create user zzrlocalhost IDENTIFIED by 123456;create user 这是创建用户的开头zzr表示用户名 localhost:新建的用户可以在哪些主机上登录。即可以使用IP地址,网段&a…

竞赛选题 深度学习图像风格迁移 - opencv python

文章目录 0 前言1 VGG网络2 风格迁移3 内容损失4 风格损失5 主代码实现6 迁移模型实现7 效果展示8 最后 0 前言 🔥 优质竞赛项目系列,今天要分享的是 🚩 深度学习图像风格迁移 - opencv python 该项目较为新颖,适合作为竞赛课题…

SAP MM学习笔记39 - MRP(资材所要量计划)

这一章开始,离开请求书,学点儿新知识啦。 MRP ( Material Requirement Planning ) - 资材所要量计划。 它的位置在下面的调达周期图上来看,就是右上角的 所要量决定那块儿。 1,MRP(资材所要量计划) 的概要 MRP 的主要目的就是 确…

2024云渲染渲染100超简便的使用方法!渲染100云渲染邀请码5858

云渲染解决了本地电脑只能同时渲染一张图,并且占用本地电脑情况,让云渲染使用者也越来越多! 最近好多朋友在问我渲染100 - 官方注册邀请码【5858】如何提交渲染?今天我来总结一下 1.先在官网下载客户端,网页认证为渲染…

QT通过url下载http地址下的文件(文件夹)

前言 之前只写过通过http协议通信,没有写过下载http地址中的文件或者文件夹,了解一下在QT下如何下载。 其实很简单,同使用协议通信相同的是,创建QNetworkAccessManager和QNetworkRequest,设置QNetworkRequest的url&a…

游戏中的随机——“动态平衡概率”算法(二)

前言 本文是对上一篇文章的补充和总结。 在上一篇文章中,笔者提出了一套基本可用的“动态平衡概率”算法,本文将继续对该算法进行更加深入的探讨,解决上篇文章中的部分遗留问题,以及记录一下对“游戏中的概率”的一些思考&#…

【优选算法系列】第一节.二分查找简介加习题(704. 二分查找和34. 在排序数组中查找元素的第一个和最后一个位置)

文章目录 前言二分查找简介一、二分查找 1.1 题目描述 1.2 题目解析 1.2.1 算法原理 1.2.2 代码编写二、在排序数组中查找元素的第一个和最后一个位置 2.1 题目描述 2.2 题目解析 2.2.1 算法原理 2.2.2 代码编…

CSS选择器、CSS属性相关

CSS选择器 CSS属性选择器 通过标签的属性来查找标签&#xff0c;标签都有属性 <div class"c1" id"d1"></div>id值和class值是每个标签都自带的属性&#xff0c;还有另外一种&#xff1a;自定义属性 <div class"c1" id"d1&…

基于元学习神经网络的类人系统泛化

Nature 上介绍了一个关于AI在语言泛化方面的突破性研究。科学家们创建了一个具有人类般泛化能力的AI神经网络&#xff0c;它可以像人类一样将新学到的词汇融入现有词汇&#xff0c;并在新环境中使用它们。与ChatGPT 相比&#xff0c;该神经网络在系统性泛化测试中表现得更好。 …

pycharm切换不同的conda环境

进入file–>setting 这里会展示所有的conda环境&#xff0c;选择其中一个 点击刷新&#xff0c;展示你所有的库

RIS辅助MIMO广播信道容量

RIS辅助MIMO广播信道容量 摘要RIS辅助的BC容量矩阵形式的泰勒展开学习舒尔补 RIS-Aided Multiple-Input Multiple-Output Broadcast Channel Capacity论文阅读记录 基于泰勒展开求解了上行容量和最差用户的可达速率&#xff0c;学习其中的展开方法。 摘要 Scalable algorithm…

84.在排序数组中查找元素的第一个和最后一个位置(力扣)

目录 问题描述 代码解决以及思想 知识点 问题描述 代码解决以及思想 class Solution { public:vector<int> searchRange(vector<int>& nums, int target) {int left 0; // 定义左边界int right nums.size() - 1; // 定义右…