Skip to content

四元数

那么如何将复数从2D扩展到3D中呢,答案是引入三个虚部,分别用ijk表示,并且满足

i2=j2=k2=ijk=1

进而可知

img.png

放到表格中有

img.png

那么一个四元数定义了3D中的复数

w+xi+yj+zk

可以记作

[w,x,y,z]

也可以简化记作

[w,v]

同样的,3D中的复数(即四元数)也满足2D中复数的运算规则,而且也支持旋转3D向量旋转

负四元数

将实部和虚部都取复数即可

q=[wv]=[wxyz]

3D中任意角位移都有两个不同的四元数表示,而且它们互相为负

加法和减法

四元数的加减法与复数计算规则相同,只需要将对应位置的值相加减即可

例如加法:

q1+q1=w1+x1i+y1j+z1k+(w2+x2i+y2j+z2k)=w1+w2+(x1+x2)i+(y1+y2)j+(z1+z2)k

简化为

q1+q1=[w1+w2,v1+v2]

单位四元数

如果一个四元数的虚部都为0,那么称这个四元数为单位四元数

q=1+0x+0y+0z=1

同样的,任意四元数乘以单位四元数,结果仍是原四元数

共轭和逆

按照共轭复数的形式,定义四元数的共轭时同样将虚部转换符号,并用q*表示

对于四元数q = w + xi + yj + zk,它的共轭四元数为

q=wxibjzk

同样的按照共轭复数模长的定义,有

qq=q

四元数的逆用q-1表示,将共轭除以模得到

q1=qq

四元数的逆和实数的倒数存在有趣的对应关系,对于实数a,有

aa1=1

对于四元数而言,也存在

qq1=1

四元数与四元数的逆相乘得到单位四元数[1,0]

INFO

虽然四元数不满足交换律,而共轭四元数与四元数的乘积是满足交换律

qq=qq=q

对于单位四元数而言,由于它的模长为1,则有

q1=q

仿照复数的定义,我们可以将四元数q = w + xi + yj + zk的模长定义为

q=w2+x2+y2+z2=w2+v2

很难在几何上描述四元数的模长,因为它是在四维空间描述三维空间,只需要理解定义即可

纯四元数

如果一个四元数只有虚部,实部为0,那么称这种形式的四元数为纯四元数

q=[0,v]

因此任意形式的3D向量都可以转换为纯四元数,对于任意3D向量v,都有纯四元数

v=[0,v]

对于两个纯四元数q1、q2,它们的乘积为

q1q2=[v1·v2,v1×v2]

乘法

与标量相乘

与复数相同,四元数与标量的乘积等于标量与四元数每部分都相乘和

a(w+xi+yj+zk)=aw+axi+ayj+azk

也可以简化为

aq=a[w,v]=[aw,av]

四元数乘法(叉乘)

分为两种,分别是点乘、叉乘

计算规则与复数乘法相同,但是复数乘法遵循交换律。不用为四元数乘法使用乘号

  • 四元数相乘不遵循交换律
q1q2q2q1
  • 四元数相乘遵循结合律
(q1q2)q3=q1(q2q3)

例如:q1左乘q2结果为

q1q2=(w1+x1i+y1j+z1k)·(w2+x2i+y2j+z2k)=w1w2+w1x2i+w1y2j+w1z2k+ x1iw2+x1ix2i+x1iy2j+x1iz2k+ y1jw2+y1jx2i+y1jy2j+y1jz2k+ z1kw2+z1kx2i+z1ky2j+z1kz2k=w1w2+w1x2i+w1y2j+w1z2k+ x1w2ix1x2+x1y2kx1z2j+ y1w2jy1x2ky1y2+y1z2i+ z1w2k+z1x2jz1y2iz1z2=w1w2x1x2y1y2z1z2+ (x1w2+w1x2z1y2+y1z2)i+ (y1w2+z1x2+w1y2x1z2)j+ (z1w2y1x2+x1y2+w1z2)k

我们可以将结果进一步转化为矩阵形式

q1q2=[w1x1y1z1x1w1z1y1y1z1w1x1z1y1x1w1][w2x2y2z2]

其实四元数相乘的本质也是一个线性转换

接下来我们考虑虚部向量形式的点乘和叉乘,则有

  • 点乘
v1·v2=[x1y1z1][x2y2z2]=x1x2+y1y2+z1z2
  • 叉乘
