10.1 什么是方位
物体的“方位”主要描述的是物体的朝向。然而,“方向”和“方位”并不完全一样。向量有“方向”但没有“方位”

然而,当一个物体像上面向量一样自转时,会发现物体的方位改变了

从技术角度来说,在 3D 中,只要用两个数字,就能用参数表示一个方向(direction)。但是要确定一个方位(orientation),就至少要三个数字
我们知道不能用绝对坐标来描述物体的位置。描述位置实际上是描述相对于给定参考点(通常是原点)的位移。描述物体的方位同理,需要通过与相对已知方位(对应就是原方位)的旋转来描述。旋转的量称作角位移
换句话说,在数学上描述方位就等价于描述角位移。具体来说,我们用矩阵和四元数来表示“角位移”,用欧拉角来表示“方位”
10.2 矩阵形式
3D 中,描述坐标系中方位的一种方法是列出这个坐标系的基向量,用它们来构成一个 3x3 矩阵,然后就能用矩阵形式来描述方位。换句话说,能用一个旋转矩阵来描述这两个坐标系(基准坐标系与物体坐标系)间的相对方位

10.2.1 用哪个矩阵
用哪个不重要,只要知道方位是用矩阵来描述的,而矩阵表示的是转换后的基向量就够了
10.2.2 矩阵形式的优点
- 可以立即进行向量的旋转
- 矩阵的形式被图形 API 所使用
- 多个角位移连接
- 矩阵的逆。因为旋转矩阵是正交的,所以求逆的话只需要简单的转置运算
10.2.3 矩阵形式的缺点
矩阵用 9 个数来保存方位,而实际上只需要 3 个就可确定了(解释。可见 U3D 中的 Rotation,就是用了 3 个数字)

这些“多余”会带来一些问题
- 占用了更多的内存
- 难于使用。矩阵对人类来说并不直观,欧拉角更加符合人类思维
- 矩阵可能是病态的。有可能并非是一个有效的旋转矩阵,因为正交矩阵的条件(3 个基向量为标准正交基)相对苛刻
- 矩阵还可能包含其他转换操作
- 可能从外部数据源获得“坏”数据
- 可能因为浮点数的舍入错误产生“坏”数据。大量的矩阵乘法(矩阵蠕变)最终可能导致病态矩阵(需正交化修正)
10.2.4 小结

10.3 欧拉角
另一种描述方位的常用方法是欧拉角
10.3.1 什么是欧拉角
基本思想是将角位移分解为绕三个互相垂直轴的三个旋转组成的序列(???)。是哪三个轴?按什么顺序?最有意义的是使用 笛卡尔坐标系 并 按一定顺序 所组成的旋转序列
最常用的约定,是所谓的“heading-pitch-bank”约定。它的基本思想是让物体开始于“标准”方位,然后让物体作 heading、pitch、bank 旋转,最后得到想要的物体方位

⬆️(注意是绕物体坐标系轴旋转,而不是惯性坐标系的轴)
当我们说旋转的顺序是 heading-pitch-bank 时,是指从惯性坐标系到物体坐标系。若从物体坐标系变换到惯性坐标系,旋转的顺序就是相反的
“heading-pitch-bank”也叫作“roll-pitch-yaw”,roll 类似于 bank,yaw 类似于 heading(事实上,yaw并不严格等于 heading,参见 10.3.2 节)。注意,在 roll-pitch-yaw 系统中,角度的命名顺序是与从物体坐标系到惯性坐标系的旋转顺序一致的
10.3.2 关于欧拉角的其他约定
前面提到,绕任意三个互相垂直轴的任意旋转序列都能定义一个方位。所以就导致了欧拉角约定的多样性:
- heading-pitch-bank 系统有多个名称(并不代表不同的约定)
- 任意三个轴都能作为旋转轴,不一定必须是笛卡尔轴,但使用笛卡尔轴最有意义
- 决定每个旋转的正方向时不一定必须遵守左手或右手法则
- 也是最重要的,旋转可以以不同的顺序进行
10.3.3 欧拉角的优点
它仅使用三个数来表达方位,且三个数都是角度
- 欧拉角对我们来说很容易使用
- 最简洁的表达方式
- 任意三个数都是合法的
10.3.4 欧拉角的缺点
- 给定方位的表达方式不唯一。将一个角度加上 360 的倍数时,方位不变,但数值改变了
- 两个角度间求插值非常困难
因为三个角度不互相独立,物体的同一个方位,有多种表达方法。为了解决这个问题,必须限制角度的范围。这样对于任意方位,仅存在一个限制欧拉角(将 heading 和 bank 限制在 ±180 度之间,pitch 限制在 ±90 度之间)能代表这个方位
欧拉角最著名的别名问题是——万向锁。它指的是,当陀螺仪两相接转面共面时,旋转外圈不能改变内圈姿态,自由度失去
⬆️其情况如下图左方所示。下图右方为正常情况
为了消除这个问题,规定万向锁情况下由 heading 完成绕竖直轴的全部旋转。换句话说,在限制欧拉角中,如果 pitch 为 ±90 度,则 bank 为零
接下来看两个方位 A 和 B 间的插值问题。解决问题的方法是使用限制欧拉角(要将 pitch 值限制在 ±90度之间需要一些技巧)
然而即使是这样也不能完全解决问题。设 A 的 heading 为-170 度,B 的 heading 为 170 度,它们都在限制范围内。事实上这两个值只差 20 度,但插值操作又一次发生错误,旋转是沿“长弧”绕了 340 度而不是更短的 20 度

