zoukankan      html  css  js  c++  java
  • 理解四元数

     

    Understanding Quaternions

     转载自:http://www.qiujiawei.com/understanding-quaternions/

    正文

    在这篇文章中我会尝试用简单的方式去解释四元数的概念,即用可视化的方式解释四元数以及几种对四元数的操作。我将把矩阵、欧拉角和四元数放在一起比较,并解释什么时候该用四元数、什么时候该用欧拉角或矩阵。

     

     

     

    介绍

    在计算机图形学中,我们使用转换矩阵来表示空间中的一个位置以及朝向。一个转换矩阵还可以表示对一个目标的缩放(scale)或错切(shear)等。 我们可以把转换矩阵想象成一个空间,当你用这个矩阵乘以向量、点(甚至矩阵)后, 你就把向量、点、矩阵转换进这个空间了。

    在这篇文章中,我不会讨论转换矩阵的细节。你可以查看我前面的文章,文章中描述了转换矩阵的细节。

    在这篇文章中,我想要讨论一个可替代的方案,即用四元数来描述空间里的物体的朝向。

    四元数的概念是由爱尔兰数学家Sir William Rowan Hamilton发明的(1843年,都柏林)。Hamilton当时正和他的妻子前往爱尔兰皇家研究院,当他从Brougham桥通过皇家运河时,他领悟到了一个激动人心的东西,并立刻把它刻在桥的一个石头上:

    2.jpg

    William Rowan Hamilton Plaque on Broome Bridge on the Royal Canal commemorating his discovery of the fundamental formula for quaternion multiplication.

    复数

    在我们能够完全理解四元数之前,我们必须先知道四元数是怎么来的。四元数的根源其实是复数

    除了知名的数集(自然数、整数、实数、分数)之外,复数系统引入了一个新的数集——虚数。虚数的发明是为了解决一些特定的无解的方程,例如:

    要解决这个等式,必须让这当然是不行的,因为任意实数的平方都是非负数。

    一般而言,数学家是不能忍受一个等式是无解的。于是,一个新的术语被发明了,它就是虚数,一个可以解决上面这个等式的数。

    虚数有这样的形式:

    不要为这个术语较真,因为逻辑上这个数是不存在的。只要知道i是一个平方等于-1的东西即可。

    虚数的集合可以用来表示

    复数的集合是一个实数和一个虚数的和,形式如下:

    可以认为所有实数都是b=0的复数、所有虚数都是a=0的复数。

    复数的加减

    加法:

    减法:

    复数的系数缩放
    复数的积
    复数的平方
    共轭复数

    复数的共轭就是指把复数的虚数部分变成负的。共轭复数的符号是

    复数和它的共轭复数的乘积是:

    复数的绝对值

    我们使用共轭复数来计算复数的绝对值:

    两复数的商
    i的幂

    如果的平方等于-1,那么的n次幂也应该存在:

    如果按照这个顺序写下去,会出现这样一个模式: (1,mathbf i,-1,-mathbf i,1,...)

    一个类似的模式也出现在递增的负数幂:

    i

    你可能已经在数学里头见过类似的模式,但是是以(x,y,-x,-y,x,...)的形式,这是在2D笛卡尔平面对一个点逆时针旋转90度时生成的;(x,-y,-x,y,x,...)则是在2D笛卡尔平面对一个点顺时针旋转90度时生成的。

    3.png

    复数平面

    我们也能够把复数映射到一个2D网格平面——复数平面,只需要把实数映射到横轴、虚数映射到纵轴。

    4.png

    如前面的序列所示,我们可以认为,对一个复数乘以i,这个复数就在复数平面上旋转了90度。

    让我们看看这是不是真的。我们随机地在复数平面上取一个点:

    t刚好是开始的p。如果我们把这些复数放到复数平面上,就得到下面的图:

    5.png

    我们也可以按顺时针方向旋转,只需要把上面的乘数i改成-i。

    旋转数(Rotors)

    我们也可以在复数平面上进行任意角度的旋转,只需要定义下面这个复数:

    这也是一个在复数平面绕原点逆时针旋转任意点的方法。(译注:这句话应该是在说旋转矩阵)

    四元数

    补充:

    6.png(mathbf i mathbf j, mathbf j mathbf k, mathbf k mathbf i这几个性质的可视化)

    上图展示了如何用i、j、k作为笛卡尔坐标系的单位向量。

    四元数的加减

    四元数的积

    再把这个表达式扩展成多个有序对的和:

    如果把后3个四元数相加,并提取公共部分,就可以把等式改写成:

    这个等式是2个有序对的和。第1个有序对是一个四元数,第2个是一个四元数。这两个四元数也可以合并成一个:

    如果把下面的表达式代入上面的等式:

    (译注:注意,第三条和第四条并不是四元数的点积和叉积,而是向量的点积和叉积)

    我们就得到了:

    这就是四元数乘积的一般式。

    实四元数

    四元数的系数缩放

    纯四元数

    四元数的加法形式

    我们可以把四元数写成实四元数和纯四元数的和:

    单位四元数

    给定任意的向量v,我们可以把这个向量写成一个系数和一个单位方向向量的乘积:

    将这个定义和纯四元数的定义结合,就得到了:

    四元数的二元形式

    我们现在可以把单位四元数的定义和四元数的加法形式结合到一起,就创造了一种新的四元数的表示法,这种表示法和复数的表示法形似:

    这就给了我们一种和复数非常相似的四元数表示法:

    共轭四元数

    四元数范数

    四元数规范化

    四元数的逆

    四元数的点积

    和向量的点积相似,我们也可以计算2个四元数的点积,只需要将各个对应的系数相乘,然后相加:

    旋转

    前面我们定义了一个特殊的复数:旋转数。它是用来旋转2D复数平面的点的:

    根据四元数和复数的相似性,应该有可能设计一个可以旋转3D空间的点的四元数:

    让我们测试一下这个理论是否可靠,方法就是计算四元数q和向量p的积。第一步,我们把p写成纯四元数的形式:

    以及单位四元数q:

    我们可以看到结果是一个同时有系数、有虚向量的四元数。

    好了,现在计算下qp:

    这正是我们所期望的!

    我们可以用图像展示旋转过程:

    7.png

    现在,让我们考虑更一般化的四元数,即和p不正交的四元数。现在让我们把p的向量部分偏移45度:

    我们可以用图像展示旋转过程:

    8.png

    然而,还有一线生机。Hamilton发现(但没有正式宣布),如果对qp右乘q的逆,出来的结果是一个纯四元数,并且四元数向量部分的范数可以保持不变。让我们试试应用在我们的例子里。

    现在,把前面算出来的qp再次拿出来:

    下面的图像展示了旋转结果:

    9.png

    所以我们可以看到,这个结果是一个纯四元数,并且原四元数的向量的范数也保持住了。但是还有一个问题:向量被旋转了90度而不是45度。这刚好是我们需要的度数的两倍!为了正确地让一个向量绕某个轴向量旋转某个角度,我们必须以目标角度的一半来计算。因此,我们构造了下面的四元数:

    这就是旋转四元数的一般形式!

    四元数插值

    在计算机图形学中使用四元数,其中一个重要原因是四元数非常适合用来表示空间中的旋转。四元数解决了其他3维空间旋转算法会遇到的恼人的问题,比如使用欧拉角来表示旋转操作时会遇到的万向节锁问题(Gimbal lock)。

    使用四元数,我们可以定义好几种方案来表示3维空间的转动插值。第一种是SLERP,它被用来把一个点(物体)从一个朝向平滑地插值到另一个朝向。第二个是SLERP的扩展版本,被称为SQAD,它被用来处理用一系列朝向定义得到的一条路径的插值。

    SLERP

    四元数的差

    四元数的幂运算

    接下来的目标是干掉上面四元数的差的分数部分,方法是计算四元数的t次幂(就是上面的那个插值参数t,区间是[0,1])。

    四元数的幂运算的一般化公式是:

    (译注:上述的2次公式推导,其实省略了很多证明过程。具体可以参考:http://bpeers.com/blog/?itemid=861,http://bpeers.com/blog/?itemid=863,http://bpeers.com/blog/?itemid=866http://bpeers.com/blog/?itemid=1001 )

    对于t = 0,我们有:

    而对于t = 1,有:

    2个四元数的分数差

    对于角旋转的插值计算,我们利用q1和q2的角度分数差来调整原始朝向q1:

    这也就是使用四元数的球面线性插值的一般形式。然而,这不是slerp函数的常用形式。

    我们可以应用类似的用于计算向量的球面插值公式,到四元数里。计算向量的球面插值的一般形式定义如下:

    用图像表示如下:

    10.png

    这个公式可以原封不动地应用到四元数:

    注意事项

    这个方案有2个问题,必须在实现过程中加以考虑。

    第一,如果四元数点积的结果是负值,那么后面的插值就会在4D球面上绕远路,这并不是我们想要的。为了解决这个问题,我们测试点积的结果,当结果是负值时,我们将2个四元数的其中一个取反,取反它的系数和向量部分,并不会改变它代表的朝向。而经过这一步操作,可以保证这个旋转走的是最短路径。

    q1q1和q2q2的角度差非常小,小到导致sinθ=0sinθ=0 时,会出现第二个问题。如果这个情况出现了,当我们除以sinθsinθ时就会得到一个未定义的结果。在这个情况下,我们可以回退去使用q1q1和q2q2的线性插值。

    SQUAD

    总结

    除了特别难理解之外,相比矩阵或欧拉角,四元数在表示旋转这个事情上,拥有一些明显的优点。

    • SLERP和SQUAD,提供了一种使得在朝向之间可以平滑过渡的方法。

    • 使用四元数来串联"旋转",要比使用矩阵快得多。

    • 对于单位四元数,逆向旋转可以通过对向量部分取反来实现。而计算一个矩阵的逆矩阵是被认为比较慢的,如果这个矩阵未被标准正交化的话(标准正交矩阵的逆矩阵是它的转置矩阵)。

    • 从四元数转换到矩阵,要比从欧拉角转换到矩阵快一点。

    • 四元数只需要4个数字(如果旋转四元数已经单位化了那么只需要3个,实数部分可以在运行时计算)来表示一个旋转,而矩阵需要至少9个数字。

    尽管使用四元数有这么多优点,还是有缺点存在的。

    • 因为浮点数的舍入运算错误,四元数可能会变无效。不过,这个错误可以通过重新单位化四元数来避免。

    • 使用四元数最具威慑性的地方,还是四元数的理解难度大。我希望这个问题可以通过阅读本文来解决。

    存在一些已经实现了四元数、并且是正确的的数学程序库。在我的个人经验里,我发现GLM(OpenGL Math Library)是一个优秀的数学库,它的四元数的实现极其不错。如果你对在你的程序中使用四元数感兴趣,那么我会推荐你使用这个数学库。

    下载Demo

    我实现了一个小demo来演示一个四元数如何被用来旋转一个3维物体。这个demo是用Unity3.5.2实现的,你可以免费下载它和阅读它的脚本。zip文件也包含了一个Windows版的Unity程序。当然你可以自己构建一个Mac的版本。

    Understanding Quaternions.zip

     
  • 相关阅读:
    SQL注入原理
    攻防世界-wp
    BUUCTF-warmup
    springboot邮箱验证功能部署到服务器后报25 timeout的解决方式
    关于MySQL建立库表时大写自动转换为小写的解决方案
    springboot格式化timestamp时间
    mysql高级查询
    pip更新一直time out 的解决方法
    关于springboot使用mybatis查询出现空指针,以及debug出现All Elements all Null的解决方法
    抽象工厂模式
  • 原文地址:https://www.cnblogs.com/ne-zha/p/7302543.html
Copyright © 2011-2022 走看看