本文将以常用的Tait-Bryan欧拉角为基础,描述如何从欧拉角转换为旋转矩阵,以及如何从旋转矩阵转换为欧拉角,以及如何将多个旋转连接为一个旋转矩阵。

文章分为两个部分,第一部分将详细解释相关假设和数学约定,第二部分将描述重要的转换部分以及Java和C++代码。

任何需要对三维空间的物体进行操作的人员都需要熟悉欧拉角和旋转矩阵,欧拉角有利于理解和描述3D旋转,因此欧拉角在许多三维系统中被广泛使用,而旋转矩阵是软件开发人员在软件中实现三维旋转的有效途径。

但是,欧拉角和旋转矩阵之间的相互转换是一个非常令人困惑的问题。有人说数学知识是欧拉角和旋转矩阵之间转换的非常困难的主要原因,其实并不是因为其中的数学知识非常复杂,而是由于欧拉角的定义有几十种互斥的方式,不同的作者可能使用不同的欧拉角约定,一般情况下也不会说明在系统中使用的是哪种约定,这就导致转换变得十分困难。

在本文中,我们将介绍欧拉角的一个最常见的约定,并详细阐述其定义以及如何与旋转矩阵进行转换。

1 欧拉角

1.1 欧拉角旋转系统的复杂性

欧拉角用于指定对象在三维空间中的旋转变换,包含三个旋转角度。欧拉角中的三个旋转角度都指定了围绕三维笛卡尔坐标系中一个轴的旋转角度,但是欧拉角的约定是非常灵活的,要完整的定义欧拉角系统,必须从下排列中进行选择:

  • Tait-Bryan与Classic:在Tait-Bryan约定中,欧拉角的三个角度都围绕了不同的坐标轴进行旋转,例如,第一个角度为绕Z轴的旋转角度,第二个角度为绕Y轴的旋转角度,第三个角度为绕X轴的旋转角度。而Classic约定中,欧拉角的三个角度仅围绕两个不同的坐标轴旋转,例如,第一个角度为绕Z轴的旋转角度,第二个角度为绕Y轴的旋转角度,而第三个角度依然为绕Z轴的旋转角度。上述两种约定系统都可以表示所有可能的3D旋转,并且也不能说哪一种约定系统就比另一种约定系统好。不过大多数现代使用者会倾向使用Tait-Bryan约定,这也是本文中将使用的欧拉角系统。
  • 旋转顺序:在Tait-Bryan约定中,欧拉角的三个角度需要围绕三个不同的坐标轴进行选择,那么执行旋转的顺序也是很重要的。以ZYX的旋转顺序进行旋转的结果将与其他五种旋转顺序(ZXY,XYZ,XZY,YXZ,YZX)产生的结果截然不同。在本文中,我们将使用先绕Z轴,再绕Y轴,最后绕X轴的ZYX的旋转顺序。
  • 内在旋转和外在旋转:内在旋转系统中,每个元素的旋转都是在自己的坐标系上进行的,而外在旋转系统中,每个元素的旋转都是在世界坐标系上进行的,世界坐标系是固定不动的。例如,假设欧拉角的三个角度分别对应绕Z轴,Y轴,X轴的旋转角度,并且按照ZYX的旋转顺序,那么对于内在旋转系统和外在旋转系统,围绕Z轴的第一个元素的旋转将是相同的。然后,对于内在旋转系统,第二个元素旋转是在第一次旋转后的新位置上围绕Y轴进行选择,而对于外在旋转系统,在第一旋转后的新位置在在原始的(未旋转)的世界坐标系的Y轴进行旋转的。而最后的,对于内在旋转系统,第三个元素的旋转是在内部旋转系统中的前两个操作的基础上再围绕X轴进行,而对于外在旋转系统,则是围绕原始的(未旋转)的世界坐标系的X轴进行旋转。
  • 主动旋转与被动旋转:主动旋转是指点相对于坐标系旋转,而被动旋转是指坐标系相对于点旋转,这两种旋转将产生相反的旋转结果。
  • 坐标系约定:在本文中,我们将使用右手笛卡尔坐标系,在右手坐标系中,右手旋转意味着从X、Y、Z轴的负方向看向正方向时,正向旋转为顺时针方向。

