三维空间刚体运动4-1:四元数表示变换(各形式相互转换加代码——下篇)
三维空间刚体运动4-1:四元数表示变换(各形式相互转换加代码——下篇)
- 4. 四元数到其它旋转表示的相互转换
- 4.1 旋转向量
- 4.2 旋转矩阵
- 4.3 欧拉角
- 4.3.1 转换关系
- 4.3.2 转换中的万象锁问题
- 5. 四元数的其他性质
- 5.1 旋转的复合
- 5.2 双倍覆盖
- 5.3 指数形式
- 5.4 幂函数性质
- 6. 实践
- 6.1 四元数旋转运算
- 6.2 坐标变换
序:本篇系列文章参照高翔老师《视觉SLAM十四讲从理论到实践》,讲解三维空间刚体运动,为读者打下坚实的数学基础。博文将原第三讲分为五部分来讲解,其中四元数部分较多较复杂,又分为四部分。如果读者急于实践,可直接阅读第五部分的机器人运动轨迹,此部分详细讲解了安装准备工作。此系列总体目录如下:
- 旋转矩阵和变换矩阵;
- 旋转向量表示旋转;
- 欧拉角表示旋转;
- 四元数包括以下部分:
4-1.1 四元数表示变换——上篇;
4-1.2 四元数表示变换——下篇;
4-2. 四元数线性插值方法:LinEuler/LinMat/Lerp/Nlerp/Slerp;
4-3. 四元数多点插值方法:Squad;
4-4. 四元数多点连续解析解插值方法:Spicv;
4-5. 四元数多点离散数值解插值方法:Sping。 - 实践:SLAM中显示机器人运动轨迹及相机位姿。
4. 四元数到其它旋转表示的相互转换
任意单位四元数描述了一个旋转,该旋转也可用旋转向量、旋转矩阵或欧拉角描述。现在来考察四元数与旋转向量、旋转矩阵以及欧拉角的相互转换关系。
4.1 旋转向量
本节介绍旋转向量与四元数之间的转换关系。
绕坐标轴的多次旋转可以等效为绕某一转轴旋转一定的角度。假设已知等效旋转轴方向单位旋转向量 u u u的坐标为 u = [ u x u y u z ] u=\begin{bmatrix} u_{x}\\ u_{y}\\ u_{z} \end{bmatrix} u= uxuyuz ,旋转角为 θ \theta θ。根据3.2.3推导的定理3D旋转公式(一般情况四元数型),设其等效的单位四元数为 q = [ q 0 , q 1 , q 2 , q 3 ] q=[q_{0},q_{1},q_{2},q_{3}] q=[q0,q1,q2,q3],则有: { q 0 = c o s ( 1 2 θ ) q 1 = u x s i n ( 1 2 θ ) q 2 = u y s i n ( 1 2 θ ) q 3 = u z s i n ( 1 2 θ ) (4.1) \left\{\begin{matrix} q_{0}=cos(\frac{1}{2}\theta )\\ q_{1}=u_{x}sin(\frac{1}{2}\theta )\\ q_{2}=u_{y}sin(\frac{1}{2}\theta )\\ q_{3}=u_{z}sin(\frac{1}{2}\theta ) \end{matrix}\right.\tag{4.1} ⎩ ⎨ ⎧q0=cos(21θ)q1=uxsin(21θ)q2=uysin(21θ)q3=uzsin(21θ)(4.1)
同理,当已知单位四元数 q = [ q 0 , q 1 , q 2 , q 3 ] q=[q_{0},q_{1},q_{2},q_{3}] q=[q0,q1,q2,q3],其对应的旋转角 θ \theta θ和旋转向量 u u u为: { θ = 2 arccos q 0 [ u x , u y , u z ] T = [ q 1 , q 2 , q 3 ] T / sin ( θ 2 ) (4.2) \left\{\begin{matrix} \theta=2\arccos q_{0}\\ [u_{x},u_{y},u_{z}]^{T}=[q_{1},q_{2},q_{3}]^{T}/\sin(\frac{\theta}{2}) \end{matrix}\right.\tag{4.2} {θ=2arccosq0[ux,uy,uz]T=[q1,q2,q3]T/sin(2θ)(4.2)
4.2 旋转矩阵
已知单位四元数 q q q,根据博文《三维空间刚体运动2:旋转向量与罗德里格斯公式》中的罗德里格斯公式展开式(2.3)及本文公式(4.2),将单位旋转向量 u u u及旋转角 θ \theta θ替换为单位四元数 q q q,省去计算过程,得到单位四元数到旋转矩阵的转换公式为: R = [ 1 − 2 q 2 2 − 2 q 3 2 2 ( q 1 q 2 − q 0 q 3 ) 2 ( q 1 q 3 + q 0 q 2 ) 2 ( q 1 q 2 + q 0 q 3 ) 1 − 2 q 1 2 − 2 q 3 2 2 ( q 2 q 3 − q 0 q 1 ) 2 ( q 1 q 3 − q 0 q 2 ) 2 ( q 2 q 3 + q 0 q 1 ) 1 − 2 q 1 2 − 2 q 2 2 ] (4.3) R=\begin{bmatrix} 1-2q_{2}^{2}-2q_{3}^{2} & 2(q_{1}q_{2}-q_{0}q_{3}) & 2(q_{1}q_{3}+q_{0}q_{2})\\ 2(q_{1}q_{2}+q_{0}q_{3}) & 1-2q_{1}^{2}-2q_{3}^{2} & 2(q_{2}q_{3}-q_{0}q_{1})\\ 2(q_{1}q_{3}-q_{0}q_{2}) & 2(q_{2}q_{3}+q_{0}q_{1}) & 1-2q_{1}^{2}-2q_{2}^{2} \end{bmatrix}\tag{4.3} R= 1−2q22−2q322(q1q2+q0q3)2(q1q3−q0q2)2(q1q2−q0q3)1−2q12−2q322(q2q3+q0q1)2(q1q3+q0q2)2(q2q3−q0q1)1−2q12−2q22 (4.3)
假设已知变换矩阵: R = [ r 11 r 12 r 13 r 21 r 22 r 23 r 31 r 32 r 33 ] R=\begin{bmatrix} r_{11} & r_{12} & r_{13}\\ r_{21} & r_{22} & r_{23}\\ r_{31} & r_{32} & r_{33} \end{bmatrix} R= r11r21r31r12r22r32r13r23r33 根据公式(4.3),推导对应的单位四元数为: { q 0 = 1 2 1 + r 11 + r 22 + r 33 q 1 = r 32 − r 23 4 q 0 q 2 = r 13 − r 31 4 q 0 q 3 = r 21 − r 12 4 q 0 (4.4) \left\{\begin{matrix} q_{0}=\frac{1}{2}\sqrt{1+r_{11}+r_{22}+r_{33}}\\ q_{1}=\frac{r_{32}-r_{23}}{4q_{0}}\\ q_{2}=\frac{r_{13}-r_{31}}{4q_{0}}\\ q_{3}=\frac{r_{21}-r_{12}}{4q_{0}} \end{matrix}\right.\tag{4.4} ⎩ ⎨ ⎧q0=211+r11+r22+r33q1=4q0r32−r23q2=4q0r13−r31q3=4q0r21−r12(4.4)
4.3 欧拉角
4.3.1 转换关系
那么将Z-Y-X欧拉角(或RPY角:绕固定坐标系的X-Y-Z依次旋转α,β,γ角)转换为四元数: q = [ cos γ 2 0 0 sin γ 2 ] [ cos β 2 0 sin β 2 0 ] [ cos α 2 sin α 2 0 0 ] = [ cos α 2 cos β 2 cos γ 2 + sin α 2 sin β 2 sin γ 2 sin α 2 cos β 2 cos γ 2 − cos α 2 sin β 2 sin γ 2 cos α 2 sin β 2 cos γ 2 + sin α 2 cos β 2 sin γ 2 cos α 2 cos β 2 sin γ 2 − sin α 2 sin β 2 cos γ 2 ] q=\begin{bmatrix} \cos\frac{\gamma}{2}\\ 0\\ 0\\ \sin\frac{\gamma}{2} \end{bmatrix} \begin{bmatrix} \cos\frac{\beta}{2}\\ 0\\ \sin\frac{\beta}{2}\\ 0 \end{bmatrix} \begin{bmatrix} \cos\frac{\alpha}{2}\\ \sin\frac{\alpha}{2}\\ 0\\ 0 \end{bmatrix} =\begin{bmatrix} \cos\frac{\alpha}{2}\cos\frac{\beta}{2}\cos\frac{\gamma}{2}+\sin\frac{\alpha}{2}\sin\frac{\beta}{2}\sin\frac{\gamma}{2}\\ \sin\frac{\alpha}{2}\cos\frac{\beta}{2}\cos\frac{\gamma}{2}-\cos\frac{\alpha}{2}\sin\frac{\beta}{2}\sin\frac{\gamma}{2}\\ \cos\frac{\alpha}{2}\sin\frac{\beta}{2}\cos\frac{\gamma}{2}+\sin\frac{\alpha}{2}\cos\frac{\beta}{2}\sin\frac{\gamma}{2}\\ \cos\frac{\alpha}{2}\cos\frac{\beta}{2}\sin\frac{\gamma}{2}-\sin\frac{\alpha}{2}\sin\frac{\beta}{2}\cos\frac{\gamma}{2} \end{bmatrix} q= cos2γ00sin2γ cos2β0sin2β0 cos2αsin2α00 = cos2αcos2βcos2γ+sin2αsin2βsin2γsin2αcos2βcos2γ−cos2αsin2βsin2γcos2αsin2βcos2γ+sin2αcos2βsin2γcos2αcos2βsin2γ−sin2αsin2βcos2γ
根据上面的公式可以求出逆解,即由四元数 q = ( q 0 , q 1 , q 2 , q 3 ) q=(q_{0},q_{1},q_{2},q_{3}) q=(q0,q1,q2,q3)到欧拉角的转换为: [ α β γ ] = [ a t a n 2 ( 2 ( q 0 q 1 + q 2 q 3 ) , 1 − 2 ( q 1 2 + q 2 2 ) ) arcsin ( 2 ( q 0 q 2 − q 1 q 3 ) a t a n 2 ( 2 ( q 0 q 3 + q 1 q 2 ) , 1 − 2 ( q 2 2 + q 3 2 ) ) ] \begin{bmatrix} \alpha\\ \beta\\ \gamma \end{bmatrix} =\begin{bmatrix} atan2(2(q_{0}q_{1}+q_{2}q_{3}),1-2(q^{2}_{1}+q^{2}_{2}))\\ \arcsin (2(q_{0}q_{2}-q_{1}q_{3})\\ atan2(2(q_{0}q_{3}+q_{1}q_{2}),1-2(q^{2}_{2}+q^{2}_{3})) \end{bmatrix} αβγ = atan2(2(q0q1+q2q3),1−2(q12+q22))arcsin(2(q0q2−q1q3)atan2(2(q0q3+q1q2),1−2(q22+q32)) 这里使用了 a t a n 2 ( y , x ) atan2(y,x) atan2(y,x)而不是 a r c t a n ( y x ) arctan(\frac{y}{x}) arctan(xy),因为 a r c t a n ( y x ) arctan(\frac{y}{x}) arctan(xy)的取值范围为 [ − π 2 , π 2 ] [-\frac{\pi}{2},\frac{\pi}{2}] [−2π,2π],只有 180 ° 180° 180°,而绕某个轴旋转时范围是 360 ° 360° 360°, a t a n 2 ( y , x ) atan2(y,x) atan2(y,x)正好满足需求。 a t a n 2 ( y , x ) atan2(y,x) atan2(y,x)是一个函数,在C语言里返回的是指方位角,函数原型为 d o u b l e a t a n 2 ( d o u b l e y , d o u b l e x ) double \space atan2(double y, double x) double atan2(doubley,doublex) ,返回以弧度表示的 y / x y/x y/x 的反正切。y 和 x 的值的符号决定了正确的象限。也可以理解为计算复数 x + y i x+yi x+yi 的辐角,计算时atan2 比 atan 稳定。
4.3.2 转换中的万象锁问题
另外需要注意的就是奇异性问题,即万向锁,下面分析这种情况。当刚体绕Y轴旋转了 90 ° 90° 90°(俯仰角pitch=90)时,如何计算横滚角roll和偏航角yaw?这时会发生自由度丢失的情况,即Yaw和Roll会变为一个自由度。此时再使用上面的公式根据四元数计算欧拉角会出现问题: arcsin ( 2 ( q 0 q 2 − q 1 q 3 ) \arcsin (2(q_{0}q_{2}-q_{1}q_{3}) arcsin(2(q0q2−q1q3)的定义域为 [ − 1 , 1 ] [-1,1] [−1,1],当 2 ( q 0 q 2 − q 1 q 3 ) = ± 0.5 2(q_{0}q_{2}-q_{1}q_{3})=\pm 0.5 2(q0q2−q1q3)=±0.5时(在程序中浮点数不能直接进行等于判断,要使用合理的阈值),俯仰角 β \beta β为 ± 90 ° \pm 90° ±90°,将其带入正向公式计算出四元数 ( q 0 , q 1 , q 2 , q 3 ) (q_{0},q_{1},q_{2},q_{3}) (q0,q1,q2,q3),然后可以发现逆向公式中atan2函数中的参数全部为0,即出现了 0 0 \frac{0}{0} 00的情况!无法计算。
当 β = 90 ° \beta=90° β=90°时, sin β 2 = cos β 2 = 0.707 \sin \frac{\beta}{2}=\cos \frac{\beta}{2}=0.707 sin2β=cos2β=0.707,将其带入公式中有: q = [ q 0 q 1 q 2 q 3 ] = [ 0.707 ( cos α 2 cos γ 2 + sin α 2 sin γ 2 ) 0.707 ( sin α 2 cos γ 2 − cos α 2 sin γ 2 ) 0.707 ( cos α 2 cos γ 2 + sin α 2 sin γ 2 ) 0.707 ( cos α 2 sin γ 2 − sin α 2 cos γ 2 ) ] = [ 0.707 cos α − γ 2 0.707 sin α − γ 2 0.707 cos α − γ 2 − 0.707 sin α − γ 2 ] q=\begin{bmatrix} q_{0}\\ q_{1}\\ q_{2}\\ q_{3} \end{bmatrix} =\begin{bmatrix} 0.707(\cos\frac{\alpha}{2}\cos\frac{\gamma}{2}+\sin \frac{\alpha}{2}\sin \frac{\gamma}{2})\\ 0.707(\sin\frac{\alpha}{2}\cos\frac{\gamma}{2}-\cos\frac{\alpha}{2}\sin \frac{\gamma}{2})\\ 0.707(\cos\frac{\alpha}{2}\cos\frac{\gamma}{2}+\sin \frac{\alpha}{2}\sin \frac{\gamma}{2})\\ 0.707(\cos\frac{\alpha}{2}\sin\frac{\gamma}{2}-\sin \frac{\alpha}{2}\cos\frac{\gamma}{2}) \end{bmatrix} =\begin{bmatrix} 0.707\cos\frac{\alpha - \gamma}{2}\\ 0.707\sin \frac{\alpha - \gamma}{2}\\ 0.707\cos \frac{\alpha - \gamma}{2}\\ -0.707\sin \frac{\alpha - \gamma}{2} \end{bmatrix} q= q0q1q2q3 = 0.707(cos2αcos2γ+sin2αsin2γ)0.707(sin2αcos2γ−cos2αsin2γ)0.707(cos2αcos2γ+sin2αsin2γ)0.707(cos2αsin2γ−sin2αcos2γ) = 0.707cos2α−γ0.707sin2α−γ0.707cos2α−γ−0.707sin2α−γ 则 q 1 q 0 = − q 3 q 2 = tan α − γ 2 \frac{q_{1}}{q_{0}}=-\frac{q_{3}}{q_{2}}=\tan\frac{\alpha - \gamma}{2} q0q1=−q2q3=tan2α−γ,于是有: α − γ = 2 a t a n 2 ( q 1 , q 0 ) \alpha - \gamma = 2atan2(q_{1},q_{0}) α−γ=2atan2(q1,q0)通常令 α = 0 \alpha=0 α=0,这时 γ = − 2 a t a n 2 ( q 1 , q 0 ) \gamma = -2atan2(q_{1},q_{0}) γ=−2atan2(q1,q0)。当俯仰角 β \beta β为-90°,即刚体竖直向下时的情况也与之类似,可以推导出奇异姿态时的计算公式。
将四元数转换为欧拉角的代码可以参考附件。需要注意欧拉角有12种旋转次序,而上面推导的公式是按照Z-Y-X顺序进行的,所以有时会在网上看到不同的转换公式(因为对应着不同的旋转次序),在使用时一定要注意旋转次序是什么。比如ADAMS软件里就默认Body 3-1-3次序,即Z-X-Z欧拉角,而VREP中则按照X-Y-Z欧拉角旋转。另外万向锁问题代码的Z-Y-X欧拉角代码见类CameraSpacePoint。
5. 四元数的其他性质
为更全面理解四元数和方便引入slerp插值,这一节补充四元数的其它性质:旋转复合、双倍覆盖和指数形式。
5.1 旋转的复合
旋转的复合其实在我们之前证明 q 2 = q q = [ c o s ( 2 θ ) , s i n ( 2 θ ) u ] q^{2}=qq=[cos(2\theta),sin(2\theta)u] q2=qq=[cos(2θ),sin(2θ)u]的时候就已经涉及到一点了,但是这里我们考虑的是更一般的情况。
假设有两个表示沿着不同轴、不同角度旋转的四元数 q 1 、 q 2 q_{1}、q_{2} q1、q2。首先,我们实施 q 1 q_{1} q1的变换,变换之后的 v ′ v^{'} v′为 v ′ = q 1 v q 1 ∗ v^{'}=q_{1}vq^{*}_{1} v′=q1vq1∗接下来,对 v ′ v^{'} v′进行 q 2 q_{2} q2的变换,得到 v ′ ′ v^{''} v′′: v ′ ′ = q 2 v ′ q 2 ∗ = q 2 q 1 v q 1 ∗ q 2 ∗ = q n e t v q n e t ∗ v^{''}=q_{2}v^{'}q^{*}_{2}=q_{2}q_{1}vq^{*}_{1}q^{*}_{2}=q_{net}vq^{*}_{net} v′′=q2v′q2∗=q2q1vq1∗q2∗=qnetvqnet∗其中 q n e t = q 2 q 1 q_{net}=q_{2}q_{1} qnet=q2q1,另外写成上面的形式,还用到性质 q 1 ∗ q 2 ∗ = ( q 2 q 1 ) ∗ q^{*}_{1}q^{*}_{2}=(q_{2}q_{1})^{*} q1∗q2∗=(q2q1)∗。
注意四元数乘法的顺序,这和矩阵、复数的复合非常相似,都是从右向左叠加。另外要注意的是, q 1 q_{1} q1与 q 2 q_{2} q2的等价旋转 q n e t q_{net} qnet并不是沿着 q 1 q_{1} q1与 q 2 q_{2} q2的两个旋转轴进行的两次旋转,它是沿着一个全新的旋转轴进行的一个等价旋转,仅仅只有旋转的结果相同。
5.2 双倍覆盖
四元数与 3D 旋转的关系并不是一对一的,同一个 3D 旋转可以使用两个不同的四元数来表示.对任意的单位四元数 q = [ c o s ( 1 2 θ ) , s i n ( 1 2 θ ) u ] q=[cos(\frac{1}{2}\theta),sin(\frac{1}{2}\theta)u] q=[cos(21θ),sin(21θ)u], q q q与 − q −q −q代表的是同一个旋转。如果 q q q表示的是沿着旋转轴 u u u旋转 θ θ θ度,那么 − q −q −q代表的是沿着相反的旋转轴 − u −u −u旋转 ( 2 π − θ ) (2π − θ) (2π−θ),负负得正: − q = [ − c o s ( 1 2 θ ) , − s i n ( 1 2 θ ) u ] = [ c o s ( π − 1 2 θ ) , s i n ( π − 1 2 θ ) ( − u ) ] \begin{aligned} -q &= [-cos(\frac{1}{2}\theta),-sin(\frac{1}{2}\theta)u] \\ &= [cos(\pi - \frac{1}{2}\theta),sin(\pi - \frac{1}{2}\theta)(-u)]\end{aligned} −q=[−cos(21θ),−sin(21θ)u]=[cos(π−21θ),sin(π−21θ)(−u)]所以,这个四元数旋转的角度为 2 ( π − 1 2 θ ) = 2 π − θ 2(\pi - \frac{1}{2}\theta)=2\pi-\theta 2(π−21θ)=2π−θ,从下面的图中我们可以看到,这两个旋转是完全等价的:
其实从四元数的旋转公式中也能推导出相同的结果: ( − q ) v ( − q ) ∗ = ( − 1 ) 2 q v q ∗ = q v q ∗ (-q)v(-q)^{*}=(-1)^{2}qvq^{*}=qvq^{*} (−q)v(−q)∗=(−1)2qvq∗=qvq∗所以,我们经常说单位四元数与3D旋转有一个「2对1满射同态」(2-1 Surjective Homomorphism) 关系,或者说单位四元数双倍覆盖(Double Cover)了3D旋转。它的严格证明会用到一些李群的知识,这里不再拓展。
有一点需要注意的是,虽然 q q q与 − q −q −q是两个不同的四元数,但是由于旋转矩阵中的每一项都包含了四元数两个分量的乘积,它们的旋转矩阵是完全相同的,即旋转矩阵并不会出现双倍覆盖的问题。
5.3 指数形式
在讨论复数的时候,我们利用欧拉公式将2D的旋转写成了 v ′ = e i θ v v^{'}=e^{i\theta v} v′=eiθv这样的指数形式。实际上,我们也可以利用四元数将 3D 旋转写成类似的形式。
类似于复数的欧拉公式,四元数也有一个类似的公式。如果 u u u是一个单位向量,那么对于单位四元数 u q = [ 0 , u ] u_{q}= [0, u] uq=[0,u],有: e u θ = c o s ( θ ) + u q s i n ( θ ) = c o s ( θ ) + u s i n ( θ ) e^{u\theta}=cos(\theta)+u_{q}sin(\theta)=cos(\theta)+usin(\theta) euθ=cos(θ)+uqsin(θ)=cos(θ)+usin(θ)这也就是说, q = [ cos ( θ ) , sin ( θ ) u ] q = [\cos(\theta), \sin(\theta)u] q=[cos(θ),sin(θ)u]可以使用指数表示为 e u θ e^{u\theta} euθ。这个公式的证明与欧拉公式的证明非常类似,直接使用级数展开就可以了,这里不再扩展。
注意,因为 u u u是一个单位向量, u 2 = [ − u ⋅ u , 0 ] = − ∥ u ∥ 2 = − 1 u^{2}= [−u · u, 0] = −∥u∥^{2}= −1 u2=[−u⋅u,0]=−∥u∥2=−1.这与欧拉公式中的 i i i是非常类似的。有了指数型的表示方式,我们就能够将之前四元数的旋转公式改写为指数形式了,由此可得到定义:
3D旋转公式(指数型):任意向量 v v v沿着以单位向量定义的旋转轴 u u u旋转 θ θ θ角度之后的 v ′ v^{'} v′可以使用四元数的指数表示。令 w = [ 0 , v ] , u q = [ 0 , u ] w= [0, v], u_{q}= [0, u] w=[0,v],uq=[0,u],那么: w ′ = e u q θ 2 v e − u q θ 2 w^{'}=e^{u_{q}\frac{\theta}{2}}ve^{-u_{q}\frac{\theta}{2}} w′=euq2θve−uq2θ有了四元数的指数定义,我们就能够定义四元数的更多运算了。首先是自然对数 l o g log log,对任意单位四元数 q = [ c o s ( θ ) , s i n ( θ ) u ] = e u q θ q = [cos(θ), sin(θ)u]=e^{u_{q}\theta} q=[cos(θ),sin(θ)u]=euqθ: l o g ( q ) = l o g ( e u q θ ) = [ 0 , u θ ] log(q)=log(e^{u_{q}\theta})=[0,{u\theta}] log(q)=log(euqθ)=[0,uθ]接下来是单位四元数的幂运算: q t = ( e u q θ ) t = e u q ( t θ ) = [ c o s ( t θ ) , s i n ( t θ ) u ] q^{t}=(e^{u_{q}\theta})^{t}=e^{u_{q}(t\theta)}=[cos(tθ), sin(tθ)u] qt=(euqθ)t=euq(tθ)=[cos(tθ),sin(tθ)u]可以看到,一个单位四元数的 t t t次幂等同于将它的旋转角度缩放至 t t t倍,并且不会改变它的旋转轴( u u u必须是单位向量,所以一般不能与 t t t结合)。这些运算会在之后讨论四元数插值时非常有用。限于篇幅,四元数插值的讲解见下一篇博客《三维空间刚体运动4-2:四元数插值(lerp,Nlerp,Slerp,Squad等)》
5.4 幂函数性质
上面给出四元数的指数形式后,可推导出一个重要的指函数性质,这一性质下一篇将用到。首先看推论:
四元数推论1 :设 q ∈ H 1 , p = [ a , b v ] q\in \mathbb{H}_{1},p=[a,b\mathbf{v}] q∈H1,p=[a,bv],其中 a , b , r ∈ R , v ∈ R 3 a,b,r\in \mathbb{R},\mathbf{v}\in\mathbb{R}^{3} a,b,r∈R,v∈R3,如果 q [ r , v ] q ∗ = [ r , v ′ ] q[r,\mathbf{v}]q^{*}=[r,\mathbf{v^{'}}] q[r,v]q∗=[r,v′],那么 q [ a , b v ] q ∗ = [ a , b v ′ ] q[a,b\mathbf{v}]q^{*}=[a,b\mathbf{v^{'}}] q[a,bv]q∗=[a,bv′]。
推论证明: q p q ∗ = q [ a , b v ] q ∗ = q b [ a b , v ] q ∗ = b [ a b , v ′ ] = [ a , b v ′ ] \begin{aligned}qpq^{*}&=q[a,b\mathbf{v}]q^{*}\\&=qb[\frac{a}{b},\mathbf{v}]q^{*}\\&=b[\frac{a}{b},\mathbf{v^{'}}]\\&=[a,b\mathbf{v^{'}}]\end{aligned} qpq∗=q[a,bv]q∗=qb[ba,v]q∗=b[ba,v′]=[a,bv′]根据推论,得出以下定理:
四元数定理2 :设 q , p ∈ H 1 , p = [ cos θ , sin θ v ] , t ∈ R q,p\in \mathbb{H}_{1},p=[\cos\theta,\sin\theta\mathbf{v}],t\in\mathbb{R} q,p∈H1,p=[cosθ,sinθv],t∈R,那么 q p t q ∗ = ( q p q ∗ ) t qp^{t}q^{*}=(qpq^{*})^{t} qptq∗=(qpq∗)t。
定理证明:
根据推论,存在 v ′ ∈ R 3 \mathbf{v^{'}}\in\mathbb{R}^{3} v′∈R3,使得 q [ cos θ , sin θ v ] q ∗ = [ cos θ , sin θ v ′ ] q[\cos\theta,\sin\theta\mathbf{v}]q^{*}=[\cos\theta,\sin\theta\mathbf{v^{'}}] q[cosθ,sinθv]q∗=[cosθ,sinθv′],因此得到: q p t q ∗ = q ( exp ( t log p ) ) q ∗ = q ( exp ( t [ 0 , θ v ] ) ) q ∗ = q ( exp [ 0 , t θ v ] ) q ∗ = q ( [ cos t θ , sin t θ v ] ) q ∗ = [ cos t θ , sin t θ v ′ ] = exp ( t [ 0 , θ v ′ ] ) = exp ( t log [ cos θ , sin θ v ′ ] ) = exp ( t log ( q p q ∗ ) ) = ( q p q ∗ ) t \begin{aligned} qp^{t}q^{*} &=q(\exp(t\log p))q^{*} \\&= q(\exp(t[0,\theta\mathbf{v}]))q^{*} \\&= q(\exp[0,t\theta\mathbf{v}])q^{*} \\&= q([\cos t\theta,\sin t\theta\mathbf{v}])q^{*} \\ &= [\cos t\theta,\sin t\theta\mathbf{v^{'}}] \\&= \exp(t[0,\theta\mathbf{v^{'}}]) \\&= \exp(t\log[\cos \theta,\sin \theta\mathbf{v^{'}}]) \\&= \exp(t\log(qpq^{*})) \\&= (qpq^{*})^{t}\end{aligned} qptq∗=q(exp(tlogp))q∗=q(exp(t[0,θv]))q∗=q(exp[0,tθv])q∗=q([costθ,sintθv])q∗=[costθ,sintθv′]=exp(t[0,θv′])=exp(tlog[cosθ,sinθv′])=exp(tlog(qpq∗))=(qpq∗)t
至此,四元数表示旋转的理论部分讲解完了。
6. 实践
现在,我们通过两个小程序实际演练四元数的运算。其中四元数的基础运算和高阶运算程序实现放在下一讲Slerp中。
6.1 四元数旋转运算
第一个小程序是演示四元数的常规运算,包括与旋转矩阵、旋转向量的转换,以及用四元数旋转一个向量,如下所示:
#include<iostream>
#include<cmath>
using namespace std;#include<eigen3/Eigen/Core>
#include<eigen3/Eigen/Geometry>using namespace Eigen;int main(int argc, char **argv){//Eigen/Geometry模块提供了各种旋转和平移的表示,3D旋转矩阵直接使用Matrix3d或Matrix3fMatrix3d rotation_matrix = Matrix3d::Identity();//旋转向量使用AngleAxis,它底层不直接是Matrix,但运算可以当做矩阵,因为重载了运算符AngleAxisd rotation_vector(M_PI/4, Vector3d(0, 0, 1));//设置输出精度cout.precision(3);cout<<"rotation matrix =\n"<<rotation_matrix<<endl;cout<<"rotation vector =\n"<<rotation_vector.matrix()<<endl;//旋转向量转换的矩阵可以直接赋值给旋转矩阵rotation_matrix = rotation_vector.toRotationMatrix();cout<<"rotation vector to Matrix =\n"<<rotation_matrix<<endl;//旋转向量Vector3d v(1, 0, 0);cout<<"v = "<<v.transpose()<<endl;//四元数,可以直接把AngleAxis赋值给四元数,反之亦然Quaterniond q = Quaterniond(rotation_vector);//coeffs:多项式系数(coefficients),其顺序为(x,y,z,w),w为实部,xyz为虚部cout<<"quaternion from rotation vector = "<<q.coeffs().transpose()<<endl;//也可以直接把旋转矩阵赋给它q = Quaterniond(rotation_matrix);cout<<"quaternion from rotation matrix = "<<q.coeffs().transpose()<<endl;//使用四元数旋转一个向量,使用重载的乘法即可//注意q*v在数学上是qvq^{-1}v_rotated = q*v;cout<<"(1,0,0) after rotation = "<<v_rotated.transpose()<<endl;//用常规向量乘法表示qvq^{-1},则计算如下:Quaterniond q_rotate_v = q*Quaterniond(0,1,0,0)*q.inverse();cout<<"should be equal to "<<q_rotate_v.coeffs().transpose()<<endl;return 0;
输出结果如下:
rotation matrix =
1 0 0
0 1 0
0 0 1
rotation vector =0.707 -0.707 00.707 0.707 00 0 1
rotation vector to Matrix =0.707 -0.707 00.707 0.707 00 0 1
v = 1 0 0
v transofrmed = 1.71 3.71 4
quaternion from rotation vector = 0 0 0.383 0.924
quaternion from rotation matrix = 0 0 0.383 0.924
(1,0,0) after rotation = 0.707 0.707 0
should be equal to 0.707 0.707 0 0
请读者注意,程序代码通常和数学表示有一些细微的差别。例如,通过运算符重载,四元数和三维向量可以直接计算乘法,但在数学上则需要先把向量转成虚四元数,再利用四元数乘法进行计算,同样的情况也适用于变换矩阵乘三维向量的情况。总体而言,程序中的用法会比数学公式更灵活。
6.2 坐标变换
下面我们举一个小例子来演示坐标变换。
例子:设有小萝卜一号和小萝卜二号位于世界坐标系中。记世界坐标系为 W W W,小萝卜们的坐标系分别为 R 1 R_{1} R1和 R 2 R_{2} R2。小萝卜一号的位姿为 q 1 = [ 0.35 , 0.2 , 0.3 , 0.1 ] T q_{1}=[0.35,0.2,0.3,0.1]^{T} q1=[0.35,0.2,0.3,0.1]T, t 1 = [ 0.3 , 0.1 , 0.1 ] T t_{1}=[0.3,0.1,0.1]^{T} t1=[0.3,0.1,0.1]T。小萝卜二号的位姿为 q 2 = [ − 0.5 , 0.4 , − 0.1 , 0.2 ] T q_{2}=[-0.5,0.4,-0.1,0.2]^{T} q2=[−0.5,0.4,−0.1,0.2]T, t 2 = [ − 0.1 , 0.5 , 0.3 ] T t_{2}=[-0.1,0.5,0.3]^{T} t2=[−0.1,0.5,0.3]T。这里的 q q q和 t t t表达的是 T R k , W , k = 1 , 2 T_{R_{k},W},k=1,2 TRk,W,k=1,2,也就是世界坐标系到相机坐标系的变换关系。现在,小萝卜一号看到某个点在自身的坐标系下坐标为 p R 1 = [ 0.5 , 0 , 0.2 ] T p_{R_{1}}=[0.5,0,0.2]^{T} pR1=[0.5,0,0.2]T,求该向量在小萝卜二号坐标系下的坐标。
这是一个非常简单,但又具有代表性的例子。在实际场景中你经常需要在同一个机器人的不同部分,或者不同机器人之间转换坐标。计算过程也很简单,只需计算: p R 2 = T R 2 , W T W , R 1 p R 1 p_{R_{2}}=T_{R_{2},W}T_{W,R_{1}}p_{R_{1}} pR2=TR2,WTW,R1pR1下面请看程序:
#include<iostream>
#include<vector>
#include<algorithm>
#include<eigen3/Eigen/Core>
#include<eigen3/Eigen/Geometry>using namespace std;
using namespace Eigen;int main(int argc, char** argv){//位姿四元数q1,q2Quaterniond q1(0.35, 0.2, 0.3, 0.1), q2(-0.5, 0.4, -0.1, 0.2);cout<<"q1.coeffs=\n"<<q1.coeffs()<<endl;cout<<"q2.coeffs=\n"<<q2.coeffs()<<endl;//四元数转换为旋转矩阵cout<<"q1.matrix=\n"<<q1.matrix()<<endl;cout<<"q2.matrix=\n"<<q2.matrix()<<endl;//归一化,转换为单位四元数q1.normalize();q2.normalize();cout<<"q1.normalize="<<q1.matrix()<<endl;cout<<"q2.normalize="<<q2.matrix()<<endl;//位移向量t1,t2,小萝卜一号下的坐标pr1Vector3d t1(0.3, 0.1, 0.1), t2(-0.1, 0.5, 0.3);Vector3d pr1(0.5, 0, 0.2);//Eigen::Isometry3d:欧式变换矩阵4*4Isometry3d Twr1(q1), Tr2w(q2);cout<<"Twr1.matrix="<<Twr1.matrix()<<endl;cout<<"Tr2w.matrix="<<Tr2w.matrix()<<endl;//坐标转换,计算T=Ra+tTwr1.pretranslate(t1);Tr2w.pretranslate(t2);//先将pr1转换为世界坐标系,然后转换为小萝卜二号下的坐标pr2Vector3d pr2 = Tr2w * Twr1.inverse()*pr1;cout<<"pr2.transpose="<<pr2.transpose()<<endl;return 0;
}
输出结果如下:
q1.coeffs=0.20.30.1
0.35
q2.coeffs=0.4
-0.10.2
-0.5
q1.matrix=0.8 0.05 0.250.19 0.9 -0.08
-0.17 0.2 0.74
q2.matrix=0.9 0.12 0.26
-0.28 0.6 0.360.06 -0.44 0.66
q1.normalize=0.238095 0.190476 0.9523810.72381 0.619048 -0.304762-0.647619 0.761905 0.00952381
q2.normalize=0.782609 0.26087 0.565217
-0.608696 0.130435 0.7826090.130435 -0.956522 0.26087
Twr1.matrix=0.238095 0.190476 0.952381 00.72381 0.619048 -0.304762 0-0.647619 0.761905 0.00952381 00 0 0 1
Tr2w.matrix=0.782609 0.26087 0.565217 0
-0.608696 0.130435 0.782609 00.130435 -0.956522 0.26087 00 0 0 1
pr2.transpose=
-0.0309731 0.73499 0.296108
四元数的第一部分讲解到此。
本文基于《视觉SLAM十四讲:从理论到实践》和《Quaternions, Interpolation and Animation》编写,但相对于原文会适当精简,同时为便于全面理解,会收集其他网络好文,根据作者理解,加入一些注解和扩展知识点,如果您觉得还不错,请一键四连(点赞关注收藏评论),让更多的人看到。
参考文献:
- 《视觉SLAM十四讲:从理论到实践》,高翔、张涛等著,中国工信出版社
- Quaternions, Interpolation and Animation
- 四元数与三维旋转
- 四元数与欧拉角(RPY角)的相互转换
- 如何形象地理解四元数?
相关文章:

三维空间刚体运动4-1:四元数表示变换(各形式相互转换加代码——下篇)
三维空间刚体运动4-1:四元数表示变换(各形式相互转换加代码——下篇) 4. 四元数到其它旋转表示的相互转换4.1 旋转向量4.2 旋转矩阵4.3 欧拉角4.3.1 转换关系4.3.2 转换中的万象锁问题 5. 四元数的其他性质5.1 旋转的复合5.2 双倍覆盖5.3 指数…...
PyTorch如何通过 torch.unbind 和torch.stack动态调整张量的维度顺序
笔者一篇博客PyTorch 的 torch.unbind 函数详解与进阶应用:中英双语中有一个例子如下: # 创建一个 3x2x2 的三维张量 x torch.tensor([[[1, 2], [3, 4]],[[5, 6], [7, 8]],[[9, 10], [11, 12]]])# 第一步:沿第 0 维分解为 3 个 2x2 张量 un…...

【Unity3D】报错libil2cpp.so找不到问题
mainTemplate.gradle文件末尾添加: **IL_CPP_BUILD_SETUP** 此报错发生在低版本的Unity升级到高版本后,例如Unity2019升级到Unity2021,而Unity2019默认创建的mainTemplate.gradle文件是不包含**IL_CPP_BUILD_SETUP** 因此会导致libil2cpp.so…...
事件冒泡机制详解
一、事件传播的三个阶段 1. 捕获阶段 事件从最外层元素(如document)开始,沿着 DOM 树向目标元素传播。这个阶段就像是事件的“下行通道”,在这个过程中,事件会经过目标元素的祖先元素。不过,在捕获阶段&a…...

红米Note 9 Pro5G刷LineageOS
LineageOS介绍 LineageOS 是一个基于 Android 的开源操作系统,是面向智能手机和平板电脑等设备的替代性操作系统。它是 CyanogenMod 的继承者,而 CyanogenMod 是曾经非常受欢迎的一个第三方 Android 定制 ROM。 在 2016 年,CyanogenMod 项目因…...

6.3.1 MR实战:计算总分与平均分
在本次实战中,我们的目标是利用Apache Hadoop的MapReduce框架来处理和分析学生成绩数据。具体来说,我们将计算一个包含五名学生五门科目成绩的数据集的总分和平均分。这个过程包括在云主机上准备数据,将成绩数据存储为文本文件,并…...

ARM循环程序和子程序设计
1、计算下列两组数据的累加和并存入到sum1和 sum2 单元中。datal:0x12,0x935,0x17,0x100,0x95,0x345。 data2:0x357,0x778,0x129,0x188,0x190,0x155,0x167。 1.定义数据段 ;定义数据段,类型为data(表示为数据段),权限为可读可写(程序可以读取和修改这…...
静态路由、RIP、OSPF、BGP的区别
静态路由:是管理员手动将路由写入到路由器中,配置简单开销小,但不能适应网络变化,只用于小型的网络 RIP,路由信息协议,属于距离矢量路由协议的一种,根据跳数来判断最优路由,如果跳数…...

知识分享第二十八天-数学篇一
组合.二项式定理.常见导数 组合 让我们通过一个具体的例子来理解组合(Combinations)的概念 假设你有一个装有5个不同颜色球的袋子:红、蓝、绿、黄和紫。你想从中随机抽取3个球, 不考虑顺序,那么你可以有多少种不同的…...
BigDecimal在进行除法运算时需要注意四舍五入的位置
我们在进行A除B的时候,需要将四舍五入的逻辑放入除法的过程中就定义,不要等到A/B结果出来了再去进行四舍五入,这样会出现问题。下面举例 10%3 我们拿10除3为例,很明显,结果是一个除不尽的小数3.3333… 直接除 publi…...
第二部分:进阶主题 14 . 性能优化 --[MySQL轻松入门教程]
MySQL性能优化是一个广泛的话题,它涉及到数据库设计、查询语句的编写、索引的使用、服务器配置等多个方面。下面是一些常见的MySQL性能优化策略: 1. 数据库和表结构优化 下面是三个关于MySQL数据库和表结构优化的具体示例: 示例 1: 合理选…...

Mac电脑设置鼠标的滚轮方向
Mac电脑使用鼠标时,上下滚动,方向与Windows相反,如果要保持与Windows一致,则下载MOS这个软件,然后在MOS中进行配置,就可以让鼠标操作方式与Windows一致。 软件下载地址: https://mos.caldis.me…...

【LDAP】LDAP概念和原理介绍
目录 一、前言 二、什么是LDAP? 2.1 什么是目录服务? 2.2 LDAP的介绍 2.3 为什么要使用LDAP 三、LDAP的主要产品线 四、LDAP的基本模型 4.1 目录树概念 4.2 LDAP常用关键字列表 4.3 objectClass介绍 五、JXplorer工具使用 一、前言 对于许多的…...

Android系统(android app和系统架构)
文章目录 AndroidAndroid Apps四大组件 Android系统Platform API之下:一个微笑内核adb(Android Debug Bridge) Android包管理机制Android的Intent机制参考 Android LinuxFrameworkJVM 在Linux/Java上做了个二次开发?并不完全是:Android定义…...
Android HandlerThread、Looper、MessageQueue 源码分析
Android HandlerThread、Looper、MessageQueue 源码分析 简介 在 Android 开发中,大家应该对 HandlerThread 有一定了解。顾名思义,HandlerThread 是 Thread 的一个子类。与普通的 Thread 不同,Thread 通常一次只能执行一个后台任务&#x…...
HTML知识点详解教程
文章目录 HTML知识点详解教程1. HTML基本语法2. HTML标签详解2.1 分区标签 <div>2.2 标题标签 <h1> ~ <h6>2.3 段落标签 <p>2.4 图片标签 <img>2.5 列表标签 <ul> 和 <ol>无序列表 <ul>有序列表 <ol> 2.6 超链接标签 &l…...

[数据结构#1] 并查集 | FindRoot | Union | 优化 | 应用
目录 1. 并查集原理 问题背景 名称与编号映射 数据结构设计 2. 并查集基本操作 (1) 初始化 (2) 查询根节点 (FindRoot) (3) 合并集合 (Union) (4) 集合操作总结 并查集优化 (1) 路径压缩 (2) 按秩合并 3. 并查集的应用 (1) 统计省份数量 (2) 判断等式方程是否成…...

科研绘图系列:R语言绘制网络图和密度分布图(network density plot)
禁止商业或二改转载,仅供自学使用,侵权必究,如需截取部分内容请后台联系作者! 文章目录 介绍加载R包数据下载图1图2图3图4图5图6图7图8系统信息参考介绍 R语言绘制网络图和密度分布图(network & density plot) 加载R包 library(magrittr) library(dplyr) library(…...

Linux中输入和输出基本过程
1.文件内核级缓冲区 前面在如何理解Linux一切皆文件的特点中提到为了保证在Linux中所有进程访问文件时的方式趋近相 同,在f ile 结构体中存在一个 files_operations 结构体指针,对应的结构体保存所有文件操作的函 数指针(这个结构体也被称为…...
使用 acme.sh 签发和自动续期 ssl https 证书
acme.sh 是一个热度非常高的签发和自动续期 https 证书的工具,虽然官网上提供了充分的操作说明,但是不够简洁,本文以在 nginx 中签发和配置http 为例,列出必要的几个简单步骤。 安装 因为网络原因,github 大部分人是…...

UE5 学习系列(二)用户操作界面及介绍
这篇博客是 UE5 学习系列博客的第二篇,在第一篇的基础上展开这篇内容。博客参考的 B 站视频资料和第一篇的链接如下: 【Note】:如果你已经完成安装等操作,可以只执行第一篇博客中 2. 新建一个空白游戏项目 章节操作,重…...

大型活动交通拥堵治理的视觉算法应用
大型活动下智慧交通的视觉分析应用 一、背景与挑战 大型活动(如演唱会、马拉松赛事、高考中考等)期间,城市交通面临瞬时人流车流激增、传统摄像头模糊、交通拥堵识别滞后等问题。以演唱会为例,暖城商圈曾因观众集中离场导致周边…...

(二)原型模式
原型的功能是将一个已经存在的对象作为源目标,其余对象都是通过这个源目标创建。发挥复制的作用就是原型模式的核心思想。 一、源型模式的定义 原型模式是指第二次创建对象可以通过复制已经存在的原型对象来实现,忽略对象创建过程中的其它细节。 📌 核心特点: 避免重复初…...

屋顶变身“发电站” ,中天合创屋面分布式光伏发电项目顺利并网!
5月28日,中天合创屋面分布式光伏发电项目顺利并网发电,该项目位于内蒙古自治区鄂尔多斯市乌审旗,项目利用中天合创聚乙烯、聚丙烯仓库屋面作为场地建设光伏电站,总装机容量为9.96MWp。 项目投运后,每年可节约标煤3670…...

现代密码学 | 椭圆曲线密码学—附py代码
Elliptic Curve Cryptography 椭圆曲线密码学(ECC)是一种基于有限域上椭圆曲线数学特性的公钥加密技术。其核心原理涉及椭圆曲线的代数性质、离散对数问题以及有限域上的运算。 椭圆曲线密码学是多种数字签名算法的基础,例如椭圆曲线数字签…...

用docker来安装部署freeswitch记录
今天刚才测试一个callcenter的项目,所以尝试安装freeswitch 1、使用轩辕镜像 - 中国开发者首选的专业 Docker 镜像加速服务平台 编辑下面/etc/docker/daemon.json文件为 {"registry-mirrors": ["https://docker.xuanyuan.me"] }同时可以进入轩…...
大数据学习(132)-HIve数据分析
🍋🍋大数据学习🍋🍋 🔥系列专栏: 👑哲学语录: 用力所能及,改变世界。 💖如果觉得博主的文章还不错的话,请点赞👍收藏⭐️留言Ǵ…...

项目部署到Linux上时遇到的错误(Redis,MySQL,无法正确连接,地址占用问题)
Redis无法正确连接 在运行jar包时出现了这样的错误 查询得知问题核心在于Redis连接失败,具体原因是客户端发送了密码认证请求,但Redis服务器未设置密码 1.为Redis设置密码(匹配客户端配置) 步骤: 1).修…...

从物理机到云原生:全面解析计算虚拟化技术的演进与应用
前言:我的虚拟化技术探索之旅 我最早接触"虚拟机"的概念是从Java开始的——JVM(Java Virtual Machine)让"一次编写,到处运行"成为可能。这个软件层面的虚拟化让我着迷,但直到后来接触VMware和Doc…...

软件工程 期末复习
瀑布模型:计划 螺旋模型:风险低 原型模型: 用户反馈 喷泉模型:代码复用 高内聚 低耦合:模块内部功能紧密 模块之间依赖程度小 高内聚:指的是一个模块内部的功能应该紧密相关。换句话说,一个模块应当只实现单一的功能…...