注:本文为 “旋转变换” 相关合辑。
图片清晰度受引文原图所限。
略作重排,如有内容异常,请看原文。
旋转变换(一)旋转矩阵
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 变换步骤
- 绕 x x x 轴旋转 α \alpha α 角:将向量 u \boldsymbol{u} u 旋转至 x o z xoz xoz 平面;
- 绕 y y y 轴旋转 − β -\beta −β 角:将向量 u \boldsymbol{u} u 旋转至与 z z z 轴重合;
- 绕 z z z 轴旋转 θ \theta θ 角:执行目标旋转操作;
- 绕 y y y 轴旋转 β \beta β 角:步骤 2 的逆变换;
- 绕 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+c2c,sinα=b2+c2b
对应的旋转矩阵为:
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+c2cb2+c2b00−b2+c2bb2+c2c00001
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+c2b2+c2,sinβ=a2+b2+c2a
代入绕
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+c2b2+c20a2+b2+c2a00100−a2+b2+c2a0a2+b2+c2b2+c200001
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+c2b2+c20−a2+b2+c2a00100a2+b2+c2a0a2+b2+c2b2+c200001
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+c2c−b2+c2b00b2+c2bb2+c2c00001
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 轴的旋转可形成多种组合顺序,主要分为两类:
- 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;
- 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 旋转的参考坐标系
根据旋转轴的参考基准,欧拉角可分为:
- 外旋(Extrinsic Rotations):所有旋转均围绕固定坐标系(如世界坐标系)的坐标轴进行,旋转轴方向始终不变;
- 内旋(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) 实现。这是因为欧拉角的旋转顺序存在耦合关系,角度叠加不满足线性性。
工程实践中,连续旋转的计算通常采用以下流程:
- 将两次旋转对应的欧拉角分别转换为旋转矩阵或四元数;
- 通过矩阵乘法或四元数乘法实现旋转效果的叠加;
- (可选)将叠加后的旋转结果转换回欧拉角。
需注意:多次转换可能引入累积误差,因此连续旋转场景中更推荐直接使用旋转矩阵或四元数进行计算,避免频繁的欧拉角转换。
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 应用建议
若必须使用欧拉角,需注意以下两点:
- 严格统一欧拉角的定义约定(旋转顺序、参考坐标系、角度正负规则),避免不同模块间的描述冲突;
- 根据应用场景选择合适的旋转顺序,尽量避开易触发万向节死锁的角度范围(如避免第二次旋转角度接近 ± 9 0 ∘ \pm 90^\circ ±90∘)。
4.2 替代方案
为解决欧拉角的耦合性与万向节死锁问题,推荐采用以下更优的旋转描述方式:
- 旋转矩阵:无万向节死锁问题,支持直接的旋转叠加计算,适用于大多数工程场景;
- 四元数:仅需 4 个参数即可描述三维旋转,计算效率高于旋转矩阵,且无万向节死锁,是机器人学、计算机视觉中的首选方案;
- 旋转轴-旋转角:通过一个单位向量(旋转轴)和一个角度(旋转量)描述旋转,直观且无自由度丢失,适用于需要明确旋转方向的场景。
欧拉角 (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θ
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,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);
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) 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] ); }
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
实现代码:
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 为模的平方)。
实现:
// 共轭四元数(虚部取反)
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,构造旋转四元数的逻辑:
- 旋转轴为 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 基础实现(含特殊处理)
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θ,简化计算:
// 替换一般情况的代码块
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)。
实现代码:
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∗
优化实现(避免直接四元数乘法,降低计算量):
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] 为插值因子。
实现代码:
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.youkuaiyun.com/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:
- 旋转变换(一)旋转矩阵 - 二维与三维旋转变换-优快云博客
https://blog.youkuaiyun.com/csxiaoshui/article/details/65446125 - 旋转变换(二)欧拉角 - 欧拉角详解-优快云博客
https://blog.youkuaiyun.com/csxiaoshui/article/details/65437633- 欧拉角(Euler Angle)-优快云博客
https://thefist.blog.youkuaiyun.com/article/details/126595776
- 欧拉角(Euler Angle)-优快云博客
- 旋转变换(三)四元数 - 四元数与三维旋转-优快云博客
https://blog.youkuaiyun.com/csxiaoshui/article/details/65445633




49

被折叠的 条评论
为什么被折叠?



