复数
欧拉公式: \[\begin{equation} \begin{aligned} z&=x+y\\&=rcos{\theta}+rsin{\theta}i\\&=re^{i{\theta}} \end{aligned} \end{equation}\] 单位复数乘法可以达到一个二维旋转的效果。
四元数
四元数的定义
从复数推导四元数: \[\begin{equation} \begin{aligned} e^{\textbf{i}\cdot\hat{\textbf{n}}\frac{\theta}{2}}&=cos{\frac{\theta}{2}}+\textbf{i}\cdot\hat{\textbf{n}}sin{\frac{\theta}{2}}\\ &=q_w+\textbf{i}\cdot\textbf{q}_v\\ &=q_w+q_xi+q_yj+q_zk \end{aligned} \end{equation}\] 将复数的一个虚部换成三个虚部,\(\textbf{i} = (i,j,k)\),且两两相交。\(\hat{\textbf{n}}\)为三维的单位向量,这便是四元数的常规表达形式,定义为: \[\textbf{q}=q_w+q_xi+q_yj+q_zk\]一个四元数有一个实部和三个虚部,三个虚部之间满足如下关系。 \[\left\{\begin{matrix} i^{2}=j^{2}=k^{2}=-1\\ ij=-ji=k\\ jk=-kj=i\\ ki=-ik=j \end{matrix}\right.\] 或者也用一个标量和一个向量来表达四元数: \[\mathbf{q} = \left[ q_w, \mathbf{q}_v \right], \quad q_w \in \mathbb{R}, \mathbf{q}_v = [q_x, q_y, q_z] \in \mathbb{R}^3.\] 这里,标量\(\mathbf{q}_w\)称为四元数的实部,而向量\(\mathbf{q}_v\)称为它的虚部。如果一个四元数虚部为0,称之为实四元数。反之,若它的实部为0,称之为虚四元数(纯四元数),是四维空间在\(q_w=0\)时的一个子空间的点,形式为\(\{0, \textbf{q}_v\}\)。该定义和复数是相似的。
四元数的理解
矩阵及旋转
矩阵表示的是一个空间向另一个空间转换的变换关系。三维空间的旋转变换由旋转后的空间在世界空间的三个基来表示。假如某个点绕Z轴旋转α角,也就是说旋转后的Z坐标是不变的,变化的只是X、Y坐标,可以写出下面这个式子: \[\begin{pmatrix} x^{'}\\ y^{'}\\ z \end{pmatrix}=\begin{pmatrix} cos\alpha & -sin\alpha & 0\\ sin\alpha & cos\alpha & 0\\ 0 & 0 & 1 \end{pmatrix}\begin{pmatrix} x\\ y\\ z \end{pmatrix}\] 这个式子中的系数矩阵可以记为如下形式: \[R_{Z}(\alpha)=\begin{pmatrix} cos\alpha & -sin\alpha & 0\\ sin\alpha & cos\alpha & 0\\ 0 & 0 & 1 \end{pmatrix}\] 将\(R_Z\)称为旋转矩阵。同样可推出:
\[\begin{matrix} R_{X}(\alpha)=\begin{pmatrix} 1 & 0 & 0\\ 0 & cos\alpha & -sin\alpha\\ 0 & sin\alpha & cos\alpha \end{pmatrix}\\ R_{Y}(\alpha)=\begin{pmatrix} cos\alpha & 0 & sin\alpha\\ 0 & 1 & 0\\ -sin\alpha & 0 & cos\alpha \end{pmatrix}\end{matrix}\] 矩阵旋转使用了一个4*4大小的矩阵来表示绕任意轴旋转的变换矩阵,而欧拉选择则是按照一定的坐标轴顺序(例如先x、再y、最后z),每个轴旋转一定角度来变换坐标或向量,它实际上是一系列坐标轴旋转的组合,比如: \[R = R_{Z}(\alpha)R_{Y}(\beta)R_{X}(\gamma)=\begin{pmatrix} cos\alpha cos\beta & cos\alpha sin\beta sin\gamma - sin\alpha cos\gamma & cos\alpha sin\beta cos\gamma + sin\alpha sin\gamma\\ sin\alpha cos\beta & cos\alpha cos\gamma + sin\alpha sin\beta sin\gamma & sin\alpha sin\beta cos\gamma - sin\gamma cos\alpha\\ -sin\beta & cos\beta sin\gamma & cos\beta cos\gamma \end{pmatrix}\]
OpenGL中如果已经通过鼠标或者键盘得到了 yaw、pitch 和 roll 的值,就可以通过类似下面的方法计算得到 view 矩阵。 1
2
3
4
5
6
7
8
9
10
11
12
13glm::mat4 CalculateView(float yaw, float pitch, float roll, glm::vec3 eye_pos)
{
glm::mat4 matRoll = glm::rotate(matRoll, roll, glm::vec3(0.0f, 0.0f, 1.0f));
glm::mat4 matPitch = glm::rotate(matPitch, pitch, glm::vec3(1.0f, 0.0f, 0.0f));
glm::mat4 matYaw = glm::rotate(matYaw, yaw, glm::vec3(0.0f, 1.0f, 0.0f));
// 顺序是非常重要的
glm::mat4 rotate = matYaw * mattRoll * matPitch;
glm::mat4 translate = glm::translate(translate, -eye_pos);
viewMatrix = rotate * translate;
}
旋转到一定程度,某一个轴可以被其他两个轴线性表示,那么就缺失了一个维度,产生了万向节死锁。只要使用矩阵来表示旋转,就有发生万向节死锁的风险。
用四元数表示旋转
一个四元数可以和一个矩阵旋转对应。使用四元数来表示旋转有多方面的原因:
- 解决万向节死锁问题
- 更少的存储空间(4 floats vs 16 floats)
- 绕任意轴旋转非常方便,而旋转矩阵实现非常复杂
- 方便追踪旋转
- 旋转结合时计算量较少,无论是求逆、串联等操作,相比矩阵更加高效
- 方便平滑插值,而旋转矩阵的实现方法不能保证绝对平滑
还有一种说法是解决向量乘法,向量之间乘法有内积和外积,但这两个运算均不完美,即不满足群的条件(当然四元数诞生的时候也还没有内积外积的说法)。那向量之间是否存在这样一个非常完美的乘法,于是三维空间无法解决的问题就映射到四维空间。这便是四元数诞生的契机。四元数并不是生来为了解决三维旋转,而是它的性质非常有利于表达旋转信息。
基础概念:
空间和子空间的映射 我们将二维空间表示为(x,y),当y=0时,其实可以看成是一维的,只不过它表示成(x,0)这种形式。推到四维,(w,x,y,z),当w=0时,(0,x,y,z)就是一个三维子空间,这也是为什么我们可以用单位四元数对三维向量进行操作,其实我们是将三维向量映射到四维的三维子空间(w=0,这种形式也称纯四元数),然后对其进行旋转,最终得到的向量结果依然是这个三维子空间中的,因而可以映射回三维空间。
广义球 这里的球是广义上的。我们在二维平面上,广义球其实指代circle,三维空间上就是我们认知上的球,称为two-sphere,而四维空间中广义球其实是一个超球(hyper-sphere),又称为three-sphere。单位向量其实就是广义球上面的点,而单位四元数也就是three-sphere上面的点。
约束与特征向量 空间中的一点由x, y, z等参数来表示,一般来说参数的数量与维数相等,二维空间的点用{x, y}参数,四维空间的点用{x, y, z, w}参数。但是对于空间的点加以约束,则会减少参数的数量,比如三维空间的点在某一单位球面上,原本三个参数{x, y, z}才能表达的点现在只需要两个参数{u, v}就可以表达。如果{u, v}是单位向量,也可以称{u, v}是{x, y, z}的特征向量。
空间映射理解 \(x^{2}+y^{2}+z^{2}+w^{2}=1\)中取x、y和z来表示超球。四维空间投影到三维超平面(w=0)可能是一个two-sphere。当投影点在整个two-sphere的边缘时,w一定为0,值得一提的是在这个空间内的四元数是一个纯四元数。当投影点落在two-sphere的内部时,也分为两种情况,w>0和w<0。但是可以发现这两种情况下对应的特征向量是一样的,所以将旋转矩阵向四元数转换时,是有两个对应值的,四元数的范围是2倍覆盖于3D旋转(2:1 mapping)。
四元数作为四维空间中一个超球上面的点,主要用于描述3D旋转。在复数域\(\mathbb{C}\),可以用一个复数\(e^{i\theta}\)表示2D的旋转,类似的,3D空间也可以用单位四元数表示旋转。假设某个旋转是绕单位向量\(\mathbf{\hat{n}}=[n_x,n_y,n_z]^T\)进行了角度为θ的旋转,那么这个旋转的四元数形式为: \[\begin{equation} \mathbf{q} = \left[\cos\frac{\theta}{2},n_x\sin\frac{\theta}{2}, n_y\sin\frac{\theta}{2}, n_z\sin\frac{\theta}{2}\right]^T \end{equation}\] 这是一个模长为1的单位四元数,实部是\(cos\frac{θ}{2}\),虚部有3个,两两互相正交,为一个单位轴乘以\(sin\frac{θ}{2}\)。若四元数长度不为1,可以通过归一化转换为模长为1的四元数。式中的θ加上2π,得到一个相同的旋转,但此时对应的四元数变成了−q。因此在四元数中,任意的旋转都可以由两个互为相反数的四元数表示。同理,取θ为0,则得到一个没有任何旋转的四元数:\(\begin{equation}\mathbf{q}_0=\left[{\pm1,0,0,0}\right]^T\end{equation}\)。 齐次形式\((w,x,y,z)\)的四元数满足\(x^{2}+y^{2}+z^{2}+w^{2}=1\),有 \[\left\{\begin{matrix} x = n_{x}sin\frac{\theta}{2}\\ y = n_{y}sin\frac{\theta}{2}\\ z = n_{z}sin\frac{\theta}{2}\\ w = cos\frac{\theta}{2} \end{matrix}\right.\] 由于存在\(x^{2}+y^{2}+z^{2}+w^{2}=1\)这个约束,四元数的自由度其实只有3,且每个四元数可以对应一个特征向量,即\(\hat{\textbf{n}}\)。四元数并不是与特征向量一一对应的。 假设有一个空间三维点\(\mathbf{v} = [x,y,z]\in \mathbb{R}^3\),以及一个由旋转轴和夹角\(\mathbf{n},\theta\) 指定的旋转。用一个虚四元数来描述该空间三维点:\(\mathbf{p} = [0, x, y, z] = [0, \mathbf{v}]\)。然后用另一个四元数表示这个旋转:\(\mathbf{q}=[\cos \frac{\theta}{2}, \mathbf{n} \sin \frac{\theta}{2} ]\),那么旋转后的点\(\mathbf{p}'\)即可表示为这样的乘积: \[\begin{equation} \mathbf{p}' = \mathbf{q} \mathbf{p} \mathbf{q}^{-1} \end{equation}\] 可以验证,计算结果的实部为\(\mathbf{n}^T(\mathbf{n} \times \mathbf{v})=0\),故计算结果为纯虚四元数。其虚部的三个分量表示旋转后3D点的坐标。
四元数的运算
四元数和通常复数一样,可以进行一系列的运算。常见的有四则运算、内积、求逆、共轭、求指数/对数等等。表示姿态时,它还可以进行插值。 现有两个四元数\(\mathbf{q}_a,\mathbf{q}_b\),它们的向量表示为\([s_a, \mathbf{v}_a], [s_b, \mathbf{v}_b]\),或者原始四元数表示为\(s_a+x_ai+y_aj+z_ak, s_b+x_bi+y_bj+z_bk.\),则有:
- 加减法 \[\begin{equation} \mathbf{q}_a \pm \mathbf{q}_b = \left[ s_a \pm s_b, \mathbf{v}_a \pm \mathbf{v}_b \right]\end{equation}\]
- 乘法 $$\begin{equation} \begin{array}{lll} \mathbf{q}_a \mathbf{q}_b &=& {s_a}{s_b} - {x_a}{x_b} - {y_a}{y_b} - {z_a}{z_b}\\ &&+ \left( {{s_a}{x_b} + {x_a}{s_b} + {y_a}{z_b} - {z_a}{y_b}} \right)i\\ &&+ \left( {{s_a}{y_b} - {x_a}{z_b} + {y_a}{s_b} + {z_a}{b_b}} \right)j\\ &&+ \left( {{s_a}{z_b} + {x_a}{y_b} - {x_b}{y_a} + {z_a}{s_b}} \right)k \end{array} \end{equation}$$ 写成向量形式并利用内外积运算表达: \[\begin{equation} \mathbf{q}_a \mathbf{q}_b = \left[ s_a s_b - \mathbf{v}_a \cdot \mathbf{v}_b, s_a\mathbf{v}_b + s_b\mathbf{v}_a + \mathbf{v}_a \times \mathbf{v}_b \right] \end{equation}\] 在该乘法定义下,两个实的四元数乘积仍是实的,这与复数也是一致的。然而,注意到,由于最后一项外积的存在,该乘法通常是不可交换的,除非\(\mathbf{v}_a\)和\(\mathbf{v}_b\)在\(\mathbb{R}^3\)中共线。
- 模长 四元数的模长定义为: \[\begin{equation} \| \mathbf{q}_a \| = \sqrt{ s_a^2 + x_a^2 + y_a^2 + z_a^2 } = \sqrt{\mathbf{q}_a^{*T} \mathbf{q}_a} \end{equation}\] 可以验证,两个四元数乘积的模即为模的乘积。这保证单位四元数相乘后仍是单位四元数。 \[\begin{equation} \| \mathbf{q}_a \mathbf{q}_b \| = \|\mathbf{q}_a \| \| \mathbf{q}_b \| \end{equation}\]
- 逆 \[\begin{equation} \mathbf{q}^{-1} = \mathbf{q}^* / \| \mathbf{q} \| ^2 \end{equation}\] 按此定义,四元数和自己的逆的乘积为实四元数的1: \[\begin{equation} \mathbf{q} \mathbf{q}^{-1} = \mathbf{q}^{-1} \mathbf{q} = 1 \end{equation}\] 同时,乘积的逆有和矩阵相似的性质: \[\begin{equation} \left( \mathbf{q}_a \mathbf{q}_b \right)^{-1} = \mathbf{q}_b^{-1} \mathbf{q}_a^{-1} \end{equation}\] 对于单位四元数,即\(\|\mathbf{q}\|=1\),它的逆即是它的共轭四元数。
- 数乘与点乘 和向量相似,四元数可以与数相乘: \[\begin{equation} k \mathbf{q} = \left[ ks, k\mathbf{v} \right] \end{equation}\] 点乘是指两个四元数每个位置上的数值分别相乘: \[\begin{equation} \mathbf{q}_a \cdot \mathbf{q}_b = s_a s_b + x_a x_b i + y_a y_b j + z_a z_b k \end{equation}\]
四元数到旋转矩阵的转换
由于任意单位四元数都可表示为一个3D旋转,即SO(3)中的元素,我们可以找到一个旋转矩阵与之对应。最简单的方式是由四元数q解出旋转角θ和旋转轴n,但那样要计算一个arccos函数,代价较大。实际上这个计算是可以通过一定的计算技巧绕过的。为省略篇幅,我们直接给出四元数到旋转矩阵的转换方式。 \[\begin{equation} \mathbf{R} = \left[ {\begin{array}{*{20}{c}} {1 - 2q_2^2 - 2q_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 - 2q_1^2 - 2q_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 - 2q_1^2 - 2q_2^2} \end{array}} \right] \end{equation}\] 反之,由旋转矩阵到四元数的转换如下。假设矩阵为\(\mathbf{R}=\{ m_{ij}\}, i, j \in \left[ 1, 2,3 \right]\),其对应的四元数q由下式给出: $$\begin{equation} {q_0} = \frac{{\sqrt {tr(R) + 1} }}{2},{q_1} = \frac{{{m_{23}} - {m_{32}}}}{{4{q_0}}},{q_2} = \frac{{{m_{31}} - {m_{13}}}}{{4{q_0}}},{q_3} = \frac{{{m_{12}} - {m_{21}}}}{{4{q_0}}} \end{equation}$$ 其中tr(R)表示R矩阵的迹,也即矩阵R的主对角线(从左上方至右下方的对角线)上各个元素的总和。由于q和−q表示同一个旋转,事实上一个R的四元数表示并不是惟一的。存在其他三种与上式类似的计算方式,而本书省略了。实际编程中,当q0接近0时,其余三个分量会非常大,导致解不稳定,此时会考虑使用剩下的几种方式计算。