旋转变换 | 欧拉角的约定与四元数的优势

注:本文为 “旋转变换” 相关合辑。
图片清晰度受引文原图所限。
略作重排,如有内容异常,请看原文。


旋转变换(一)旋转矩阵

csxiaoshui 原创于 2017-03-27 17:15:37 发布

1. 简介

在计算机图形学中,仿射变换是一类应用广泛的特殊变换,其基本形式包括平移、旋转、缩放与剪切。本文及后续系列文章将重点探讨旋转变换,涵盖二维旋转变换、三维旋转变换及其多种数学表达形式(旋转矩阵、四元数、欧拉角等)。

2. 绕原点的二维旋转

二维旋转的关键是围绕某一固定点进行角度偏转,三维旋转则围绕某一固定轴进行。其中,绕坐标原点的二维旋转是最基础的场景,如图所示:

2DRotation

如图所示,点 v v v 绕原点旋转 θ \theta θ 角后得到点 v ′ v' v。设点 v v v 的坐标为 ( x , y ) (x, y) (x,y),原点到点 v v v 的距离为 r r r,原点与点 v v v 构成的向量与 x x x 轴的夹角为 ϕ \phi ϕ,则点 v v v 的坐标可表示为:
x = r cos ⁡ ϕ , y = r sin ⁡ ϕ x = r\cos\phi, \quad y = r\sin\phi x=rcosϕ,y=rsinϕ

v ′ v' v 的坐标 ( x ′ , y ′ ) (x', y') (x,y) 满足:
x ′ = r cos ⁡ ( θ + ϕ ) , y ′ = r sin ⁡ ( θ + ϕ ) x' = r\cos(\theta + \phi), \quad y' = r\sin(\theta + \phi) x=rcos(θ+ϕ),y=rsin(θ+ϕ)

根据三角函数和角公式展开:
x ′ = r cos ⁡ θ cos ⁡ ϕ − r sin ⁡ θ sin ⁡ ϕ y ′ = r sin ⁡ θ cos ⁡ ϕ + r cos ⁡ θ sin ⁡ ϕ x' = r\cos\theta\cos\phi - r\sin\theta\sin\phi \\ y' = r\sin\theta\cos\phi + r\cos\theta\sin\phi x=rcosθcosϕrsinθsinϕy=rsinθcosϕ+rcosθsinϕ

x = r cos ⁡ ϕ x = r\cos\phi x=rcosϕ y = r sin ⁡ ϕ y = r\sin\phi y=rsinϕ 代入上式,化简得:
x ′ = x cos ⁡ θ − y sin ⁡ θ y ′ = x sin ⁡ θ + y cos ⁡ θ x' = x\cos\theta - y\sin\theta \\ y' = x\sin\theta + y\cos\theta x=xcosθysinθy=xsinθ+ycosθ

写成矩阵形式为:
[ x ′ y ′ ] = [ cos ⁡ θ − sin ⁡ θ sin ⁡ θ cos ⁡ θ ] ⋅ [ x y ] \begin{bmatrix} x' \\ y' \end{bmatrix} = \begin{bmatrix} \cos\theta & -\sin\theta \\ \sin\theta & \cos\theta \end{bmatrix} \cdot \begin{bmatrix} x \\ y \end{bmatrix} [xy]=[cosθsinθsinθcosθ][xy]

上述推导基于三角函数的基本定义,适用于任意旋转角度(如大于 18 0 ∘ 180^\circ 180 或负角度),并非局限于图示中的锐角场景。

3. 绕任意点的二维旋转

绕原点的旋转是二维旋转的基础形式,绕任意点的旋转可通过坐标变换转化为绕原点的旋转,具体步骤如下:

  1. 将旋转中心平移至坐标原点;
  2. 执行绕原点的旋转操作(参考第 2 节);
  3. 将旋转中心平移回原始位置。

2DArbitraryRotate

设平移矩阵为 T ( t x , t y ) T(t_x, t_y) T(tx,ty)(表示沿 x x x 轴平移 t x t_x tx、沿 y y y 轴平移 t y t_y ty),则绕任意点的旋转变换可表示为:
v ′ = T ( t x , t y ) ⋅ R ( θ ) ⋅ T ( − t x , − t y ) ⋅ v v' = T(t_x, t_y) \cdot R(\theta) \cdot T(-t_x, -t_y) \cdot v v=T(tx,ty)R(θ)T(tx,ty)v
其中, v v v 为原始点坐标, v ′ v' v 为旋转后点坐标, R ( θ ) R(\theta) R(θ) 为绕原点的旋转矩阵。由于采用列向量表示点坐标,矩阵运算遵循左乘规则,先执行平移 T ( − t x , − t y ) T(-t_x, -t_y) T(tx,ty) 将旋转中心移至原点。

在计算机图形学中,为统一描述平移、旋转、缩放等变换,需引入齐次坐标。二维变换中,齐次坐标采用 ( x , y , w ) (x, y, w) (x,y,w) 表示(通常取 w = 1 w=1 w=1),通过 3×3 矩阵实现各类变换的统一表达(三维变换则需 4×4 矩阵)。

3.1 二维平移的矩阵表示

如图所示,点 P ( x , y ) P(x, y) P(x,y) 沿 x x x 轴平移 t x t_x tx、沿 y y y 轴平移 t y t_y ty 后得到点 P ′ ( x ′ , y ′ ) P'(x', y') P(x,y),其坐标关系为:
在这里插入图片描述

x ′ = x + t x , y ′ = y + t y x' = x + t_x, \quad y' = y + t_y x=x+tx,y=y+ty

采用齐次坐标表示为:
[ x ′ y ′ 1 ] = [ 1 0 t x 0 1 t y 0 0 1 ] ⋅ [ x y 1 ] \begin{bmatrix} x' \\ y' \\ 1 \end{bmatrix} = \begin{bmatrix} 1 & 0 & t_x \\ 0 & 1 & t_y \\ 0 & 0 & 1 \end{bmatrix} \cdot \begin{bmatrix} x \\ y \\ 1 \end{bmatrix} xy1 = 100010txty1 xy1

因此,二维平移矩阵为:
T ( t x , t y ) = [ 1 0 t x 0 1 t y 0 0 1 ] T(t_x, t_y) = \begin{bmatrix} 1 & 0 & t_x \\ 0 & 1 & t_y \\ 0 & 0 & 1 \end{bmatrix} T(tx,ty)= 100010txty1

若平移量为 ( − t x , − t y ) (-t_x, -t_y) (tx,ty),则平移矩阵为:
T ( − t x , − t y ) = [ 1 0 − t x 0 1 − t y 0 0 1 ] T(-t_x, -t_y) = \begin{bmatrix} 1 & 0 & -t_x \\ 0 & 1 & -t_y \\ 0 & 0 & 1 \end{bmatrix} T(tx,ty)= 100010txty1

3.2 二维旋转矩阵的齐次坐标扩展

将第 2 节中的绕原点旋转矩阵扩展为 3×3 形式,以适配齐次坐标:
[ x ′ y ′ 1 ] = [ cos ⁡ θ − sin ⁡ θ 0 sin ⁡ θ cos ⁡ θ 0 0 0 1 ] ⋅ [ x y 1 ] \begin{bmatrix} x' \\ y' \\ 1 \end{bmatrix} = \begin{bmatrix} \cos\theta & -\sin\theta & 0 \\ \sin\theta & \cos\theta & 0 \\ 0 & 0 & 1 \end{bmatrix} \cdot \begin{bmatrix} x \\ y \\ 1 \end{bmatrix} xy1 = cosθsinθ0sinθcosθ0001 xy1

3.3 绕任意点的旋转矩阵推导