1.2 欧拉角内在和外在旋转方式对计算旋转矩阵的影响

从上文我们可以了解到欧拉角约定可分为Classic和Tait-Bryan两种,其中这包括了十二种常用的欧拉角顺序,

  • Classic : ZXZ、XYX、YZY、ZYZ、XZX、YXY
  • Tait-Bryan : XYZ、YZX、ZXY、XZY、ZYX、YXZ

上述所例举的欧拉角旋转方式可以是外在的或者是内在的。假设当前欧拉角约定为Tait-Bryan,旋转顺序为ZYX,欧拉角外在旋转意思是在旋转的过程中,围绕原始坐标系的ZYX坐标轴进行旋转,整个旋转过程假设ZYX三个坐标轴固定。欧拉角内在旋转的意思是在旋转的过程中,围绕旋转的坐标系的坐标轴旋转,旋转坐标系与移动的物体坐标系保持一致。

对于相同欧拉角旋转顺序的外在和内在两种旋转方式,其在计算旋转矩阵时,矩阵乘积的顺序是相反的,详情可参照以下示例。

假设当前欧拉角系统为Tait-Bryan,旋转顺序为ZYX,欧拉角为(\theta _{1} ,\theta _{2},\theta _{3} ),那么

对于旋转顺序为ZYX的内在旋转方式,其旋转矩阵R的计算方式为:

R=Z(\theta _{1} )Y(\theta _{2} )X(\theta _{3}) \tag{式1}

对于旋转顺序为ZYX的外在旋转方式,其旋转矩阵R的计算方式为:

R=X(\theta _{3} )Y(\theta _{2} )Z(\theta _{1}) \tag{式2}

1.3 本文的欧拉角系统

综上所述,在本文中,我们将采用Tait-Bryan欧拉角约定,使用旋转顺序为ZYX的主动内在旋转系统,在这个欧拉角旋转系统中,我们将绕Z轴的旋转角度称为yaw角,绕Y轴的旋转角度称为pitch角,将绕X轴的称为roll角。这是一个非常常见的约定,因为很容易被理解和可视化。前两个旋转角度(偏航和俯仰)常用于火炮和测量的方位角和仰角以及用于相机瞄准方向的平移和倾斜角度。从飞行员的角度上看,偏航yaw-俯仰pitch-滚转roll约定可以可视化为飞机方向的变化

下图显示了以飞机为中心的该右手坐标系的示例,X轴延飞机机头方向,Y轴指向飞行员也就是飞机的右侧,Z轴指向下方,相应的偏航yaw角、俯仰pitch角、滚转roll角度的旋转正方向如旋转箭头所示。

三维旋转 – 欧拉角和旋转矩阵的基本概念以及相互转换-StubbornHuang Blog
旋转坐标轴 欧拉角名称 欧拉角符号
x Roll u
y Pitch v
z Yaw w

在上图中,飞机的坐标系与世界坐标系相互对齐,如果想旋转飞机,需要执行如下三个基本的旋转,

通过绕x轴旋转u角度:

\begin{array}{l}
\mathrm{x}_{1}=\mathrm{x}_{0} \\
\mathrm{y}_{1}=\mathrm{y}_{0} \cos (\mathrm{u})-\mathrm{z}_{0} \sin (\mathrm{u}) \\
\mathrm{z}_{1}=\mathrm{y}_{0} \sin (\mathrm{u})+\mathrm{z}_{0} \cos (\mathrm{u})
\end{array}
\tag{式3}

通过绕y轴旋转v角度:

\begin{array}{l}
\mathrm{x}_{2}=\mathrm{x}_{1} \cos (\mathrm{v})+\mathrm{z}_{1} \sin (\mathrm{v}) \\
\mathrm{y}_{2}=\mathrm{y}_{1} \\
\mathrm{z}_{2}=-\mathrm{x}_{1} \sin (\mathrm{v})+\mathrm{z}_{1} \cos (\mathrm{v})
\end{array}
\tag{式4}