解决这类问题的方法是将插值的“差”角度折到 ±180 度之间,以找到最短弧。但即使这样,欧拉角插值仍然可能碰到“万向锁”问题。它在大多数情况下会导致抖动、路径错误等现象,物体会突然飘起来像是“挂”在某个地方。下面 10.4 将学习怎样用四元数解决这些问题
10.3.5 总结

10.4 四元数
四元数通过使用四个数来表达方位,来避免三个数表达引起的“万向锁”问题
10.4.1 四元数记法
一个四元数包含一个标量分量和一个 3D 向量分量。两种记法分别是
在某些情况下,用 v 这样的短记法更方便,而另外一些情况下,“扩展”的记法会更清楚。也可以将四元数竖着写
10.4.2 四元数与复数
复数对 (a, b) 定义了数 a+bi,i 是所谓的虚数,满足 :a 称作实部,b 称作虚部。任意实数 k 都能表示为复数 (k, 0)=k+0i
复数能够相加、相减、相乘

通过使虚部变负,还能够计算复数的共轭
还能够计算复数的模。这与实数的绝对值类似,实际上若将实数表示成复数,它们将产生相同的结果

复数集存在于一个 2D 平面上,可以认为这个平面有两个轴:实轴和虚轴。这样,就能将复数 (x, y) 解释为 2D 向量。用这种方法解释复数时,它们能用来表达平面中的旋转
来看看复数 p 绕原点旋转角度 的情况

为了进行这个旋转,引入第二个复数 。现在,旋转后的复数 p' 能用复数乘法计算出来

这样,复数就提供了 2D 中旋转向量的另一种有趣的记法。3D 中的记法就需要扩展复数系统,它使用三个虚部 i, j, k。关系如下

一个四元数 [w, (x, y, z)] 定义了复数 w+xi+yj+zk ,很多标准复数的性质都能应用到四元数上。更重要的是,和复数能用来旋转 2D 中的向量类似,四元数也能用来旋转 3D 中的向量
10.4.3 四元数和轴——角对
欧拉证明了一个旋转序列等价于单个旋转。因此 3D 中的任意角位移都能表示为绕单一轴(并非指坐标轴)的单一旋转。这种描述形式称作轴——角描述法
设 n 为旋转轴, 为绕轴旋转的量,轴-角对 (n, ) 定义了一个角位移
四元数能被解释为角位移的轴-角对方式,但 n 和 并非直接存储在四个数中
10.4.4 负四元数
四元数能求负(直接地)

q 和 -q 代表的实际角位移是相同的。若我们将 \theta 加上 360 度的倍数,不会改变 q 代表的角位移,但它使 q 的四个分量都变负了(与共轭不同,共轭只是虚部变负)。因此,3D 中的任意角位移都有两种不同的四元数表示方法,它们互相为负
10.4.5 单位四元数
几何上,存在两个“单位”四元数,它们都代表没有角位移:[1, 0] 和 [-1, 0]
数学上,实际只有一个单位四元数:[1, 0]。任意四元数 q 乘以单位四元数,结果仍是 q。若乘以另一个“几何单位”四元数 [-1, 0] 得到 -q
几何上 q 和 -q 代表的角位移相同,可以认为结果相同。但在数学上它们并不相等,所以 [-1, 0] 不是“真正”的单位四元数
10.4.6 四元数的模
记法和公式与向量的类似

来看看它的几何意义。代入 和 n

n 为单位向量,所以

应用三角公式 ,得

10.4.7 四元数共轭和逆
四元数的共轭记作 ,通过将向量部分(对应复数的虚部)变负来获得

四元数的逆记作 ,定义为四元数的共轭除以它的模