结合平移矩阵与旋转矩阵,绕任意点 ( t x , t y ) (t_x, t_y) (tx,ty) 旋转 θ \theta θ 角的复合矩阵为:
M = T ( t x , t y ) ⋅ R ( θ ) ⋅ T ( − t x , − t y ) M = T(t_x, t_y) \cdot R(\theta) \cdot T(-t_x, -t_y) M=T(tx,ty)R(θ)T(tx,ty)

代入各矩阵表达式并展开:
M = [ 1 0 t x 0 1 t y 0 0 1 ] ⋅ [ cos ⁡ θ − sin ⁡ θ 0 sin ⁡ θ cos ⁡ θ 0 0 0 1 ] ⋅ [ 1 0 − t x 0 1 − t y 0 0 1 ] = [ cos ⁡ θ − sin ⁡ θ ( 1 − cos ⁡ θ ) t x + t y sin ⁡ θ sin ⁡ θ cos ⁡ θ ( 1 − cos ⁡ θ ) t y − t x sin ⁡ θ 0 0 1 ] \begin{align*} M &= \begin{bmatrix} 1 & 0 & t_x \\ 0 & 1 & t_y \\ 0 & 0 & 1 \end{bmatrix} \cdot \begin{bmatrix} \cos\theta & -\sin\theta & 0 \\ \sin\theta & \cos\theta & 0 \\ 0 & 0 & 1 \end{bmatrix} \cdot \begin{bmatrix} 1 & 0 & -t_x \\ 0 & 1 & -t_y \\ 0 & 0 & 1 \end{bmatrix} \\ &= \begin{bmatrix} \cos\theta & -\sin\theta & (1-\cos\theta)t_x + t_y\sin\theta \\ \sin\theta & \cos\theta & (1-\cos\theta)t_y - t_x\sin\theta \\ 0 & 0 & 1 \end{bmatrix} \end{align*} M= 100010txty1 cosθsinθ0sinθcosθ0001 100010txty1 = cosθsinθ0sinθcosθ0(1cosθ)tx+tysinθ(1cosθ)tytxsinθ1

4. 三维基本旋转

三维旋转变换可分解为绕三个坐标轴( x x x y y y z z z 轴)的基本旋转组合,因此需先明确绕单一坐标轴的旋转矩阵。本文采用 OpenGL 标准的右手坐标系,旋转角度的正负遵循右手定则(大拇指指向坐标轴正方向,四指弯曲方向为角度正方向),如图所示:

RightHanderRule

4.1 绕 x x x 轴的旋转

P ( x , y , z ) P(x, y, z) P(x,y,z) x x x 轴旋转 θ \theta θ 角后得到点 P ′ ( x ′ , y ′ , z ′ ) P'(x', y', z') P(x,y,z)。由于旋转过程中 x x x 坐标保持不变, y y y z z z 坐标的变化等价于在 y o z yoz yoz 平面内的二维旋转( y y y 轴对应二维旋转的 x x x 轴, z z z 轴对应二维旋转的 y y y 轴),因此:
x ′ = x y ′ = y cos ⁡ θ − z sin ⁡ θ z ′ = y sin ⁡ θ + z cos ⁡ θ x' = x \\ y' = y\cos\theta - z\sin\theta \\ z' = y\sin\theta + z\cos\theta x=xy=ycosθzsinθz=ysinθ+zcosθ

采用齐次坐标(4×4 矩阵)表示为:
[ x ′ y ′ z ′ 1 ] = [ 1 0 0 0 0 cos ⁡ θ − sin ⁡ θ 0 0 sin ⁡ θ cos ⁡ θ 0 0 0 0 1 ] ⋅ [ x y z 1 ] \begin{bmatrix} x' \\ y' \\ z' \\ 1 \end{bmatrix} = \begin{bmatrix} 1 & 0 & 0 & 0 \\ 0 & \cos\theta & -\sin\theta & 0 \\ 0 & \sin\theta & \cos\theta & 0 \\ 0 & 0 & 0 & 1 \end{bmatrix} \cdot \begin{bmatrix} x \\ y \\ z \\ 1 \end{bmatrix} xyz1 = 10000cosθsinθ00sinθcosθ00001 xyz1

4.2 绕 y y y 轴的旋转

P ( x , y , z ) P(x, y, z) P(x,y,z) y y y 轴旋转 θ \theta θ 角后得到点 P ′ ( x ′ , y ′ , z ′ ) P'(x', y', z') P(x,y,z)。旋转过程中 y y y 坐标保持不变, x x x z z z 坐标的变化等价于在 z o x zox zox 平面内的二维旋转( z z z 轴对应二维旋转的 x x x 轴, x x x 轴对应二维旋转的 y y y 轴),因此:
x ′ = x cos ⁡ θ + z sin ⁡ θ y ′ = y z ′ = − x sin ⁡ θ + z cos ⁡ θ x' = x\cos\theta + z\sin\theta \\ y' = y \\ z' = -x\sin\theta + z\cos\theta x=xcosθ+zsinθy=yz=xsinθ+zcosθ

采用齐次坐标(4×4 矩阵)表示为:
[ x ′ y ′ z ′ 1 ] = [ cos ⁡ θ 0 sin ⁡ θ 0 0 1 0 0 − sin ⁡ θ 0 cos ⁡ θ 0 0 0 0 1 ] ⋅ [ x y z 1 ] \begin{bmatrix} x' \\ y' \\ z' \\ 1 \end{bmatrix} = \begin{bmatrix} \cos\theta & 0 & \sin\theta & 0 \\ 0 & 1 & 0 & 0 \\ -\sin\theta & 0 & \cos\theta & 0 \\ 0 & 0 & 0 & 1 \end{bmatrix} \cdot \begin{bmatrix} x \\ y \\ z \\ 1 \end{bmatrix} xyz1 = cosθ0sinθ00100sinθ0cosθ00001 xyz1

4.3 绕 z z z 轴的旋转

P ( x , y , z ) P(x, y, z) P(x,y,z) z z z 轴旋转 θ \theta θ 角后得到点 P ′ ( x ′ , y ′ , z ′ ) P'(x', y', z') P(x,y,z)。旋转过程中 z z z 坐标保持不变, x x x y y y 坐标的变化等价于在 x o y xoy xoy 平面内的二维旋转,因此:
x ′ = x cos ⁡ θ − y sin ⁡ θ y ′ = x sin ⁡ θ + y cos ⁡ θ z ′ = z x' = x\cos\theta - y\sin\theta \\ y' = x\sin\theta + y\cos\theta \\ z' = z x=xcosθysinθy=xsinθ+ycosθz=z

采用齐次坐标(4×4 矩阵)表示为:
[ x ′ y ′ z ′ 1 ] = [ cos ⁡ θ − sin ⁡ θ 0 0 sin ⁡ θ cos ⁡ θ 0 0 0 0 1 0 0 0 0 1 ] ⋅ [ x y z 1 ] \begin{bmatrix} x' \\ y' \\ z' \\ 1 \end{bmatrix} = \begin{bmatrix} \cos\theta & -\sin\theta & 0 & 0 \\ \sin\theta & \cos\theta & 0 & 0 \\ 0 & 0 & 1 & 0 \\ 0 & 0 & 0 & 1 \end{bmatrix} \cdot \begin{bmatrix} x \\ y \\ z \\ 1 \end{bmatrix} xyz1 = cosθsinθ00sinθcosθ0000100001 xyz1

4.4 小结

绕三个坐标轴的旋转矩阵形式具有一致性,其差异源于旋转轴对应的正交平面不同:

  • x x x 轴旋转:正交平面为 y o z yoz yoz,矩阵非对角元素集中在第 2-3 行与第 2-3 列;
  • y y y 轴旋转:正交平面为 z o x zox zox,矩阵非对角元素集中在第 1-3 行与第 1-3 列;
  • z z z 轴旋转:正交平面为 x o y xoy xoy,矩阵非对角元素集中在第 1-2 行与第 1-2 列。

