原文:https://danceswithcode.net/engineeringnotes/quaternions/quaternions.html
译者前言
虽然经常使用四元数来处理旋转,但是其底层原理我一直不理解。之前很多次想去学习它,但是每次找到的文档都是在解释欧拉角和“万向锁”,结果看着看着就猪脑过载,最后放弃了。。。
直到这次又有个契机需要学习四元数,我搜到了这一篇文档。看了开头后很庆幸发现自己能看得下去。因此也就想借着翻译的过程去更细节地学习。
需要说明,我暂时省略了原文里“将欧拉角转换为四元数”、“将四元数转换为欧拉角”、“万向锁”这三个小节。因为欧拉角的问题确实比较复杂,也许需要之后再花时间研究作者的另一篇文档《三维旋转:欧拉角和旋转矩阵》后再翻译会更准确。而目前省略掉这些部分也不影响四元数的核心概念。
另外还有几个小节虽然我也翻译了,但我认为就算先不看也不影响了解四元数的核心概念,我使用了星号 * 来做了标记。
摘要
本文基础性地介绍了四元数在三维旋转中的应用。我们给出了四元数的简单定义,并展示了如何在四元数、轴-角表示、欧拉角、旋转矩阵之间来回转换。我们还展示了如何使用四元数向前和向后旋转对象,以及如何将多个旋转操作连接成一个四元数。
介绍
准确来说,一个四元数由四部分组成:
q
=
q
0
+
i
q
1
+
j
q
2
+
k
q
3
\boldsymbol{q}= q_0+\boldsymbol{i}q_1+\boldsymbol{j}q_2+\boldsymbol{k}q_3
q=q0+iq1+jq2+kq3
其中:
q
0
q_0
q0、
q
1
q_1
q1、
q
2
q_2
q2、
q
3
q_3
q3 都是实数。
i
\boldsymbol{i}
i、
j
\boldsymbol{j}
j、
k
\boldsymbol{k}
k 是相互正交的虚数单位向量。
q
0
q_0
q0称为“实部”,其余三项称为“虚部”。在实际应用中(以及本文的其余部分)都隐含了虚数符号,仅使用四个系数来表示一个四元数:
q
=
(
q
0
,
q
1
,
q
2
,
q
3
)
\boldsymbol{q}= (q_0,q_1,q_2,q_3)
q=(q0,q1,q2,q3)
“四元数”是一个复杂的话题。然而在本文中,我们仅讨论 “旋转四元数” 这一四元数的子集。旋转四元数是一种在三维空间中表示旋转的方式,可用于3D图形或其他应用程序中,作为 “旋转矩阵” 的替代方式。想要使用它们,并不需要理解“复数”。
旋转四元数与旋转的“轴-角表示”密切相关。因此,我们将先解释“轴-角表示”,然后展示如何转换为四元数。
3D旋转的 轴-角表示
根据欧拉旋转定理,3D中任何旋转(或旋转的序列)都可以使用两个参数来指定:“表示旋转轴的单位向量” 和 “绕该轴旋转的角度
θ
\theta
θ”。如图 1 所示:
因此,一个“轴-角旋转”可以用四个数字表示:
(
θ
,
x
^
,
y
^
,
z
^
)
(\theta,\hat{x},\hat{y},\hat{z})
(θ,x^,y^,z^)
其中:
(
x
^
,
y
^
,
z
^
)
(\hat{x},\hat{y},\hat{z})
(x^,y^,z^) 是表示旋转轴的单位向量
θ
\theta
θ 是绕该轴旋转的角度
将“轴-角”转换为“四元数”
“旋转四元数” 类似于“轴-角表示”。如果我们知道了“轴-角表示”中的
(
θ
,
x
^
,
y
^
,
z
^
)
(\theta,\hat{x},\hat{y},\hat{z})
(θ,x^,y^,z^),我们就能将其转换为旋转四元数
q
\boldsymbol{q}
q:
q
=
(
q
0
,
q
1
,
q
2
,
q
3
)
\boldsymbol{q}= (q_0,q_1,q_2,q_3)
q=(q0,q1,q2,q3)
其中:
q
0
=
cos
(
θ
2
)
q
1
=
x
^
sin
(
θ
2
)
q
2
=
y
^
sin
(
θ
2
)
q
3
=
z
^
sin
(
θ
2
)
\begin{align*} q_0&=\cos(\frac{\theta}{2})\\ q_1&=\hat{x} \sin(\frac{\theta}{2})\\ q_2&=\hat{y} \sin(\frac{\theta}{2})\\ q_3&=\hat{z} \sin(\frac{\theta}{2}) \end{align*}
q0q1q2q3=cos(2θ)=x^sin(2θ)=y^sin(2θ)=z^sin(2θ)
从中我们可以看出,四元数的 实部 q 0 q_0 q0 完全由旋转角决定,其余的三项 虚部( q 1 q_1 q1、 q 2 q_2 q2、 q 3 q_3 q3)就是旋转轴向量的三个值并且有一个共同的缩放系数。这种表示让旋转四元数的 “大小”(也就是四个分量的平方和) 始终等于 1。
也就是说,“轴-角” 和 “四元数” 表示了完全相同的信息。那么我们就有理由问:为什么我们还要大费周折地使用不直观的四元数呢?答案是:如果使用“轴-角”来执行“旋转3D物体的顶点”等操作,无论如何都必须执行“三角运算”,而大多数四元数运算只需使用“乘/除/加/减运算”即可完成,这节省了计算机运算。
*将“四元数”转换为“轴-角”
给定四元数 q = ( q 0 , q 1 , q 2 , q 3 ) \boldsymbol{q}= (q_0,q_1,q_2,q_3) q=(q0,q1,q2,q3),我们可以用如下方式转换为刚才的“轴-角表示”:
首先,先从
q
0
q_0
q0 中提取出旋转角度:
θ
=
2
cos
−
1
(
q
0
)
\theta=2\cos^{-1}(q_0)
θ=2cos−1(q0)
然后,如果
θ
\theta
θ不为0,我们可以用如下算式得到旋转轴的单位向量:
(
x
^
,
y
^
,
z
^
)
=
(
q
1
sin
θ
2
,
q
2
sin
θ
2
,
q
3
sin
θ
2
)
(\hat{x},\hat{y},\hat{z})=(\frac{q_1}{\sin{\frac{\theta}{2}}},\frac{q_2}{\sin{\frac{\theta}{2}}},\frac{q_3}{\sin{\frac{\theta}{2}}})
(x^,y^,z^)=(sin2θq1,sin2θq2,sin2θq3)
在一种特殊情况下不能使用上面算式: q = ( 1 , 0 , 0 , 0 ) \boldsymbol{q}= (1,0,0,0) q=(1,0,0,0) 的四元数称为恒等四元数(identity quaternion),它将不会产生任何旋转。在这种情况下 θ = 0 \theta=0 θ=0,然而,由于在没有旋转时旋转轴是未知的,因此上面算式将产生“除以零的错误”。因此,任何软件实现都应该测试 q 0 q_0 q0 是否等于 “1.0”,如果等于,则应设置 θ = 0 \theta=0 θ=0 并且 ( x ^ , y ^ , z ^ ) = ( 1 , 0 , 0 , 0 ) (\hat{x},\hat{y},\hat{z})=(1,0,0,0) (x^,y^,z^)=(1,0,0,0)。
值得注意的是,有多种方法可以将“四元数”转换为“轴-角”。因此如果你看到了和上面的算式不一样的方法,也不要担心。
*将“四元数”转换为“旋转矩阵”
旋转四元数
q
=
(
q
0
,
q
1
,
q
2
,
q
3
)
\boldsymbol{q}= (q_0,q_1,q_2,q_3)
q=(q0,q1,q2,q3) 所对应的旋转矩阵为:
R
=
∣
q
0
2
+
q
1
2
−
q
2
2
−
q
3
2
2
q
1
q
2
−
2
q
0
q
3
2
q
1
q
3
+
2
q
0
q
2
2
q
1
q
2
+
2
q
0
q
3
q
0
2
−
q
1
2
+
q
2
2
−
q
3
2
2
q
2
q
3
−
2
q
0
q
1
2
q
1
q
3
−
2
q
0
q
2
2
q
2
q
3
+
2
q
0
q
1
q
0
2
−
q
1
2
−
q
2
2
+
q
3
2
∣
R= \begin{vmatrix} q_0^2+q_1^2-q_2^2-q_3^2 & 2q_1q_2-2q_0q_3 & 2q_1q_3+2q_0q_2\\ 2q_1q_2+2q_0q_3 & q_0^2-q_1^2+q_2^2-q_3^2 & 2q_2q_3-2q_0q_1\\ 2q_1q_3-2q_0q_2 & 2q_2q_3+2q_0q_1 & q_0^2-q_1^2-q_2^2+q_3^2 \end{vmatrix}
R=
q02+q12−q22−q322q1q2+2q0q32q1q3−2q0q22q1q2−2q0q3q02−q12+q22−q322q2q3+2q0q12q1q3+2q0q22q2q3−2q0q1q02−q12−q22+q32
或者,等价的:
R
=
∣
1
−
2
q
2
2
−
2
q
3
2
2
q
1
q
2
−
2
q
0
q
3
2
q
1
q
3
+
2
q
0
q
2
2
q
1
q
2
+
2
q
0
q
3
1
−
2
q
1
2
−
2
q
3
2
2
q
2
q
3
−
2
q
0
q
1
2
q
1
q
3
−
2
q
0
q
2
2
q
2
q
3
+
2
q
0
q
1
1
−
2
q
1
2
−
2
q
2
2
∣
R= \begin{vmatrix} 1-2q_2^2-2q_3^2 & 2q_1q_2-2q_0q_3 & 2q_1q_3+2q_0q_2\\ 2q_1q_2+2q_0q_3 & 1-2q_1^2-2q_3^2 & 2q_2q_3-2q_0q_1\\ 2q_1q_3-2q_0q_2 & 2q_2q_3+2q_0q_1 & 1-2q_1^2-2q_2^2 \end{vmatrix}
R=
1−2q22−2q322q1q2+2q0q32q1q3−2q0q22q1q2−2q0q31−2q12−2q322q2q3+2q0q12q1q3+2q0q22q2q3−2q0q11−2q12−2q22
这两种方法都适用于所有有效的旋转四元数,包括恒等四元数。
*将“旋转矩阵”转换为“四元数”
给定旋转矩阵
R
R
R
R
=
∣
r
11
r
12
r
13
r
21
r
22
r
23
r
31
r
32
r
33
∣
R= \begin{vmatrix} r_{11} & r_{12} & r_{13}\\ r_{21} & r_{22} & r_{23}\\ r_{31} & r_{32} & r_{33} \end{vmatrix}
R=
r11r21r31r12r22r32r13r23r33
我们可以通过两个步骤找到等效的四元数:
步骤1:求每个分量的“大小”,然而我们并不知道每个分量的正负符号:
∣
q
0
∣
=
1
+
r
11
+
r
22
+
r
33
4
∣
q
1
∣
=
1
+
r
11
−
r
22
−
r
33
4
∣
q
2
∣
=
1
−
r
11
+
r
22
−
r
33
4
∣
q
3
∣
=
1
−
r
11
−
r
22
+
r
33
4
|q_0|=\sqrt{\frac{1+r_{11}+r_{22}+r_{33}}{4}}\\ |q_1|=\sqrt{\frac{1+r_{11}-r_{22}-r_{33}}{4}}\\ |q_2|=\sqrt{\frac{1-r_{11}+r_{22}-r_{33}}{4}}\\ |q_3|=\sqrt{\frac{1-r_{11}-r_{22}+r_{33}}{4}}
∣q0∣=41+r11+r22+r33∣q1∣=41+r11−r22−r33∣q2∣=41−r11+r22−r33∣q3∣=41−r11−r22+r33
步骤2:找到 q 0 , q 1 , q 2 , q 3 q_0,q_1,q_2,q_3 q0,q1,q2,q3中最大的,并假设其符号为正。然后使用下表中的算式计算其余分量:(找“最大”的值是为了避免除以太小的数值,因为那样会让数值精度降低)
如果 q 0 q_0 q0最大 | 如果 q 1 q_1 q1最大 | 如果 q 2 q_2 q2最大 | 如果 q 3 q_3 q3最大 |
---|---|---|---|
q 1 = r 32 − r 23 4 q 0 q_1=\frac{r_{32}-r_{23}}{4q_0} q1=4q0r32−r23 | q 0 = r 32 − r 23 4 q 1 q_0=\frac{r_{32}-r_{23}}{4q_1} q0=4q1r32−r23 | q 0 = r 13 − r 31 4 q 2 q_0=\frac{r_{13}-r_{31}}{4q_2} q0=4q2r13−r31 | q 0 = r 12 − r 21 4 q 3 q_0=\frac{r_{12}-r_{21}}{4q_3} q0=4q3r12−r21 |
q 2 = r 13 − r 31 4 q 0 q_2=\frac{r_{13}-r_{31}}{4q_0} q2=4q0r13−r31 | q 2 = r 12 + r 21 4 q 1 q_2=\frac{r_{12}+r_{21}}{4q_1} q2=4q1r12+r21 | q 1 = r 12 + r 21 4 q 2 q_1=\frac{r_{12}+r_{21}}{4q_2} q1=4q2r12+r21 | q 1 = r 13 + r 31 4 q 3 q_1=\frac{r_{13}+r_{31}}{4q_3} q1=4q3r13+r31 |
q 3 = r 21 − r 12 4 q 0 q_3=\frac{r_{21}-r_{12}}{4q_0} q3=4q0r21−r12 | q 3 = r 13 + r 31 4 q 1 q_3=\frac{r_{13}+r_{31}}{4q_1} q3=4q1r13+r31 | q 3 = r 23 + r 32 4 q 2 q_3=\frac{r_{23}+r_{32}}{4q_2} q3=4q2r23+r32 | q 2 = r 23 + r 32 4 q 3 q_2=\frac{r_{23}+r_{32}}{4q_3} q2=4q3r23+r32 |
正负符号之所以不确定的原因是:任何给定的旋转都可能对应两个四元数,如果已知了其中的一个,那么另一个就可以通过将所有分量取负来得到——这意味着旋转角度和旋转轴都会反转。因此,对于任意旋转四元数, ( q 0 , q 1 , q 2 , q 3 ) (q_0,q_1,q_2,q_3) (q0,q1,q2,q3) 和 ( − q 0 , − q 1 , − q 2 , − q 3 ) (-q_0,-q_1,-q_2,-q_3) (−q0,−q1,−q2,−q3) 都将产生相同的旋转。而要将旋转矩阵转换为四元数,我们只能先随便选择两种答案之中的一个。
使用四元数来旋转一个点
本节定义了四元数的 “乘法”和 “逆”,并展示了如何使用四元数来执行旋转。
四元数的乘法
两个四元数相乘:
t
=
r
s
(
t
0
,
t
1
,
t
2
,
t
3
)
=
(
r
0
,
r
1
,
r
2
,
r
3
)
×
(
s
0
,
s
1
,
s
2
,
s
3
)
\begin{align*} \boldsymbol{t}&=\boldsymbol{r}\boldsymbol{s}\\ (t_0,t_1,t_2,t_3)&=(r_0,r_1,r_2,r_3)\times(s_0,s_1,s_2,s_3) \end{align*}
t(t0,t1,t2,t3)=rs=(r0,r1,r2,r3)×(s0,s1,s2,s3)
定义为:
t
0
=
(
r
0
s
0
−
r
1
s
1
−
r
2
s
2
−
r
3
s
3
)
t
1
=
(
r
0
s
1
+
r
1
s
0
+
r
3
s
2
−
r
2
s
3
)
t
2
=
(
r
0
s
2
+
r
2
s
0
+
r
1
s
3
−
r
3
s
1
)
t
3
=
(
r
0
s
3
+
r
3
s
0
+
r
2
s
1
−
r
1
s
2
)
t_0=(r_0s_0-r_1s_1-r_2s_2-r_3s_3)\\ t_1=(r_0s_1+r_1s_0+r_3s_2-r_2s_3)\\ t_2=(r_0s_2+r_2s_0+r_1s_3-r_3s_1)\\ t_3=(r_0s_3+r_3s_0+r_2s_1-r_1s_2)
t0=(r0s0−r1s1−r2s2−r3s3)t1=(r0s1+r1s0+r3s2−r2s3)t2=(r0s2+r2s0+r1s3−r3s1)t3=(r0s3+r3s0+r2s1−r1s2)
四元数的乘法满足结合律,但是不满足交换律(除非某些特殊情况)。也就是说:
(
a
b
)
c
=
a
(
b
c
)
a
b
≠
b
a
(\boldsymbol{a}\boldsymbol{b})\boldsymbol{c}=\boldsymbol{a}(\boldsymbol{b}\boldsymbol{c})\\ \boldsymbol{a}\boldsymbol{b}\ne\boldsymbol{b}\boldsymbol{a}
(ab)c=a(bc)ab=ba
四元数的逆
四元数的逆通过对虚部取负得到:
q
−
1
=
(
q
0
,
−
q
1
,
−
q
2
,
−
q
3
)
\boldsymbol{q}^{-1}= (q_0,-q_1,-q_2,-q_3)
q−1=(q0,−q1,−q2,−q3)
使用四元数来执行旋转
要使用 四元数 q \boldsymbol{q} q 来旋转 点 ( x , y , z ) (x,y,z) (x,y,z),需要以下三个步骤:
步骤1:将要旋转的点转换为四元数
p
\boldsymbol{p}
p :将点的坐标指定为
p
\boldsymbol{p}
p的虚部,并将
p
\boldsymbol{p}
p的实部设置为零。比如
(
x
,
y
,
z
)
(x,y,z)
(x,y,z) 是要旋转的点,那么转换成的四元数
p
\boldsymbol{p}
p为:
p
=
(
p
0
,
p
1
,
p
2
,
p
3
)
=
(
0
,
x
,
y
,
z
)
\boldsymbol{p}=(p_0,p_1,p_2,p_3)=(0,x,y,z)
p=(p0,p1,p2,p3)=(0,x,y,z)
步骤2:执行旋转。这需要两次乘法:
如果是“主动”旋转,则: p ′ = q − 1 p q \boldsymbol{p'}=\boldsymbol{q}^{-1}\boldsymbol{p}\boldsymbol{q} p′=q−1pq
如果是“被动”旋转,则: p ′ = q p q − 1 \boldsymbol{p'}=\boldsymbol{q}\boldsymbol{p}\boldsymbol{q}^{-1} p′=qpq−1
其中:
p
\boldsymbol{p}
p 是步骤1中由要旋转的点所转换的四元数
q
\boldsymbol{q}
q 是表示旋转的四元数
q
−
1
\boldsymbol{q}^{-1}
q−1 是
q
\boldsymbol{q}
q 的逆
p
′
\boldsymbol{p'}
p′ 中包含了执行完旋转后的点的坐标
“主动”旋转是指点相对于坐标系旋转,“被动”旋转是指坐标系相对于点旋转。两个旋转方向相反。
注意,由于四元数乘法满足结合律,所以顺序是 ( q p ) q − 1 (\boldsymbol{q}\boldsymbol{p})\boldsymbol{q}^{-1} (qp)q−1还是 q ( p q − 1 ) \boldsymbol{q}(\boldsymbol{p}\boldsymbol{q}^{-1}) q(pq−1)都一样。
步骤3:从
p
′
\boldsymbol{p'}
p′ 中提取旋转后的坐标:
p
′
=
(
0
,
x
′
,
y
′
,
z
′
)
\boldsymbol{p'}=(0,x',y',z')
p′=(0,x′,y′,z′)
p ′ \boldsymbol{p'} p′ 像其他四元数一样有四个分量,但是它的实部总是等于0。而旋转后的点 ( x ′ , y ′ , z ′ ) (x',y',z') (x′,y′,z′) 就是 p ′ \boldsymbol{p'} p′ 的虚部。
*四元数的性质
以下是四元数一些有用的性质。到目前为止,本文仅讨论了“旋转四元数”。然而,“旋转四元数”只是所有四元数的子集,就像“旋转矩阵”是所有 3x3 矩阵的子集一样。而以下的性质适用于所有四元数,除非特别指明。
- 四元数的长度(大小)是 ∣ q ∣ = q q ∗ = q 0 2 + q 1 2 + q 2 2 + q 3 2 |\boldsymbol{q}|=\sqrt{qq^*}=\sqrt{q_0^2+q_1^2+q_2^2+q_3^2} ∣q∣=qq∗=q02+q12+q22+q32
- 如果 ∣ q ∣ = 1 |\boldsymbol{q}|=1 ∣q∣=1,则这个四元数是“单位四元数(unit quaternion)”
- 所有旋转四元数都是“单位四元数”
- 如果 q = ( 1 , 0 , 0 , 0 ) \boldsymbol{q}= (1,0,0,0) q=(1,0,0,0),则这个四元数是“恒等四元数(identity quaternion)”。它表示不进行任何旋转。对于任意四元数 q \boldsymbol{q} q 以及单位四元数 i \boldsymbol{i} i,都存在 q i = i q = q \boldsymbol{q}\boldsymbol{i}=\boldsymbol{i}\boldsymbol{q}=\boldsymbol{q} qi=iq=q
- 四元数的 共轭(conjugate) 为 q ∗ = ( q 0 , − q 1 , − q 2 , − q 3 ) \boldsymbol{q}^{*}= (q_0,-q_1,-q_2,-q_3) q∗=(q0,−q1,−q2,−q3)
- 四元数的逆为 q − 1 = q ∗ ∣ q ∣ 2 \boldsymbol{q}^{-1}= \frac{\boldsymbol{q}^{*}}{|\boldsymbol{q}|^2} q−1=∣q∣2q∗。一个四元数和自己的共轭相乘的结果是恒等四元数: q q − 1 = q − 1 q = ( 1 , 0 , 0 , 0 ) \boldsymbol{q}\boldsymbol{q^{-1}}=\boldsymbol{q^{-1}}\boldsymbol{q}=(1,0,0,0) qq−1=q−1q=(1,0,0,0)。注意在这个特殊的情况下,这里的乘法是满足交换律的。
- 对于旋转四元数,它的逆和共轭是一样的: q − 1 = q ∗ = ( q 0 , − q 1 , − q 2 , − q 3 ) \boldsymbol{q^{-1}}=\boldsymbol{q}^{*}= (q_0,-q_1,-q_2,-q_3) q−1=q∗=(q0,−q1,−q2,−q3)
- 旋转四元数的逆/共轭具有反转旋转轴的效果,这会将其修改为沿与原始方向相反的方向旋转。也就是说,如果使用 q \boldsymbol{q} q 将一个点旋转到一个新位置,则使用 q − 1 \boldsymbol{q^{-1}} q−1或 q ∗ \boldsymbol{q}^{*} q∗再次旋转它会将其返回到原始位置。
- 任何给定的旋转都有两个可能的四元数表示。如果已知其中一个,则可以通过对所有分量取负来找到另一个。这使得旋转角度和旋转轴都反转。因此,如果 q \boldsymbol{q} q 是旋转四元数,则 q \boldsymbol{q} q 和 − q -\boldsymbol{q} −q 将产生相同的旋转。
- 先 q a \boldsymbol{q_a} qa 随后 q b \boldsymbol{q_b} qb 的旋转可以被合并为一个旋转 q c = q a q b \boldsymbol{q_c}=\boldsymbol{q_a}\boldsymbol{q_b} qc=qaqb。这可以扩展到任意数量的旋转。但请注意,顺序很重要(因为四元数的乘法不满足交换律)。
- 四元数的乘法满足结合律: ( a b ) c = a ( b c ) (\boldsymbol{a}\boldsymbol{b})\boldsymbol{c}=\boldsymbol{a}(\boldsymbol{b}\boldsymbol{c}) (ab)c=a(bc)
- 四元数的乘法不满足交换律: a b ≠ b a \boldsymbol{a}\boldsymbol{b}\ne\boldsymbol{b}\boldsymbol{a} ab=ba
译者后记
虽然翻译完了除开欧拉角和万向锁的内容,不过文章里的很多公式也值得再去推导一遍。
作者的另一篇文档《三维旋转:欧拉角和旋转矩阵》 之后需要花时间研究。
旋转转换工具 是作者提供的一个可以在欧拉角、四元数、轴-角和旋转矩阵表示形式之间进行转换的工具。