1、前言

从事导航、制导或者控制时,经常需要将各个物理矢量从A坐标系转换至B坐标系,在这里涉及到的坐标系旋转常用欧拉角、旋转矩阵、旋转矢量或者四元数进行表示。

2、旋转矩阵

任意坐标系Android 矢量旋转动画 旋转矢量应用_矩阵Android 矢量旋转动画 旋转矢量应用_线性代数_02之间转换关系,都可以通过一个3X3的旋转矩阵(旋转矢量/四元数)来描述。例如矢量Android 矢量旋转动画 旋转矢量应用_Android 矢量旋转动画_03在坐标系Android 矢量旋转动画 旋转矢量应用_矩阵中的分量为Android 矢量旋转动画 旋转矢量应用_旋转矩阵_05,则在Android 矢量旋转动画 旋转矢量应用_线性代数_02中的分量Android 矢量旋转动画 旋转矢量应用_旋转矩阵_07为:
Android 矢量旋转动画 旋转矢量应用_旋转矩阵_08

****注意:****坐标变换和刚体旋转不是同一回事,区别和联系如下

(1)坐标变换,是刚体不动,坐标系进行旋转

(2)刚体旋转,是坐标系不动,对在坐标系中的刚体进行旋转

(3)坐标变换和刚体旋转是互逆过程,比如,刚体绕X轴旋转了10°,可以等效为坐标系绕着X轴旋转了-10°

(4)在不特殊说明的情况下坐标旋转均指坐标系旋转

Android 矢量旋转动画 旋转矢量应用_坐标变换_09

上述旋转矩阵可以分解为旋转三次,比如常见的旋转顺序先偏航、再俯仰最后滚转,其拆解如下:
坐标系绕着X、Y和Z轴旋转的基元旋转矩阵分别是:

(1)绕X轴旋转矩阵(滚转角度Android 矢量旋转动画 旋转矢量应用_旋转矩阵_10)

Android 矢量旋转动画 旋转矢量应用_坐标变换_11

(2)绕Y轴旋转矩阵(俯仰角Android 矢量旋转动画 旋转矢量应用_矩阵_12)
Android 矢量旋转动画 旋转矢量应用_矩阵_13

(3)绕Z轴旋转矩阵(偏航角Android 矢量旋转动画 旋转矢量应用_旋转矩阵_14)
Android 矢量旋转动画 旋转矢量应用_旋转矩阵_15

任何两个坐标系之间的关系都可以通过若干次上述基元旋转来实现。
比如从导航系沿着zyx三轴顺序旋转至机体坐标系,那么从导航系至机体坐标系的旋转矩阵如下:
Android 矢量旋转动画 旋转矢量应用_坐标变换_16

其中,Android 矢量旋转动画 旋转矢量应用_Android 矢量旋转动画_17表示导航坐标系Android 矢量旋转动画 旋转矢量应用_矩阵_18到机体坐标系Android 矢量旋转动画 旋转矢量应用_线性代数_19的旋转矩阵,注意下标是从右往左,表明矩阵是左乘旋转矩阵的乘法为左乘
也就最终的旋转矩阵,通过 左乘 获得。


优点:无奇点,理解起来较为直观;

缺点:有九个参数(3×3的方阵),过于冗杂,使得计算复杂度增加

3、欧拉角

上文中旋转拆解的三个角度俯仰、偏航、滚转即为欧拉角度。

Android 矢量旋转动画 旋转矢量应用_矩阵_20


欧拉角适用于小机动无人机或车类运动,因为其俯仰角度或者滚转角度就不会超过90度。


优点:参数少,几何上较为直观;

缺点:存在奇点(万向锁问题,例如先仰45°再俯90°,这与先俯90°再仰45°是等价的。事实上,一旦选择±90°作为俯角,就会导致第一次旋转和第三次旋转等价,整个旋转表示系统被限制在只能绕竖直轴旋转,丢失了一个表示维度。这种角度为±90°的第二次旋转使得第一次和第三次旋转的旋转轴相同的现象,称作万向锁)

4、旋转矢量

旋转矢量也叫轴角,顾名思义就是绕某条单位轴旋转一定角度,从这个意义上看,它与四元数互相转换相当方便。
Android 矢量旋转动画 旋转矢量应用_旋转矩阵_21
其中Android 矢量旋转动画 旋转矢量应用_Android 矢量旋转动画_22为旋转角度,Android 矢量旋转动画 旋转矢量应用_矩阵_18为旋转矢量,其四元数即可以表达为:
Android 矢量旋转动画 旋转矢量应用_矩阵_24