若调整坐标向量的书写顺序(如将 ( x , y , z ) (x, y, z) (x,y,z) 改为 ( z , y , x ) (z, y, x) (z,y,x)),绕 y y y 轴的旋转矩阵可与其他两个轴的旋转矩阵在形式上完全统一,其要点均遵循二维旋转矩阵 [ cos ⁡ θ − sin ⁡ θ sin ⁡ θ cos ⁡ θ ] \begin{bmatrix} \cos\theta & -\sin\theta \\ \sin\theta & \cos\theta \end{bmatrix} [cosθsinθsinθcosθ] 的结构。

5. 绕任意轴的三维旋转

绕任意轴的三维旋转可通过坐标变换分解为一系列基本旋转操作,具体思路为:将旋转轴平移并旋转至与某一坐标轴(如 z z z 轴)重合,执行绕该坐标轴的旋转后,再通过逆变换将旋转轴恢复至原始位置。

5.1 问题描述

如图所示,点 P P P 绕向量 u = ( a , b , c ) \boldsymbol{u} = (a, b, c) u=(a,b,c) 旋转 θ \theta θ 角后得到点 Q Q Q,已知点 P P P 的坐标与向量 u \boldsymbol{u} u,求点 Q Q Q 的坐标。

RotateArbitrary

5.2 变换步骤

  1. x x x 轴旋转 α \alpha α:将向量 u \boldsymbol{u} u 旋转至 x o z xoz xoz 平面;
  2. y y y 轴旋转 − β -\beta β:将向量 u \boldsymbol{u} u 旋转至与 z z z 轴重合;
  3. z z z 轴旋转 θ \theta θ:执行目标旋转操作;
  4. y y y 轴旋转 β \beta β:步骤 2 的逆变换;
  5. x x x 轴旋转 − α -\alpha α:步骤 1 的逆变换。

5.3 各步骤矩阵推导

5.3.1 步骤 1:绕 x x x 轴旋转 α \alpha α

设向量 u = ( a , b , c ) \boldsymbol{u} = (a, b, c) u=(a,b,c),其在 y o z yoz yoz 平面的投影为 ( 0 , b , c ) (0, b, c) (0,b,c),投影与 z z z 轴的夹角为 α \alpha α。根据三角函数定义:
cos ⁡ α = c b 2 + c 2 , sin ⁡ α = b b 2 + c 2 \cos\alpha = \frac{c}{\sqrt{b^2 + c^2}}, \quad \sin\alpha = \frac{b}{\sqrt{b^2 + c^2}} cosα=b2+c2 c,sinα=b2+c2 b

对应的旋转矩阵为:
R x ( α ) = [ 1 0 0 0 0 c b 2 + c 2 − b b 2 + c 2 0 0 b b 2 + c 2 c b 2 + c 2 0 0 0 0 1 ] R_x(\alpha) = \begin{bmatrix} 1 & 0 & 0 & 0 \\ 0 & \frac{c}{\sqrt{b^2 + c^2}} & -\frac{b}{\sqrt{b^2 + c^2}} & 0 \\ 0 & \frac{b}{\sqrt{b^2 + c^2}} & \frac{c}{\sqrt{b^2 + c^2}} & 0 \\ 0 & 0 & 0 & 1 \end{bmatrix} Rx(α)= 10000b2+c2 cb2+c2 b00b2+c2 bb2+c2 c00001

5.3.2 步骤 2:绕 y y y 轴旋转 − β -\beta β

向量 u \boldsymbol{u} u 经步骤 1 旋转后在 x o z xoz xoz 平面的投影与 z z z 轴的夹角为 β \beta β。根据三角函数定义:
cos ⁡ β = b 2 + c 2 a 2 + b 2 + c 2 , sin ⁡ β = a a 2 + b 2 + c 2 \cos\beta = \frac{\sqrt{b^2 + c^2}}{\sqrt{a^2 + b^2 + c^2}}, \quad \sin\beta = \frac{a}{\sqrt{a^2 + b^2 + c^2}} cosβ=a2+b2+c2 b2+c2 ,sinβ=a2+b2+c2 a

代入绕 y y y 轴旋转矩阵并替换 θ \theta θ − β -\beta β,得到:
R y ( − β ) = [ b 2 + c 2 a 2 + b 2 + c 2 0 − a a 2 + b 2 + c 2 0 0 1 0 0 a a 2 + b 2 + c 2 0 b 2 + c 2 a 2 + b 2 + c 2 0 0 0 0 1 ] R_y(-\beta) = \begin{bmatrix} \frac{\sqrt{b^2 + c^2}}{\sqrt{a^2 + b^2 + c^2}} & 0 & -\frac{a}{\sqrt{a^2 + b^2 + c^2}} & 0 \\ 0 & 1 & 0 & 0 \\ \frac{a}{\sqrt{a^2 + b^2 + c^2}} & 0 & \frac{\sqrt{b^2 + c^2}}{\sqrt{a^2 + b^2 + c^2}} & 0 \\ 0 & 0 & 0 & 1 \end{bmatrix} Ry(β)= a2+b2+c2 b2+c2 0a2+b2+c2 a00100a2+b2+c2 a0a2+b2+c2 b2+c2 00001

5.3.3 步骤 3:绕 z z z 轴旋转 θ \theta θ

旋转矩阵为:
R z ( θ ) = [ cos ⁡ θ − sin ⁡ θ 0 0 sin ⁡ θ cos ⁡ θ 0 0 0 0 1 0 0 0 0 1 ] R_z(\theta) = \begin{bmatrix} \cos\theta & -\sin\theta & 0 & 0 \\ \sin\theta & \cos\theta & 0 & 0 \\ 0 & 0 & 1 & 0 \\ 0 & 0 & 0 & 1 \end{bmatrix} Rz(θ)= cosθsinθ00sinθcosθ0000100001

5.3.4 步骤 4 与 5:逆变换矩阵

步骤 4(绕 y y y 轴旋转 β \beta β 角)的矩阵为 R y ( β ) R_y(\beta) Ry(β),步骤 5(绕 x x x 轴旋转 − α -\alpha α 角)的矩阵为 R x ( − α ) R_x(-\alpha) Rx(α),二者分别为步骤 2 与步骤 1 矩阵的逆矩阵,仅需将角度替换为相反数即可:
R y ( β ) = [ b 2 + c 2 a 2 + b 2 + c 2 0 a a 2 + b 2 + c 2 0 0 1 0 0 − a a 2 + b 2 + c 2 0 b 2 + c 2 a 2 + b 2 + c 2 0 0 0 0 1 ] R_y(\beta) = \begin{bmatrix} \frac{\sqrt{b^2 + c^2}}{\sqrt{a^2 + b^2 + c^2}} & 0 & \frac{a}{\sqrt{a^2 + b^2 + c^2}} & 0 \\ 0 & 1 & 0 & 0 \\ -\frac{a}{\sqrt{a^2 + b^2 + c^2}} & 0 & \frac{\sqrt{b^2 + c^2}}{\sqrt{a^2 + b^2 + c^2}} & 0 \\ 0 & 0 & 0 & 1 \end{bmatrix} Ry(β)= a2+b2+c2 b2+c2 0a2+b2+c2 a00100a2+b2+c2 a0a2+b2+c2 b2+c2 00001
R x ( − α ) = [ 1 0 0 0 0 c b 2 + c 2 b b 2 + c 2 0 0 − b b 2 + c 2 c b 2 + c 2 0 0 0 0 1 ] R_x(-\alpha) = \begin{bmatrix} 1 & 0 & 0 & 0 \\ 0 & \frac{c}{\sqrt{b^2 + c^2}} & \frac{b}{\sqrt{b^2 + c^2}} & 0 \\ 0 & -\frac{b}{\sqrt{b^2 + c^2}} & \frac{c}{\sqrt{b^2 + c^2}} & 0 \\ 0 & 0 & 0 & 1 \end{bmatrix} Rx(α)= 10000b2+c2 cb2+c2 b00b2+c2 bb2+c2 c00001

