四元数
那么如何将复数从2D扩展到3D中呢,答案是引入三个虚部,分别用i
、j
、k
表示,并且满足
进而可知
放到表格中有
那么一个四元数定义了3D中的复数
可以记作
也可以简化记作
同样的,3D中的复数(即四元数)也满足2D中复数的运算规则,而且也支持旋转3D向量旋转
负四元数
将实部和虚部都取复数即可
3D中任意角位移都有两个不同的四元数表示,而且它们互相为负
加法和减法
四元数的加减法与复数计算规则相同,只需要将对应位置的值相加减即可
例如加法:
简化为
单位四元数
如果一个四元数的虚部都为0,那么称这个四元数为单位四元数
同样的,任意四元数乘以单位四元数,结果仍是原四元数
共轭和逆
按照共轭复数的形式,定义四元数的共轭时同样将虚部转换符号,并用q*表示
对于四元数q = w + xi + yj + zk
,它的共轭四元数为
同样的按照共轭复数模长的定义,有
四元数的逆用q-1表示,将共轭除以模得到
四元数的逆和实数的倒数存在有趣的对应关系,对于实数a,有
对于四元数而言,也存在
四元数与四元数的逆相乘得到单位四元数[1,0]
INFO
虽然四元数不满足交换律,而共轭四元数与四元数的乘积是满足交换律
的
对于单位四元数
而言,由于它的模长为1
,则有
模
仿照复数的定义,我们可以将四元数q = w + xi + yj + zk
的模长定义为
很难在几何上描述四元数的模长,因为它是在四维空间描述三维空间,只需要理解定义即可
纯四元数
如果一个四元数只有虚部,实部为0,那么称这种形式的四元数为纯四元数
因此任意形式的3D向量都可以转换为纯四元数,对于任意3D向量v
,都有纯四元数
对于两个纯四元数q1、q2,它们的乘积为
乘法
与标量相乘
与复数相同,四元数与标量的乘积等于标量与四元数每部分都相乘和
也可以简化为
四元数乘法(叉乘)
分为两种,分别是点乘、叉乘
计算规则与复数乘法相同,但是复数乘法遵循交换律。不用为四元数乘法使用乘号
- 四元数相乘不遵循交换律
- 四元数相乘遵循结合律
例如:q1左乘q2结果为
我们可以将结果进一步转化为矩阵形式
其实四元数相乘的本质也是一个线性转换
接下来我们考虑虚部向量形式的点乘和叉乘,则有
- 点乘
- 叉乘
我们将q1左乘q2结果再变换下
观察后可知,存在以下等式
这个等式又叫Graβmann Product积
点乘
四元数的点乘计算规则与向量相同,得到的结果是一个标量,对应位置乘积之和
四元数点乘的几何意义与向量点乘的几何意义相似,表示四元数与另一个四元数的"相似"
程度
对数运算
四元数对数运算的结果也是一个四元数
对于四元数q
,有
对应的单位四元数有
INFO
证明需要用到欧拉公式
幂次运算
四元数可以作为底数,记作qt,类似实数求幂
当t从0到1时,q从1到q;当t从1到0时,q从q到1
类似的,对应四元数,当t从0到1时,q从[1, 0]到[q, 0];当t从1到0时,q从[q, 0]到[1, 0]
例如,如果一个四元数q表示一个角位移,那么q1/3表示这个角位移的1/3部分
相应的,如果四元数q代表绕旋转轴顺时针旋转θ角度,则q2表示2θ角度的顺时针旋转,q-1表示θ角度的逆时针旋转
一般来说,凡事涉及到指数运算的代数公式,如
在四元数的幂次运算中都是不适用的
const power = function (w, x, y, z, time) {
const alpha = Math.acos(w);
const newAlpha = alpha * time;
const mult = Math.sin(newAlpha) / Math.sin(alpha);
return [Math.cos(newAlpha), x * mult, y * mult, z * mult];
};
与3D旋转
3D中的任意角位移都可以通过绕任意轴的单一旋转来完成,轴-角对表示为(n,θ)
实际上,轴-角对是表示旋转的第四种方式,但是在描述轴-角对时一般被欧拉角和四元数替代了
向量形式
任意向量v
绕单位向量n
旋转θ
,用四元数表示为
旋转后的向量
可以使用四元数乘法来获得,令
则转换后后的向量v'(证明参考3.2章节)为
则有
矩阵形式
指数形式
获取旋转角度θ
、旋转轴
如果有一个四元数q = [w, v]
表示的旋转,那么它对应的旋转角度为
对应的旋转轴为
旋转的复合
如果有两个绕着不同旋转轴,不同旋转角度的四元数q1、q2,向量为v,最终旋转向量为v',则有
推广到n个旋转,则有
插值
在两个变换之间插入变换,可以让变换更加平滑
slerp
可以在两个四元数之间平滑插值,而且它避免了欧拉角插值的所有问题
开始和结束的四元数分别是q0、q1,插值参数设为变量t,t在0和1之间变化,表示为slerp(q0,q0,t)
这是理论上的表达式,但是实践中有更有效的计算方式
w为q0、q1的夹角,有
let w0, x0, y0, z0; // 四元数1
let w1, x1, y1, z1; // 四元数2
let t; // 插值变量
// 首先用点乘判断这两个四元数之间的夹角是否为钝角
let cosOmega = w0 * w1 + x0 * x1 + y0 * y1 + z0 * z1;
if (cosOmega < 0) {
// 为钝角时则将其中一个转换为负值
w0 = -w0;
x0 = -x0;
y0 = -y0;
z0 = -z0;
cosOmega = -cosOmega;
}
let k0, k1;
// 如果两个四元数过于接近,则判定为为线性插值
if (cosOmega > 0.9999) {
k0 = 1 - t;
k1 = t;
} else {
const sinOmega = Math.sqrt(1 - cosOmega ** 2);
const omega = Math.atan2(sinOmega, cosOmega);
const oneOverSinOmega = 1 / sinOmega;
k0 = Math.sin((1 - t) * omega) * oneOverSinOmega;
k1 = sin(t * omega) * oneOverSinOmega;
}
// 插值
w = w0 * k0 + w1 * k1;
x = x0 * k0 + x1 * k1;
t = y0 * k0 + y1 * k1;
z = z0 * k0 + z1 * k1;
squad
slerp提供了两个方位之间的插值,当多于两个方位的插值时需要使用squad
优缺点
优点
- 可以平滑插值,另外两种方式都无法插值
- 快速连接和角位移求逆
- 四元数叉乘可以快速的将转换序列组合称一个转换,而矩阵效率低一些
- 共轭四元数提供了反角位移更加简便的方式,矩阵的转置矩阵也可以快速计算反角位移下的旋转,但是没有四元数快
- 可以和矩阵形式快速转换,四元数与矩阵形式的相互转换高于欧拉角与矩阵的相互转换效率
- 占用空间低,仅用四个数
- 欧拉角需要3个数(θ,γ,β),四元数需要4个数(w,x,y,z),矩阵需要9个数
缺点
- 四元数可能不合法,由于精度丢失、坏数据点等问题导致四元数不可用,但是可以通过四元数标准化解决
- 难于使用,相对于矩阵、欧拉角,四元数相对编码要求高
参考:
【2】四元数与空间旋转
【3】四元数与三维旋转
【4】四元数的可视化
【5】如何形象地理解四元数?
【6】四元数速查手册
【7】欧拉公式