假设同一个坐标系中的两个矢量p和q,计算让p和q重合的旋转矩阵。在这里我们利用旋转矢量则非常简单,只要绕着同时垂直p和q的矢量n旋转σ角度即可,因此现在将问题转换为计算垂直矢量和旋转角度**(PX4里面多旋翼姿态控制即利用这点)**。

(1)垂直矢量n,可以通过p和q的叉乘实现,并做归一化
Android 矢量旋转动画 旋转矢量应用_线性代数_25
(2)旋转角度σ,可以通过点乘或叉乘的定义来计算
Android 矢量旋转动画 旋转矢量应用_旋转矩阵_26
因此旋转角度为:
Android 矢量旋转动画 旋转矢量应用_坐标变换_27
然后通过利用下述公式将旋转矢量转为旋转矩阵,其公式如下:
Android 矢量旋转动画 旋转矢量应用_矩阵_28
其中
Android 矢量旋转动画 旋转矢量应用_旋转矩阵_29
Android 矢量旋转动画 旋转矢量应用_旋转矩阵_30
也可以通过先将旋转矢量转换为四元数,再将四元数转为旋转矩阵。


优点:几何上较为直观

缺点:在数学推导和数值计算上并不实用(常常很难找到旋转轴的准确位置)

5、四元数

四元数数学运算

为了方便后面的推导说明,首先定义以下两个四元数:
Android 矢量旋转动画 旋转矢量应用_旋转矩阵_31
(1)与标量(矢量)运算

当标量(或矢量)和四元数进行数学运算时,先将标量(或矢量)升级为四元数,升级方法为
Android 矢量旋转动画 旋转矢量应用_旋转矩阵_32
(2)共轭运算

共轭运算,只需要将虚部添加一个负号即可
Android 矢量旋转动画 旋转矢量应用_坐标变换_33
(3)加减法运算
Android 矢量旋转动画 旋转矢量应用_坐标变换_34
(4)乘法运算

在推导四元数乘法运算之前,先定义矢量的乘积为叉乘与点乘之,注意不是和:
Android 矢量旋转动画 旋转矢量应用_矩阵_35
可以看出矢量的乘积是一个四元数。
下面推导四元数的乘法运算:
Android 矢量旋转动画 旋转矢量应用_坐标变换_36
需要注意是,四元数乘法的虚部包含叉乘运算,因此四元数乘法不支持交换律,但是满足结合律和分配律(因为叉乘不满足交换)
将式展开并写成矩阵的形式
Android 矢量旋转动画 旋转矢量应用_矩阵_37
(5) 标量与四元数的乘法
Android 矢量旋转动画 旋转矢量应用_Android 矢量旋转动画_38
(6) 矢量与四元数的乘法,直接将矢量升级为实部为0的四元数,然后采用式4计算即可

(7) 求模运算,四元数和共轭四元数的乘积
Android 矢量旋转动画 旋转矢量应用_旋转矩阵_39
(8) 逆运算
Android 矢量旋转动画 旋转矢量应用_Android 矢量旋转动画_40
两个互逆四元数的乘积是单位四元数
Android 矢量旋转动画 旋转矢量应用_旋转矩阵_41
注意:利用四元数进行坐标系转换时,各个坐标系之间左乘右乘同其所选取的基底有关,不同于旋转矩阵的左乘。当其所选统一基底进行表达的四元数时,其进行左乘;当其选用不同基底时,即进行右乘(较常用)。详见邓正隆的惯性技术。

6、坐标系转换示例

上述主要是介绍相邻坐标系如何通过欧拉角、旋转矩阵、四元数和旋转矢量进行表示。工作中我们经常遇到的是不同时刻不同坐标系间的变换。这里主要介绍如何处理这一情景(不考虑平移情景,平移可以看做先旋转在矢量相加)。

在此以求取slam世界坐标系与导航坐标系的旋转矩阵举例