5.4 复合旋转矩阵

绕任意轴 u \boldsymbol{u} u 旋转 θ \theta θ 角的复合矩阵为各步骤矩阵的左乘组合(按逆变换顺序排列):
M R = R x ( − α ) ⋅ R y ( β ) ⋅ R z ( θ ) ⋅ R y ( − β ) ⋅ R x ( α ) M_R = R_x(-\alpha) \cdot R_y(\beta) \cdot R_z(\theta) \cdot R_y(-\beta) \cdot R_x(\alpha) MR=Rx(α)Ry(β)Rz(θ)Ry(β)Rx(α)

若向量 u \boldsymbol{u} u 为单位向量(满足 a 2 + b 2 + c 2 = 1 a^2 + b^2 + c^2 = 1 a2+b2+c2=1),复合矩阵可简化为:

[ u 2 + ( 1 − u 2 ) cos ⁡ θ u v ( 1 − cos ⁡ θ ) − w sin ⁡ θ u w ( 1 − cos ⁡ θ ) + v sin ⁡ θ 0 u v ( 1 − cos ⁡ θ ) + w sin ⁡ θ v 2 + ( 1 − v 2 ) cos ⁡ θ v w ( 1 − cos ⁡ θ ) − u sin ⁡ θ 0 u w ( 1 − cos ⁡ θ ) − v sin ⁡ θ v w ( 1 − cos ⁡ θ ) + u sin ⁡ θ w 2 + ( 1 − w 2 ) cos ⁡ θ 0 0 0 0 1 ] \begin{bmatrix} u^2 + (1 - u^2)\cos\theta & uv(1 - \cos\theta) - w\sin\theta & uw(1 - \cos\theta) + v\sin\theta & 0 \\ uv(1 - \cos\theta) + w\sin\theta & v^2 + (1 - v^2)\cos\theta & vw(1 - \cos\theta) - u\sin\theta & 0 \\ uw(1 - \cos\theta) - v\sin\theta & vw(1 - \cos\theta) + u\sin\theta & w^2 + (1 - w^2)\cos\theta & 0 \\ 0 & 0 & 0 & 1 \end{bmatrix} u2+(1u2)cosθuv(1cosθ)+wsinθuw(1cosθ)vsinθ0uv(1cosθ)wsinθv2+(1v2)cosθvw(1cosθ)+usinθ0uw(1cosθ)+vsinθvw(1cosθ)usinθw2+(1w2)cosθ00001


旋转变换(二)欧拉角

csxiaoshui 于 2017-03-28 17:00:56 发布

欧拉角(Euler Angles)是描述三维旋转的常用数学工具,其他表达方式还包括旋转矩阵、四元数、旋转轴-旋转角等。欧拉角的理论基础是欧拉旋转定理,该定理指出:任意三维旋转均可通过三个独立的旋转参数唯一表示。

1. 欧拉角的定义约定

欧拉角的描述缺乏统一标准,不同应用场景中可能采用不同的定义方式。为确保旋转描述的一致性,需明确以下四项约定:

1.1 旋转轴的组合顺序

三维空间中,围绕 x x x y y y z z z 轴的旋转可形成多种组合顺序,主要分为两类:

  1. Proper Euler 角(经典欧拉角):旋转轴包含两次同一坐标轴的旋转,剩余一次为其他坐标轴,共 6 种组合: z z z- x x x- z z z x x x- y y y- x x x y y y- z z z- y y y z z z- y y y- z z z x x x- z z z- x x x y y y- x x x- y y y
  2. Tait-Bryan 角:旋转轴为三个不同坐标轴的组合,共 6 种组合: x x x- y y y- z z z y y y- z z z- x x x z z z- x x x- y y y x x x- z z z- y y y z z z- y y y- x x x y y y- x x x- z z z。这类角度也被称为 Cardan 角、航海角(heading-elevation-bank)或姿态角(yaw-pitch-roll)。

两类欧拉角的本质区别在于:Proper Euler 角通过“同一轴两次旋转+另一轴一次旋转”实现三维覆盖,而 Tait-Bryan 角通过“三轴各一次旋转”实现,后者在机器人学、航空航天等领域应用更广泛。

1.2 旋转的参考坐标系

根据旋转轴的参考基准,欧拉角可分为:

  1. 外旋(Extrinsic Rotations):所有旋转均围绕固定坐标系(如世界坐标系)的坐标轴进行,旋转轴方向始终不变;
  2. 内旋(Intrinsic Rotations):每次旋转围绕物体自身坐标系的坐标轴进行,旋转轴方向随物体姿态变化而改变。

结合 1.1 中的 12 种旋转顺序,外旋与内旋可形成 12 × 2 = 24 12 \times 2 = 24 12×2=24 种欧拉角定义组合。外旋在计算机图形学、游戏编程中更易理解(统一世界坐标系),内旋则在物理学、刚体动力学中应用较多。

1.3 旋转角度的正负规则

旋转角度的正负由坐标系的左右手定则确定(与坐标系本身的左右手属性无关):

  • 右手定则:大拇指指向旋转轴正方向,四指弯曲方向为角度正方向;
  • 等价描述:从旋转轴正方向的反方向观察,逆时针旋转为正角度,顺时针旋转为负角度。

PositiveAngle

1.4 旋转角的常用记法

不同领域对欧拉角的三个旋转参数有不同命名,下表列出常见记法对应关系:

顺序飞行器领域望远镜领域符号表示角速度对应
第一旋转Heading(航向角)Azimuth(方位角) θ \theta θYaw(偏航)
第二旋转Attitude(姿态角)Elevation(仰角) ϕ \phi ϕPitch(俯仰)
第三旋转Bank(横滚角)Tilt(倾斜角) ψ \psi ψRoll(横滚)

欧拉角的物理意义示意图如下:

EulerAngle

2. 欧拉角的连续旋转特性

若物体先经过欧拉角 ( α 1 , α 2 , α 3 ) (\alpha_1, \alpha_2, \alpha_3) (α1,α2,α3) 描述的旋转,再经过 ( β 1 , β 2 , β 3 ) (\beta_1, \beta_2, \beta_3) (β1,β2,β3) 描述的旋转,其总效果不能通过直接叠加角度 ( α 1 + β 1 , α 2 + β 2 , α 3 + β 3 ) (\alpha_1+\beta_1, \alpha_2+\beta_2, \alpha_3+\beta_3) (α1+β1,α2+β2,α3+β3) 实现。这是因为欧拉角的旋转顺序存在耦合关系,角度叠加不满足线性性。

工程实践中,连续旋转的计算通常采用以下流程:

  1. 将两次旋转对应的欧拉角分别转换为旋转矩阵或四元数;
  2. 通过矩阵乘法或四元数乘法实现旋转效果的叠加;
  3. (可选)将叠加后的旋转结果转换回欧拉角。

需注意:多次转换可能引入累积误差,因此连续旋转场景中更推荐直接使用旋转矩阵或四元数进行计算,避免频繁的欧拉角转换。

3. 万向节死锁(Gimbal Lock)

3.1 现象描述

万向节死锁是欧拉角的固有缺陷,指旋转过程中因两个旋转轴重合导致丢失一个旋转自由度的现象。这种情况通常发生在第二次旋转角度为 ± 9 0 ∘ \pm 90^\circ ±90(即 π / 2 \pi/2 π/2 弧度)时,此时第三次旋转与第一次旋转的效果完全等价,无法实现三维空间的任意姿态调整。