通过绕z轴旋转w角度:

\begin{array}{l}
\mathrm{x}_{3}=\mathrm{x}_{2} \cos (\mathrm{w})-\mathrm{y}_{2} \sin (\mathrm{w}) \\
\mathrm{y}_{3}=\mathrm{x}_{2} \sin (\mathrm{w})+\mathrm{y}_{2} \cos (\mathrm{w}) \\
\mathrm{z}_{3}=\mathrm{z}_{2}
\end{array}
\tag{式5}

其中,\left ( x_{0},y_{0},z_{0} \right )为原始未旋转点的坐标,\left ( x_{1},y_{1},z_{1} \right )为第一次旋转后点的坐标,\left ( x_{2},y_{2},z_{2} \right )为第二次旋转后的点的坐标,\left ( x_{3},y_{3},z_{3} \right )为所有三个旋转完成后点的坐标。

在这里需要对操作顺序进行澄清说明。在上文中,我们强调过元素旋转将按照yaw-pitch-roll的旋转顺序进行操作,但是上面的公式却是相反的操作顺序,这不是出错了。按照上述公式顺序将产生内在的偏航俯仰滚转旋转,而这正是我们所需要的。也就是说如果我们想要产生正确的欧拉角变换,对于内在旋转约定,需要先进行滚转、在进行俯仰、最后进行偏航操作。因为在上文中提到,一般情况下,内在旋转可以通过反转旋转顺序转为外在旋转,所以上述公式展现的内在旋转反转顺序之后就是先绕Z轴、再绕Y轴最后绕X轴的正确旋转顺序。

如果需要将旋转结果点坐标反旋转到其原始坐标,只需要反正旋转顺序,并更改三个旋转角度的符号。比如,如果正向旋转是yaw(w)-pitch(v)-roll(u),则反向旋转为roll(-u)-pitch(-v)-yaw(-w)

如上所示,分别执行三个旋转不是实现3D旋转的好方法,其主要缺点是很难将一系列的旋转转化为单个旋转,所以旋转矩阵就变得尤为重要。

2 旋转矩阵

旋转矩阵为一个3x3的矩阵,如下所示

R=\begin{bmatrix}
r_{11} & r_{12} & r_{13}\\
r_{21} & r_{22} & r_{23}\\
r_{31} & r_{32} & r_{33}
\end{bmatrix}
\tag{式6}

与欧拉角一个很大不同的是,旋转矩阵不需要约定旋转顺序。对于给定的旋转结果,由于旋转顺序约定的不同,同一旋转过程有多种不同的欧拉角组合结果,而旋转矩阵只有一个。

2.1 使用旋转矩阵旋转点

给定旋转矩阵R,可以使用以下公式旋转给定的任意点,

\begin{bmatrix}
x_{1} \\
y_{1} \\
z_{1}
\end{bmatrix}
=R\begin{bmatrix}
x_{0} \\
y_{0} \\
z_{0}
\end{bmatrix}
\tag{式7}

以上矩阵可以扩展为:

\begin{array}{l}
\mathrm{x}_{1}=\mathrm{r}_{11} \mathrm{x}_{0}+\mathrm{r}_{12} \mathrm{y}_{0}+\mathrm{r}_{13} \mathrm{z}_{0} \\
\mathrm{y}_{1}=\mathrm{r}_{21} \mathrm{x}_{0}+\mathrm{r}_{22} \mathrm{y}_{0}+\mathrm{r}_{23} \mathrm{z}_{0} \\
\mathrm{z}_{1}=\mathrm{r}_{31} \mathrm{x}_{0}+\mathrm{r}_{32} \mathrm{y}_{0}+\mathrm{r}_{33} \mathrm{z}_{0}
\end{array}
\tag{式8}

其中,(x_{0},y_{0},z_{0})为原始点坐标(旋转前点的坐标),(x_{1},y_{1},z_{1})为旋转后的点的坐标,r_{nn}为旋转矩阵R的元素。

