依旧是前言
我在前一篇欧拉角的介绍里介绍过欧拉角,这是一种很直观的旋转表示。直观并没有什么不对的地方,但是我们人认为的直观,和机器认为的直观是有区别的…
欧拉角,这个旋转的表达方式从欧拉提出开始沿用至今,依然有顽强的生命力。现在的几种流行的 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θ)
这就是我们说的作用于垂直分量的四元数 q
再进一步,我们把旋转写成一个很好看的形式:v=u∥+qu⊥=pp−1u∥+ppu⊥
因为 p是单位四元数,所以 p − 1 = p ⋆
(不理解的话可以参考二元复数的性质)注意 p 的矢量部分,是和转轴平行的,那么把下面的引理一说,大家就明白我要做什么了:
四元数运算的 Python 实现
四元数这个东西说年轻也不算年轻,那么是不是有相关的库呢?我在 GitHub 上面翻了翻,还真有,而且已经有 300+ stars 了。
具体来说,这个库是为 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 的四元数与三维旋转一文。文章讲解十分详实,推荐阅读。
就到这里啦,有什么问题或发现什么错误可以留言或者私信呀。
祝福每一个在努力学习的人 : )
评论(0)
您还未登录,请登录后发表或查看评论