3.2 典型示例

3.2.1 二维万向节死锁

用固定望远镜跟踪飞机时,望远镜可通过水平(方位角)和垂直(仰角)旋转调整指向。当飞机飞至望远镜正上方并转向 9 0 ∘ 90^\circ 90 飞行时,仅通过水平旋转无法继续跟踪,必须中断连续调整并重新设定垂直角度,此时望远镜的旋转自由度被限制为一维,即出现万向节死锁。

3.2.2 三维万向节死锁

以航空航天领域的姿态角(yaw-pitch-roll)为例:

  • 初始状态:飞机水平飞行,pitch(俯仰角)= 0 ∘ 0^\circ 0
  • 当 pitch 旋转至 9 0 ∘ 90^\circ 90 时,飞机机头垂直向上,此时 yaw(偏航角)对应的旋转轴与 roll(横滚角)对应的旋转轴重合;
  • 后续调整 yaw 或 roll 时,两者效果完全一致,飞机无法实现绕原偏航轴的旋转,即丢失一个自由度。

万向节死锁

3.3 数学本质

以 Tait-Bryan 角 z z z- y y y- x x x 顺序(外旋)为例,其对应的旋转矩阵为:
R = R z ( α ) ⋅ R y ( β ) ⋅ R x ( γ ) R = R_z(\alpha) \cdot R_y(\beta) \cdot R_x(\gamma) R=Rz(α)Ry(β)Rx(γ)

β = π / 2 \beta = \pi/2 β=π/2 时, R y ( π / 2 ) = [ 0 0 1 0 1 0 − 1 0 0 ] R_y(\pi/2) = \begin{bmatrix} 0 & 0 & 1 \\ 0 & 1 & 0 \\ -1 & 0 & 0 \end{bmatrix} Ry(π/2)= 001010100 ,代入旋转矩阵展开后得到:
R = [ 0 sin ⁡ ( α − γ ) cos ⁡ ( α − γ ) 0 cos ⁡ ( α − γ ) − sin ⁡ ( α − γ ) − 1 0 0 ] R = \begin{bmatrix} 0 & \sin(\alpha-\gamma) & \cos(\alpha-\gamma) \\ 0 & \cos(\alpha-\gamma) & -\sin(\alpha-\gamma) \\ -1 & 0 & 0 \end{bmatrix} R= 001sin(αγ)cos(αγ)0cos(αγ)sin(αγ)0

可见,矩阵中仅包含 α − γ \alpha-\gamma αγ 这一组合项,调整 α \alpha α γ \gamma γ 对最终旋转效果的影响完全等价,即两个角度的自由度相互抵消,导致旋转矩阵无法表示三维空间的任意姿态,从而出现万向节死锁。

4. 欧拉角的应用注意事项与替代方案

4.1 应用建议

若必须使用欧拉角,需注意以下两点:

  1. 严格统一欧拉角的定义约定(旋转顺序、参考坐标系、角度正负规则),避免不同模块间的描述冲突;
  2. 根据应用场景选择合适的旋转顺序,尽量避开易触发万向节死锁的角度范围(如避免第二次旋转角度接近 ± 9 0 ∘ \pm 90^\circ ±90)。

4.2 替代方案

为解决欧拉角的耦合性与万向节死锁问题,推荐采用以下更优的旋转描述方式:

  1. 旋转矩阵:无万向节死锁问题,支持直接的旋转叠加计算,适用于大多数工程场景;
  2. 四元数:仅需 4 个参数即可描述三维旋转,计算效率高于旋转矩阵,且无万向节死锁,是机器人学、计算机视觉中的首选方案;
  3. 旋转轴-旋转角:通过一个单位向量(旋转轴)和一个角度(旋转量)描述旋转,直观且无自由度丢失,适用于需要明确旋转方向的场景。

欧拉角 (Euler Angle)

thefist11 原创于 2022-08-30 06:11:09

本文介绍了在三维空间中使用欧拉角表示物体朝向的方法,包括俯仰角、偏航角和滚转角的数学计算过程及应用实例。

1. 定义

在三维空间中,通过指定与三个旋转轴相关联的三个角度,可以最小参数化地表示任意方向。具体而言,依次围绕三个轴旋转三次后得到的三个值,可用于表示物体的朝向。需注意以下两点:

  • 轴的旋转顺序并无严格要求。
  • 每次旋转后,围绕的轴会发生变化。
     
    旋转示意图

如图所示,分别绕原坐标系的 z z z 轴(蓝色)、一次旋转后的 x x x 轴(绿色)以及两次旋转后的 z z z 轴(红色)旋转,最终生成的红色坐标系即表示目标方向。
 旋转结果

1.1 数学计算

新生成的坐标系 ( X , Y , Z ) (X,Y,Z) (X,Y,Z) 可通过原坐标系 ( x , y , z ) (x,y,z) (x,y,z) 乘以旋转矩阵得到。
( X Y Z ) = M ( x y z ) \begin{pmatrix} X \\ Y \\ Z \end{pmatrix} = M \begin{pmatrix} x \\ y \\ z \end{pmatrix} XYZ =M xyz

M = Rot ( z , γ ) ⋅ Rot ( x , β ) ⋅ Rot ( z , α ) = ( cos ⁡ γ − sin ⁡ γ 0 sin ⁡ γ cos ⁡ γ 0 0 0 1 ) ( 1 0 0 0 cos ⁡ β − sin ⁡ β 0 sin ⁡ β cos ⁡ β ) ( cos ⁡ α − sin ⁡ α 0 sin ⁡ α cos ⁡ α 0 0 0 1 ) = ( cos ⁡ α cos ⁡ γ − sin ⁡ α cos ⁡ β sin ⁡ γ − sin ⁡ α cos ⁡ γ − cos ⁡ α cos ⁡ β sin ⁡ γ sin ⁡ β sin ⁡ γ cos ⁡ α sin ⁡ γ + sin ⁡ α cos ⁡ β cos ⁡ γ − sin ⁡ α sin ⁡ γ + cos ⁡ α cos ⁡ β cos ⁡ γ − sin ⁡ β cos ⁡ γ sin ⁡ α sin ⁡ β cos ⁡ α sin ⁡ β cos ⁡ β ) \begin{aligned} M &= \text{Rot}(z, \gamma) \cdot \text{Rot}(x, \beta) \cdot \text{Rot}(z, \alpha) \\ &= \begin{pmatrix} \cos \gamma & -\sin \gamma & 0 \\ \sin \gamma & \cos \gamma & 0 \\ 0 & 0 & 1 \end{pmatrix} \begin{pmatrix} 1 & 0 & 0 \\ 0 & \cos \beta & -\sin \beta \\ 0 & \sin \beta & \cos \beta \end{pmatrix} \begin{pmatrix} \cos \alpha & -\sin \alpha & 0 \\ \sin \alpha & \cos \alpha & 0 \\ 0 & 0 & 1 \end{pmatrix} \\ &= \begin{pmatrix} \cos \alpha \cos \gamma - \sin \alpha \cos \beta \sin \gamma & -\sin \alpha \cos \gamma - \cos \alpha \cos \beta \sin \gamma & \sin \beta \sin \gamma \\ \cos \alpha \sin \gamma + \sin \alpha \cos \beta \cos \gamma & -\sin \alpha \sin \gamma + \cos \alpha \cos \beta \cos \gamma & -\sin \beta \cos \gamma \\ \sin \alpha \sin \beta & \cos \alpha \sin \beta & \cos \beta \end{pmatrix} \end{aligned} M=Rot(z,γ)Rot(x,β)Rot(z,α)= cosγsinγ0sinγcosγ0001 1000cosβsinβ0sinβcosβ cosαsinα0sinαcosα0001 = cosαcosγsinαcosβsinγcosαsinγ+sinαcosβcosγsinαsinβsinαcosγcosαcosβsinγsinαsinγ+cosαcosβcosγcosαsinβsinβsinγsinβcosγcosβ

