四元数是由哈密顿在1843年爱尔兰发现的。当时他正研究扩展复数到更高的维次(复数可视为平面上的点)。他不能做到三维空间的例子,但四维则造出四元数。根据哈密顿记述,他于10月16日跟他的妻子在都柏林的皇家运河(Royal Canal)上散步时突然想到
i² = j² = k² = ijk = -1
的方程解。之后哈密顿立刻将此方程刻在附近布鲁穆桥(Brougham Bridge,现称为金雀花桥 Broom Bridge)。
不只如此,哈密顿还创造了向量的内外积。他亦把四元数描绘成一个有序的四重实数:一个标量(a)和向量(bi + cj + dk)的组合。若两个标量部为零的四元数相乘,所得的标量部便是原来的两个向量部的标量积的负值,而向量部则为向量积的值,但它们的重要性仍有待发掘。
一个四元数包含一个标量分量 和 一个3D向量分量,经常记标量分量为 w,记向量分量为 单一的 V 或者 (x, y, z):
[w,V] 或 [ w, x, y, z]
四元数扩展了复数系统,一个四元数【w,(x,y,z)】定义了复数 w + xi + yj + zk;它使用3个虚部 i, j, k 关系如下:
i^2 = j^2 = k^2 = -1
ij = k, ji = -k
jk = i, kj = -i
ki = j,ik = -j
四元数中的数和 旋转轴 和 角度的关系:
几何上,存在两个“单位”四元数,他们代表没有角位移:【1,(0,0,0)】和 【-1,(0,0,0)】;当角度是360的偶数倍时,对应【1,(0,0,0)】;当角度是360的奇数倍时,对应于【-1,(0,0,0)】;它的意义在于:
当旋转角是360的整数倍时,方位并没有改变,并且旋转轴也是无关紧要的。
数学上,实际只有一个单位四元数:【1,(0,0,0)】,任何四元数q乘以这个单位四元数的结果还是其本身q;而乘以【-1,(0,0,0)】得到的却是-q,数学上 q 和 -q 并不相等,但是在几何上,q 和 -q 代表的角位移相同。
当旋转轴为单位向量时:
四元数的共轭是通过让四元数的向量部分变负来获得:
四元数的逆定义为 四元数的共轭除以它的模:
一个四元数q 乘以 它的逆,即可得到单位四元数【1,(0,0,0)】
四元数q 与 它的共轭 q* 具有相反的角位移,因为共轭是向量取反,即颠倒了旋转的方向;当然也可理解位另一定义:w 取反,v不变
四元数相乘的公式:
四元数的叉乘满足结合律,但是不满足交换律:
四元数乘积的模等于模的乘积:
四元数乘积的逆等于各个四元数的逆以相反的顺序相乘:
一个标准的3D点(x,y,z)到四元数空间,通过定义四元数p【0,(x,y,z)】即可;设 q 为旋转四元素形式 【cos(a/2),nsin(a/2)】,n 为旋转轴,单位向量; a 为 旋转角度,下面乘法为 3D点 p 绕 n 旋转:
3D点多次旋转(先 a ,后 b),等价于执行ba 乘积代表的单一旋转:
四元数的差被定义为从一个方位到另一个方位的角位移:
ad = b
四元数的点乘和 向量的点乘类似,四元数的点乘的绝对值越大,两个四元数代表的角位移就越相似。
四元数的对数运算:
四元数的指数运算:
p = [0 an] = [0 (ax,ay,az)]
||n|| = 1
四元数求幂可以从角位移抽取“一部分”,如:四元数q代表一个角位移,想要得到1/3 的这个角位移的四元数,可以这样计算:
q^(1/3)
四元数表达角位移时使用最短圆弧,不能绕圈;比如q为绕x轴顺时针旋转60°,那么q^4 并不是绕x轴顺时针旋转240°,而是逆时针旋转120°。
四元数求幂:
//四元数求幂代码块
float w,x,y,z;
float exponent;
if(fabs(w) > 0.9999f){
float alpha = acos(w);//alpha = theta *0.5
float newAlpha = alpha * ecponent;
w = cos(newAlpha);
//这里的思想是原始的 x = n sin(alpha) ,利用 n 旋转轴是保持不变的,来求新的x,y,z
float mult = sin(newalpha) / sin(alpha);
x *= mult;
y *= mult;
z *= mult;
}
slerp的基本思想是沿着4D球面上连接两个四元数的弧插值;可以把这种思想表现在平面上,如下第一张图中显示,两个2D向量 v0 ,v1 都是单位向量,w为两个向量弧所截的角,t为插值参数,在0~1之间变化;所以 vt可以表示为图2显示的线性组合:
由于v0、v1是单位向量,利用三角公式可以计算出:
k0 = sin((1-t)w ) / sinw
k1 = sintw / sin w
Vt 可以表示为:
将同样的思想扩展到四元数,可得到四元数的slerp插值公式:
四元数q 和 -q 代表相同的方位,但他们作为slerp的参数时可能导致不一样的结果,这是因为4D球面不是欧氏空间的直接扩展,解决方法是选择q0 和 q1的符号使得点乘的结果非负 ?
如果 q0 和 q1 非常接近,sinw的 值非常小,这时候除法会出现问题,为了避免这个问题,当sinw很小时,才用线性插值。
//四元数 slerp 实现的代码段
float w0,x0,y0,z0;
float w1,x1,y1,z1;
float t;
float w,x,y,z;
//用点乘计算两个四元数夹角
float cosOmega = w0*w1 + x0*x1 + y0*y1 + z0*z1;
//如果为负,需要反转一个四元数
if(cosOmega < 0.0f)
{
w1 = -w1;
x1 = -x1;
y1 = -y1;
z1 = -z1;
cosOmega = -cosOmega;
}
float k0,k1;
//当sinOmega 很小的时候,做线性插值
if(cosOmega > 0.9999f)
{
k0 = 1.f - t;
k1 = t;
}else
{
float sinOmega = sqrt(1.f - cosOmega*cosOmega);
float omega = atan2(sinOmega,cosOmega);
float oneOverSinOmega = 1.f / sinOmega;
k0 = sin( (1.f - t)* omega) * oneOverSinOmega;
k1 = sin(t*omega) * oneOverSinOmega;
}
w = w0 * k0 + w1 * k1;
x = x0 * k0 + x1 * k1;
y = y0 * k0 + y1 * k1;
z = z0 * k0 + z1 * k1;