此文章旨在讲清楚欧拉角使用中的细节问题,让大家能够以专业的方式表达和交流欧拉角.

1欧拉角简介

欧拉角是由Leonhard Euler 提出的概念,用来描述刚体/移动坐标系在一个固定坐标系中的姿态.简单的说是使用XYZ三个轴的旋转分量,来描述一个6自由度的旋转.

欧拉角一般具有两大类表示方式,每类按照旋转次序的不同分为6小类:

Proper Euler angles (z-x-z, x-y-x, y-z-y, z-y-z, x-z-x, y-x-y)

Tait–Bryan angles (x-y-z, y-z-x, z-x-y, x-z-y, z-y-x, y-x-z).

每个大类都使用了3个变量描述三次旋转过程中的旋转角度, 差别在于Proper Euler angles只涉及两个转轴.而Tait–Bryan angles涉及三个转轴.一般在SLAM中我们使用的是Tait–Bryan angles.

Tait–Bryan angles 也被称为Cardan angles, nautical angles, (heading, elevation, and bank),(yaw, pitch, and roll). 我们接触的比较多的是yaw(偏航), pitch(俯仰), roll(横滚).三个变量一般对应(车体,飞行器的)z,y,x三个坐标轴.

2欧拉角描述旋转

2.1一些欧拉角特性

一般对于旋转矩阵(3*3),旋转向量/角轴(3*1),四元数(4*1),我们给定一串数字,就能表示清楚一个姿态/旋转.比如这里给出一个旋转矩阵R:

unity 欧拉角求反 欧拉角不同旋转顺序_算法

表示刚体在A某坐标系下的姿态, 我们就可以确切的画出刚体A的姿态.

但如果我给出一组欧拉角(后面都是指Tait–Bryan angles),绕x,y,z三个轴的转角分别为(α,β,γ),我们不能能确定一个明确的姿态.需要再追加两个属性:(1)旋转顺序(2)内旋/外旋.才能确定的给出这组欧拉角对应的姿态.

2.2关于旋转顺序

旋转顺序就是我们上文提到的Tait–Bryan angles (x-y-z, y-z-x, z-x-y, x-z-y, z-y-x, y-x-z).

我们指定绕x轴旋转α,绕y轴旋转β.但是可以有多个旋转顺序, 比如:

情况1:先绕x轴旋转α,再绕y轴旋转β.得到姿态

unity 欧拉角求反 欧拉角不同旋转顺序_旋转矩阵_02

 情况2:先绕y轴旋转β,再绕x轴旋转α,得到姿态

unity 欧拉角求反 欧拉角不同旋转顺序_算法_03

 得到的

unity 欧拉角求反 欧拉角不同旋转顺序_python_04

 一般是不等于

unity 欧拉角求反 欧拉角不同旋转顺序_算法_05

 的.

对于x,y,z三个轴的不同旋转顺序一共有(x-y-z, y-z-x, z-x-y, x-z-y, z-y-x, y-x-z)六种组合.我们需要明确旋转顺序,才能确定欧拉角所指的姿态.

2.3关于内旋/外旋

首先列出几种等价的概念:

内旋(intrinsic rotations) = 旋转轴(rotated axis)

外旋(extrinsic rotations) = 固定轴(static/fixed axis)

我们后面直接用内旋/外旋来描述.

内旋/外旋的定义:

假设在世界坐标系中XYZ中存在物体,物体自身坐标系为xyz,假设初始状态物体相对XYZ的旋转为(0,0,0),即xyz与XYZ重合.我们定义旋转顺序为z->y->x,转角分别为γ,β,α.

先绕z轴旋转γ,旋转过后,物体的x,y轴的坐标系发生了改变,z轴不变,得到新的物体自身坐标系

unity 欧拉角求反 欧拉角不同旋转顺序_旋转矩阵_06

,此时的坐标轴

unity 欧拉角求反 欧拉角不同旋转顺序_算法_07

,

不再与世界坐标系的坐标轴XY重合.

内旋,外旋的区别在于:

在转β(第二个转角)时:

内旋按照旋转后物体的坐标y轴,也就是unity 欧拉角求反 欧拉角不同旋转顺序_旋转矩阵_08

旋转.外旋按照世界坐标系中的Y轴旋转.

旋转最后一个角度时亦然.

因此, 增加了这两个概念(旋转顺序, 内外旋)后,我们描述一个能表示确定姿态/旋转的欧拉角,应该这样:

旋转角度(α,β,γ),旋转顺序(z->y->x),外旋.

或者:

旋转角度(α,β,γ),旋转顺序(x->y->z),内旋.

等等, 三个元素缺一不可.

旋转矩阵的表述方式:

上述的表述方式是否太罗嗦? 其实一般我们用欧拉角是为了方便主观理解,最终我们一般还是要转换为旋转矩阵,或者四元数来参与计算.

所以我们不如直接提供欧拉角与旋转矩阵的换算方式,一举两得.

比如上述: 旋转角度(α,β,γ),旋转顺序(z->y->x),外旋.

可以写为:

unity 欧拉角求反 欧拉角不同旋转顺序_python_09

(公式a)

其中:

unity 欧拉角求反 欧拉角不同旋转顺序_旋转矩阵_10

这个公式(a)根据旋转矩阵构成的定义即可得到.

而上述(α,β,γ),旋转顺序(x->y->z),内旋.

可以写为:

unity 欧拉角求反 欧拉角不同旋转顺序_矩阵_11

(公式b)

这个公式的推导稍微复杂些,这里只给出定义,推导过程不在本文的探讨范围内.

可以发现, 公式(a)与公式(b)是一样的.也就是说x->y->z内旋等价于z->y->x,外旋.