3 将欧拉角转换为旋转矩阵

欧拉角中的绕三个坐标轴的旋转可用矩阵形式表示为:

绕x轴旋转滚转角u

\left[\begin{array}{l}
x_{1} \\
y_{1} \\
z_{1}
\end{array}\right]=R_{x}(u)\left[\begin{array}{l}
x_{0} \\
y_{0} \\
z_{0}
\end{array}\right]
\tag{式9}

绕y轴旋转俯仰角v

\left[\begin{array}{l}
x_{1} \\
y_{1} \\
z_{1}
\end{array}\right]=R_{y}(v)\left[\begin{array}{l}
x_{0} \\
y_{0} \\
z_{0}
\end{array}\right]
\tag{式10}

绕z轴旋转偏航角w

\left[\begin{array}{l}
x_{1} \\
y_{1} \\
z_{1}
\end{array}\right]=R_{z}(w)\left[\begin{array}{l}
x_{0} \\
y_{0} \\
z_{0}
\end{array}\right]
\tag{式11}

其中,

\begin{array}{c}
R_{x}(u) &=\left[\begin{array}{ccc}
1 & 0 & 0 \\
0 & \cos (u) & -\sin (u) \\
0 & \sin (u) & \cos (u)
\end{array}\right] \\
R_{y}(v) &=\left[\begin{array}{ccc}
\cos (v) & 0 & \sin (v) \\
0 & 1 & 0 \\
-\sin (v) & 0 & \cos (v)
\end{array}\right] \\
R_{z}(w) &=\left[\begin{array}{ccc}
\cos (w) & -\sin (w) & 0 \\
\sin (w) & \cos (w) & 0 \\
0 & 0 & 1
\end{array}\right]
\end{array}
\tag{式12}

我们可以将R_{x}(u)R_{y}(v)R_{z}(w)的三个旋转矩阵以给定的顺序相乘,将其组合成一个旋转矩阵,那么本文中的欧拉角的旋转顺序yaw-pitch-roll的完整的旋转矩阵为:

R_{z}(w) R_{y}(v) R_{x}(u)=\left[\begin{array}{ccc}
\mathrm{c}(v) \mathrm{c}(w) & s(u) \mathrm{s}(v) c(w)-\mathrm{c}(u) \mathrm{s}(w) & \mathrm{s}(u) \mathrm{s}(w)+\mathrm{c}(u) \mathrm{s}(v) \mathrm{c}(w) \\
\mathrm{c}(v) \mathrm{s}(w) & \mathrm{c}(u) \mathrm{c}(w)+\mathrm{s}(u) \mathrm{s}(v) \mathrm{s}(w) & \mathrm{c}(u) \mathrm{s}(v) \mathrm{s}(w)-\mathrm{s}(u) \mathrm{c}(w) \\
-\mathrm{s}(v) & \mathrm{s}(u) \mathrm{c}(v) & \mathrm{c}(u) \mathrm{c}(v)
\end{array}\right]
\tag{式13}

其中,(u,v,w)为三个欧拉角(roll,pitch,yaw),分别代表绕x轴,绕y轴,绕z轴的旋转。c(n)表示余弦,s(n)表示正弦。

4 将旋转矩阵转换为欧拉角

给定一个旋转矩阵R,可以将其转换为欧拉角。不同的欧拉角旋转顺序约定所需要的转换公式是不同的,本文中采用的yaw-pitch-roll的Tait-Bryan欧拉角的转换公式如下所示:

偏航角w

w=\tan ^{-1}\left(\frac{r_{21}}{r_{11}}\right)=\operatorname{atan} 2\left(r_{21}, r_{11}\right)
\tag{式14}

俯仰角v

v=-\sin ^{-1}\left(r_{31}\right)=-\operatorname{asin}\left(r_{31}\right)
\tag{式15}

滚转角u

u=\tan ^{-1}\left(\frac{r_{\mathrm{3} 2}}{r_{\mathrm{3} 3}}\right)=\operatorname{atan} 2\left(r_{32}, r_{33}\right)
\tag{式16}

