注:本文为 "旋转变换" 相关合辑。
图片清晰度受引文原图所限。
略作重排,如有内容异常,请看原文。
旋转变换(一)旋转矩阵
csxiaoshui 原创于 2017-03-27 17:15:37 发布
1. 简介
在计算机图形学中,仿射变换是一类应用广泛的特殊变换,其基本形式包括平移、旋转、缩放与剪切。本文及后续系列文章将重点探讨旋转变换,涵盖二维旋转变换、三维旋转变换及其多种数学表达形式(旋转矩阵、四元数、欧拉角等)。
2. 绕原点的二维旋转
二维旋转的关键是围绕某一固定点进行角度偏转,三维旋转则围绕某一固定轴进行。其中,绕坐标原点的二维旋转是最基础的场景,如图所示:
如图所示,点 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} \[x′y′\]=\[cosθsinθ−sinθcosθ\]⋅\[xy
上述推导基于三角函数的基本定义,适用于任意旋转角度(如大于 18 0 ∘ 180^\circ 180∘ 或负角度),并非局限于图示中的锐角场景。
3. 绕任意点的二维旋转
绕原点的旋转是二维旋转的基础形式,绕任意点的旋转可通过坐标变换转化为绕原点的旋转,具体步骤如下:
- 将旋转中心平移至坐标原点;
- 执行绕原点的旋转操作(参考第 2 节);
- 将旋转中心平移回原始位置。
设平移矩阵为 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} x′y′1 = 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)= 100010−tx−ty1 #### 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} x′y′1 = cosθsinθ0−sinθ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θ0−sinθcosθ0001 ⋅ 100010−tx−ty1 = cosθsinθ0−sinθcosθ0(1−cosθ)tx+tysinθ(1−cosθ)ty−txsinθ1 ### 4. 三维基本旋转 三维旋转变换可分解为绕三个坐标轴( x x x、 y y y、 z z z 轴)的基本旋转组合,因此需先明确绕单一坐标轴的旋转矩阵。本文采用 OpenGL 标准的右手坐标系,旋转角度的正负遵循右手定则(大拇指指向坐标轴正方向,四指弯曲方向为角度正方向),如图所示:  #### 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} x′y′z′1 = 10000cosθsinθ00−sinθ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} x′y′z′1 = cosθ0−sinθ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} x′y′z′1 = cosθsinθ00−sinθ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 的坐标。  #### 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 b00−b2+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 a00100−a2+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θ00−sinθ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 0−a2+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 c−b2+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+(1−u2)cosθuv(1−cosθ)+wsinθuw(1−cosθ)−vsinθ0uv(1−cosθ)−wsinθv2+(1−v2)cosθvw(1−cosθ)+usinθ0uw(1−cosθ)+vsinθvw(1−cosθ)−usinθw2+(1−w2)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 旋转角度的正负规则 旋转角度的正负由坐标系的左右手定则确定(与坐标系本身的左右手属性无关): * 右手定则:大拇指指向旋转轴正方向,四指弯曲方向为角度正方向; * 等价描述:从旋转轴正方向的反方向观察,逆时针旋转为正角度,顺时针旋转为负角度。  #### 1.4 旋转角的常用记法 不同领域对欧拉角的三个旋转参数有不同命名,下表列出常见记法对应关系: | 顺序 | 飞行器领域 | 望远镜领域 | 符号表示 | 角速度对应 | |:-----|:--------------|:--------------|:------------|:----------| | 第一旋转 | Heading(航向角) | Azimuth(方位角) | θ \\theta θ | Yaw(偏航) | | 第二旋转 | Attitude(姿态角) | Elevation(仰角) | ϕ \\phi ϕ | Pitch(俯仰) | | 第三旋转 | Bank(横滚角) | Tilt(倾斜角) | ψ \\psi ψ | Roll(横滚) | 欧拉角的物理意义示意图如下:  ### 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)= 00−1010100 ,代入旋转矩阵展开后得到: 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= 00−1sin(α−γ)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γ0−sinγcosγ0001 1000cosβsinβ0−sinβcosβ cosαsinα0−sinα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θ > > ```cpp > direction.y = sin(glm::radians(pitch)); // 注意先把角度转为弧度 > > direction.x = cos(glm::radians(pitch)); > direction.z = cos(glm::radians(pitch)); > ``` > > #### 2.2 偏航角(Yaw) > > 设偏航角为 ϕ \\phi ϕ,则方向向量的计算公式为: >  > > ```cpp > // 计算方向向量的 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,z∈R) 其中虚部满足运算规则: 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,b∈R,i2=−1) ##### 1.1.1 复数的运算 * **共轭复数** :将虚部取反,记为 z ∗ = a − b i z\^\* = a - bi z∗=a−bi; * **复数的模** : ∣ 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)=(ac−bd)+(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′=q⋅z=(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} \[a′b′\]=\[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 四元数的表示形式
四元数有两种常用表示法:
- 拆分形式: q = [ s , v ] q = [s, \boldsymbol{v}] q=[s,v]( s ∈ R s \in \mathbb{R} s∈R 为实部, v = ( x , y , z ) ∈ R 3 \boldsymbol{v} = (x,y,z) \in \mathbb{R}^3 v=(x,y,z)∈R3 为虚部向量);
- 完整形式: 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);
cppinline 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) q1−q2=(s1−s2,x1−x2,y1−y2,z1−z2);
cppinline 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)(s1s2−x1x2−y1y2−z1z2)+(s1x2+x1s2+y1z2−z1y2)i+(s1y2−x1z2+y1s2+z1x2)j+(s1z2+x1y2−y1x2+z1s2)k
实现代码:
cpp
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=q1⋅q2−1,其中逆四元数 q − 1 = q ∗ ∣ q ∣ 2 q^{-1} = \frac{q^*}{|q|^2} q−1=∣q∣2q∗( q ∗ q^* q∗ 为共轭四元数, ∣ q ∣ 2 |q|^2 ∣q∣2 为模的平方)。
实现:
cpp
// 共轭四元数(虚部取反)
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θ
实现代码:
cpp
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,构造旋转四元数的逻辑:
- 旋转轴为 u × v \boldsymbol{u} \times \boldsymbol{v} u×v;
- 旋转角为 u \boldsymbol{u} u 与 v \boldsymbol{v} v 的夹角;
- 特殊处理向量共线(夹角 0 ∘ 0^\circ 0∘ 或 18 0 ∘ 180^\circ 180∘)的情况。
3.2.1 基础实现(含特殊处理)
cpp
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θ=21−cosθ 、 cos θ 2 = 1 + cos θ 2 \cos\frac{\theta}{2} = \sqrt{\frac{1+\cos\theta}{2}} cos2θ=21+cosθ ,简化计算:
cpp
// 替换一般情况的代码块
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)=1−w2 (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)。
实现代码:
cpp
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′=q⋅p⋅q∗
优化实现(避免直接四元数乘法,降低计算量):
cpp
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((1−t)θ)qa+sinθsin(tθ)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θ=qa⋅qb), t ∈ [ 0 , 1 ] t \in [0,1] t∈[0,1] 为插值因子。
实现代码:
cpp
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. 四元数的优势
- 无万向节死锁:相比欧拉角,四元数通过单一参数描述旋转,避免了旋转轴重合导致的自由度丢失;
- 插值平滑:球面插值(SLERP)可实现旋转的连续、平滑过渡,无欧拉角插值的"生硬感";
- 计算高效:四元数仅需 4 个分量,乘法运算量远低于 4×4 旋转矩阵;
- 易于归一化:单位四元数(模为 1)可保证旋转的正交性,归一化操作简单。
参考文献
- Wiki Rotation (mathematics)
https://en.wikipedia.org/wiki/Rotation_(mathematics) - Euler's rotation theorem
https://en.wikipedia.org/wiki/Euler's_rotation_theorem - Maths - Rotation Matrice
https://www.euclideanspace.com/maths/algebra/matrix/orthogonal/rotation/index.htm - 绕任意轴旋转
https://www.cnblogs.com/graphics/archive/2012/08/10/2627458.html - Rotation About an Arbitrary Axis in 3 Dimension
https://inside.mines.edu/fs_home/gmurray/ArbitraryAxisRotation/ - Rotation about an Arbitrary Axis (Line)
https://www.engr.uvic.ca/~mech410/lectures/4_2_RotateArbi.pdf - Maths - Euler Angle
http://www.euclideanspace.com/maths/geometry/rotations/euler/ - Wiki-Euler angle
https://en.wikipedia.org/wiki/Euler_angles - Euler Angle
http://mathworld.wolfram.com/EulerAngles.html - Wiki-Gimbal lock
https://en.wikipedia.org/wiki/Gimbal_lock - 欧拉角、内部旋转、外部旋转
http://blog.csdn.net/hy3316597/article/details/50966633 - Understanding Quaternion
https://www.3dgep.com/understanding-quaternions/ - 如何形象地理解四元数?
https://www.zhihu.com/question/23005815/answer/33971127 - Quaternion
http://mathworld.wolfram.com/Quaternion.html - Quaternion
https://en.wikipedia.org/wiki/Quaternion - Maths - Quaternion
https://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/index.htm - Beautiful maths simplification: quaternion from two vector
https://lolengine.net/blog/2013/09/18/beautiful-maths-quaternion-from-vectors - Rotating vector3 by a quaternion
https://gamedev.stackexchange.com/questions/28395/rotating-vector3-by-a-quaternion - quaternion vector product - the principle of the element-transformed vector algorithm
https://www.gamedev.net/topic/466725-quaternion-vector-product/
via:
- 旋转变换(一)旋转矩阵 - 二维与三维旋转变换-CSDN博客
https://blog.csdn.net/csxiaoshui/article/details/65446125 - 旋转变换(二)欧拉角 - 欧拉角详解-CSDN博客
https://blog.csdn.net/csxiaoshui/article/details/65437633- 欧拉角(Euler Angle)-CSDN博客
https://thefist.blog.csdn.net/article/details/126595776
- 欧拉角(Euler Angle)-CSDN博客
- 旋转变换(三)四元数 - 四元数与三维旋转-CSDN博客
https://blog.csdn.net/csxiaoshui/article/details/65445633