最近在做的东西涉及到四元数的知识,做的过程中发现四元数有一些很容易让人迷惑的点,现写这篇文章备忘。
四元数作为对象的朝向(Orientation)以及需要做朝向之间的插值时
一个3D的对象一般有这些基本几何属性:
空间位置。用一个vector3d即可表示
缩放系数。同样也可以用一个vector3d表示,每个分量代表物体在每一个正交基的缩放系数
朝向。
朝向,这是个看名字很容易知道含义的属性,但是用数学语言描述的话却有很多种方式,不像"位置"、"缩放"那么容易就可以确定用一个vector3d来实现。
基于笛卡尔3D坐标系,普遍在用的朝向表示方法有这么几种:
欧拉角
四元数
这3个随便一个都是蛮复杂的东西(不是学3D的应该都不知道这3个吧)。首先比较下3种方法的数据存储效率:
欧拉角,需要3个分量( 前进方向(heading)、海拔(elevation)、 倾斜(bank) 或 偏航(yaw), 俯仰(pitch)、 翻滚(roll))。
旋转矩阵。4 x 4 = 16个分量(不考虑压缩)
四元数。4个分量。
显然从数据存储上看,欧拉角最有优势,但问题是,欧拉角(or旋转矩阵)是有万向节锁问题的。实际上,只要是用三个旋转角来表示朝向的变化就会出现万向节锁问题,即使存储朝向时用了四元数。这是因为欧拉角和四元数可以等价转换,朝向变换计算时用欧拉角,只是存储的时候转换成四元数,这样四元数并没有什么卵用。
考虑这一点,四元数直接表示朝向以及朝向之间的插值的优势就凸现出来了,比欧拉角多一个分量,但能处理好万向节锁问题。为什么会这样呢?这其中的神奇之处就在于四元数的4个分量的含义和欧拉角的三个分量的含义差别很大,我举例说明下:
欧拉角(pitch yaw roll) <—> 四元数(第一个值是虚部,后面三个是实部)
0.0, 0.0, 0.0 <—> 1.000000, 0.000000, 0.000000, 0.000000
90.0, 0.0, 0.0 <—> 0.707107, 0.707107, 0.000000, 0.000000
0.0, 90.0, 0.0 <—> 0.707107, 0.000000, 0.707107, 0.000000
0.0, 0.0, 90.0 <—> 0.707107, 0.000000, 0.000000, 0.707107
可以看到,用四元数表示的朝向,只看数值是无法直观知道这个四元数是代表什么朝向的。所以不能以轴-旋转角度的概念去看待朝向四元数,完全不是同一回事。
总结一下:
明确你的程序里的欧拉角和四元数的对应关系后,做一个欧拉角到四元数的转换接口。初始化对象朝向时,先以欧拉角的思考角度确定欧拉角的三个值,然后用这个接口把欧拉角转成四元数,就可以用四元数来做朝向数据的存储(多消耗一个float)。
使用四元数朝向数据时,不能转换回欧拉角来使用(如果你这样就等同于只在存储时应用四元数),而是应该继续以四元数的思考角度来做各种操作,如做朝向的插值。(四元数SLERP)。
四元数作为旋转变换参数时
这种情景下,朝向是否用四元数存储不再重要。只是在做旋转这个事情时,使用了四元数。这个四元数也被称为旋转四元数。旋转四元数的构造需要2个东西:笛卡尔坐标系下的旋转轴\( \hat { \mathbf v } \)(Vector3d)和绕着这个旋转轴的旋转角度\( \theta \)(Radian):
\[ q = [cos\frac {1}{2}\theta ,sin\frac {1}{2}\theta \mathbf { \hat { \mathbf v } }] \]
构造了旋转四元数后,剩下的就是怎么使用的问题。假设现在要对一个3D坐标点\( \vec p \)做\( \mathbf q \)旋转,那么变换公式如下:
\[ \mathbf p' = \mathbf q \mathbf p \mathbf q^{-1} \]
其中的\( \mathbf p \)四元数的虚部为0,实部为\( \vec p \)向量。
(详细的公式细节可以查阅Understanding Quaternions 中文翻译《理解四元数》)
写作不易,您的支持是我写作的动力!