其中,偏航角w和滚转角u将始终处于-\pi+\pi-180^{\circ}180^{\circ})的范围内,而俯仰角v将处于-\frac{\pi}{2}\frac{\pi}{2}-90^{\circ}+90^{\circ})的范围内。

5 欧拉角的万向节锁

上述公式14-16是从旋转矩阵转换为欧拉角的通用方式。如果俯仰角v=+ 90^{\circ}或者v=- 90^{\circ}时,\cos \left ( v \right ) = 0,从公式13我们可以看出,旋转矩阵的r_{11}r_{21}r_{32}r_{33}则会等于0。由于atan2函数没有在(0,0)处定义,因此当俯仰角v=+ 90^{\circ}或者v=- 90^{\circ}时,公式14和公式16无效,这就是可怕的万向节锁。

在万向节锁发生的情况下,也就是俯仰角v=+ 90^{\circ}或者v=- 90^{\circ}时,偏航角w和滚转角u在世界坐标系中相互对齐,这导致任何方向都可以使用不同的偏航角和滚转角组合来进行描述。

那么万向节锁该如何妥善的处理呢?我们可以通过r_{31}的值实时检测俯仰角v什么时候等于+ 90^{\circ}或者- 90^{\circ}的情况,

如果俯仰角v=- 90^{\circ}时,则将r_{31}赋值为1,并且:

\mathrm{u}+\mathrm{w}=\operatorname{atan} 2\left(-\mathrm{r}_{12},-\mathrm{r}_{13}\right)
\tag{式17}

如果俯仰角v=+ 90^{\circ}时,则将r_{31}赋值为-1,并且:

\mathrm{u}-\mathrm{w}=\operatorname{atan} 2\left(\mathrm{r}_{12},\mathrm{r}_{13}\right)
\tag{式18}

在实践中,我们会将其中一个角度设置为0并且求解另一个角度。例如,我们可以将偏航角w设置为0,并求解公式17和18中的滚转角u

需要注意的地方时,两个万向节锁点附近的区域,其从旋转空间到欧拉角的映射是不连续的,这意味着非常小的方向变化会导致相应欧拉角的不连续跳跃变化。例如,欧拉角\left ( 0^{\circ},89^{\circ},0^{\circ} \right )和欧拉角\left ( 90^{\circ},89^{\circ},90^{\circ} \right ),尽管看上去数值有很大的区别,但是实际上这两者表示仅隔1度的方向。一个很好的类比是飞机在飞越北极或南极时的经度不连续跳跃的方式。当尝试在方向之间进行插值或找到多个方向的平均值时,此行为会引发问题。

还要注意,虽然欧拉角容易受到万向节锁定的影响,但旋转矩阵却不会。对于每个可能的旋转,只有一个旋转矩阵。此外,对于旋转矩阵,映射是连续的。也就是说,旋转的微小变化总是等同于旋转矩阵的微小变化。

6 反向旋转

在许多实际应用中,很多地方都会使用到正向旋转和方向旋转,即使用不到也会需要了解。旋转矩阵具有逆矩阵就等于转置矩阵R^{-1} = R^{T}的特殊性质。

因此,如果R是前向旋转矩阵,则可以通过转置R的行和列创建逆矩阵。

前向旋转矩阵R

\boldsymbol{R}_{f}=\left[\begin{array}{lll}
r_{11} & r_{12} & r_{13} \\
r_{21} & r_{22} & r_{23} \\
r_{31} & r_{32} & r_{33}
\end{array}\right]
\tag{式19}

反向旋转矩阵:

\boldsymbol{R}_{\boldsymbol{r}}=\boldsymbol{R}_{\boldsymbol{f}}^{-1}=\boldsymbol{R}_{\boldsymbol{f}}^{\boldsymbol{T}}=\left[\begin{array}{lll}
r_{11} & r_{21} & r_{31} \\
r_{12} & r_{22} & r_{32} \\
r_{13} & r_{23} & r_{33}
\end{array}\right]
\tag{式20}