2. 三种欧拉角

欧拉角包括俯仰角(Pitch)、偏航角(Yaw)和滚转角(Roll)。
 
欧拉角示意图

2.1 俯仰角(Pitch)

设俯仰角为 θ \theta θ,y 值等于 sin ⁡ θ \sin \theta sinθ

direction.y = sin(glm::radians(pitch)); // 注意先把角度转为弧度

direction.x = cos(glm::radians(pitch));
direction.z = cos(glm::radians(pitch));

2.2 偏航角(Yaw)

设偏航角为 ϕ \phi ϕ,则方向向量的计算公式为:
在这里插入图片描述

// 计算方向向量的 x 分量
direction.x = cos(glm::radians(pitch)) * cos(glm::radians(yaw));
//译注:direction 表示摄像机的前轴(Front),其方向与本文第一幅图中的第二个摄像机方向向量相反。

// 计算方向向量的 y 分量
direction.y = sin(glm::radians(pitch));

// 计算方向向量的 z 分量
direction.z = cos(glm::radians(pitch)) * sin(glm::radians(yaw));

旋转变换(三)四元数

csxiaoshui 原创于 2017-03-29 11:59:38 发布

1. 简介

四元数(Quaternion)是描述三维旋转的高效数学工具,通过 4 个分量(1 个实部 + 3 个虚部)实现旋转表达,其形式为:
q = s + x i + y j + z k ( s , x , y , z ∈ R ) q = s + xi + yj + zk \quad (s,x,y,z \in \mathbb{R}) q=s+xi+yj+zk(s,x,y,zR)
其中虚部满足运算规则:
i 2 = j 2 = k 2 = i j k = − 1 i^2 = j^2 = k^2 = ijk = -1 i2=j2=k2=ijk=1

四元数的设计灵感源于复数对二维旋转的描述能力,因此先从复数的旋转特性入手理解四元数的本质。

1.1 复数与二维旋转

复数的基本定义为:
z = a + b i ( a , b ∈ R , i 2 = − 1 ) z = a + bi \quad (a,b \in \mathbb{R}, i^2 = -1) z=a+bi(a,bR,i2=1)

1.1.1 复数的运算
  • 共轭复数:将虚部取反,记为 z ∗ = a − b i z^* = a - bi z=abi
  • 复数的模 ∣ z ∣ = a 2 + b 2 |z| = \sqrt{a^2 + b^2} z=a2+b2
  • 复数乘法 ( a + b i ) ( c + d i ) = ( a c − b d ) + ( a d + b c ) i (a+bi)(c+di) = (ac-bd) + (ad+bc)i (a+bi)(c+di)=(acbd)+(ad+bc)i
1.1.2 复数的旋转意义

复数可映射到复平面(实轴对应 x x x 轴,虚轴对应 y y y 轴),若定义旋转因子:
q = cos ⁡ θ + i sin ⁡ θ q = \cos\theta + i\sin\theta q=cosθ+isinθ
则任意复数 z = a + b i z = a+bi z=a+bi q q q 相乘的结果为:
z ′ = q ⋅ z = ( a cos ⁡ θ − b sin ⁡ θ ) + ( a sin ⁡ θ + b cos ⁡ θ ) i z' = q \cdot z = (a\cos\theta - b\sin\theta) + (a\sin\theta + b\cos\theta)i z=qz=(acosθbsinθ)+(asinθ+bcosθ)i

写成矩阵形式为:
[ a ′ b ′ ] = [ cos ⁡ θ − sin ⁡ θ sin ⁡ θ cos ⁡ θ ] ⋅ [ a b ] \begin{bmatrix} a' \\ b' \end{bmatrix} = \begin{bmatrix} \cos\theta & -\sin\theta \\ \sin\theta & \cos\theta \end{bmatrix} \cdot \begin{bmatrix} a \\ b \end{bmatrix} [ab]=[cosθsinθsinθcosθ][ab]
这与二维旋转矩阵完全一致,说明复数乘法可等价描述复平面内绕原点的旋转

2. 四元数的定义与运算

复数可描述二维旋转,但直接拓展为“三维复数”( z = a + b i + c j z = a+bi+cj z=a+bi+cj)会导致乘法不闭合(运算结果超出三维空间)。威廉·哈密顿(William Hamilton)于 1843 年提出四元数,通过引入第四个分量解决了这一问题,并将公式刻于爱尔兰布鲁姆桥(Broom Bridge)。

2.1 四元数的表示形式

四元数有两种常用表示法:

  1. 拆分形式: q = [ s , v ] q = [s, \boldsymbol{v}] q=[s,v] s ∈ R s \in \mathbb{R} sR 为实部, v = ( x , y , z ) ∈ R 3 \boldsymbol{v} = (x,y,z) \in \mathbb{R}^3 v=(x,y,z)R3 为虚部向量);
  2. 完整形式: q = s + x i + y j + z k q = s + xi + yj + zk q=s+xi+yj+zk i , j , k i,j,k i,j,k 为虚部单位,满足 i 2 = j 2 = k 2 = − 1 i^2=j^2=k^2=-1 i2=j2=k2=1,且 i j = k , j i = − k , j k = i , k j = − i , k i = j , i k = − j ij=k, ji=-k, jk=i, kj=-i, ki=j, ik=-j ij=k,ji=k,jk=i,kj=i,ki=j,ik=j)。

2.2 四元数的基本运算

以下实现基于四元数类 Quat,其成员 _v[4] 存储 ( x , y , z , w ) (x,y,z,w) (x,y,z,w) w w w 对应实部 s s s)。

2.2.1 加减法
  • 加法:对应分量相加, q 1 + q 2 = ( s 1 + s 2 , x 1 + x 2 , y 1 + y 2 , z 1 + z 2 ) q_1 + q_2 = (s_1+s_2, x_1+x_2, y_1+y_2, z_1+z_2) q1+q2=(s1+s2,x1+x2,y1+y2,z1+z2)

    inline Quat operator + (const Quat& rhs) const
    {
        return Quat(
            _v[0] + rhs._v[0],
            _v[1] + rhs._v[1],
            _v[2] + rhs._v[2],
            _v[3] + rhs._v[3]
        );
    }
    
  • 减法:对应分量相减, q 1 − q 2 = ( s 1 − s 2 , x 1 − x 2 , y 1 − y 2 , z 1 − z 2 ) q_1 - q_2 = (s_1-s_2, x_1-x_2, y_1-y_2, z_1-z_2) q1q2=(s1s2,x1x2,y1y2,z1z2)

    inline Quat operator - (const Quat& rhs) const
    {
        return Quat(
            _v[0] - rhs._v[0],
            _v[1] - rhs._v[1],
            _v[2] - rhs._v[2],
            _v[3] - rhs._v[3]
        );
    }
    
2.2.2 乘法

四元数乘法遵循多项式展开规则,结合虚部运算规则,最终展开式为:
( s 1 + x 1 i + y 1 j + z 1 k ) ( s 2 + x 2 i + y 2 j + z 2 k ) = ( s 1 s 2 − x 1 x 2 − y 1 y 2 − z 1 z 2 ) + ( s 1 x 2 + x 1 s 2 + y 1 z 2 − z 1 y 2 ) i + ( s 1 y 2 − x 1 z 2 + y 1 s 2 + z 1 x 2 ) j + ( s 1 z 2 + x 1 y 2 − y 1 x 2 + z 1 s 2 ) k \begin{align*} &(s_1+x_1i+y_1j+z_1k)(s_2+x_2i+y_2j+z_2k) \\ =& (s_1s_2 - x_1x_2 - y_1y_2 - z_1z_2) \\ &+ (s_1x_2 + x_1s_2 + y_1z_2 - z_1y_2)i \\ &+ (s_1y_2 - x_1z_2 + y_1s_2 + z_1x_2)j \\ &+ (s_1z_2 + x_1y_2 - y_1x_2 + z_1s_2)k \end{align*} =(s1+x1i+y1j+z1k)(s2+x2i+y2j+z2k)(s1s2x1x2y1y2z1z2)+(s1x2+x1s2+y1z2z1y2)i+(s1y2x1z2+y1s2+z1x2)j+(s1z2+x1y2y1x2+z1s2)k

