3d数学
(3)四元数
也就是3d复数,由四个分量组成的超复数系统,有3个虚部。
(3.1)记法
由一个标量w和一个向量\(\boldsymbol{v}\)表示
(3.2)四元数与复数
2d向量可表示复数(2d复数),而复数乘法的几何意义是幅值相乘、辐角相加。
因此,将幅值固定为1可得只表示旋转的复数。2d上的旋转可构造幅值为1的复数\(z=cos\theta+\boldsymbol{i}sin\theta\)和另一个2d复数相乘,即可将其保持模长不变、辐角增加θ,也即2d向量逆时针旋转了θ角。

那么3d向量对应的复数(3d复数)是怎样的呢?
哈密顿希望将复数扩展到三维空间,寻找一种可以表示三维旋转的代数系统。他最初尝试使用两个虚数单位,但发现这样的数乘法不封闭(乘积会引入第三个虚部)。最终他在都柏林散步时顿悟:需要三个虚数单位,并且它们必须满足:
\(\boldsymbol{i}\boldsymbol{j}\boldsymbol{k}=-1\)是哈密顿构造四元数时的基本定义之一,而不是推导出来的定理。
(3.3)负四元数
-
-q和q表示同一个旋转方位。
-
四元数的符号不改变它所表示的旋转,因为旋转公式中两个负号相互抵消,且几何上旋转角加 ( 2\pi ) 等价于原旋转。
- 代数说明
旋转一个向量 ( \mathbf{v} )(用纯四元数 ( v = (0, \mathbf{v}) ) 表示):
\[v' = q \, v \, q^{-1} \]对于单位四元数,( q^{-1} = q^* )(共轭)。
用 ( -q ) 代替 ( q ):\[(-q) \, v \, (-q)^{-1} = (-q) \, v \, (-q^*) = (-q) \, v \, (-q^*) = q \, v \, q^* \]因为负号相乘抵消:( (-q)(-q^) = q q^ = 1 ),且 ( (-q) v (-q^) = (-1)^2 q v q^ = q v q^* )。
- 几何说明
单位四元数 ( q = \cos\frac{\theta}{2} + \sin\frac{\theta}{2} , \mathbf{u} ) 表示绕轴 ( \mathbf{u} ) 旋转 ( \theta ) 角度。
那么:\[-q = -\cos\frac{\theta}{2} - \sin\frac{\theta}{2} \, \mathbf{u} \]可以写成:
\[-q = \cos\frac{\theta + 2\pi}{2} + \sin\frac{\theta + 2\pi}{2} \, \mathbf{u} \]即绕同一轴 ( \mathbf{u} ) 旋转 ( \theta + 2\pi ) 弧度。
由于旋转 ( 2\pi ) 是恒等变换,旋转 ( \theta + 2\pi ) 与旋转 ( \theta ) 效果完全相同。另一种等价形式:( -q ) 也可视为绕相反轴 ( -\mathbf{u} ) 旋转 ( 2\pi - \theta ),但最终得到的旋转矩阵与绕 ( \mathbf{u} ) 旋转 ( \theta ) 一致。
(3.4)共轭
表示旋转相反方向。
复数有共轭\(z=a+\boldsymbol{i}b,z^*=a-\boldsymbol{i}b\),虚部变为相反数。
四元数的共轭也是实部不变、虚部取反:
(3.5)单位四元数
表示旋转角为0。
(3.6)模
旋转四元数的模:
(3.7)逆
对于旋转四元数来说,模长为1,故有:
(3.8)标量乘(数乘)
(3.9)点乘
两四元数的点乘,等于差的实数部分。
四元数的点乘和向量类似,也可由两四元数的角位移描述,同样点乘也用于描述两者的相似度(投影)。
-
注意:
可通过点乘求得夹角的余弦值,若\(cos<q_1,q_2><0\),表明夹角大于\(\pi/2\),四元数为了处理的方便,一旦两个四元数的夹角超过90°,就将其中一个变为负四元数,这样夹角变为小于90°,因为负四元数和原本表示的旋转相同不影响。
(3.10)乘法(叉乘)
四元数乘法默认是叉乘。
叉乘是有方向的,和矩阵乘法一样,不满足交换律。
标准叉乘:(旋转叠加为左乘)
改进叉乘:(旋转叠加为右乘)
(3.11)“差”
不是两个四元数作差,而是一个方位到另一个方位的角位移,是两个四元数代表角度的差。
类似于除法,运算上是一个四元数的逆叉乘另一个四元数。
两四元数的差,实数部分等于两四元数的点积。
(3.12)对数
(3.13)指数
(3.14)幂
- \(q^t\)表示旋转t个q表示的角度。比如:\(q\)表示旋转30°,那么\(q^2\)表示旋转60°,\(q^{1/3}\)表示旋转10°。
- \(n=(n_x,n_y,n_z)\) 是单位旋转轴(纯方向,长度=1)
q.x, q.y, q.z不是旋转轴本身,而是旋转轴被sinθ缩放后的结果。\[q.x = sin(θ) * nx\\ q.y = sin(θ) * ny\\ q.z = sin(θ) * nz\\ \]
(3.15)旋转四元数
- 以单位四元数可以方便的使用旋转角位移的正余弦值表示旋转。
- 所有非零倍数的四元数表示的角位移都相同。
- \(q和-q\)表示相同的角位移。
- 旋转四元数的“差”,表示角位移的差值。
- 旋转四元数的叉乘,表示角位移的和。
- 旋转四元数的幂,表示角位移的倍数。
下面均为右手系:
-
2d中的旋转复数为:\(z=cos\theta+\boldsymbol{i}sin\theta\)
-
3d中的旋转四元数为(其中\(\boldsymbol{n}=(n_x,n_y,n_z)\)为3d任意方向的旋转轴的单位方向向量(类似于\(\boldsymbol{i}\))):
\[q=[cos(\theta/2) \quad sin(\theta/2)\boldsymbol{n}]\\ \]
-
将坐标,也表示为四元数\(p=[0 \ (x \ y \ z)]\),左右各叉乘一个四元数得到旋转后的坐标:
\[标准叉乘下的坐标变换公式:\\ p'=qpq^{-1}\\ =b(apa^{-1})b^{-1}=(ba)p(ba)^{-1} \]这个公式表明:先旋转a,再旋转b,叠加起来的四元数是a左乘b,从右往左这不利于编程,也不符合从左往右的习惯。
-
为了改变这个,使用另一个叉乘公式(见四元数的叉乘):
此时,坐标变换公式变为:
该公式中旋转动作的叠加按照先后是从左往右的。
- 在很多游戏引擎的代码中,默认了旋转四元数是单位四元数(规范化后的),也即\(||q||=1,q^{-1}=q^*\)。使用\(q^*pq\)替代\(q^{-1}pq\)计算旋转。 所以若计算中使用的q不是单位四元数,就会导致\(q_{-1} \ne q ^*\),所以才需要
Normalize()函数。
(3.16)标准化(单位化,模长置1)
旋转四元数必须是单位四元数。
-
在很多游戏引擎的代码中,默认了旋转四元数是单位四元数(规范化后的),也即\(||q||=1,q^{-1}=q^*\)。使用\(q^*pq\)替代\(q^{-1}pq\)计算旋转。 所以若计算中使用的q不是单位四元数,就会导致\(q_{-1} \ne q ^*\),所以才需要
Normalize()函数。 -
四元数在各种计算中,因为分量都是浮点数,可能会导致大量计算后因误差导致模长不再为1。此时就需要将其规范化(也叫正则化),将其模重新变为1。
-
重点:要判断模长<=0的情况
原因:
- 如果四元数分量极小(接近 FLT_MIN),平方后可能下溢为0。
- 或者由于浮点误差,平方和计算结果为极小的负数(虽然理论上不可能,但浮点运算有舍入)。
void Normalize() { float mag = Magnitude(); if (mathUtil::IsEqual(mag, 1.0f)) return; if (mag > 0.0f) { float oneOverMag = 1.0f / mag; w_ *= oneOverMag; x_ *= oneOverMag; y_ *= oneOverMag; z_ *= oneOverMag; } else { assert(false); SetIndentity(); } }[!NOTE]
-
FLT_MIN是什么,为什么平方后可能下溢为0?
浮点数范围限制
float 的指数范围约为 2−126 到 2127 (即 ~10−38 到 ~1038 )。
下溢(Underflow)过程
假设:某个分量值为 √FLT_MIN ≈ 1.084e-19 计算平方:(√FLT_MIN)² = FLT_MIN ≈ 1.175e-38 ✓ 还能表示 但如果分量更小,比如 FLT_MIN 本身: (FLT_MIN)² = (1.175e-38)² ≈ 1.38e-76 ✗ 远小于 FLT_MIN 结果:下溢为 0(或变为非正规化数后进一步变为0)具体例子
分量值 平方后 结果 1e-201e-40下溢为 0(小于 FLT_MIN) 1e-301e-60下溢为 0 FLT_MIN(1.175e-38)~1.38e-76 下溢为 0 在四元数中的场景
// 假设一个"合法"但极小的四元数 float w = 1e-20f, x = 1e-20f, y = 1e-20f, z = 1e-20f; // 平方和计算 float sum = w*w + x*x + y*y + z*z; // = 0 + 0 + 0 + 0 = 0 (每个平方都下溢了!) float mag = sqrt(sum); // sqrt(0) = 0此时
mag = 0,但原始四元数并非零向量,只是数值太小。
(3.17)四元数到矩阵
把矩阵看作是从物体系到惯性系
把矩阵看作是从惯性系到物体系
(3.18)矩阵到四元数
把当前的矩阵视为从物体系到惯性系
《QUATERNION AND 4X4MATRICES》这本书告诉我们,计算方法应为:使用根号公式分别计算w,x,y,z,保留最大值,剩下3个值均由后续公式求取。
所以最终得到的公式如下:
把当前矩阵看作是从惯性系到物体系
也即:
所以可知:同一个矩阵得到的四元数表示的旋转相同。
(3.19)欧拉角到四元数
这里因为是从欧拉角计算四元数,所以采用四元数的标准叉乘,欧拉角的3次旋转转成四元数形式,三次旋转则为3个四元数的叉乘。
欧拉角看作物体系到惯性系
欧拉角看作惯性系到物体系
(3.20)四元数到欧拉角
四元数--->旋转矩阵--->欧拉角
四元数看作是物体系到惯性系、看作是惯性系到物体系时,计算公式相同:
万向锁以\(sin(pitch)=-m_{23}=-2.0f(yz-wx)\)和1比较是否相等计算。
(3.21)矩阵、欧拉角、四元数转换
-
这种转换,可以看作是一种映射f,输入和输出对应。
-
如果希望输出从
object->inertial变为inertial->object,有两种方法:- 将输入从
object->inertial变为inertial->object,带入原公式(保持f不变) - 保持输入为
object->inertial不变,改变公式(映射关系f)。 - 但改变输入,再按照原公式计算的方法,会多一步对输入进行改变的步骤,因此一般是改变f更加有效率。
- 将输入从
-
旋转矩阵\(R_{object->inertial}\)和四元数\(q_{object->inertial}\)是对应的。
旋转矩阵\(R_{inertial->object}\)和四元数\(q_{inertial->object}\)是对应的。
但是,
上面4个对应的欧拉角的三个值都是一样的,因为欧拉角的
object->inertial和inertial->object的区别是三个角的旋转顺序:Y-X-Z或Z-X-Y
-
总结
inertial->object:-
矩阵--->欧拉角
将计算公式中的m的下标变为转置。
-
欧拉角--->矩阵
将矩阵转置,带入欧拉角计算。
-
矩阵--->四元数
公式中m的下标变为转置,带入矩阵。
-
四元数--->矩阵
把矩阵转置,带入四元数。
-
欧拉角--->四元数
四元数取共轭,带入欧拉角。
-
四元数--->欧拉角
矩阵转置,带入四元数得到\(R_{inertial->object}\),矩阵到欧拉角的公式中的m下标变为转置。
实际从结果上来说,只有四元数到矩阵的时候发生了转置,不论是\(R_{object->inertial}\)还是\(R_{inertial->object}\)得到的欧拉角的三个值是一样的,因为矩阵--->欧拉角当矩阵转置后,求取欧拉角的m下标也会对应转置,所以抵消。
-
(3.22)四元数的球面线性插值(Slerp)
只有四元数可以插值,矩阵不可以,欧拉角虽然可以但存在万向锁问题。
平滑插值函数命名为Slerp(),当夹角>90°时,可以用 ( -q_1 ) 替代 ( q_1 ) 来得到更短的插值路径,而不影响最终的旋转结果。
Slerp()在\(\boldsymbol{q_0}\)和\(\boldsymbol{q_1}\)之间插入一个值,值的位置由参数t决定。
-
标量的线性插值(Lerp)
\[\Delta a = a_0 - a_1\\ lerp(a_0,a_1,t)=a_0+t\Delta a \] -
四元数线性插值(Lerp)
\[角位移之差:\Delta \boldsymbol{q} = \boldsymbol{q_0^{-1}}\boldsymbol{q_{1}}\\ 角位移的倍数:(\Delta \boldsymbol{q})^t\\ q0的角位移基础上增加\Delta q的角位移:Slerp(\boldsymbol{q_0}, \boldsymbol{q_1},t)=\boldsymbol{q_0}(\Delta \boldsymbol{q})^t=\boldsymbol{q_0}(\boldsymbol{q_0^{-1}}\boldsymbol{q_{1}}) \] -
向量线性插值(Lerp)
\(\omega=v_1-v_0\)是向量差,插值路径是一条直线,适用于欧氏向量空间。
\[\omega=\boldsymbol{v_1}-\boldsymbol{v_0}\\ \boldsymbol{v_t}=\boldsymbol{v_0}+t\omega,0 \le t \le 1\\ \] -
向量的旋转插值(Slerp)
如果直接对单位向量做线性插值,结果会偏离单位球面(长度变化),且旋转角速度不恒定,导致动画不自然。因此需要球面线性插值(Slerp),其路径是大圆弧,角速度恒定。
在常见的计算机图形学和机器人学语境下,“旋转插值”通常就是指“球面线性插值”(Slerp),虽然两者并不完全等价。
球面线性插值(Slerp) 是一种具体的数学方法,用于在单位球面(或单位四元数空间)上沿大圆弧以恒定角速度进行插值。它天然适合表示旋转,因为旋转可以映射为单位四元数或单位方向向量。

将向量\(\boldsymbol{v_t}\)分解为以\(\boldsymbol{v_0}\)和\(\boldsymbol{v_1}\)为基的分量\(k_0\boldsymbol{v_0}\)和\(k_1\boldsymbol{v_1}\)。
\(\omega\)表示\(\boldsymbol{v_1}\)和\(\boldsymbol{v_0}\)的夹角,\(t\omega\)表示\(\boldsymbol{v_t}\)和\(\boldsymbol{v_0}\)的夹角。
这里的向量都是单位向量(单位四元数表示旋转),因此\(||\boldsymbol{v}||=1\)。
\[sin\omega \ ||k_1\boldsymbol{v_1}||=sin(t\omega) \ \Rightarrow \ k_1=\frac{sin(t\omega)}{sin\omega}\\ k_0\boldsymbol{v_0}正对角为 (1-t)\omega \quad 由正弦定理,可得:\\ \frac{k_0}{sin[(1-t)\omega]} \ = \frac{k_1}{sin(t\omega)} \ = \frac{1}{sin(\pi-\omega)}\\ \Rightarrow \ k_0=\frac{sin[(1-t)\omega]}{sin\omega}\\ \therefore \ \boldsymbol{v_t}=k_0\boldsymbol{v_0}+k_1\boldsymbol{v_1}=\frac{sin[(1-t)\omega]}{sin\omega}\boldsymbol{v_0}+\frac{sin(t\omega)}{sin\omega}\boldsymbol{v_1} \] -
四元数的旋转插值(Slerp)
因为四元数具有相同的数学结构——它们都位于内积空间中的单位球面上。
因此可以从向量的Slerp公式直接得到四元数的Slerp公式。
\[Slerp(\boldsymbol{q_0},\boldsymbol{q_1},t)=k_0\boldsymbol{v_0}+k_1\boldsymbol{v_1}=\frac{sin[(1-t)\omega]}{sin\omega}\boldsymbol{v_0}+\frac{sin(t\omega)}{sin\omega}\boldsymbol{v_1} \]
[!NOTE]
- 为什么从向量推导的公式能直接应用在四元数上得出slerp(q0,q1,t)的公式?
从向量推导的球面线性插值(Slerp)公式可以直接应用到四元数上,是因为四元数与向量共享相同的数学结构——它们都位于内积空间中的单位球面上。
具体原因如下:
- 内积空间结构
三维单位向量 v0,v1v0,v1 满足 ∥v0∥=∥v1∥=1∥v0∥=∥v1∥=1,其夹角 ωω 由 cosω=v0⋅v1cosω=v0⋅v1 定义。
单位四元数 q0,q1q0,q1 也可视为四维实向量,其内积定义为 q0⋅q1=12(q0∗q1+q1∗q0)q0⋅q1=21(q0∗q1+q1∗q0)(即各分量乘积之和)。若将四元数视为四维向量,则有 ∥q∥=q⋅q=1∥q∥=q⋅q=1,且夹角 ωω 满足 cosω=q0⋅q1cosω=q0⋅q1。- 球面线性插值的几何本质
Slerp 的目的是在单位球面上沿大圆弧进行匀速插值。推导过程仅依赖于:
- 向量的线性组合 vt=k0v0+k1v1v**t=k0v0+k1v1
- 夹角条件 ∠(vt,v0)=tω∠(v**t,v0)=t**ω,∠(vt,v1)=(1−t)ω∠(v**t,v1)=(1−t)ω
- 单位长度约束 ∥vt∥=1∥v**t∥=1
这些条件通过解三角形(正弦定理)得到系数 k0=sin((1−t)ω)sinωk0=sinωsin((1−t)ω),k1=sin(tω)sinωk1=sinωsin(t**ω)。该推导不依赖维数,只要向量内积和夹角定义明确,结果对任意维数单位球面都成立。- 四元数的内积与向量完全一致
四元数 q=(w,x,y,z)q=(w,x,y,z) 与四维向量 (w,x,y,z)(w,x,y,z) 一一对应,点积 q0⋅q1=w0w1+x0x1+y0y1+z0z1q0⋅q1=w0w1+x0x1+y0y1+z0z1 与向量相同。因此,两个单位四元数之间的球面大圆弧插值公式与三维向量的形式完全相同。- 旋转插值的物理意义
单位四元数表示三维旋转,Slerp 插值得到的四元数序列对应旋转的角速度恒定的路径(即最短弧路径)。这恰好是向量 Slerp 在四维球面上的直接推广,而旋转群 SO(3)SO(3) 与单位四元数群 S3S3 是双覆盖关系,因此公式可直接使用。结论:因为四元数构成四维内积空间,单位四元数位于四维单位球面上,而 Slerp 公式的推导仅依赖于内积和夹角,不依赖于维数,所以向量形式的公式可以直接照搬到四元数上,得到正确的旋转插值表达式。

浙公网安备 33010602011771号