第十篇:TF、eigen、经纬度等坐标变换

系列文章目录

提示:这里可以添加系列文章的所有文章的目录,目录需要自己手动添加
TODO:写完再整理


前言

认知有限,望大家多多包涵,有什么问题也希望能够与大家多交流,共同成长!

本文先对**TF坐标变换(导航定位必须用到!)**做个简单的介绍,具体内容后续再更,其他模块可以参考去我其他文章


提示:以下是本篇文章正文内容

一、ROS_TF功能包作用

1.TF介绍

坐标转换和坐标订阅发布

TF库的目的是实现系统任一个点在所有坐标系之间的坐标变换,也就是说,只要给定一个坐标系下的一个点的坐标,就可以获得这个点在其他坐标系下的坐标,而且这些坐标变换是带有时间戳stamp

tf功能包就是给定起始位姿坐标,终止位姿坐标,TF功能包把每个位姿坐标通过tf_tree动态/静态链接依赖,并实时找到其变换矩阵【防盗标记–盒子君hzj】

使用的是广播订阅的通讯,也使用了launch文件管理启动,tf是ROS中的坐标变换系统,在机器人的建模仿真中经常用到
.

2.ROS-TF坐标转换树作用

在这里插入图片描述
(1)位置标定

实现各个坐标系的坐标变换,提供了ROS中各个组件(如:底盘、激光雷达,相机,IMU等),位置标定是机器人定位的基础

(2)访问关于机器人及其组件的时间和坐标的信息

我们可以从ROS-TF坐标转换树话题中访问关于机器人及其组件的时间和坐标的信息【防盗标记–盒子君hzj】

(3)记录和推算坐标系之间的关系

(1)五秒之前,机器人的参考坐标系相对于全局参考坐标系的关系是怎样的
(2)机器人上的激光雷达相对于机器人参考坐标系是怎样的
(3)机器人上的相机相对于机器人参考坐标系是怎样的

3.常用参考系类型

(1)全局坐标系(map)
(2)机器人身体坐标系(base_link)
(3)激光雷达参考坐标系
(4)摄像头参考坐标系【防盗标记–盒子君hzj】
(5)IMU参考坐标系
.
.

二、TF的常用功能

1.【transform】坐标系的数据格式、转换及运算

【位姿:欧拉角(rpy)、四元数(quternion)、旋转向量(vector)】

头文件#include<tf/transform_datatypes.h>

(1)tf::Quaternion类【四元素相关–直观】

(0)将一个坐标系下的点转化到另一个坐标系下【transformPoint()】
geometry_msgs::PointStamped point1;
point1.header.stamp=ros::Time();
point1.header.frame_id="child_frame";【防盗标记–盒子君hzj】
point1.point.x=1;
point1.point.y=2;
point1.point.z=3;
geometry_msgs::PointStamped point2;
try
{
     listener.transformPoint("frame",point1,point2);
}
catch (tf::TransformException &ex) 
{
    ROS_ERROR("%s",ex.what());
    ros::Duration(1.0).sleep();
 }
(1)用TF初始化创建四元数类型数据

在这里插入图片描述

(2)两种四元数数据类型转换【tf::Quaternion&&geometry_msgs::Quaternion】

在这里插入图片描述

(3)三种姿态形式的转换【四元数(quternion)、欧拉角(rpy)、旋转向量(vector)】

在这里插入图片描述
源码实现

tfScalar getAngle() const 【防盗标记–盒子君hzj】
{
	tfScalar s = tfScalar(2.) * tfAcos(m_floats[3]);
}
Vector3 getAxis() const
{
	tfScalar s_squared = tfScalar(1.) - tfPow(m_floats[3], tfScalar(2.));
	if (s_squared < tfScalar(10.) * TFSIMD_EPSILON) //Check for divide by zero
	        return Vector3(1.0, 0.0, 0.0);  // Arbitrary
	tfScalar s = tfSqrt(s_squared);
	return Vector3(m_floats[0] / s, m_floats[1] / s, m_floats[2] / s);
}
(4)四元数运算【相乘,点乘,求逆,加减乘除】

(1)四元数求模:sqrt(q.xq.x + q.yq.y + q.zq.z + q.wq.w)

tfScalar length() const
{
	return tfSqrt(length2());
}

(2)四元数模的平方:q.xq.x + q.yq.y + q.zq.z + q.wq.w

tfScalar length2() const
{
	return dot(*this);【防盗标记–盒子君hzj】
}

(3)四元数归一化

Quaternion& normalize() 
{
	return *this /= length();
}

Quaternion normalized() const 
{
	return *this / length();
}

(4)四元数点乘