实现代码:

inline const Quat operator*(const Quat& rhs) const
{
    return Quat( 
        rhs._v[3]*_v[0] + rhs._v[0]*_v[3] + rhs._v[1]*_v[2] - rhs._v[2]*_v[1],
        rhs._v[3]*_v[1] - rhs._v[0]*_v[2] + rhs._v[1]*_v[3] + rhs._v[2]*_v[0],
        rhs._v[3]*_v[2] + rhs._v[0]*_v[1] - rhs._v[1]*_v[0] + rhs._v[2]*_v[3],
        rhs._v[3]*_v[3] - rhs._v[0]*_v[0] - rhs._v[1]*_v[1] - rhs._v[2]*_v[2] 
    );
}
2.2.3 除法(逆运算)

四元数乘法不满足交换律,因此除法定义为 q 1 / q 2 = q 1 ⋅ q 2 − 1 q_1 / q_2 = q_1 \cdot q_2^{-1} q1/q2=q1q21,其中逆四元数 q − 1 = q ∗ ∣ q ∣ 2 q^{-1} = \frac{q^*}{|q|^2} q1=q2q q ∗ q^* q 为共轭四元数, ∣ q ∣ 2 |q|^2 q2 为模的平方)。

实现:

// 共轭四元数(虚部取反)
inline Quat conj () const
{
    return Quat( -_v[0], -_v[1], -_v[2], _v[3] );
}

// 模的平方
value_type length2() const
{
    return _v[0]*_v[0] + _v[1]*_v[1] + _v[2]*_v[2] + _v[3]*_v[3];
}

// 逆四元数
inline const Quat inverse () const
{
    return conj() / length2();
}

// 除法
inline const Quat operator/(const Quat& denom) const
{
    return ( (*this) * denom.inverse() );
}

3. 四元数与三维旋转

四元数是三维旋转的最优描述方式之一,无万向节死锁问题,且插值平滑、计算高效。其思想是:任意三维旋转可表示为绕单位向量 u \boldsymbol{u} u 旋转 θ \theta θ 角,对应的四元数由旋转轴和旋转角构造

3.1 绕任意轴旋转的四元数构造

设旋转轴为单位向量 u = ( u x , u y , u z ) \boldsymbol{u} = (u_x, u_y, u_z) u=(ux,uy,uz),旋转角为 θ \theta θ,则对应的四元数为:
q = cos ⁡ θ 2 + ( u x i + u y j + u z k ) sin ⁡ θ 2 q = \cos\frac{\theta}{2} + (u_xi + u_yj + u_zk)\sin\frac{\theta}{2} q=cos2θ+(uxi+uyj+uzk)sin2θ

实现代码:

void makeRotate(value_type angle, value_type x, value_type y, value_type z)
{
    const value_type epsilon = 1e-7;
    value_type length = sqrt(x*x + y*y + z*z);
    if (length < epsilon)
    {
        *this = Quat(); // 零旋转
        return;
    }

    value_type inversenorm = 1.0 / length; // 单位化旋转轴
    value_type coshalfangle = cos(0.5*angle);
    value_type sinhalfangle = sin(0.5*angle);

    _v[0] = x * sinhalfangle * inversenorm;
    _v[1] = y * sinhalfangle * inversenorm;
    _v[2] = z * sinhalfangle * inversenorm;
    _v[3] = coshalfangle;
}

3.2 从向量旋转构造四元数

已知初始向量 u \boldsymbol{u} u 和目标向量 v \boldsymbol{v} v,构造旋转四元数的逻辑:

  1. 旋转轴为 u × v \boldsymbol{u} \times \boldsymbol{v} u×v
  2. 旋转角为 u \boldsymbol{u} u v \boldsymbol{v} v 的夹角;
  3. 特殊处理向量共线(夹角 0 ∘ 0^\circ 0 18 0 ∘ 180^\circ 180)的情况。
3.2.1 基础实现(含特殊处理)
void makeRotate(const Vec3<value_type>& from, const Vec3<value_type>& to)
{
    const value_type epsilon = 1e-7;
    value_type length1 = from.length();
    value_type length2 = to.length();
    value_type cosangle = from*to / (length1*length2); // 点乘求夹角余弦

    // 向量同向(夹角0°)
    if (fabs(cosangle - 1) < epsilon)
    {
        makeRotate(0.0, 0.0, 0.0, 1.0); // 零旋转
    }
    // 向量反向(夹角180°)
    else if (fabs(cosangle + 1.0) < epsilon)
    {
        // 构造垂直于from的任意轴
        Vec3<value_type> tmp;
        if ((fabs(from.x())) < fabs(from.y()))
        {
            tmp = (fabs(from.x()) < fabs(from.z())) ? Vec3<value_type>(1,0,0) : Vec3<value_type>(0,0,1);
        }
        else
        {
            tmp = (fabs(from.y()) < fabs(from.z())) ? Vec3<value_type>(0,1,0) : Vec3<value_type>(0,0,1);
        }
        Vec3<value_type> axis = from ^ tmp; // 叉乘得旋转轴
        axis.normalize();
        _v[0] = axis[0]; _v[1] = axis[1]; _v[2] = axis[2]; _v[3] = 0.0; // 旋转角180°
    }
    // 一般情况
    else
    {
        Vec3<value_type> axis = from ^ to;
        value_type angle = acos(cosangle);
        makeRotate(angle, axis.x(), axis.y(), axis.z());
    }
}
3.2.2 优化实现(避免反三角函数)

利用三角恒等式 sin ⁡ θ 2 = 1 − cos ⁡ θ 2 \sin\frac{\theta}{2} = \sqrt{\frac{1-\cos\theta}{2}} sin2θ=21cosθ cos ⁡ θ 2 = 1 + cos ⁡ θ 2 \cos\frac{\theta}{2} = \sqrt{\frac{1+\cos\theta}{2}} cos2θ=21+cosθ ,简化计算:

// 替换一般情况的代码块
else
{
    value_type normFromAndTo = sqrt(from.length2()*to.length2());
    value_type cos_theta = from * to / normFromAndTo;
    value_type half_cos = sqrt(0.5 * (1+cos_theta));
    Vec3<value_type> axis = (from ^ to) / (normFromAndTo * 2 * half_cos);

    _v[0] = axis[0];
    _v[1] = axis[1];
    _v[2] = axis[2];
    _v[3] = half_cos;
}

3.3 从四元数提取旋转轴与旋转角

已知四元数 q = w + x i + y j + z k q = w + xi + yj + zk q=w+xi+yj+zk,反推旋转参数的公式:
θ = 2 arccos ⁡ ( w ) u = ( x , y , z ) 1 − w 2 ( w ≠ ± 1 ) \begin{align*} \theta &= 2\arccos(w) \\ \boldsymbol{u} &= \frac{(x,y,z)}{\sqrt{1-w^2}} \quad (w \neq \pm1) \end{align*} θu=2arccos(w)=1w2 (x,y,z)(w=±1)

特殊处理:

  • w = 1 w=1 w=1(旋转角 0 ∘ 0^\circ 0):分母为 0,直接取 ( x , y , z ) (x,y,z) (x,y,z) 为旋转轴;
  • w = − 1 w=-1 w=1(旋转角 18 0 ∘ 180^\circ 180): u = ( x , y , z ) \boldsymbol{u} = (x,y,z) u=(x,y,z)