事实上,每种特定顺序的外旋等价于其相反顺序的内旋.

2.4 总结及示例代码

至此,我们得出提供一组能够精确描述旋转的欧拉角, 可以如下描述:

方法1:

旋转角度(α,β,γ),旋转顺序(x->y->z),内旋.

方法2:

见(公式b)

以下所有代码表示的都是:

unity 欧拉角求反 欧拉角不同旋转顺序_矩阵_12

  1. Python tf lib:
from tf import transformations
import math
T = transformations.euler_matrix(x, y, z, "sxyz")

(2)python numpy:

import numpy as np
Rz = np.mat([[c1,-s1, 0],
[s1, c1, 0],
[0,0,1]])

Ry = np.mat([[c2, 0, s2],
[0, 1, 0],
[-s2, 0, c2]])

Rx = np.mat([[1,0, 0],
[0, c3, -s3],
[0,s3,c3]])

print(np.dot(Rz ,np.dot(Ry ,Rx)))

(3)c++ eigen

#include <Eigen/Core>
#include <Eigen/Geometry>

Eigen::Matrix3d R = Eigen::AngleAxisd(euler[2], Eigen::Vector3d::UnitZ()) *
                    Eigen::AngleAxisd(euler[1], Eigen::Vector3d::UnitY()) * 
                    Eigen::AngleAxisd(euler[0], Eigen::Vector3d::UnitX());

3欧拉角与旋转矩阵/四元数的转换

3.1欧拉角转旋转矩阵

见2.3

3.2旋转矩阵转欧拉角

参考wikipedia: https://en.wikipedia.org/wiki/Euler_angles

借用其中的这张表:

unity 欧拉角求反 欧拉角不同旋转顺序_unity 欧拉角求反_13

这张表描述了所有旋转的组合.以最后一行为例,我们可以把这里的

认为是Z->X->Y内旋 or Y->X->Z外旋.

从矩阵的角度就是Z*X*Y.

这里的1,2,3可以认为是各个转轴上的旋转角度.后面的c1表示cos(绕Z轴转角).s1表示sin(绕Z轴转角).

旋转矩阵转欧拉角可以结合这张表中的公式. 比如对一个给定的旋转矩阵R.我们需要把R分解为Z*X*Y形式的欧拉角.我们就找到最后一行的

.我们记Z,X,Y对应的角度分别为yaw,roll,pitch.而

sin(yaw) = s1, cos(yaw) = c1.

sin(roll) = s2, cos(roll) = c2.

sin(pitch) = s3, cos(pitch) = c3.

我们的计算方式是寻找每个角度对应的tan值.

我们看到R[0][1]/R[1][1] = (-c2s1)/(c1c2) = -s1/c1 = -tan1.

则yaw = -atan2(R[0][1]/R[1][1]).

我们知道了yaw, 就能计算出c1, s1, 代入R[1][1] = c1c2可以求出c2.

c2结合R[2][1]=s2可计算出tan2, 得到roll.

同样的方法,得到tan3, 计算出pitch.

至此可得yaw,roll,pitch.

一般情况下可以如此计算,不过在万向节思索状态下似乎会出现分母为0的情况.

3.3欧拉角转四元数

可以借用旋转矩阵:欧拉角->旋转矩阵->四元数

当然, 也有直接转换的方法.

3.4四元数转欧拉角

可以借用旋转矩阵:四元数->旋转矩阵->欧拉角

当然, 也有直接转换的方法.

3.5基于python tf库的转换代码

from tf import transformations
import math
import numpy a3s np

def euler_to_matrix_rad(x, y, z):
    T = transformations.euler_matrix(x, y, z, "sxyz")
    return T

def matrix_to_euler_rad(matrix):
    q = transformations.quaternion_from_matrix(matrix)
    eulers = transformations.euler_from_quaternion(q, axes='sxyz')
    return eulers

def matrix_to_quaternion(matrix):
    return transformations.quaternion_from_matrix(matrix)

#四元数是ijk3 也就是xyz的顺序
def quaternion_to_matrix(quat):
    return transformations.quaternion_matrix(quat)

def quaternion_to_euler_rad(quat):
    return transformations.euler_from_quaternion(quat, axes='sxyz')

def euler_to_quaternion_rad(x, y, z):
    return transformations.quaternion_from_euler(x, y, z, axes='sxyz')

def rad_to_degree(rad):
    return rad / math.pi * 180

def degree_to_euler(degree):
    return degree / 180 * math.pi

def inverse_matrix(matrix):
    return transformations.inverse_matrix(matrix)

#注意简单的a*b是按对应位置元素相乘
def dot_matrix(a, b):
  return np.dot(a, b)

对于库卡机器人,根据手册《库卡编程详解》如下所述,为Rabg

unity 欧拉角求反 欧拉角不同旋转顺序_算法_14

按照旋转的坐标系分为两种旋转方式:

内旋-intrinsic rotation(动态):相对变换后的(当前的,自身的)坐标系做变换,以z-y′-z′′表示。′上标表达的是该旋转是以上一次旋转为参考。
外旋-extrinsic rotation(静态):相对初始的(固定的)坐标系做变换,以z-x-z表示。

不考虑内旋与外旋时,经典欧拉角和泰特布莱恩角各有六种绕轴旋转方式;如果考虑内旋与外旋,则各有12种绕轴旋转方式。

机器人欧拉角

史陶比(Staubli)使用XY'Z''
爱得普(Adept)使用ZY'Z''
库卡(KUKA)使用ZY'X''
发那科(Fanuc)与安川(Motoman)则使用XYZ
ABB机器人使用四元数表达旋转
优傲(UR)机器人使用方向矢量