依旧是前言

我在前一篇欧拉角的介绍里介绍过欧拉角,这是一种很直观的旋转表示。直观并没有什么不对的地方,但是我们人认为的直观,和机器认为的直观是有区别的…

你懂我意思吧

欧拉角,这个旋转的表达方式从欧拉提出开始沿用至今,依然有顽强的生命力。现在的几种流行的 MOCAP 文件格式,ASF / AMC, BVH, C3D,除了最后一种存的是三维坐标,前两种的旋转全部是用欧拉角表示的。说实话,这并不是什么好事儿。都 0202 年了,欧拉角已经两百多年了,居然还在沿用。要说这个表示方法是什么很完美,很经典的方式也就算了,可欧拉角有很多严重的缺陷:比如万向节死锁的问题,会导致一个自由度完全失效。

所以,人们其实研究了一些替代品,比如轴角法表示,以及进一步的四元数表示。四元数表示起来最大的好处就是,连续。这一篇将会简单讲讲四元数的来历,以及推导旋转的四元数表示。

四元数是怎么来的

形式主义思维

感性来说,四元数就是这样的一个数:

q=a+bi+cj+dk

其中 a , b , c , d ∈ R i , j , k是三个虚数单位。

哈,这篇文章当然不会只有这些,只是这个形式其实是很重要的。

复数是怎么来的

先看两元数复数的由来。

这一下子就把数域给拓宽了。数学家们又发现i ii有很良好的周期性,于是他们把复数和平面的几何变换结合起来,诶,还挺合适的,复数在平面几何里面发挥了意想不到的作用。

两元到四元

四元数和旋三维旋的关系

向量的四元数表示

四元数基本运算

吹水吹完了,要开始硬核了。

为了方便表示,这里规定两个四元数作为我们接下来演示用的小白鼠:

根据这些,我们把两个四元数当做多项式来计算,就可以得出结果。由于结果太长了,我们换个写法:假定 q = q 1 q 2 = [ w , x , y , z ] 那么

按照直觉,四元数 q的模 ∣ ∣ q ∣ ∣,应该就是把四个成员取平方加起来再开方,也就是常说的二范数。实际上也是如此。这部分就这样,公式就不写了。是不是很无聊?这可能是本文最无聊的一个小节了 2333。

共轭 & 逆

四元数有三个虚部,怎么求共轭呢?答案很简单,就是把三个虚部都反过来。举个例子,q = ( s , u ),那么 q  的共轭为
q=(s,u)

这个定义符合在二元复数里我们对共轭的一般认知,也是就是,满足共轭的运算律。比如说:

旋转的四元数表示的来历

旋转的分解

代数小 trick

我们已经很接近最终结果了,这个结论和最终结论的差异就在于,被乘的是整个 q而不是它的分量。要做的就是找到一种运算,可以不影响平行分量,只作用于垂直分量。看看在本文开始提出的最终结果,如果把它取平方的话,会发现
p2=(cosθ,nsinθ)

这就是我们说的作用于垂直分量的四元数 

再进一步,我们把旋转写成一个很好看的形式:v=u+qu=pp−1u+ppu

因为 p是单位四元数,所以 p − 1 = p ⋆
  (不理解的话可以参考二元复数的性质)注意 p 的矢量部分,是和转轴平行的,那么把下面的引理一说,大家就明白我要做什么了:

四元数运算的 Python 实现

四元数这个东西说年轻也不算年轻,那么是不是有相关的库呢?我在 GitHub 上面翻了翻,还真有,而且已经有 300+ stars 了。

mobile/quaternion

> 链接在这 <

具体来说,这个库是为 numpy 提供了一个四元数的 dtype,而对于简单的单个四元数的运算也是完全可以胜任的。注意,如果你对运行速度有要求(或者单纯有强迫症,不想每次运行都看到 warning),最好安装一下 numba,这是一个提高 Python 运行速度的工具。

这个库的安装很简单,因为它已经加入 PyPI 了。注意这个包是基于 numpy 的所以要先安装 numpy.
pip install numpy-quaternion
有了这个工具,我们就可以轻松地应对四元数的运算了:

import numpy as np
import quaternion

q1 = np.quaternion(1, 2, 3, 4)
q2 = np.quaternion(5, 6, 7, 8)

print("q1 = ", q1)
print("\nq2 = ", q2)
print("\nq1 + q2 = ", q1 + q2) # quaternion(6, 8, 10, 12)
print("\nq2 - q1 = ", q2 - q1) # quaternion(4, 4, 4, 4)
print("\nq2 * q1 = ", q1 * q2) # quaternion(-60, 12, 30, 24)
print("\nThe conjugate of q1 is ", q1.conjugate()) # quaternion(1, -2, -3, -4)
print("\nThe Cayley norm of q1 is ", q1.norm()) # 30.0 这里输出的实际上是模的平方,要计算模的话用 q1.abs()

arr = np.array([[q1, q2]])
print("\nThere is an example of array of quaternions:\n", arr)
print("\nIts transpose times itself is:\n", arr.T * arr)
print("\nIts elementwise exponential in base e is:\n", np.exp(arr))

注意几个点:

  • numpy 的一些逐元素执行的运算,如上面例子中的 numpy.exp 在这个库中也是实现了的。
    • 这里我没有讲四元数的指数运算,不过其实很简单 w 感兴趣的可以自行了解或者在评论区留言。
  • 四元数乘法没有交换律,这一点在矩阵运算的时候也有体现。

结尾

例行总结好无聊啊,不想写,那就写一下本文的参考吧。

本文的求解思路参考了 Krasjet 四元数与三维旋转一文。文章讲解十分详实,推荐阅读。

就到这里啦,有什么问题或发现什么错误可以留言或者私信呀。

祝福每一个在努力学习的人 : )