tfScalar dot(const Quaternion& q) const
{
	return m_floats[0] * q.x() + m_floats[1] * q.y() + m_floats[2] * q.z() + m_floats[3] * q.m_floats[3];
}

(5)四元数相乘

TFSIMD_FORCE_INLINE Quaternion operator*(const Quaternion& q1, const Quaternion& q2) {
return Quaternion(
	q1.w() * q2.x() + q1.x() * q2.w() + q1.y() * q2.z() - q1.z() * q2.y(),
	q1.w() * q2.y() + q1.y() * q2.w() + q1.z() * q2.x() - q1.x() * q2.z(),
	q1.w() * q2.z() + q1.z() * q2.w() + q1.x() * q2.y() - q1.y() * q2.x(),【防盗标记–盒子君hzj】
	q1.w() * q2.w() - q1.x() * q2.x() - q1.y() * q2.y() - q1.z() * q2.z()); 
}

Quaternion& operator*=(const Quaternion& q)
{
	setValue(m_floats[3] * q.x() + m_floats[0] * q.m_floats[3] + m_floats[1] * q.z() - m_floats[2] * q.y(),
	        m_floats[3] * q.y() + m_floats[1] * q.m_floats[3] + m_floats[2] * q.x() - m_floats[0] * q.z(),
	        m_floats[3] * q.z() + m_floats[2] * q.m_floats[3] + m_floats[0] * q.y() - m_floats[1] * q.x(),
	        m_floats[3] * q.m_floats[3] - m_floats[0] * q.x() - m_floats[1] * q.y() - m_floats[2] * q.z());
	return *this;
}
(5)四元数插值
Quaternion slerp(const Quaternion& q, const tfScalar& t) const
注意

移植TF库的时候,这些实现原理都要懂

.
.
.

(2)tf::Matrix3x3类【变换矩阵相关–不直观】

(简单认为就是tf::Quaternion类是一维的,tf::Matrix3x3类是三维的)

在Matrix3x3类中,主要涉及到有各种不同方式的旋转矩阵初始化,欧拉角转旋转矩阵,旋转矩阵转欧拉角,四元数转旋转矩阵,旋转矩阵转四元数,以及一些常用的矩阵操作(矩阵相乘,求逆,转置,求行列式,…)等操作 :【防盗标记–盒子君hzj】

.
.
.

2.【Broadcaster/Listenter】订阅/发布每个关节的坐标(实现步骤)

(1)创建一个TF坐标变换的功能包

【当然这个功能可以在其他功能包内嵌套,但是最好还是单独成立一个tf坐标变换的功能包,好排除bug】

在功能包下的src文件夹新建一个文件夹tf,在tf文件夹里新建tf_boardcaster.cpp\tf_listener.cpp
.
.

(2)(发布者)广播TF变换

【Broadcaster发布坐标】
向系统中广播坐标系与坐标系变换的关系,系统中存在多个不同TF变换广播,每个广播都会直接在坐标系变换树中插入,有助于更快的查询、增加和删除TF节点

实现TF广播器程序步骤

(1)头文件
#include<tf/transform_broadcaster.h>

(2)定义发布器
tf::TransformBroadcaster broadcaster;【防盗标记–盒子君hzj】

(3)发布一个tf(源文件随时发布)
broadcaster.sendTransform( stamped_transform ) ;
【stamped_transform是一个tf::StampedTransform类型的数据】

.
.

(3)(订阅者)监听TF变换

【Listenter订阅坐标】
接收并缓存系统中发布的所有坐标系变换、并从中查询所需要的参考系变换

实现TF监听器程序步骤

(1)头文件
#include<tf/transform_listenter.h>

(2)定义监听器
tf::TransformListener  listener

(3)订阅TF的坐标信息【lookuoTransform()】

使用方法
	tf::StampedTransform stamped_transform;   //定义存放变换关系的变量
	try
	{
	//得到child_frame坐标系原点,在frame坐标系下的坐标与旋转角度
	      listener.lookupTransform("frame", "child_frame",ros::Time(0), stamped_transform);                   
	}
	    catch (tf::TransformException &ex)
	{
	      ROS_ERROR("%s",ex.what());
	      ros::Duration(1.0).sleep();
	      continue;
	}

	listener.lookupTransform("frame", "child_frame",ros::Time(0), stamped_transform);
	//获取到stamped_transform后可以使用这些信息
	transform.getOrigin().x()
	transform.getOrigin().y()
	 
	transform.getRotation().getW();
	transform.getRotation().getX();【防盗标记–盒子君hzj】
	transform.getRotation().getY();
	transform.getRotation().getZ();

(4)编译代码