- 对于实数 a,它的逆 为 ,可知
- 四元数(本质是复数)也有同样的性质,q 乘以它的逆 ,可得到单位四元数 [1, 0]
由于我们只使用单位四元数(模为 1),所以四元数的共轭与逆是相等的
共轭的几何意义,因向量部分 v 变负,即令旋转轴反向,导致最终旋转方向与原来相反。即 q 和 代表相反的角位移
针对我们的目的而言,四元数共轭有另一种更方便的定义:w 变负,v 不变(原来是 w 不变,v 变负)。这是直接使旋转角变负,而旋转轴不变
10.4.8 四元数乘法(叉乘)
根据 10.4.2 节中的复数解释来相乘

这导出了四元数乘法的标准定义,下面以两种四元数记法给出

本书将使用一种稍微不同的形式,后面会给出它的定义和意义
四元数叉乘满足结合律,但不满足交换律

现在看看两个四元数叉乘的模

最后应用四元数模的定义得到公式

因此,四元数乘积的模等于模的乘积。这个结论非常重要,因为它保证了两个单位四元数相乘的结果还是单位四元数。
四元数乘积的逆等于各个四元数的逆以相反的顺序相乘

“扩展”一个标准 3D 点 (x, y, z) 到四元数空间,得到 p=[0, (x, y, z)]。设 q 为旋转四元数 [cos(), nsin()] (n 为旋转单位轴); 为旋转角。我们惊奇地发现,执行下面的乘法可以使 3D 点 p 绕 n 旋转
四元数乘法的优势体现在哪?我们看多次旋转的情况。将点 p 用一个四元数 a 旋转然后再用另一个四元数 b 旋转

注意,先进行 a 旋转再进行 b 旋转等价于执行乘积 ba 代表的单一旋转。因此,四元数乘法能用来连接多次旋转,这和矩阵乘法的效果一样。非常不幸,这个旋转是以从右向左的顺序发生的
为了解决“顺序颠倒”的问题,本书将违背标准定义,以相反运算顺序来定义四元数乘法(仅仅向量叉乘部分受影响)

原来的是

根据我们的定义,将四元数放在向量右边,而把它的逆放在向量的左边
能看到下面这个表达了多个旋转连接的等式,它是自左向右的,与旋转发生的顺序一致

对于我们来说,让四元数代表角位移的“高级”能力,使其易于使用,这比坚持正式标准更加重要
10.4.9 四元数“差”
利用四元数的乘法和逆,就能计算两个四元数的“差”——一个方位到另一个方位的角位移。换句话说,给定方位 a 和 b,能够计算从 a 旋转到 b 的角位移 d。用四元数等式表示为
现在来求 d,两边同时左乘
应用结合律,化简得

现在我们有了求得代表一个方位到另一个方位角位移的四元数方法
10.4.10 四元数点乘
记法、定义和向量点乘非常类似

和向量点乘一样,其结果是个标量。对于单位四元数 a 和 b,有 。通常我们只关心 的绝对值,因为 ,所以 b 和 -b 代表相同的角位移
四元数点乘的几何解释类似于向量点乘的几何解释。四元数点乘 a·b 的绝对值越大,a 和 b 代表的角位移越“相似”。
10.4.11 四元数的对数、指数和标量乘运算
尽管很少使用它们,但它们是某些重要四元数运算的基础
10.4.12 四元数求幂
四元数作为底数,记作 。当 t 从 0 变到 1 时, 从 1 到 a。四元数求幂有类似的结论:当 t 从 0 变到 1 , 从 [1, 0] 到 q
这对四元数求幂非常有用,因它可以从角位移中抽取“一部分”。例如 代表 1/3 这个角位移; 代表两倍角位移; 代表1/3反方向这个角位移
注意四元数表达角位移使用最短圆弧,不能“绕圈”。设 q 是绕 x 轴顺时针旋转 60 度, 并不是预期的绕 x 轴顺时针旋转 240 度,而是逆时针 80 度
10.4.13 四元数插值——"slerp"
它可以在两个四元数间平滑插值,它避免了欧拉角插值的所有问题
10.4.14 四元数样条——"squad"
当有多于两个的方位序列(它描述了我们想要经过的插值“路径”),我们可以在“控制点”间使用 slerp
squard 就是用来描绘控制点间的路径
10.4.15 四元数的优点和缺点
其他角位移表示方法没有的优点

缺点

10.5 各方法比较

一些建议:

10.6 表达形式之间的转换
- 欧拉角和矩阵之间的互相转换
- 四元数和矩阵之间的互相转换
- 欧拉角和四元数之间的互相转换