**背景:**无人机利用相机做slam时,其世界坐标系在初始化成功时刻t1时候才确立,记Android 矢量旋转动画 旋转矢量应用_线性代数_42系,该时刻的相机坐标系为Android 矢量旋转动画 旋转矢量应用_矩阵_43,该时刻无人机机体坐标系为Android 矢量旋转动画 旋转矢量应用_坐标变换_44。无人机导航坐标系是在无人机起飞前t0时刻就确立的,在此取较常用的北东地坐标系为导航坐标系Android 矢量旋转动画 旋转矢量应用_矩阵_18,该时刻对应的机体坐标系为Android 矢量旋转动画 旋转矢量应用_矩阵_46。我们需要将slam获取的相机相对世界系的位姿转换至导航系,从而修正无人机在导航系的位姿;也需要将世界系的障碍物转换至导航系,故我们需要知道世界系与导航系的旋转矩阵。

**翻译:**将上述背景翻译成数学语言,即已知Android 矢量旋转动画 旋转矢量应用_矩阵_18系、Android 矢量旋转动画 旋转矢量应用_矩阵_46系、Android 矢量旋转动画 旋转矢量应用_矩阵_43Android 矢量旋转动画 旋转矢量应用_坐标变换_44Android 矢量旋转动画 旋转矢量应用_线性代数_51,求取Android 矢量旋转动画 旋转矢量应用_旋转矩阵_52
由于相机与无人机绑定,其之间的安装角度可以通过事先校准获取,即t1时刻机体坐标系与该时刻的相机坐标系转换关系Android 矢量旋转动画 旋转矢量应用_线性代数_53已知;而t1时刻的机体坐标系与导航坐标系可以通过无人机自身导航系统获取,即可以得到Android 矢量旋转动画 旋转矢量应用_旋转矩阵_54
根据旋转矩阵章节的左乘链式法则可以得到如下等式:
Android 矢量旋转动画 旋转矢量应用_Android 矢量旋转动画_55

7、工程应用

工程上我们进行坐标系转换时,更关注的是如何利用现有的库进行实现,而非根据上述公式重复造轮子。常见的坐标系转换矩阵库主要有以下:

  1. Eigen
    eigen是一种c++库,里面集成了各种矩阵旋转和平移函数
    Eigen的官方文档
AngleAxisd rotation_vector(M_PI / 4, Vector3d(0, 0, 1));     //旋转矢量法,沿Z轴旋转 45 度
 rotation_matrix = rotation_vector.toRotationMatrix();  //旋转矢量转变为旋转矩阵
 euler_angles = rotation_matrix.eulerAngles(2, 1, 0); // ZYX顺序,欧拉角转旋转矩阵
 Quaterniond q = Quaterniond(rotation_vector);  //旋转矢量转四元数
 
 rotation_vector2.fromRotationMatrix(rotation_matrix2);   //旋转矩阵转旋转矢量
 euler_angle2 = rotation_matrix2.eulerAngles(2, 1, 0); //旋转矩阵转欧拉角
 Eigen::Quaterniond quaternion2(rotation_matrix2);    //旋转矩阵转四元数
 
 Eigen::AngleAxisd rotation_vector3 = Eigen::AngleAxisd(euler_angle3[0], Eigen::Vector3d::UnitZ()) *
                     Eigen::AngleAxisd(euler_angle3[1], Eigen::Vector3d::UnitY()) *
                     Eigen::AngleAxisd(euler_angle3[2], Eigen::Vector3d::UnitX()); //欧拉角转旋转矢量
  1. matlab
    matlab里面有大量的相互转换及相关运算,在这里介绍最常见的:
dcm = angle2dcm(yaw,theta,roll,'zyx') %%欧拉角转旋转矩阵,zyx为其旋转顺序
Euler = dcm2angle(dcm);  %%旋转矩阵转换为欧拉角
quat = angle2quat(yaw,theta,roll,'zyx') %%欧拉角转四元数,zyx为其旋转顺序
Euler = quat2angle(quat);  %%四元数转换为欧拉角
dcm = quat2dcm(quat); %% 四元数转旋转矩阵
quat = dcm2quat(dcm); %% 旋转矩阵转四元数
R = roty(ang); %%绕y轴旋转ang
quatprod = quatmultiply(q,r); %%四元数相乘
quatNormalized = normalize(quat); %%四元数归一化,利用四元数进行导航解算时需要

3、 TF
TF(TransForm)是ros世界里的一个基本的也是很重要的概念,用来进行坐标转换的。

tf::Quaternion q;
q.setRPY(yaw,pitch,roll);  //欧拉角转四元数
v=q.getAxis(); //四元数转轴角
Matrix.setRotation(q); //四元数转旋转矩阵
Matrix.getEulerYPR(m_yaw,m_pitch,m_roll); //旋转矩阵转欧拉角