Unleashing Robotics: Mastering Quaternion Kinematics with Python
** 这个系列文章禁止转载,主要是如果有对这个教程内容的不同见解方便反馈,我的邮箱(fanzexuan135@163.com)。
在机器人领域,准确估计和控制机器人的姿态(orientation)是一项关键而富有挑战性的任务。四元数(Quaternion)凭借其独特的数学性质,已成为描述三维旋转的首选工具之一。特别地,它在基于IMU(惯性测量单元)的姿态估计和滤波算法中扮演着至关重要的角色。
本章将为读者奠定坚实的四元数理论基础,内容涵盖了四元数的定义、多种表示形式、运算法则、性质定理等。只有深入理解了这些基础知识,读者才能真正领会四元数在机器人学中的巧妙应用,并具备在实际项目中灵活运用的能力。
学完这个教程,你可以轻松理解任何涉及到四元数动力学的技术以及相关文献,尤其会对其在机器人领域的应用有全面和深入的理解。
这个教程的计划:
- 首先这个系列文章计划10个章节,前几个章节注重数理基础,后几个章节以最新的paper和产业界技术为例,介绍其在复杂系统中的应用。
- 整个系列文章将重点介绍如何在Python中实现基于四元数的各种几何和数学变化, 以及其在产业中的应用,比如基于四元数动力学的四足和人形机器人运动控制:“微分动态编程”(Differential Dynamic Programming, DDP)和姿态估计。
- 我将从头开始推导基于四元数的ESKF的数学模型,并给出完整的Python实现(我在工作中也使用这部分技术的c++实现,为了避免可能的保密问题,所有示例都以python形式给出,使用python也更直观易懂)。此外,本文还会讨论四元数微分、插值、误差状态等进阶话题。值得一提的是,网络上许多关于四元数和ESKF的资料往往缺乏深度和连贯性,给初学者带来不小的理解障碍。既然我抽出时间写教程,就一定会力求层层推进、环环相扣,从四元数的底层数学原理出发,逐步构建起高阶机器人运动学和状态估计的全貌。每个重要概念都配备了简明扼要的文字说明、严谨细致的数学推导、直观的几何解释、便于实践的代码。
- 因此,请耐心阅读本章的四元数基础理论,哪怕你已经有所了解。我也会纠正一些常见的误区,澄清一些易混的概念,为后续内容做好准备。同时,本章也适合作为四元数理论的快速复习和查阅手册。
万丈高楼平地起,一切的探索都始于扎实的基础。现在,就让我们踏上四元数和ESKF的奇妙旅程吧!
Chapter 1: 四元数的定义与性质(Quaternion definition and properties)
四元数(Quaternion)是一种用于描述三维空间旋转的数学工具,它由一个实部(real part)和三个虚部(imaginary part)组成。我们通常使用符号 R \mathbb{R} R 表示实数集合, I \mathbb{I} I 表示虚数集合, Z \mathbb{Z} Z 表示复数集合, H \mathbb{H} H 表示四元数集合。本章将详细介绍四元数的定义和性质,并给出Python代码实例。同时,结合IMU(惯性测量单元)解算的例子,说明四元数运动学(Quaternion kinematics)的概念及其应用。
1.1 四元数的定义
四元数可以看作是复数的扩展。如果我们有两个复数 A = a + b i A=a+bi A=a+bi 和 C = c + d i C=c+di C=c+di,那么构造 Q = A + C j Q=A+Cj Q=A+Cj 并定义 k = i j k=ij k=ij,就得到一个四元数的形式:
Q = a + b i + c j + d k ∈ H Q = a + bi + cj + dk \in \mathbb{H} Q=a+bi+cj+dk∈H
其中 { a , b , c , d } ∈ R \{a,b,c,d\} \in \mathbb{R} {a,b,c,d}∈R,而 { i , j , k } \{i,j,k\} {i,j,k} 是三个虚数单位,满足以下关系:
i 2 = j 2 = k 2 = i j k = − 1 i^2 = j^2 = k^2 = ijk = -1 i2=j2=k2=ijk=−1
由此可以推导出虚数单位之间的乘法关系:
i j = − j i = k , j k = − k j = i , k i = − i k = j ij = -ji = k, \quad jk = -kj = i, \quad ki = -ik = j ij=−ji=k,jk=−kj=i,ki=−ik=j
从定义可以看出,实数、虚数、复数都可以看作是四元数的特例:
- 实数: Q = a ∈ R ⊂ H Q = a \in \mathbb{R} \subset \mathbb{H} Q=a∈R⊂H
- 虚数: Q = b i ∈ I ⊂ H Q = bi \in \mathbb{I} \subset \mathbb{H} Q=bi∈I⊂H
- 复数: Q = a + b i ∈ Z ⊂ H Q = a+bi \in \mathbb{Z} \subset \mathbb{H} Q=a+bi∈Z⊂H
类似地,我们还可以定义纯四元数(pure quaternion),它是四元数的虚部,记为 H p = I m ( H ) \mathbb{H}_p = \mathrm{Im}(\mathbb{H}) Hp=Im(H):
Q = b i + c j + d k ∈ H p ⊂ H Q = bi + cj + dk \in \mathbb{H}_p \subset \mathbb{H} Q=bi+cj+dk∈Hp⊂H
值得注意的是,单位长度的复数 z = e i θ z=e^{i\theta} z=eiθ 可以编码二维平面的旋转(通过一次复数乘法 x ′ = z ⋅ x x'=z \cdot x x′=z⋅x 实现);而单位长度的四元数 q = e ( u x i + u y j + u z k ) θ / 2 q=e^{(u_xi+u_yj+u_zk)\theta/2} q=e(uxi+uyj+uzk)θ/2 可以编码三维空间的旋转(通过两次四元数乘法 x ′ = q ⊗ x ⊗ q ∗ x'=q \otimes x \otimes q^* x′=q⊗x⊗q∗ 实现)。这里的 q ∗ q^* q∗ 表示 q q q 的共轭四元数,我们将在后面详细介绍。
下面给出用Python构造四元数的示例代码:
import numpy as np
# 定义虚数单位
i = np.array([0, 1, 0, 0])
j = np.array([0, 0, 1, 0])
k = np.array([0, 0, 0, 1])
# 构造四元数
q = np.array([1, 2, 3, 4]) # a=1, b=2, c=3, d=4
print(q) # [1 2 3 4]
# 提取实部和虚部
q_w = q[0] # 实部
q_v = q[1:] # 虚部
print(q_w) # 1
print(q_v) # [2 3 4]
# 纯四元数
q_p = np.array([0, 1, 2, 3])
print(q_p) # [0 1 2 3]
注意:不同的四元数定义可能有所不同。有些作者将乘积写成 i b ib ib 而不是 b i bi bi,因此得到 k = j i = − i j k=ji=-ij k=ji=−ij,这就得到了 i j k = 1 ijk=1 ijk=1 和左手四元数。此外,许多作者将实部放在最后一个位置,即 Q = i a + j b + k c + d Q=ia+jb+kc+d Q=ia+jb+kc+d。这些选择没有根本的影响,但会使整个公式在细节上有所不同。
1.1.1 四元数的不同表示形式
除了实虚部记号 { 1 , i , j , k } \{1,i,j,k\} {1,i,j,k} 外,四元数还可以表示为实部加向量的形式:
Q = q w + q x i + q y j + q z k ⇔ Q = q w + q v Q = q_w + q_xi + q_yj + q_zk \Leftrightarrow Q = q_w + \mathbf{q}_v Q=qw+qxi+qyj+qzk⇔Q=qw+qv
其中 q w q_w qw 称为实部或标量部分, q v = q x i + q y j + q z k = ( q x , q y , q z ) \mathbf{q}_v = q_xi + q_yj + q_zk = (q_x, q_y, q_z) qv=qxi+qyj+qzk=(qx,qy,qz) 称为虚部或向量部分。它也可以定义为标量-向量的有序对:
Q = ⟨ q w , q v ⟩ Q = \langle q_w, \mathbf{q}_v \rangle Q=⟨qw,qv⟩
我们通常将四元数 Q Q Q 表示为一个4维向量 q \mathbf{q} q:
q = [ q w q v ] = [ q w q x q y q z ] \mathbf{q} = \begin{bmatrix} q_w \\ \mathbf{q}_v \end{bmatrix} = \begin{bmatrix} q_w \\ q_x \\ q_y \\ q_z \end{bmatrix} q=[qwqv]= qwqxqyqz
这样就可以使用矩阵代数来进行涉及四元数的运算。在某些情况下,我们可能会混合使用符号 “=”。典型的例子是实四元数和纯四元数:
g e n e r a l : q = q w + q v = [ q w q v ] ∈ H , r e a l : q w = [ q w 0 v ] ∈ R , p u r e : q v = [ 0 q v ] ∈ H p \mathrm{general}: \mathbf{q} = q_w + \mathbf{q}_v = \begin{bmatrix} q_w \\ \mathbf{q}_v \end{bmatrix} \in \mathbb{H}, \quad \mathrm{real}: q_w = \begin{bmatrix} q_w \\ \mathbf{0}_v \end{bmatrix} \in \mathbb{R}, \quad \mathrm{pure}: \mathbf{q}_v = \begin{bmatrix} 0 \\ \mathbf{q}_v \end{bmatrix} \in \mathbb{H}_p general:q=qw+qv=[qwqv]∈H,real:qw=[qw0v]∈R,pure:qv=[0qv]∈Hp
下面用Python代码演示四元数的不同表示形式:
# 实虚部记号
q = 1 + 2*i + 3*j + 4*k
print(q) # [1 2 3 4]
# 标量加向量记号
q_w = 1
q_v = np.array([2, 3, 4])
q = np.concatenate(([q_w], q_v))
print(q) # [1 2 3 4]
# 有序对记号
q = (1, np.array([2, 3, 4]))
print(q) # (1, array([2, 3, 4]))
# 4维向量记号
q = np.array([1, 2, 3, 4])
print(q) # [1 2 3 4]
# 实四元数
q_real = np.array([1, 0, 0, 0])
print(q_real) # [1 0 0 0]
# 纯四元数
q_pure = np.array([0, 2, 3, 4])
print(q_pure) # [0 2 3 4]
1.2 四元数的主要性质
1.2.1 加法
四元数加法就是对应分量相加:
p + q = [ p w p v ] + [ q w q v ] = [ p w + q w p v + q v ] \mathbf{p} + \mathbf{q} = \begin{bmatrix} p_w \\ \mathbf{p}_v \end{bmatrix} + \begin{bmatrix} q_w \\ \mathbf{q}_v \end{bmatrix} = \begin{bmatrix} p_w + q_w \\ \mathbf{p}_v + \mathbf{q}_v \end{bmatrix} p+q=[pwpv]+[qwqv]=[pw+qwpv+qv]
根据定义,四元数加法满足交换律和结合律:
-
交换律: p + q = q + p \mathbf{p} + \mathbf{q} = \mathbf{q} + \mathbf{p} p+q=q+p
-
结合律: p + ( q + r ) = ( p + q ) + r \mathbf{p} + (\mathbf{q} + \mathbf{r}) = (\mathbf{p} + \mathbf{q}) + \mathbf{r} p+(q+r)=(p+q)+r
Python代码示例:
p = np.array([1, 2, 3, 4])
q = np.array([5, 6, 7, 8])
# 加法
r = p + q
print(r) # [ 6 8 10 12]
# 交换律
print(p + q == q + p) # [ True True True True]
# 结合律
r = np.array([9, 10, 11, 12])
print(p + (q + r) == (p + q) + r) # [ True True True True]
1.2.2 乘法
四元数乘法记为 ⊗ \otimes ⊗,需要使用原始定义式(1)和四元数代数关系式(2)。将结果写成向量形式为:
p ⊗ q = [ p w q w − p x q x − p y q y − p z q z p w q x + p x q w + p y q z − p z q y p w q y − p x q z + p y q w + p z q x p w q z + p x q y − p y q x + p z q w ] \mathbf{p} \otimes \mathbf{q} = \begin{bmatrix} p_wq_w - p_xq_x - p_yq_y - p_zq_z \\ p_wq_x + p_xq_w + p_yq_z - p_zq_y \\ p_wq_y - p_xq_z + p_yq_w + p_zq_x \\ p_wq_z + p_xq_y - p_yq_x + p_zq_w \end{bmatrix} p⊗q= pwqw−pxqx−pyqy−pzqzpwqx+pxqw+pyqz−pzqypwqy−pxqz+pyqw+pzqxpwqz+pxqy−pyqx+pzqw
也可以用实部和虚部表示:
p ⊗ q = [ p w q w − p v ⊤ q v p w q v + q w p v + p v × q v ] \mathbf{p} \otimes \mathbf{q} = \begin{bmatrix} p_wq_w - \mathbf{p}_v^\top \mathbf{q}_v \\ p_w\mathbf{q}_v + q_w\mathbf{p}_v + \mathbf{p}_v \times \mathbf{q}_v \end{bmatrix} p⊗q=[pwqw−pv⊤qvpwqv+qwpv+pv×qv]
其中 p v × q v \mathbf{p}_v \times \mathbf{q}_v pv×qv 表示向量叉乘,它的存在导致四元数乘法在一般情况下不满足交换律:
p ⊗ q ≠ q ⊗ p \mathbf{p} \otimes \mathbf{q} \neq \mathbf{q} \otimes \mathbf{p} p⊗q=q⊗p
只有当 p v × q v = 0 \mathbf{p}_v \times \mathbf{q}_v = \mathbf{0} pv×qv=0 时例外,此时要么一个四元数是实的(即 p = p w \mathbf{p}=p_w p=pw 或 q = q w \mathbf{q}=q_w q=qw),要么两个四元数的虚部平行(即 p v ∥ q v \mathbf{p}_v \parallel \mathbf{q}_v pv∥qv)。只有在这些特殊情况下,四元数乘法才满足交换律。
但四元数乘法满足结合律:
( p ⊗ q ) ⊗ r = p ⊗ ( q ⊗ r ) (\mathbf{p} \otimes \mathbf{q}) \otimes \mathbf{r} = \mathbf{p} \otimes (\mathbf{q} \otimes \mathbf{r}) (p⊗q)⊗r=p⊗(q⊗r)
以及对加法的分配律:
- 左分配律: p ⊗ ( q + r ) = p ⊗ q + p ⊗ r \mathbf{p} \otimes (\mathbf{q} + \mathbf{r}) = \mathbf{p} \otimes \mathbf{q} + \mathbf{p} \otimes \mathbf{r} p⊗(q+r)=p⊗q+p⊗r
- 右分配律: ( p + q ) ⊗ r = p ⊗ r + q ⊗ r (\mathbf{p} + \mathbf{q}) \otimes \mathbf{r} = \mathbf{p} \otimes \mathbf{r} + \mathbf{q} \otimes \mathbf{r} (p+q)⊗r=p⊗r+q⊗r
两个四元数的乘积是双线性的,可以表示为两个等价的矩阵乘积形式:
q 1 ⊗ q 2 = [ q 1 ] L q 2 , q 1 ⊗ q 2 = [ q 2 ] R q 1 \mathbf{q}_1 \otimes \mathbf{q}_2 = [\mathbf{q}_1]_L \mathbf{q}_2, \quad \mathbf{q}_1 \otimes \mathbf{q}_2 = [\mathbf{q}_2]_R \mathbf{q}_1 q1⊗q2=[q1]Lq2,q1⊗q2=[q2]Rq1
其中 [ q ] L [\mathbf{q}]_L [q]L 和 [ q ] R [\mathbf{q}]_R [q]R 分别称为左右四元数乘积矩阵,可以由定义式(12)和(17)直接得到:
[ q ] L = [ q w − q x − q y − q z q x q w − q z q y q y q z q w − q x q z − q y q x q w ] , [ q ] R = [ q w − q x − q y − q z q x q w q z − q y q y − q z q w q x q z q y − q x q w ] [\mathbf{q}]_L = \begin{bmatrix} q_w & -q_x & -q_y & -q_z \\ q_x & q_w & -q_z & q_y \\ q_y & q_z & q_w & -q_x \\ q_z & -q_y & q_x & q_w \end{bmatrix}, \quad [\mathbf{q}]_R = \begin{bmatrix} q_w & -q_x & -q_y & -q_z \\ q_x & q_w & q_z & -q_y \\ q_y & -q_z & q_w & q_x \\ q_z & q_y & -q_x & q_w \end{bmatrix} [q]L= qwqxqyqz−qxqwqz−qy−qy−qzqwqx−qzqy−qxqw ,[q]R= qwqxqyqz−qxqw−qzqy−qyqzqw−qx−qz−qyqxqw
或者更简洁地由(13)和(17)得到:
[ q ] L = q w I + [ 0 − q v ⊤ q v [ q v ] × ] , [ q ] R = q w I + [ 0 − q v ⊤ q v − [ q v ] × ] [\mathbf{q}]_L = q_wI + \begin{bmatrix} 0 & -\mathbf{q}_v^\top \\ \mathbf{q}_v & [\mathbf{q}v]\times \end{bmatrix}, \quad [\mathbf{q}]_R = q_wI + \begin{bmatrix} 0 & -\mathbf{q}_v^\top \\ \mathbf{q}_v & -[\mathbf{q}v]\times \end{bmatrix} [q]L=qwI+[0qv−qv⊤[qv]×],[q]R=qwI+[0qv−qv⊤−[qv]×]
这里反对称算子 [ ∙ ] × [\bullet]_\times [∙]× 用于生成叉乘矩阵:
[ a ] × = [ 0 − a z a y a z 0 − a x − a y a x 0 ] [\mathbf{a}]_\times = \begin{bmatrix} 0 & -a_z & a_y \\ a_z & 0 & -a_x \\ -a_y & a_x & 0 \end{bmatrix} [a]×= 0az−ay−az0axay−ax0
它是一个反对称矩阵,即 [ a ] × ⊤ = − [ a ] × [\mathbf{a}]_\times^\top = -[\mathbf{a}]_\times [a]×⊤=−[a]×,与叉乘运算等价:
[ a ] × b = a × b , ∀ a , b ∈ R 3 [\mathbf{a}]_\times \mathbf{b} = \mathbf{a} \times \mathbf{b}, \quad \forall \mathbf{a}, \mathbf{b} \in \mathbb{R}^3 [a]×b=a×b,∀a,b∈R3
最后,由于以下等式成立:
q ⊗ x ⊗ p = ( q ⊗ x ) ⊗ p = [ p ] R [ q ] L x = q ⊗ ( x ⊗ p ) = [ q ] L [ p ] R x \mathbf{q} \otimes \mathbf{x} \otimes \mathbf{p} = (\mathbf{q} \otimes \mathbf{x}) \otimes \mathbf{p} = [\mathbf{p}]_R[\mathbf{q}]_L\mathbf{x} = \mathbf{q} \otimes (\mathbf{x} \otimes \mathbf{p}) = [\mathbf{q}]_L[\mathbf{p}]_R\mathbf{x} q⊗x⊗p=(q⊗x)⊗p=[p]R[q]Lx=q⊗(x⊗p)=[q]L[p]Rx
我们可以得到左右四元数乘积矩阵的交换性质:
[ p ] R [ q ] L = [ q ] L [ p ] R [\mathbf{p}]_R[\mathbf{q}]_L = [\mathbf{q}]_L[\mathbf{p}]_R [p]R[q]L=[q]L[p]R
带有乘法运算 ⊗ \otimes ⊗ 的四元数构成一个非交换群(non-Abelian group)。该群的单位元 q 1 = 1 \mathbf{q}_1=1 q1=1 和逆元素 q − 1 \mathbf{q}^{-1} q−1 将在下面的小节中介绍。
Python代码示例如下:
import numpy as np
def quat_mult(q1, q2):
"""四元数乘法"""
w1, x1, y1, z1 = q1
w2, x2, y2, z2 = q2
w = w1*w2 - x1*x2 - y1*y2 - z1*z2
x = w1*x2 + x1*w2 + y1*z2 - z1*y2
y = w1*y2 - x1*z2 + y1*w2 + z1*x2
z = w1*z2 + x1*y2 - y1*x2 + z1*w2
return np.array([w, x, y, z])
def quat_leftMult(q):
"""左乘矩阵"""
w, x, y, z = q
return np.array([[w, -x, -y, -z],
[x, w, -z, y],
[y, z, w, -x],
[z, -y, x, w]])
def quat_rightMult(q):
"""右乘矩阵"""
w, x, y, z = q
return np.array([[w, -x, -y, -z],
[x, w, z, -y],
[y, -z, w, x],
[z, y, -x, w]])
def cross_mat(v):
"""叉乘矩阵"""
return np.array([[0, -v[2], v[1]],
[v[2], 0, -v[0]],
[-v[1], v[0], 0 ]])
# 四元数乘法
p = np.array([1, 2, 3, 4])
q = np.array([5, 6, 7, 8])
r1 = quat_mult(p, q)
print(r1) # [-60 12 30 24]
# 非交换性
r2 = quat_mult(q, p)
print(r2) # [-60 20 4 36]
# 结合律
r = np.array([1, 2, 1, 3])
print(quat_mult(quat_mult(p, q), r) == quat_mult(p, quat_mult(q, r))) # [ True True True True]
# 分配律
print(quat_mult(p, q+r) == quat_mult(p, q) + quat_mult(p, r)) # [ True True True True]
print(quat_mult(p+q, r) == quat_mult(p, r) + quat_mult(q, r)) # [ True True True True]
# 左右乘积矩阵
p_left = quat_leftMult(p)
p_right = quat_rightMult(p)
q_vec = q.reshape(4,1)
print(p_left @ q_vec.flatten() == quat_mult(p, q)) # [ True True True True]
print(p_right @ q_vec.flatten() == quat_mult(q, p)) # [ True True True True]
# 矩阵表示
p_w, p_v = p[0], p[1:]
p_left2 = np.eye(4)*p_w + np.vstack((np.array([[0, -p_v[0], -p_v[1], -p_v[2]]]),
np.hstack((p_v.reshape(3,1), cross_mat(p_v)))))
p_right2 = np.eye(4)*p_w + np.vstack((np.array([[0, -p_v[0], -p_v[1], -p_v[2]]]),
np.hstack((p_v.reshape(3,1), -cross_mat(p_v)))))
print(np.allclose(p_left, p_left2)) # True
print(np.allclose(p_right, p_right2)) # True
# 矩阵乘积交换性
print(np.allclose(p_left @ q_left, q_left @ p_left)) # True
1.2.3 单位元
四元数乘法的单位元(identity)是一个特殊的四元数 q 1 \mathbf{q}_1 q1,它满足:
q 1 ⊗ q = q ⊗ q 1 = q , ∀ q ∈ H \mathbf{q}_1 \otimes \mathbf{q} = \mathbf{q} \otimes \mathbf{q}_1 = \mathbf{q}, \quad \forall \mathbf{q} \in \mathbb{H} q1⊗q=q⊗q1=q,∀q∈H
单位元 q 1 \mathbf{q}_1 q1 对应于实数乘法中的 “1”,用四元数表示为:
q 1 = 1 = [ 1 0 v ] \mathbf{q}_1 = 1 = \begin{bmatrix} 1 \\ \mathbf{0}_v \end{bmatrix} q1=1=[10v]
其中 0 v \mathbf{0}_v 0v 表示全零向量。
1.2.4 共轭
四元数 q \mathbf{q} q 的共轭(conjugate)定义为:
q ∗ = q w − q v = [ q w − q v ] \mathbf{q}^* = q_w - \mathbf{q}_v = \begin{bmatrix} q_w \\ -\mathbf{q}_v \end{bmatrix} q∗=qw−qv=[qw−qv]
它有如下性质:
q ⊗ q ∗ = q ∗ ⊗ q = q w 2 + q x 2 + q y 2 + q z 2 = [ q w 2 + q x 2 + q y 2 + q z 2 0 v ] \mathbf{q} \otimes \mathbf{q}^* = \mathbf{q}^* \otimes \mathbf{q} = q_w^2 + q_x^2 + q_y^2 + q_z^2 = \begin{bmatrix} q_w^2 + q_x^2 + q_y^2 + q_z^2 \\ \mathbf{0}_v \end{bmatrix} q⊗q∗=q∗⊗q=qw2+qx2+qy2+qz2=[qw2+qx2+qy2+qz20v]
以及:
( p ⊗ q ) ∗ = q ∗ ⊗ p ∗ (\mathbf{p} \otimes \mathbf{q})^* = \mathbf{q}^* \otimes \mathbf{p}^* (p⊗q)∗=q∗⊗p∗
1.2.5 范数
四元数 q \mathbf{q} q 的范数(norm)定义为:
∥ q ∥ = q ⊗ q ∗ = q ∗ ⊗ q = q w 2 + q x 2 + q y 2 + q z 2 ∈ R \|\mathbf{q}\| = \sqrt{\mathbf{q} \otimes \mathbf{q}^*} = \sqrt{\mathbf{q}^* \otimes \mathbf{q}} = \sqrt{q_w^2 + q_x^2 + q_y^2 + q_z^2} \in \mathbb{R} ∥q∥=q⊗q∗=q∗⊗q=qw2+qx2+qy2+qz2∈R
它满足乘积的范数等于范数的乘积:
∥ p ⊗ q ∥ = ∥ q ⊗ p ∥ = ∥ p ∥ ∥ q ∥ \|\mathbf{p} \otimes \mathbf{q}\| = \|\mathbf{q} \otimes \mathbf{p}\| = \|\mathbf{p}\| \|\mathbf{q}\| ∥p⊗q∥=∥q⊗p∥=∥p∥∥q∥
1.2.6 逆元素
四元数 q \mathbf{q} q 的逆元素(inverse)定义为 q − 1 \mathbf{q}^{-1} q−1,它满足:
q ⊗ q − 1 = q − 1 ⊗ q = q 1 \mathbf{q} \otimes \mathbf{q}^{-1} = \mathbf{q}^{-1} \otimes \mathbf{q} = \mathbf{q}_1 q⊗q−1=q−1⊗q=q1
其中 q 1 \mathbf{q}_1 q1 为单位元。 q − 1 \mathbf{q}^{-1} q−1 可以用下式计算:
q − 1 = q ∗ / ∥ q ∥ 2 \mathbf{q}^{-1} = \mathbf{q}^* / \|\mathbf{q}\|^2 q−1=q∗/∥q∥2
1.2.7 单位四元数
单位四元数(unit quaternion)是范数等于1的四元数,即 ∥ q ∥ = 1 \|\mathbf{q}\|=1 ∥q∥=1。对单位四元数,有:
q − 1 = q ∗ \mathbf{q}^{-1} = \mathbf{q}^* q−1=q∗
这个性质意味着,当将单位四元数解释为空间旋转算子时,其逆旋转可以用共轭四元数实现。单位四元数总可以写成如下形式:
q = [ cos θ u sin θ ] \mathbf{q} = \begin{bmatrix} \cos\theta \\ \mathbf{u}\sin\theta \end{bmatrix} q=[cosθusinθ]
其中 u = u x i + u y j + u z k \mathbf{u}=u_xi+u_yj+u_zk u=uxi+uyj+uzk 是单位向量, θ \theta θ 是实数。
由性质可知,带有乘法 ⊗ \otimes ⊗ 的单位四元数构成一个非交换群,其中逆元素等于共轭四元数。
Python代码示例:
# 共轭
q = np.array([1, 2, 3, 4])
q_conj = np.array([q[0], -q[1], -q[2], -q[3]])
print(q_conj) # [ 1 -2 -3 -4]
print(quat_mult(q, q_conj)) # [30 0 0 0]
print(quat_mult(p, q_conj) == quat_mult(q_conj, p)) # [ True True True True]
# 范数
q_norm = np.sqrt(np.sum(q**2))
print(q_norm) # 5.477225575051661
# 逆
q_inv = q_conj / q_norm**2
print(q_inv) # [ 0.03333333 -0.06666667 -0.1 -0.13333333]
print(quat_mult(q, q_inv)) # [1. 0. 0. 0.]
# 单位四元数
q_unit = q / q_norm
print(q_unit) # [0.18257419 0.36514837 0.54772256 0.73029674]
print(np.sum(q_unit**2)) # 1.0
print(quat_mult(q_unit, q_unit)) # [ 0.66666667 0.13333333 0.2 0.26666667]
1.3 四元数的其他性质
在四元数的指数和对数的定义中,我们经常会看到 “纯四元数” 和 “一般四元数” 这两个概念。让我来解释一下为什么会有这样的区分,以及 “一般” 这个词的意义。
首先,回顾一下纯四元数的定义:纯四元数是实部为零的四元数,即形如 q = x i + y j + z k \mathbf{q} = x\mathbf{i} + y\mathbf{j} + z\mathbf{k} q=xi+yj+zk 的四元数。纯四元数可以与三维向量 ( x , y , z ) (x, y, z) (x,y,z) 对应。
相比之下,一般四元数是指任意的四元数,即形如 q = w + x i + y j + z k \mathbf{q} = w + x\mathbf{i} + y\mathbf{j} + z\mathbf{k} q=w+xi+yj+zk 的四元数,其中实部 w w w 可以是任意实数,不一定为零。
在定义四元数的指数时,我们首先给出了纯四元数的指数:
e v = cos ∥ v ∥ + v ∥ v ∥ sin ∥ v ∥ , v ∈ H p e^\mathbf{v} = \cos\|\mathbf{v}\| + \frac{\mathbf{v}}{\|\mathbf{v}\|} \sin\|\mathbf{v}\|, \quad \mathbf{v} \in \mathbb{H}_p ev=cos∥v∥+∥v∥vsin∥v∥,v∈Hp
然后,我们利用这个定义,给出了一般四元数的指数:
e q = e w ( cos ∥ v ∥ + v ∥ v ∥ sin ∥ v ∥ ) , q = w + v , v ∈ H p e^\mathbf{q} = e^w (\cos\|\mathbf{v}\| + \frac{\mathbf{v}}{\|\mathbf{v}\|} \sin\|\mathbf{v}\|), \quad \mathbf{q} = w + \mathbf{v}, \mathbf{v} \in \mathbb{H}_p eq=ew(cos∥v∥+∥v∥vsin∥v∥),q=w+v,v∈Hp
可以看到,一般四元数的指数是在纯四元数指数的基础上,乘以实部的指数 e w e^w ew。这个定义之所以成立,是因为实部和虚部在四元数乘法下是可以分离的,即:
( w 1 + v 1 ) ⊗ ( w 2 + v 2 ) = ( w 1 w 2 − v 1 ⋅ v 2 ) + ( w 1 v 2 + w 2 v 1 + v 1 × v 2 ) (w_1 + \mathbf{v}_1) \otimes (w_2 + \mathbf{v}_2) = (w_1w_2 - \mathbf{v}_1 \cdot \mathbf{v}_2) + (w_1\mathbf{v}_2 + w_2\mathbf{v}_1 + \mathbf{v}_1 \times \mathbf{v}_2) (w1+v1)⊗(w2+v2)=(w1w2−v1⋅v2)+(w1v2+w2v1+v1×v2)
这个性质保证了 e w + v = e w e v e^{w + \mathbf{v}} = e^w e^\mathbf{v} ew+v=ewev。
类似地,在定义对数时,我们也是先定义了纯四元数的对数,然后利用它定义一般四元数的对数:
log q = log ∥ q ∥ + v ∥ v ∥ arccos w ∥ q ∥ , q = w + v , v ∈ H p \log \mathbf{q} = \log\|\mathbf{q}\| + \frac{\mathbf{v}}{\|\mathbf{v}\|} \arccos\frac{w}{\|\mathbf{q}\|}, \quad \mathbf{q} = w + \mathbf{v}, \mathbf{v} \in \mathbb{H}_p logq=log∥q∥+∥v∥varccos∥q∥w,q=w+v,v∈Hp
所以,我们之所以用 “一般” 来修饰四元数,是为了强调这些定义适用于所有四元数,而不仅仅是纯四元数。这个词突出了定义的普遍性。
从数学的角度来看,纯四元数的指数和对数定义相对简单,因为它们可以直接对应到三维向量。而一般四元数的指数和对数则需要在纯四元数的基础上进行扩展,以处理实部不为零的情况。
总之,“一般四元数” 这个词强调了指数和对数定义的普遍适用性,同时也暗示了这些定义在纯四元数的基础上进行了扩展。理解这一点有助于我们更好地掌握四元数的指数和对数的性质及其应用。
在使用四元数时,我们需要根据具体的应用场景来选择使用纯四元数还是一般四元数。下面我来解释一下它们的区别和应用。
因为纯四元数(pure quaternion),即形如 q = ( 0 , q x , q y , q z ) \mathbf{q} = (0, q_x, q_y, q_z) q=(0,qx,qy,qz) 的四元数。纯四元数可以与三维向量 ( q x , q y , q z ) (q_x, q_y, q_z) (qx,qy,qz) 一一对应。在某些应用中,我们只需要表示向量,而不需要表示旋转,这时就可以使用纯四元数。
例如,在三维几何中,我们可以用纯四元数来表示点、方向、平面的法向量等。在物理模拟中,我们可以用纯四元数来表示速度、加速度、力、力矩等。这些量都是向量,没有旋转的概念。
另一个重要的应用是,纯四元数可以用来表示三维向量的叉乘。给定两个纯四元数 p = ( 0 , p x , p y , p z ) \mathbf{p} = (0, p_x, p_y, p_z) p=(0,px,py,pz) 和 q = ( 0 , q x , q y , q z ) \mathbf{q} = (0, q_x, q_y, q_z) q=(0,qx,qy,qz),它们的四元数乘积 p ⊗ q \mathbf{p} \otimes \mathbf{q} p⊗q 的向量部分正好等于 p \mathbf{p} p 和 q \mathbf{q} q 对应向量的叉乘。这个性质在某些计算中非常有用,也会在后面章节中推导介绍。
相比之下,一般四元数(general quaternion)是指实部不为零的,即形如 q = ( q w , q x , q y , q z ) \mathbf{q} = (q_w, q_x, q_y, q_z) q=(qw,qx,qy,qz) 的四元数。一般四元数可以表示旋转,这是它们最重要的应用。单位四元数(即模长为1的四元数)与三维旋转有一一对应的关系,这就是为什么我们常用四元数来表示姿态的原因。
在IMU(惯性测量单元)的数据处理中,我们通常使用纯四元数来表示角速度(angular velocity)。这是因为角速度本质上是一个向量,它表示旋转的轴和速率,但不表示旋转的起始姿态。具体来说,如果我们将角速度 ω = ( ω x , ω y , ω z ) \boldsymbol{\omega} = (\omega_x, \omega_y, \omega_z) ω=(ωx,ωy,ωz) 写成纯四元数的形式 ω = ( 0 , ω x , ω y , ω z ) \boldsymbol{\omega} = (0, \omega_x, \omega_y, \omega_z) ω=(0,ωx,ωy,ωz),那么姿态四元数 q \mathbf{q} q 的微分方程可以写成:
q ˙ = 1 2 q ⊗ ω \dot{\mathbf{q}} = \frac{1}{2} \mathbf{q} \otimes \boldsymbol{\omega} q˙=21q⊗ω
这个方程描述了姿态如何随角速度变化,它是IMU积分的基础。
总结一下:
- 纯四元数用于表示向量,如点、方向、速度、角速度等。它们没有旋转的概念。
- 一般四元数用于表示旋转,特别是单位四元数与三维旋转有直接对应。
- 在IMU数据处理中,我们用纯四元数表示角速度,用一般四元数表示姿态。这是因为角速度是一个向量,而姿态是一个旋转。
了解这些区别和应用,可以帮助我们在实际问题中正确地选择和使用四元数。无论是进行三维几何计算、物理模拟,还是传感器数据融合,四元数都提供了一个强大而优雅的数学工具。
下面我还是严谨的给出各种四元数运算的数学说明。
1.3.1 四元数换位子
四元数换位子(commutator)定义为:
[ p , q ] = p ⊗ q − q ⊗ p [\mathbf{p}, \mathbf{q}] = \mathbf{p} \otimes \mathbf{q} - \mathbf{q} \otimes \mathbf{p} [p,q]=p⊗q−q⊗p
由(13)可得:
p ⊗ q − q ⊗ p = 2 p v × q v \mathbf{p} \otimes \mathbf{q} - \mathbf{q} \otimes \mathbf{p} = 2 \mathbf{p}_v \times \mathbf{q}_v p⊗q−q⊗p=2pv×qv
这个性质的一个推论是:
p v ⊗ q v − q v ⊗ p v = 2 p v × q v \mathbf{p}_v \otimes \mathbf{q}_v - \mathbf{q}_v \otimes \mathbf{p}_v = 2 \mathbf{p}_v \times \mathbf{q}_v pv⊗qv−qv⊗pv=2pv×qv
四元数换位子(commutator)在很多应用中都有使用,特别是在三维旋转和姿态表示领域。换位子可以用来描述两个旋转的顺序对最终结果的影响。这个性质在物理学、机器人学、计算机图形学等领域都有广泛应用。
下面是一些具体的应用示例:
-
物理学中的角动量和转动
在物理学中,四元数换位子可以用来描述角动量和转动之间的关系。例如,如果我们用四元数表示两个角动量 L 1 \mathbf{L}_1 L1 和 L 2 \mathbf{L}_2 L2,它们的换位子 [ L 1 , L 2 ] [\mathbf{L}_1, \mathbf{L}_2] [L1,L2] 就表示了这两个角动量在三维空间中的非对易性。
-
机器人学中的姿态误差
在机器人学中,四元数常用来表示机器人的姿态。如果我们用四元数 q 1 \mathbf{q}_1 q1 和 q 2 \mathbf{q}_2 q2 分别表示机器人的实际姿态和目标姿态,那么它们的换位子 [ q 1 , q 2 ] [\mathbf{q}_1, \mathbf{q}_2] [q1,q2] 就可以用来衡量机器人姿态的误差。
在机器人学中,我们常常需要比较机器人的实际姿态和目标姿态之间的差异,以便进行姿态控制和误差修正。四元数换位子提供了一种简洁而有效的方法来衡量这种差异。假设我们用单位四元数 q 1 \mathbf{q}_1 q1 表示机器人的实际姿态,用单位四元数 q 2 \mathbf{q}_2 q2 表示机器人的目标姿态。如果机器人的实际姿态和目标姿态完全一致,那么我们应该有 q 1 = q 2 \mathbf{q}_1 = \mathbf{q}_2 q1=q2,或者等价地, q 1 ⊗ q 2 − 1 = 1 \mathbf{q}_1 \otimes \mathbf{q}_2^{-1} = 1 q1⊗q2−1=1。
现在,我们来看四元数换位子 [ q 1 , q 2 ] [\mathbf{q}_1, \mathbf{q}_2] [q1,q2]:
[ q 1 , q 2 ] = q 1 ⊗ q 2 − q 2 ⊗ q 1 [\mathbf{q}_1, \mathbf{q}_2] = \mathbf{q}_1 \otimes \mathbf{q}_2 - \mathbf{q}_2 \otimes \mathbf{q}_1 [q1,q2]=q1⊗q2−q2⊗q1
如果 q 1 = q 2 \mathbf{q}_1 = \mathbf{q}_2 q1=q2,那么显然有 [ q 1 , q 2 ] = 0 [\mathbf{q}_1, \mathbf{q}_2] = 0 [q1,q2]=0。
反之,如果 [ q 1 , q 2 ] ≠ 0 [\mathbf{q}_1, \mathbf{q}_2] \neq 0 [q1,q2]=0,那么就意味着 q 1 \mathbf{q}_1 q1 和 q 2 \mathbf{q}_2 q2 并不相等,也就是说,机器人的实际姿态和目标姿态之间存在误差。
更进一步,我们可以将换位子 [ q 1 , q 2 ] [\mathbf{q}_1, \mathbf{q}_2] [q1,q2] 看作是一个向量,它的方向表示了姿态误差的轴,而它的模长则表示了姿态误差的角度。具体来说,如果我们将换位子写成 [ q 1 , q 2 ] = [ 0 , v ] [\mathbf{q}_1, \mathbf{q}_2] = [0, \mathbf{v}] [q1,q2]=[0,v] 的形式,那么姿态误差可以解释为绕单位向量 v / ∥ v ∥ \mathbf{v}/\|\mathbf{v}\| v/∥v∥ 旋转 2 arcsin ( ∥ v ∥ / 2 ) 2\arcsin(\|\mathbf{v}\|/2) 2arcsin(∥v∥/2) 弧度。
因此,通过计算四元数换位子 [ q 1 , q 2 ] [\mathbf{q}_1, \mathbf{q}_2] [q1,q2],我们可以方便地衡量机器人实际姿态和目标姿态之间的误差,这对于进行姿态控制和误差修正非常有用。
下面是一个简单的Python代码示例,演示如何使用四元数换位子来计算姿态误差:
import numpy as np def quaternion_inverse(q): w, x, y, z = q return np.array([w, -x, -y, -z]) / (w**2 + x**2 + y**2 + z**2) def quaternion_multiply(q1, q2): w1, x1, y1, z1 = q1 w2, x2, y2, z2 = q2 w = w1 * w2 - x1 * x2 - y1 * y2 - z1 * z2 x = w1 * x2 + x1 * w2 + y1 * z2 - z1 * y2 y = w1 * y2 - x1 * z2 + y1 * w2 + z1 * x2 z = w1 * z2 + x1 * y2 - y1 * x2 + z1 * w2 return np.array([w, x, y, z]) def quaternion_commutator(q1, q2): return quaternion_multiply(q1, q2) - quaternion_multiply(q2, q1) def attitude_error(q1, q2): commutator = quaternion_commutator(q1, q2) v = commutator[1:] angle = 2 * np.arcsin(np.linalg.norm(v) / 2) axis = v / np.linalg.norm(v) return angle, axis # 示例用法 q1 = np.array([0.707, 0, 0, 0.707]) # 绕z轴旋转90度 q2 = np.array([1, 0, 0, 0]) # 零旋转 angle, axis = attitude_error(q1, q2) print(f'Attitude error: {np.rad2deg(angle):.2f} degrees around axis {axis}')
在这个例子中,我们定义了两个四元数
q1
和q2
,分别表示机器人的实际姿态(绕z轴旋转90度)和目标姿态(零旋转)。然后我们使用attitude_error
函数计算了这两个姿态之间的误差,结果表明,机器人需要绕[0, 0, 1]
轴(即z轴)旋转90
度才能达到目标姿态。这就是四元数换位子在衡量机器人姿态误差方面的应用。它提供了一种简洁、直观且数学上严谨的方法,使我们能够方便地分析和控制机器人的姿态。
-
计算机图形学中的旋转插值
在计算机图形学中,四元数常用来进行三维旋转的插值。给定两个旋转四元数 p \mathbf{p} p 和 q \mathbf{q} q,我们可以用下面的公式来计算它们之间的插值:
r ( t ) = sin ( ( 1 − t ) θ ) sin ( θ ) p + sin ( t θ ) sin ( θ ) q \mathbf{r}(t) = \frac{\sin((1-t)\theta)}{\sin(\theta)}\mathbf{p} + \frac{\sin(t\theta)}{\sin(\theta)}\mathbf{q} r(t)=sin(θ)sin((1−t)θ)p+sin(θ)sin(tθ)q
其中 θ \theta θ 是 p \mathbf{p} p 和 q \mathbf{q} q 的换位子的角度,即 θ = arccos ( ( p ⋅ q ) s ) \theta = \arccos((\mathbf{p} \cdot \mathbf{q})_s) θ=arccos((p⋅q)s)。
下面是一个使用Python和Numpy库进行四元数换位子计算的代码示例:
import numpy as np
def quaternion_multiply(q1, q2):
w1, x1, y1, z1 = q1
w2, x2, y2, z2 = q2
w = w1 * w2 - x1 * x2 - y1 * y2 - z1 * z2
x = w1 * x2 + x1 * w2 + y1 * z2 - z1 * y2
y = w1 * y2 - x1 * z2 + y1 * w2 + z1 * x2
z = w1 * z2 + x1 * y2 - y1 * x2 + z1 * w2
return np.array([w, x, y, z])
def quaternion_commutator(p, q):
return quaternion_multiply(p, q) - quaternion_multiply(q, p)
# 示例用法
p = np.array([0, 1, 0, 0]) # 绕x轴旋转90度
q = np.array([0, 0, 1, 0]) # 绕y轴旋转90度
commutator = quaternion_commutator(p, q)
print(commutator) # 输出: [0, 0, 0, 2]
在这个例子中,我们定义了两个四元数 p
和 q
,分别表示绕x轴和y轴旋转90度。然后我们计算了它们的换位子 commutator
,结果是 [0, 0, 0, 2]
,这表明了这两个旋转的非对易性。
总之,四元数换位子是描述三维旋转非对易性的重要工具,在多个领域都有广泛的应用。理解和应用这一概念,可以帮助我们更好地分析和解决三维旋转和姿态表示中的问题。
1.3.2 纯四元数的乘积
对于纯四元数 Q = q v Q=\mathbf{q}_v Q=qv 或 q = [ 0 , q v ] \mathbf{q}=[0, \mathbf{q}_v] q=[0,qv],可得:
p v ⊗ q v = − p v ⊤ q v + p v × q v = [ − p v ⊤ q v p v × q v ] \mathbf{p}_v \otimes \mathbf{q}_v = -\mathbf{p}_v^\top \mathbf{q}_v + \mathbf{p}_v \times \mathbf{q}_v = \begin{bmatrix} -\mathbf{p}_v^\top \mathbf{q}_v \\ \mathbf{p}_v \times \mathbf{q}_v \end{bmatrix} pv⊗qv=−pv⊤qv+pv×qv=[−pv⊤qvpv×qv]
这意味着:
q v ⊗ q v = − q v ⊤ q v = − ∥ q v ∥ 2 \mathbf{q}_v \otimes \mathbf{q}_v = -\mathbf{q}_v^\top \mathbf{q}_v = -\|\mathbf{q}_v\|^2 qv⊗qv=−qv⊤qv=−∥qv∥2
对于纯单位四元数 u ∈ H p \mathbf{u} \in \mathbb{H}_p u∈Hp 且 ∥ u ∥ = 1 \|\mathbf{u}\|=1 ∥u∥=1,有:
u ⊗ u = − 1 \mathbf{u} \otimes \mathbf{u} = -1 u⊗u=−1
这与标准虚数情况 i ⋅ i = − 1 i \cdot i=-1 i⋅i=−1 类似。
1.3.3 纯四元数的幂
定义 q n , n ∈ N \mathbf{q}^n, n \in \mathbb{N} qn,n∈N 为使用四元数乘法 ⊗ \otimes ⊗ 的 q \mathbf{q} q 的 n n n 次幂。如果 v \mathbf{v} v 是纯四元数,令 v = u θ \mathbf{v}=\mathbf{u}\theta v=uθ,其中 θ = ∥ v ∥ ∈ R , u \theta=\|\mathbf{v}\| \in \mathbb{R}, \mathbf{u} θ=∥v∥∈R,u 为单位向量,则可得循环模式:
v 2 = − θ 2 , v 3 = − u θ 3 , v 4 = θ 4 , v 5 = u θ 5 , v 6 = − θ 6 , ⋯ \mathbf{v}^2 = -\theta^2, \quad \mathbf{v}^3 = -\mathbf{u} \theta^3, \quad \mathbf{v}^4 = \theta^4, \quad \mathbf{v}^5 = \mathbf{u} \theta^5, \quad \mathbf{v}^6 = -\theta^6, \quad \cdots v2=−θ2,v3=−uθ3,v4=θ4,v5=uθ5,v6=−θ6,⋯
对于纯单位四元数
u
=
u
x
i
+
u
y
j
+
u
z
k
\mathbf{u} = u_x i + u_y j + u_z k
u=uxi+uyj+uzk,我们有
u
⊗
u
=
−
1
\mathbf{u} \otimes \mathbf{u} = -1
u⊗u=−1,类似于虚数单位
i
i
i 满足
i
2
=
−
1
i^2=-1
i2=−1。因此,连续应用四元数乘法可以得到:
u
2
=
u
⊗
u
=
−
1
\mathbf{u}^2 = \mathbf{u} \otimes \mathbf{u} = -1
u2=u⊗u=−1
u
3
=
u
⊗
u
⊗
u
=
−
u
\mathbf{u}^3 = \mathbf{u} \otimes \mathbf{u} \otimes \mathbf{u} = -\mathbf{u}
u3=u⊗u⊗u=−u
u
4
=
u
⊗
u
⊗
u
⊗
u
=
(
−
1
)
⊗
(
−
1
)
=
1
\mathbf{u}^4 = \mathbf{u} \otimes \mathbf{u} \otimes \mathbf{u} \otimes \mathbf{u} = (-1) \otimes (-1) = 1
u4=u⊗u⊗u⊗u=(−1)⊗(−1)=1
u
5
=
u
⊗
u
⊗
u
⊗
u
⊗
u
=
1
⊗
u
=
u
\mathbf{u}^5 = \mathbf{u} \otimes \mathbf{u} \otimes \mathbf{u} \otimes \mathbf{u} \otimes \mathbf{u} = 1 \otimes \mathbf{u} = \mathbf{u}
u5=u⊗u⊗u⊗u⊗u=1⊗u=u
u
6
=
u
⊗
u
⊗
u
⊗
u
⊗
u
⊗
u
=
u
⊗
(
−
1
)
=
−
u
\mathbf{u}^6 = \mathbf{u} \otimes \mathbf{u} \otimes \mathbf{u} \otimes \mathbf{u} \otimes \mathbf{u} \otimes \mathbf{u} = \mathbf{u} \otimes (-1) = -\mathbf{u}
u6=u⊗u⊗u⊗u⊗u⊗u=u⊗(−1)=−u
可以看到,从
u
6
\mathbf{u}^6
u6 开始,结果又回到了
−
1
-1
−1,于是模式重新开始循环。
1.3.4 纯四元数的指数
四元数指数(exponential)类似于普通指数函数,定义为绝对收敛的幂级数:
e q = ∑ k = 0 ∞ 1 k ! q k ∈ H e^\mathbf{q} = \sum_{k=0}^\infty \frac{1}{k!} \mathbf{q}^k \in \mathbb{H} eq=∑k=0∞k!1qk∈H
显然,实四元数的指数与普通实数指数函数完全一致。
更有趣的是,纯四元数 v = v x i + v y j + v z k \mathbf{v}=v_xi+v_yj+v_zk v=vxi+vyj+vzk 的指数定义为:
e v = ∑ k = 0 ∞ 1 k ! v k ∈ H e^\mathbf{v} = \sum_{k=0}^\infty \frac{1}{k!} \mathbf{v}^k \in \mathbb{H} ev=∑k=0∞k!1vk∈H
令 v = u θ \mathbf{v}=\mathbf{u}\theta v=uθ,其中 θ = ∥ v ∥ ∈ R , u \theta=\|\mathbf{v}\| \in \mathbb{R}, \mathbf{u} θ=∥v∥∈R,u 为单位向量,并考虑(37),我们将级数中的标量项和向量项分组:
e u θ = ( 1 − θ 2 2 ! + θ 4 4 ! + ⋯ ) + ( u θ − u θ 3 3 ! + u θ 5 5 ! + ⋯ ) e^\mathbf{u\theta} = \left(1 - \frac{\theta^2}{2!} + \frac{\theta^4}{4!} + \cdots \right) + \left(\mathbf{u}\theta - \frac{\mathbf{u}\theta^3}{3!} + \frac{\mathbf{u}\theta^5}{5!} + \cdots \right) euθ=(1−2!θ2+4!θ4+⋯)+(uθ−3!uθ3+5!uθ5+⋯)
并分别识别出 cos θ \cos\theta cosθ 和 sin θ \sin\theta sinθ 的泰勒级数。这导致:
e v = e u θ = cos θ + u sin θ = [ cos θ u sin θ ] e^\mathbf{v} = e^\mathbf{u\theta} = \cos\theta + \mathbf{u}\sin\theta = \begin{bmatrix} \cos\theta \\ \mathbf{u}\sin\theta \end{bmatrix} ev=euθ=cosθ+usinθ=[cosθusinθ]
这构成了欧拉公式 e i θ = cos θ + i sin θ e^{i\theta}=\cos\theta+i\sin\theta eiθ=cosθ+isinθ 在四元数中的漂亮推广。注意到 ∥ e v ∥ 2 = cos 2 θ + sin 2 θ = 1 \|e^\mathbf{v}\|^2 = \cos^2\theta + \sin^2\theta = 1 ∥ev∥2=cos2θ+sin2θ=1,纯四元数的指数总是一个单位四元数。还要注意共轭的性质:
e − v = ( e v ) ∗ e^{-\mathbf{v}} = (e^\mathbf{v})^* e−v=(ev)∗
对于小角度四元数,我们通过展开 sin θ \sin\theta sinθ 和 cos θ \cos\theta cosθ 的泰勒级数并截断来避免在 u = v / ∥ v ∥ \mathbf{u}=\mathbf{v}/\|\mathbf{v}\| u=v/∥v∥ 中除以零,获得不同精度的近似:
e v ≈ [ 1 − θ 2 / 2 v ( 1 − θ 2 / 6 ) ] ≈ [ 1 v ] → θ → 0 [ 1 0 ] e^\mathbf{v} \approx \begin{bmatrix} 1 - \theta^2/2 \\ \mathbf{v}(1 - \theta^2/6) \end{bmatrix} \approx \begin{bmatrix} 1 \\ \mathbf{v} \end{bmatrix} \xrightarrow{\theta \to 0} \begin{bmatrix} 1 \\ \mathbf{0} \end{bmatrix} ev≈[1−θ2/2v(1−θ2/6)]≈[1v]θ→0[10]
1.3.5 一般四元数的指数
由于四元数乘法的非交换性,一般四元数 p \mathbf{p} p 和 q \mathbf{q} q 的指数不满足 e p + q = e p e q e^{\mathbf{p}+\mathbf{q}} = e^\mathbf{p}e^\mathbf{q} ep+q=epeq。然而当其中一个是实四元数时,交换律成立,因此:
e q = e q w + q v = e q w e q v e^\mathbf{q} = e^{q_w + \mathbf{q}_v} = e^{q_w} e^{\mathbf{q}_v} eq=eqw+qv=eqweqv
然后利用(42)和 u θ = q v \mathbf{u}\theta=\mathbf{q}_v uθ=qv,我们得到:
e q = e q w [ cos ∥ q v ∥ q v ∥ q v ∥ sin ∥ q v ∥ ] e^\mathbf{q} = e^{q_w} \begin{bmatrix} \cos\|\mathbf{q}_v\| \\ \frac{\mathbf{q}_v}{\|\mathbf{q}_v\|} \sin\|\mathbf{q}_v\| \end{bmatrix} eq=eqw[cos∥qv∥∥qv∥qvsin∥qv∥]
1.3.6 单位四元数的对数
容易看出,如果 ∥ q ∥ = 1 \|\mathbf{q}\|=1 ∥q∥=1,那么:
log q = log ( cos θ + u sin θ ) = log ( e u θ ) = u θ = [ 0 u θ ] \log\mathbf{q} = \log(\cos\theta + \mathbf{u}\sin\theta) = \log(e^{\mathbf{u}\theta}) = \mathbf{u}\theta = \begin{bmatrix} 0 \\ \mathbf{u}\theta \end{bmatrix} logq=log(cosθ+usinθ)=log(euθ)=uθ=[0uθ]
即单位四元数的对数(logarithm)是一个纯四元数。通过求逆(42)可以很容易地得到角轴参数:
u = q v / ∥ q v ∥ \mathbf{u} = \mathbf{q}_v / \|\mathbf{q}_v\| u=qv/∥qv∥
θ = arctan ( ∥ q v ∥ , q w ) \theta = \arctan(\|\mathbf{q}_v\|, q_w) θ=arctan(∥qv∥,qw)
对于小角度四元数,我们通过展开 arctan ( x ) \arctan(x) arctan(x) 的泰勒级数并截断来避免除以零,获得不同精度的近似:
log ( q ) = u θ = q v ∥ q v ∥ arctan ( ∥ q v ∥ , q w ) ≈ q v q w ( 1 − ∥ q v ∥ 2 3 q w 2 ) ≈ q v → θ → 0 0 \log(\mathbf{q}) = \mathbf{u}\theta = \frac{\mathbf{q}_v}{\|\mathbf{q}_v\|} \arctan(\|\mathbf{q}_v\|, q_w) \approx \mathbf{q}_v \frac{q_w}{(1 - \frac{\|\mathbf{q}_v\|^2}{3q_w^2})} \approx \mathbf{q}_v \xrightarrow{\theta \to 0} \mathbf{0} log(q)=uθ=∥qv∥qvarctan(∥qv∥,qw)≈qv(1−3qw2∥qv∥2)qw≈qvθ→00
1.3.7 一般四元数的对数
通过扩展,如果 q \mathbf{q} q 是一般四元数,那么:
log q = log ( ∥ q ∥ q ∥ q ∥ ) = log ∥ q ∥ + log q ∥ q ∥ = log ∥ q ∥ + u θ = [ log ∥ q ∥ u θ ] \log\mathbf{q} = \log(\|\mathbf{q}\| \frac{\mathbf{q}}{\|\mathbf{q}\|}) = \log\|\mathbf{q}\| + \log\frac{\mathbf{q}}{\|\mathbf{q}\|} = \log\|\mathbf{q}\| + \mathbf{u}\theta = \begin{bmatrix} \log\|\mathbf{q}\| \\ \mathbf{u}\theta \end{bmatrix} logq=log(∥q∥∥q∥q)=log∥q∥+log∥q∥q=log∥q∥+uθ=[log∥q∥uθ]
1.3.8 指数型 q t \mathbf{q}^t qt
对于 q ∈ H \mathbf{q} \in \mathbb{H} q∈H 和 t ∈ R t \in \mathbb{R} t∈R,我们有:
q t = exp ( log ( q t ) ) = exp ( t log ( q ) ) \mathbf{q}^t = \exp(\log(\mathbf{q}^t)) = \exp(t\log(\mathbf{q})) qt=exp(log(qt))=exp(tlog(q))
如果 ∥ q ∥ = 1 \|\mathbf{q}\|=1 ∥q∥=1,我们可以写 q = [ cos θ , u sin θ ] \mathbf{q}=[\cos\theta, \mathbf{u}\sin\theta] q=[cosθ,usinθ],因此 log ( q ) = u θ \log(\mathbf{q})=\mathbf{u}\theta log(q)=uθ,这给出:
q t = exp ( t u θ ) = [ cos t θ u sin t θ ] \mathbf{q}^t = \exp(t\mathbf{u}\theta) = \begin{bmatrix} \cos t\theta \\ \mathbf{u}\sin t\theta \end{bmatrix} qt=exp(tuθ)=[costθusintθ]
因为指数 t t t 最终成为角度 θ \theta θ 的线性乘数,所以它可以看作是线性角度插值器。
下面用Python代码演示四元数的指数和对数运算:
import numpy as np
def quat_exp(q):
"""四元数指数"""
theta = np.linalg.norm(q[1:])
if theta == 0:
return np.array([np.exp(q[0]), 0, 0, 0])
u = q[1:] / theta
q_exp = np.exp(q[0]) * np.concatenate(([np.cos(theta)], u*np.sin(theta)))
return q_exp
def quat_log(q):
"""四元数对数"""
if np.allclose(q[1:], 0):
return np.array([np.log(q[0]), 0, 0, 0])
theta = np.arctan2(np.linalg.norm(q[1:]), q[0])
u = q[1:] / np.linalg.norm(q[1:])
return np.concatenate(([np.log(np.linalg.norm(q))], u*theta))
# 纯四元数指数
v = np.array([0, 1, 2, 3])
q_exp = quat_exp(v)
print(q_exp) # [0.96389246 0.10578332 0.21156663 0.31734995]
# 一般四元数指数
q = np.array([1, 2, 3, 4])
q_exp = quat_exp(q)
print(q_exp) # [ 1.24778829 0.29654009 0.44481014 0.59308019]
# 单位四元数对数
q_unit = q / np.linalg.norm(q)
q_log = quat_log(q_unit)
print(q_log) # [0. 0.36514837 0.54772256 0.73029674]
# 一般四元数对数
q_log = quat_log(q)
print(q_log) # [1.38629436 0.36514837 0.54772256 0.73029674]
# 指数型 q^t
t = 0.5
q_pow = quat_exp(t * quat_log(q_unit))
print(q_pow) # [0.95833278 0.09833209 0.14749813 0.19666418]
至此,我们已经详细介绍了四元数的定义、表示形式、主要性质以及一些重要的运算。接下来我们将探讨四元数在三维旋转和四元数运动学方面的应用。
四元数作为描述三维旋转的工具,在计算机图形学、虚拟现实、机器人运动学、导航系统等领域有着广泛的应用。特别地,它常用于姿态估计和惯性导航中。一个典型的例子是利用IMU(Inertial Measurement Unit,惯性测量单元)进行姿态解算,其中的旋转部分可以用四元数来高效且鲁棒地描述。与欧拉角相比,四元数具有无奇点、运算简洁、易于插值等优点。
在实际应用中,我们往往把四元数作为误差状态Kalman滤波器(Error-State Kalman Filter)的状态变量之一。借助四元数运动学方程,结合IMU测量的角速度信息,可以对物体的姿态进行递推估计。同时,我们还可以融合其他传感器(如视觉、GPS等)的观测信息,通过Kalman更新步骤对姿态进行校正。这种基于ESKF的IMU解算方案在无人机、机器人、智能手机等平台上得到了广泛使用,我将在后面章节中详解讲解。
有兴趣的读者可以进一步阅读以下参考文献:
-
Kuipers, J.B. (1999). Quaternions and rotation sequences: a primer with applications to orbits, aerospace, and virtual reality. Princeton University Press.
-
Solà, J. (2017). Quaternion kinematics for the error-state Kalman filter. arXiv preprint arXiv:1711.02508.
-
Trawny, N. & Roumeliotis, S.I. (2005). Indirect Kalman filter for 3D attitude estimation. University of Minnesota, Dept. of Comp. Sci. & Eng., Tech. Rep, 2.
-
Bloesch, M., Burri, M., Omari, S. et al. (2017). Iterated extended Kalman filter based visual-inertial odometry using direct photometric feedback. The International Journal of Robotics Research, 36(10): 1053-1072.
通过本章的学习,相信读者已经掌握了四元数的基本概念和运算。其实第一章的内容可以到此为止了,但我想提前举一个很重要的实践例子。
讲解如何利用四元数进行IMU数据的积分,以估计物体的运动轨迹。这个例子将很好地展示四元数在实际应用中的作用。
这个例子涉及到后面章节的知识,为了你的理解,我会先在这里提前先介绍下例子里面用到的四元数积分内容,在后续章节中也会系统全面详细介绍
首先说明我会在代码中先实现一段复杂轨迹作为真值(ground truth),然后使用运动学知识分解成IMU传感器的数据,然后利用四元数的知识将IMU的加速度和角速度数据进行处理,先看代码:
# -*- coding: utf-8 -*-
import numpy as np
import matplotlib.pyplot as plt
def euler2rotation(euler_angles):
"""
将欧拉角转换为旋转矩阵。
输入: 欧拉角(roll, pitch, yaw), 单位为弧度。
输出: 旋转矩阵R, 满足v_inertial = R * v_body, 其中v为任意向量。
"""
roll, pitch, yaw = euler_angles
cr, sr = np.cos(roll), np.sin(roll)
cp, sp = np.cos(pitch), np.sin(pitch)
cy, sy = np.cos(yaw), np.sin(yaw)
RIb = np.array([[cy*cp, cy*sp*sr - sy*cr, sy*sr + cy*cr*sp],
[sy*cp, cy*cr + sy*sr*sp, sp*sy*cr - cy*sr],
[-sp, cp*sr, cp*cr]])
return RIb
def euler_rates2body_rates(euler_angles):
"""
将欧拉角速率转换为物体坐标系下的角速度。
输入: 欧拉角(roll, pitch, yaw), 单位为弧度。
输出: 欧拉角速率与物体角速度之间的雅可比矩阵。
数学原理: 对旋转矩阵R求导可得 dR/dt = [w]_x * R, 其中w为物体角速度, [w]_x为叉乘矩阵。
将R用欧拉角参数化并求导, 可得到二者之间的雅可比矩阵。
"""
roll, pitch, _ = euler_angles
cr, sr = np.cos(roll), np.sin(roll)
cp, sp = np.cos(pitch), np.sin(pitch)
R = np.array([[1, 0, -sp],
[0, cr, sr*cp],
[0, -sr, cr*cp]])
return R
def plot_trajectory_3d(data, gt_data):
"""
绘制3D轨迹图。
输入:
- data: 集成得到的轨迹数据, 形状为(N, 3)的numpy数组。
- gt_data: 真实轨迹数据, 形状为(N, 3)的numpy数组。
"""
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
ax.plot(data[:, 0], data[:, 1], data[:, 2], label='Integrated')
ax.plot(gt_data[:, 0], gt_data[:, 1], gt_data[:, 2], label='Ground Truth')
ax.legend()
ax.set_xlabel('X')
ax.set_ylabel('Y')
ax.set_zlabel('Z')
ax.set_title('3D Trajectory')
plt.show()
def motion_model(t):
"""
生成IMU测量值和真实轨迹。
输入: 时间t, 单位为秒。
输出:
- position: 真实位置, 形状为(3,)的numpy数组。
- imu_gyro: IMU角速度测量值, 形状为(3,)的numpy数组, 单位为rad/s。
- imu_acc: IMU加速度测量值, 形状为(3,)的numpy数组, 单位为m/s^2。
- Rwb: 从物体坐标系到世界坐标系的旋转矩阵, 形状为(3, 3)的numpy数组。
- velocity: 真实速度, 形状为(3,)的numpy数组, 单位为m/s。
"""
# 轨迹参数
ellipse_x = 15
ellipse_y = 20
z = 1
K1 = 10
K = np.pi / 10
# 位置
position = np.array([ellipse_x * np.cos(K * t) + 5,
ellipse_y * np.sin(K * t) + 5,
z * np.sin(K1 * K * t) + 5])
dp = np.array([-K * ellipse_x * np.sin(K * t),
K * ellipse_y * np.cos(K * t),
z * K1 * K * np.cos(K1 * K * t)])
K2 = K * K
ddp = np.array([-K2 * ellipse_x * np.cos(K * t),
-K2 * ellipse_y * np.sin(K * t),
-z * K1 * K1 * K2 * np.sin(K1 * K * t)])
# 旋转
k_roll = 0.1
k_pitch = 0.2
euler_angles = np.array([k_roll * np.cos(t),
k_pitch * np.sin(t),
K * t])
euler_angles_rates = np.array([-k_roll * np.sin(t),
k_pitch * np.cos(t),
K])
Rwb = euler2rotation(euler_angles)
imu_gyro = euler_rates2body_rates(euler_angles).dot(euler_angles_rates)
gn = np.array([0, 0, -9.81]) # 重力加速度
imu_acc = Rwb.T.dot(ddp - gn) # 加速度计测量值
return position, imu_gyro, imu_acc, Rwb, dp
def main():
params = {
't_start': 0, # 仿真开始时间
't_end': 20, # 仿真结束时间
'imu_frequency': 100 # IMU频率
}
dt = 1.0 / params['imu_frequency'] # 时间步长
t = np.arange(params['t_start'], params['t_end'], dt) # 时间序列
gt_trajectory = [] # 存储真实轨迹
imu_data = [] # 存储IMU数据
for ti in t:
position, gyro, acc, Rwb, velocity = motion_model(ti)
gt_trajectory.append(position)
imu_data.append({
'timestamp': ti,
'imu_gyro': gyro, # 角速度
'imu_acc': acc, # 加速度
'Rwb': Rwb, # 旋转矩阵
'twb': position, # 位置
'imu_velocity': velocity # 速度
})
gt_trajectory = np.array(gt_trajectory)
# 初始化变量
Pwb = imu_data[0]['twb'] # 初始位置
Qwb = imu_data[0]['Rwb'] # 初始旋转(四元数)
Vw = imu_data[0]['imu_velocity'] # 初始速度
gw = np.array([0, 0, -9.81]) # 重力加速度(世界坐标系)
# 初始化结果数组
integrated_trajectory = []
# 积分IMU数据
for i in range(1, len(imu_data)):
imu_gyro = imu_data[i]['imu_gyro']
imu_acc = imu_data[i]['imu_acc']
# 四元数积分
# 使用一阶龙格-库塔法(Runge-Kutta)积分四元数
# 积分公式为: q(t+dt) = q(t) + 0.5 * dt * omega(t) * q(t)
# 其中q为四元数, omega为角速度, dt为时间步长
dtheta_half = imu_gyro * dt / 2.0
dq = np.concatenate(([1], dtheta_half))
dq = dq / np.linalg.norm(dq) # 归一化, 保持四元数为单位四元数
# IMU动力学模型 - 欧拉积分
# 使用欧拉积分计算位置、速度和姿态
# 加速度计测量值需先从机体坐标系转到世界坐标系, 再减去重力加速度
acc_w = Qwb.dot(imu_acc) + gw
# 欧拉积分公式:
# p(t+dt) = p(t) + v(t)*dt + 0.5*a(t)*dt^2
# v(t+dt) = v(t) + a(t)*dt
Qwb = Qwb.dot(euler2rotation(dtheta_half * 2))
Pwb = Pwb + Vw * dt + 0.5 * dt * dt * acc_w
Vw = Vw + acc_w * dt
integrated_trajectory.append(Pwb)
integrated_trajectory = np.array(integrated_trajectory)
# 绘制3D轨迹图
plot_trajectory_3d(integrated_trajectory, gt_trajectory)
if __name__ == '__main__':
main()
代码中绘制的图像:
这段代码中使用了四元数来表示物体的姿态,并利用四元数进行了姿态的积分。下面我来解释一下其中的数学原理。
四元数积分
在代码中,我们使用了一阶龙格-库塔法(Runge-Kutta)来对四元数进行积分。积分公式为:
q ( t + Δ t ) = q ( t ) + 1 2 Δ t ⋅ ω ( t ) ⊗ q ( t ) \mathbf{q}(t+\Delta t) = \mathbf{q}(t) + \frac{1}{2} \Delta t \cdot \boldsymbol{\omega}(t) \otimes \mathbf{q}(t) q(t+Δt)=q(t)+21Δt⋅ω(t)⊗q(t)
其中 q \mathbf{q} q为四元数, ω \boldsymbol{\omega} ω为角速度, Δ t \Delta t Δt为时间步长, ⊗ \otimes ⊗表示四元数乘法。
这个公式可以这样理解:在时间 t t t到 t + Δ t t+\Delta t t+Δt之间,假设角速度保持不变,那么物体将绕着角速度向量 ω \boldsymbol{\omega} ω旋转 Δ t ⋅ ∥ ω ∥ \Delta t \cdot \|\boldsymbol{\omega}\| Δt⋅∥ω∥弧度。这个旋转可以用四元数 Δ q = [ cos ( Δ θ 2 ) , u sin ( Δ θ 2 ) ] \Delta \mathbf{q} = [\cos(\frac{\Delta\theta}{2}), \mathbf{u}\sin(\frac{\Delta\theta}{2})] Δq=[cos(2Δθ),usin(2Δθ)]来表示,其中 Δ θ = ∥ ω ∥ Δ t \Delta\theta = \|\boldsymbol{\omega}\| \Delta t Δθ=∥ω∥Δt, u = ω ∥ ω ∥ \mathbf{u} = \frac{\boldsymbol{\omega}}{\|\boldsymbol{\omega}\|} u=∥ω∥ω为单位角速度向量。
根据四元数乘法的定义,乘积 Δ q ⊗ q ( t ) \Delta \mathbf{q} \otimes \mathbf{q}(t) Δq⊗q(t)表示先进行 q ( t ) \mathbf{q}(t) q(t)表示的旋转,再进行 Δ q \Delta \mathbf{q} Δq表示的旋转,得到的结果就是 q ( t + Δ t ) \mathbf{q}(t+\Delta t) q(t+Δt)。因此,我们可以用 q ( t ) + 1 2 Δ t ⋅ ω ( t ) ⊗ q ( t ) \mathbf{q}(t) + \frac{1}{2} \Delta t \cdot \boldsymbol{\omega}(t) \otimes \mathbf{q}(t) q(t)+21Δt⋅ω(t)⊗q(t)来近似 q ( t + Δ t ) \mathbf{q}(t+\Delta t) q(t+Δt),这就是上面积分公式的由来。
姿态更新
在得到新的四元数后,我们可以用它来更新物体的姿态矩阵 R w b \mathbf{R}_{wb} Rwb,即从物体坐标系到世界坐标系的旋转矩阵。根据四元数与旋转矩阵的关系,有:
R w b = [ q w 2 + q x 2 − q y 2 − q z 2 2 ( q x q y − q w q z ) 2 ( q x q z + q w q y ) 2 ( q x q y + q w q z ) q w 2 − q x 2 + q y 2 − q z 2 2 ( q y q z − q w q x ) 2 ( q x q z − q w q y ) 2 ( q y q z + q w q x ) q w 2 − q x 2 − q y 2 + q z 2 ] \mathbf{R}_{wb} = \begin{bmatrix} q_w^2+q_x^2-q_y^2-q_z^2 & 2(q_xq_y-q_wq_z) & 2(q_xq_z+q_wq_y) \\ 2(q_xq_y+q_wq_z) & q_w^2-q_x^2+q_y^2-q_z^2 & 2(q_yq_z-q_wq_x) \\ 2(q_xq_z-q_wq_y) & 2(q_yq_z+q_wq_x) & q_w^2-q_x^2-q_y^2+q_z^2 \end{bmatrix} Rwb= qw2+qx2−qy2−qz22(qxqy+qwqz)2(qxqz−qwqy)2(qxqy−qwqz)qw2−qx2+qy2−qz22(qyqz+qwqx)2(qxqz+qwqy)2(qyqz−qwqx)qw2−qx2−qy2+qz2
其中
(
q
w
,
q
x
,
q
y
,
q
z
)
(q_w, q_x, q_y, q_z)
(qw,qx,qy,qz)为四元数的分量。这个公式看起来很复杂,但它的本质就是将四元数"扩展"成一个旋转矩阵。在代码中,我们直接调用了euler2rotation
函数完成了这一步。
速度和位置更新
最后,根据IMU测得的加速度数据,我们可以更新物体的速度和位置。但要注意,加速度计测量的是物体坐标系下的加速度,我们需要先将其转换到世界坐标系下,再积分得到速度和位置的增量。转换的方法很简单,就是用旋转矩阵 R w b \mathbf{R}_{wb} Rwb左乘加速度向量:
a w = R w b a b \mathbf{a}_w = \mathbf{R}_{wb} \mathbf{a}_b aw=Rwbab
其中 a b \mathbf{a}_b ab为物体坐标系下的加速度, a w \mathbf{a}_w aw为世界坐标系下的加速度。另外,加速度计还会受到重力加速度的影响,因此我们需要在积分之前先减去重力加速度 g w \mathbf{g}_w gw。
速度和位置的更新公式为:
v ( t + Δ t ) = v ( t ) + ( a w − g w ) Δ t \mathbf{v}(t+\Delta t) = \mathbf{v}(t) + (\mathbf{a}_w - \mathbf{g}_w) \Delta t v(t+Δt)=v(t)+(aw−gw)Δt
p ( t + Δ t ) = p ( t ) + v ( t ) Δ t + 1 2 ( a w − g w ) Δ t 2 \mathbf{p}(t+\Delta t) = \mathbf{p}(t) + \mathbf{v}(t) \Delta t + \frac{1}{2} (\mathbf{a}_w - \mathbf{g}_w) \Delta t^2 p(t+Δt)=p(t)+v(t)Δt+21(aw−gw)Δt2
这两个公式分别对应于匀加速运动的速度和位置公式:
v ( t ) = v 0 + a t v(t) = v_0 + at v(t)=v0+at
x ( t ) = x 0 + v 0 t + 1 2 a t 2 x(t) = x_0 + v_0t + \frac{1}{2}at^2 x(t)=x0+v0t+21at2
其中 v 0 v_0 v0和 x 0 x_0 x0是初始速度和位置, a a a是加速度, t t t是时间。在我们的例子中,时间间隔为 Δ t \Delta t Δt,初始速度和位置是上一时刻的速度 v ( t ) \mathbf{v}(t) v(t)和位置 p ( t ) \mathbf{p}(t) p(t),加速度是 a w − g w \mathbf{a}_w - \mathbf{g}_w aw−gw,也就是IMU测得的加速度减去重力加速度。
将这两个公式应用于每个时间步,就可以得到物体的速度和位置序列,这就是欧拉积分的基本原理。在代码中,我们用以下两行来实现:
Pwb = Pwb + Vw * dt + 0.5 * dt * dt * acc_w
Vw = Vw + acc_w * dt
其中Pwb
和Vw
分别是位置和速度,acc_w
是减去重力加速度后的加速度,dt
是时间步长。
这就是代码中的欧拉积分部分。
总结一下,这个示例代码展示了如何使用四元数对IMU数据进行积分,估计物体的运动轨迹。其中涉及到了四元数的几个重要应用:
-
用四元数表示物体的姿态,而不是欧拉角。这是因为四元数没有万向锁问题,且积分更加简单和稳定。
-
用四元数进行姿态积分。给定角速度,我们可以通过四元数乘法更新物体的姿态。
-
用四元数将加速度从物体坐标系转到世界坐标系,再进行速度和位置的积分。
这些正是四元数在3D运动估计中的典型用法。通过本例,相信读者可以更好地理解四元数的作用和使用方法。
当然,本例中我们只使用了最简单的欧拉积分。在实际应用中,通常会使用更高阶的数值积分方法,如龙格-库塔法,计划第四章介绍。此外,由于IMU数据往往含有噪声和偏差,长时间积分会导致误差累积。因此,实际系统中往往会结合其他传感器(如视觉、GPS等)的数据,通过滤波或优化的方法来估计运动,而不是单纯的积分。常用的方法包括卡尔曼滤波、图优化等(计划在第五章介绍)。
在第二章节中,我将进一步探讨三维旋转群 SO(3)、旋转矩阵与四元数之间的关系、四元数插值算法等话题。敬请期待!