实现代码:

void getRotate(value_type& angle, value_type& x, value_type& y, value_type& z) const 
{
    Quat q1 = *this;
    if (_v[3] > 1) q1.normalize(); // 确保单位四元数

    angle = 2 * acos(q1._v[3]);
    value_type s = sqrt(1 - q1._v[3]*q1._v[3]);

    if (s < 1e-6) // 旋转角0°
    {
        x = q1._v[0]; y = q1._v[1]; z = q1._v[2];
    }
    else // 一般情况
    {
        x = q1._v[0] / s; y = q1._v[1] / s; z = q1._v[2] / s;
    }
}

3.4 四元数旋转向量

将向量 v \boldsymbol{v} v 转换为纯虚四元数 p = 0 + v x i + v y j + v z k p = 0 + v_xi + v_yj + v_zk p=0+vxi+vyj+vzk,旋转后的向量为:
v ′ = q ⋅ p ⋅ q ∗ \boldsymbol{v}' = q \cdot p \cdot q^* v=qpq

优化实现(避免直接四元数乘法,降低计算量):

Vec3<value_type> operator* (const Vec3<value_type>& v)
{
    // nVidia SDK 优化实现
    Vec3<value_type> uv, uuv;
    Vec3<value_type> qvec(_v[0], _v[1], _v[2]);
    uv = qvec ^ v;          // 叉乘
    uuv = qvec ^ uv;        // 二次叉乘
    uv *= (2.0f * _v[3]);   // 实部缩放
    uuv *= 2.0f;            // 缩放
    return v + uv + uuv;    // 最终旋转向量
}

3.5 四元数的球面插值(SLERP)

四元数的球面插值(Spherical Linear Interpolation)可实现平滑的旋转过渡,公式为:
q t = sin ⁡ ( ( 1 − t ) θ ) sin ⁡ θ q a + sin ⁡ ( t θ ) sin ⁡ θ q b q_t = \frac{\sin((1-t)\theta)}{\sin\theta}q_a + \frac{\sin(t\theta)}{\sin\theta}q_b qt=sinθsin((1t)θ)qa+sinθsin()qb
其中 θ \theta θ q a q_a qa q b q_b qb 的夹角( cos ⁡ θ = q a ⋅ q b \cos\theta = q_a \cdot q_b cosθ=qaqb), t ∈ [ 0 , 1 ] t \in [0,1] t[0,1] 为插值因子。

实现代码:

void slerp(value_type t, const Quat& from, const Quat& to )
{
    const double epsilon = 0.00001;
    double omega, cosomega, sinomega, scale_from, scale_to ;

    Quat quatTo(to);
    cosomega = from.asVec4() * to.asVec4(); // 点乘求夹角余弦

    // 确保插值方向最短
    if (cosomega < 0.0)
    {
        cosomega = -cosomega;
        quatTo = -to;
    }

    if( (1.0 - cosomega) > epsilon )
    {
        // 非近距离插值:球面插值
        omega = acos(cosomega);
        sinomega = sin(omega);
        scale_from = sin((1.0-t)*omega)/sinomega;
        scale_to = sin(t*omega)/sinomega;
    }
    else
    {
        // 近距离插值:线性插值
        scale_from = 1.0 - t;
        scale_to = t;
    }

    *this = (from*scale_from) + (quatTo*scale_to);
}

4. 四元数的优势

  1. 无万向节死锁:相比欧拉角,四元数通过单一参数描述旋转,避免了旋转轴重合导致的自由度丢失;
  2. 插值平滑:球面插值(SLERP)可实现旋转的连续、平滑过渡,无欧拉角插值的“生硬感”;
  3. 计算高效:四元数仅需 4 个分量,乘法运算量远低于 4×4 旋转矩阵;
  4. 易于归一化:单位四元数(模为 1)可保证旋转的正交性,归一化操作简单。

参考文献

  1. Wiki Rotation (mathematics)
    https://en.wikipedia.org/wiki/Rotation_(mathematics)
  2. Euler’s rotation theorem
    https://en.wikipedia.org/wiki/Euler’s_rotation_theorem
  3. Maths - Rotation Matrice
    https://www.euclideanspace.com/maths/algebra/matrix/orthogonal/rotation/index.htm
  4. 绕任意轴旋转
    https://www.cnblogs.com/graphics/archive/2012/08/10/2627458.html
  5. Rotation About an Arbitrary Axis in 3 Dimension
    https://inside.mines.edu/fs_home/gmurray/ArbitraryAxisRotation/
  6. Rotation about an Arbitrary Axis (Line)
    https://www.engr.uvic.ca/~mech410/lectures/4_2_RotateArbi.pdf
  7. Maths - Euler Angle
    http://www.euclideanspace.com/maths/geometry/rotations/euler/
  8. Wiki-Euler angle
    https://en.wikipedia.org/wiki/Euler_angles
  9. Euler Angle
    http://mathworld.wolfram.com/EulerAngles.html
  10. Wiki-Gimbal lock
    https://en.wikipedia.org/wiki/Gimbal_lock
  11. 欧拉角、内部旋转、外部旋转
    http://blog.youkuaiyun.com/hy3316597/article/details/50966633
  12. Understanding Quaternion
    https://www.3dgep.com/understanding-quaternions/
  13. 如何形象地理解四元数?
    https://www.zhihu.com/question/23005815/answer/33971127
  14. Quaternion
    http://mathworld.wolfram.com/Quaternion.html
  15. Quaternion
    https://en.wikipedia.org/wiki/Quaternion
  16. Maths - Quaternion
    https://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/index.htm
  17. Beautiful maths simplification: quaternion from two vector
    https://lolengine.net/blog/2013/09/18/beautiful-maths-quaternion-from-vectors
  18. Rotating vector3 by a quaternion
    https://gamedev.stackexchange.com/questions/28395/rotating-vector3-by-a-quaternion
  19. quaternion vector product - the principle of the element-transformed vector algorithm
    https://www.gamedev.net/topic/466725-quaternion-vector-product/

via:

内容概要:本文详细介绍了“秒杀商城”微服务架构的设计实战全过程,涵盖系统从需求分析、服务拆分、技术选型到核心功能开发、分布式事务处理、容器化部署及监控链路追踪的完整流程。重点解决了高并发场景下的超卖问题,采用Redis预减库存、消息队列削峰、数据库乐观锁等手段保障数据一致性,并通过Nacos实现服务注册发现配置管理,利用Seata处理跨服务分布式事务,结合RabbitMQ实现异步下单,提升系统吞吐能力。同时,项目支持Docker Compose快速部署和Kubernetes生产级编排,集成Sleuth+Zipkin链路追踪Prometheus+Grafana监控体系,构建可观测性强的微服务系统。; 适合人群:具备Java基础和Spring Boot开发经验,熟悉微服务基本概念的中高级研发人员,尤其是希望深入理解高并发系统设计、分布式事务、服务治理等核心技术的开发者;适合工作2-5年、有志于转型微服务或提升架构能力的工程师; 使用场景及目标:①学习如何基于Spring Cloud Alibaba构建完整的微服务项目;②掌握秒杀场景下高并发、超卖控制、异步化、削峰填谷等关键技术方案;③实践分布式事务(Seata)、服务熔断降级、链路追踪、统一配置中心等企业级中间件的应用;④完成从本地开发到容器化部署的全流程落地; 阅读建议:建议按照文档提供的七个阶段循序渐进地动手实践,重点关注秒杀流程设计、服务间通信机制、分布式事务实现和系统性能优化部分,结合代码调试监控工具深入理解各组件协作原理,真正掌握高并发微服务系统的构建能力。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值