v1×v2=[x1y1z1]×[x2y2z2]=y1z2z1y2+z1x2x1z2+x1y2y1x2

我们将q1左乘q2结果再变换下

w1w2(x1x2+y1y2+z1z2)+(x1w2+w1x2+y1z2z1y2)i+(y1w2+w1y2+z1x2x1z2)j+(z1w2+w1z2+x1y2y1x2)k

观察后可知,存在以下等式

q1q2=[w1w2v1·v2,w2v1+w1v2+v1×v2]

这个等式又叫Graβmann Product积

点乘

四元数的点乘计算规则与向量相同,得到的结果是一个标量,对应位置乘积之和

q1·q2=[w1x1y1z1][w2x2y2z2]=w1w2+x1x2+y1y2+z1z2

四元数点乘的几何意义与向量点乘的几何意义相似,表示四元数与另一个四元数的"相似"程度

对数运算

四元数对数运算的结果也是一个四元数

对于四元数q,有

logq=logqqq=logq+logqq=logq+log(cosθ+nsinθ)=logq+nθ=[logqnθ]

对应的单位四元数有

[0nθ]

INFO

证明需要用到欧拉公式

eiθ=cosθ+isinθ

幂次运算

四元数可以作为底数,记作qt,类似实数求幂

qt=etlnq

当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表示θ角度的逆时针旋转

一般来说,凡事涉及到指数运算的代数公式,如

(as)t=ast

在四元数的幂次运算中都是不适用的

js
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旋转θ,用四元数表示为

q=[cosθ2nsinθ2]=[cosθ2nxsinθ2nysinθ2nzsinθ2]

旋转后的向量可以使用四元数乘法来获得,令

v=[0,v]

则转换后后的向量v'(证明参考3.2章节)为

v=qvq=qvq1

则有

v=[0,cosθv+(1cosθ)(u·v)u+sinθ(u×v)]

矩阵形式

img.png

指数形式

img.png

获取旋转角度θ、旋转轴

如果有一个四元数q = [w, v]表示的旋转,那么它对应的旋转角度为

θ2=arccosw

对应的旋转轴为

n=vsin(arccosw)=vsinθ2

旋转的复合

如果有两个绕着不同旋转轴,不同旋转角度的四元数q1、q2,向量为v,最终旋转向量为v',则有

v=q2q1vq1q2

推广到n个旋转,则有

v=qnqn1q2q1vq1q2qn1qn

插值

在两个变换之间插入变换,可以让变换更加平滑

slerp

可以在两个四元数之间平滑插值,而且它避免了欧拉角插值的所有问题

开始和结束的四元数分别是q0、q1,插值参数设为变量t,t在0和1之间变化,表示为slerp(q0,q0,t)

slerp(q0,q0,t)=q0(q01q1)t

这是理论上的表达式,但是实践中有更有效的计算方式

w为q0、q1的夹角,有

slerp(q0,q0,t)=sin((1t)w)sinwq0+sin(tw)sinwq1
js
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

si=elogqi+1qi1+logqi1qi14qisquad(qi,qi+1,si,si+1,h)=slerp(slerp(qi,qi+1,h),slerp(si,si+1,h),2h(1h))

优缺点

优点

  • 可以平滑插值,另外两种方式都无法插值
  • 快速连接和角位移求逆
    • 四元数叉乘可以快速的将转换序列组合称一个转换,而矩阵效率低一些
    • 共轭四元数提供了反角位移更加简便的方式,矩阵的转置矩阵也可以快速计算反角位移下的旋转,但是没有四元数快
  • 可以和矩阵形式快速转换,四元数与矩阵形式的相互转换高于欧拉角与矩阵的相互转换效率
  • 占用空间低,仅用四个数
    • 欧拉角需要3个数(θ,γ,β),四元数需要4个数(w,x,y,z),矩阵需要9个数

缺点

  • 四元数可能不合法,由于精度丢失、坏数据点等问题导致四元数不可用,但是可以通过四元数标准化解决
  • 难于使用,相对于矩阵、欧拉角,四元数相对编码要求高

参考:

【1】Understanding Quaternions

【2】四元数与空间旋转

【3】四元数与三维旋转

【4】四元数的可视化

【5】如何形象地理解四元数?

【6】四元数速查手册

【7】欧拉公式