1、在cmakelist.txt中设置编译选项,设置链接库
2、在元功能包下编译

.
.

3.TF变换的运算本质

TF变换就是两个刚性坐标系的平移和旋转的矩阵运算,不过它是实时运行的,包括位置和姿态

TF变换是基于右手坐标系的
拿起你的右手,先给自己竖个大拇指,然后打开手掌,将大拇指的方向朝向是z轴,让剩下的四根手指朝向的x轴,此时朝向手心外的是y轴的方向

三、使用eigen实现tf功能包的坐标转换功能替换(公司的导航系统没有用ROS_TF,eigen是矩阵运算的首选)

eigen的坐标转换功能有很多,要用什么才移植什么,太多了~

1、eigen的原理学习

https://blog.csdn.net/sunqin_csdn/article/details/108134138
.
.

2、位姿pose转矩阵

Eigen::Matrix4d Converter::poseToTransformMatrix( const Eigen::Vector3d& _ned, Eigen::Vector3d& _euler_angle, const Eigen::Vector3d& _offset_angle, const Eigen::Vector3d& _offset_position) {

  Eigen::Quaterniond _offset_qua;
  eulerToQuaternion(_offset_angle, _offset_qua);
  Eigen::Quaterniond _qua;
  eulerToQuaternion(_euler_angle, _qua);
  Eigen::Translation3d _offset_translation(_offset_position(0), _offset_position(1), _offset_position(2));

  Eigen::Translation3d translation3(_ned[0], _ned[1], _ned[2]);
  Eigen::Matrix4d transform = Eigen::Isometry3d(translation3 * _qua * _offset_translation * _offset_qua) .matrix();  // angle_yaw*angle_pitch*angle_roll
  return transform;
}

3、欧拉角euler转四元数Quaternion

void Converter::eulerToQuaternion(const Eigen::Vector3d& _euler, Eigen::Quaterniond& _qua) {
  Eigen::AngleAxis<double> angle_roll(_euler[0] * 0.0174532922f,  Eigen::Vector3d(1, 0, 0));
  Eigen::AngleAxis<double> angle_pitch(_euler[1] * 0.0174532922f, Eigen::Vector3d(0, 1, 0));
  Eigen::AngleAxis<double> angle_yaw(_euler[2] * 0.0174532922f, Eigen::Vector3d(0, 0, 1));
  _qua = Eigen::Quaterniond(angle_yaw * angle_pitch * angle_roll);
}

四、注意

(1)四元数(0,0,0,1)代表方向都为0的全能角【防盗标记–盒子君hzj】

(2)四元数是用来表示姿态pose的,而不是表示位置point的。能表示姿态还可以是旋转向量vector、欧拉角rpy、四元数(x,y,z,w)

(3)tf坐标连接关系可以通过rosrun rqt_tf_tree rqt_tf_tree查看,若是节点断开了就代表tf连接有问题,去源码看看有没有问题,实在不行就在launch文件中用静态链接指令手动连接在一起(只是一个隐藏的bug).静态链接指令[x,y,z,r,p,y/x,y,z,四元数 : 父节点坐标、子节点坐标]【防盗标记–盒子君hzj】

五、经纬度转换

1、北东地坐标表转经纬度坐标

void Converter::nedToLls(const Eigen::Vector3i& _lls_takeoff, const Eigen::Vector3d& _ned, Eigen::Vector3i& _lls) {
  _lls[0] = static_cast<int32_t>(static_cast<double>(_ned[0]) / LANLON_COEFFICIENT) + _lls_takeoff[0];
  _lls[1] = static_cast<int32_t>((static_cast<double>(_ned[1]) / cos(static_cast<double>(_lls[0]) / pow(10, 7) * 0.0174532925f)) / LANLON_COEFFICIENT) + _lls_takeoff[1];
  _lls[2] = _lls_takeoff[2] - _ned[2] * 100.0f;
}

2、经纬度坐标转北东地坐标表

void Converter::llsToNed(const Eigen::Vector3i& _lls_takeoff, const Eigen::Vector3i& _lls, Eigen::Vector3d& _ned) {
  double temp = static_cast<double>(_lls[1] - _lls_takeoff[1]) * LANLON_COEFFICIENT;
  temp *= cos(static_cast<double>(_lls[0]) / pow(10, 7) * 0.0174532925f);
  _ned[1] = temp;
  temp = static_cast<double>(_lls[0] - _lls_takeoff[0]) * LANLON_COEFFICIENT;
  _ned[0] = temp;
  _ned[2] = -static_cast<double>(_lls[2] - _lls_takeoff[2]) / 100.0f;
}


  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

盒子君~

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值