三维空间刚体运动3:欧拉角表示旋转(全面理解万向锁、RPY角和欧拉角)-程序员宅基地

技术标签: 自动驾驶  算法  SLAM  线性代数  


序:本篇系列文章参照高翔老师《视觉SLAM十四讲从理论到实践》,讲解三维空间刚体运动,为读者打下坚实的数学基础。博文将原第三讲分为五部分来讲解,其中四元数部分较多较复杂,又分为四部分。如果读者急于实践,可直接阅读第五部分的机器人运动轨迹,此部分详细讲解了安装准备工作。此系列总体目录如下:

  1. 旋转矩阵和变换矩阵
  2. 旋转向量表示旋转
  3. 欧拉角表示旋转
  4. 四元数包括以下部分:
    4-1. 四元数表示变换
    4-2. 四元数线性插值方法:LinEuler/LinMat/Lerp/Nlerp/Slerp
    4-3. 四元数多点插值方法:Squad
    4-4. 四元数多点连续解析解插值方法:Spicv
    4-5. 四元数多点离散数值解插值方法:Sping
  5. 实践:SLAM中显示机器人运动轨迹及相机位姿

1. 欧拉角

1.1 定义

无论是旋转矩阵还是旋转向量,它们虽然能描述旋转,但对人类来说非常不直观,而欧拉角则提供了一种非常直观的方式来描述旋转----它使用了3个分离的转角,把一个旋转分解成3次绕不同轴的旋转。

欧拉角:欧拉角是在空间中,描述从一个用于表示某个固定的参考系的、已知的方向,经过一系列基本旋转得到新的、代表另一个参考系的方向的方式。因为只有旋转,所以原点位置并没有发生变化。

在讨论欧拉角的具体形式之前,需要明确几个概念:

  1. 左手坐标系与右手坐标系:既然是在坐标系中进行变换,首先就要了解坐标系的类别。同样的数据在左手坐标系和右手坐标系会有不同的呈现结果。本文在右手坐标系中讨论。
  2. 旋转的具体形式:物体旋转有多种,比如绕某个定点进行旋转,绕坐标轴进行旋转,绕任意轴进行旋转。不同的旋转需要用不同的数学方式来表达。注意,本文讨论的是三维中绕坐标轴的旋转。
  3. 欧拉角顺规:欧拉角定义了一组物体的旋转次序,称为顺规,可以理解为欧拉角旋转顺序的规定。 ( α , β , γ ) (\alpha ,\beta, \gamma) (α,β,γ)在不同的旋转顺序下会有不同的结果,先绕X轴旋转 α \alpha α,还是先绕y轴旋转 β \beta β,最后的结果是不一样的。本文根据不同旋转方式会采用不同的顺规。
  4. 静态动态欧拉角:从参考坐标系上区分,将欧拉角分为静态和动态,其中静态欧拉角以绝对坐标系为参考,不随转动改变,一般用小写的x-y-z来表示静态坐标系;动态欧拉角以刚体自身的物体坐标系为参考,一般用大写的X-Y-Z表示。

1.2 RPY角与Z-Y-X欧拉角

描述坐标系 B B B相对于参考坐标系 A A A的姿态有两种方式:RPY角与Z-Y-X欧拉角。
RPY角:RPY角是绕固定(参考)坐标系 A A A旋转:假设开始两个坐标系重合,先将 B B B A A A的X轴旋转 α \alpha α,然后绕 A A A的Y轴旋转 β \beta β,最后绕 A A A的Z轴旋转 γ \gamma γ,就能旋转到当前姿态。可以称其为X-Y-Z fixed anglesRPY角(Roll, Pitch, Yaw)。由于是绕固定坐标系旋转,则旋转矩阵为: R X Y Z ( α , β , γ ) = R Z ( γ ) R Y ( β ) R X ( α ) . R_{XYZ}(\alpha,\beta ,\gamma) = R_{Z}(\gamma)R_{Y}(\beta)R_{X}(\alpha). RXYZ(α,β,γ)=RZ(γ)RY(β)RX(α).其乘法顺序为:从右到左,即XYZ。展开式为式(3.1)。
Z-Y-X欧拉角:另一种姿态描述方式是绕自身坐标轴 B B B旋转:假设开始两个坐标系重合,先将 B B B绕自身的Z轴旋转 γ \gamma γ,然后绕Y轴旋转 β \beta β,最后绕X轴旋转 α \alpha α,就能旋转到当前姿态。称其为Z-Y-X欧拉角,由于是绕自身坐标轴进行旋转,则旋转矩阵为: R X ′ Y ′ Z ′ ( α , β , γ ) = R Z ′ ( γ ) R Y ′ ( β ) R X ′ ( α ) R_{X^{'}Y^{'}Z^{'}}(\alpha ,\beta ,\gamma) = R_{Z^{'}}(\gamma)R_{Y^{'}}(\beta)R_{X^{'}}(\alpha ) RXYZ(α,β,γ)=RZ(γ)RY(β)RX(α)其乘法顺序为:从左到右,即 Z ′ Y ′ X ′ Z^{'}Y^{'}X^{'} ZYX。其展开式为(3.1)。
由于展开式相等,所以绕定轴X-Y-Z旋转(RPY角) 等价于 绕动轴Z-Y-X旋转(Euler角)。
此处高翔博士对RPY角和欧拉角的解释与其它版本不同,经反复确认后,决定采用熊有伦等编著的《机器人学》,也是网上流传最多的版本,相信,读者明白这一个知识点后,后边关于欧拉角及万向锁理解的误区会迎刃而解。
此处为方便理解,根据航空中的使用的RPY角定义再对此进行阐释。欧拉角定义方式上的不确定性带来了很多实际当中的困难,所幸在特定领域内,欧拉角通常有统一的定义方式。当中常用的一种是航空领域的,用“偏航-仰俯-滚转”(yaw-pitch-row)3个角度来描述的旋转,即RPY角,它是绕定轴x-y-z轴(从右至左)的旋转。RPY角相当于Z-Y-X欧拉角(从左至右)。那么,RPY角相当于把任意旋转分解成以下3个轴的转角,如图:
图1.1 Oxyz坐标轴旋转
在这里插入图片描述

  1. 绕物体的Z轴旋转 γ \gamma γ,得到偏航角yaw。
  2. 绕旋转之后的Y轴旋转 β \beta β,得到仰俯角pitch。
  3. 绕旋转之后的X轴旋转 α \alpha α,得到滚转角roll。

此时,可以使用 [ r , p , y ] T [r,p,y]^{T} [r,p,y]T(即欧拉角)这样一个三维的向量描述任意旋转。rpy角的旋转顺序(从右到左)是XYZ。此外,为规避万向锁现象,常用的顺序是ZXY,但是无法完全消除万向锁,下面会解释原因。下面讲解欧拉角与旋转矩阵转换的推导,然后引出万向锁问题。

2. 欧拉角到旋转矩阵

一个旋转矩阵具有三个自由度,可以与欧拉角进行转换。下边推导欧拉角到旋转矩阵的转换。欧拉角旋转每次只绕一个坐标轴旋转,因此推导可分解为三次二维情况下的坐标变换,再延伸到三维。二维下称之为基本旋转矩阵,由基本旋转组成三维下的组合旋转矩阵。先讨论二维坐标下的情景。如下图所示:
图2.1 二维坐标旋转
图2.1 二维坐标旋转
P ( x , y ) P(x,y) P(x,y)在原坐标系中与X轴夹角为 α \alpha α,绕Z轴旋转角 θ \theta θ后的坐标为 P ′ ( x ′ , y ′ ) P'(x',y') P(x,y),现在来推导 ( x ′ , y ′ ) (x',y') (x,y)关于 ( x , y ) (x,y) (x,y)的表示法。
推导方法有两种,第一种是利用三角恒等式的两角和差公式,第二种是利用三角形的性质推导。本文采用第一种,对第二种感兴趣的童鞋可祥阅《旋转矩阵(Rotation Matrix)的推导及其应用》
由图可知, O P = O P ′ = x 2 + y 2 = d OP=OP'=\sqrt{x^{2} + y^{2}}=d OP=OP=x2+y2 =d,由正余弦定理可得到两组方程: { sin ⁡ α = y d cos ⁡ α = x d (2.1) \left\{\begin{matrix} \sin\alpha = \frac{y}{d}\\ \cos\alpha = \frac{x}{d} \end{matrix}\right. \tag{2.1} { sinα=dycosα=dx(2.1) { sin ⁡ ( α + θ ) = y ′ d cos ⁡ ( α + θ ) = x ′ d (2.2) \left\{\begin{matrix} \sin(\alpha + \theta )= \frac{y'}{d}\\ \cos(\alpha + \theta )= \frac{x'}{d} \end{matrix}\right. \tag{2.2} { sin(α+θ)=dycos(α+θ)=dx(2.2)
由两角和差公式可知: { sin ⁡ ( α + θ ) = sin ⁡ α cos ⁡ θ + cos ⁡ α sin ⁡ θ cos ⁡ ( α + θ ) = cos ⁡ α cos ⁡ θ − sin ⁡ α sin ⁡ θ (2.3) \left\{\begin{matrix} \sin(\alpha + \theta )= \sin\alpha\cos\theta+\cos\alpha\sin\theta\\ \cos(\alpha + \theta )= \cos\alpha\cos\theta-\sin\alpha\sin\theta \end{matrix}\right. \tag{2.3} { sin(α+θ)=sinαcosθ+cosαsinθcos(α+θ)=cosαcosθsinαsinθ(2.3)将式(2.1)和(2.3)代入(2.2),消掉 d d d得到: { x ′ = x cos ⁡ θ − y sin ⁡ θ y ′ = y cos ⁡ θ + x sin ⁡ θ (2.4) \left\{\begin{matrix} x' = x\cos\theta-y\sin\theta\\ y' = y\cos\theta+x\sin\theta \end{matrix}\right.\tag{2.4} { x=xcosθysinθy=ycosθ+xsinθ(2.4)因此有如下推导,设 P ′ P' P的齐次坐标为 P ′ ~ \tilde{P'} P~ P ′ ~ = [ x ′ , y ′ , 1 ] T = [ c o s θ − sin ⁡ θ 0 sin ⁡ θ cos ⁡ θ 0 0 0 1 ] [ x y 1 ] = R Z ( θ ) [ x y 1 ] (2.5) \tilde{P'}=[x',y',1]^{T}=\begin{bmatrix} cos\theta & -\sin\theta & 0\\ \sin\theta & \cos\theta & 0\\ 0 & 0 & 1 \end{bmatrix}\begin{bmatrix} x\\ y\\ 1 \end{bmatrix}=R_Z(\theta )\begin{bmatrix} x\\ y\\ 1 \end{bmatrix}\tag{2.5} P~=[x,y,1]T= cosθsinθ0sinθcosθ0001 xy1 =RZ(θ) xy1 (2.5)因此,绕Z轴的旋转矩阵为: R Z ( θ ) = [ c o s θ − sin ⁡ θ 0 sin ⁡ θ cos ⁡ θ 0 0 0 1 ] (2.6) R_Z(\theta )=\begin{bmatrix} cos\theta & -\sin\theta & 0\\ \sin\theta & \cos\theta & 0\\ 0 & 0 & 1 \end{bmatrix}\tag{2.6} RZ(θ)= cosθsinθ0sinθcosθ0001 (2.6)同理可推得绕Y轴的旋转矩阵为: R Y ( θ ) = [ c o s θ 0 sin ⁡ θ 0 1 0 − sin ⁡ θ 0 cos ⁡ θ ] (2.7) R_Y(\theta )=\begin{bmatrix} cos\theta & 0 & \sin\theta\\ 0 & 1 & 0\\ -\sin\theta & 0 & \cos\theta \end{bmatrix}\tag{2.7} RY(θ)= cosθ0sinθ010sinθ0cosθ (2.7)绕X轴的旋转矩阵为: R X ( θ ) = [ 1 0 0 0 cos ⁡ θ − sin ⁡ θ 0 sin ⁡ θ cos ⁡ θ ] (2.8) R_X(\theta )=\begin{bmatrix} 1 & 0 & 0\\ 0 & \cos\theta & -\sin\theta\\ 0 & \sin\theta & \cos\theta \end{bmatrix}\tag{2.8} RX(θ)= 1000cosθsinθ0sinθcosθ (2.8)组合旋转矩阵等于基本旋转矩阵的连乘,连乘顺序依基本旋转的先后次序由右向左排列,因此顺规为ZYX的组合旋转矩阵为: R Z Y X = R X ( α ) R Y ( β ) R Z ( γ ) (2.9) R_{ZYX}=R_X(\alpha)R_Y(\beta)R_Z(\gamma)\tag{2.9} RZYX=RX(α)RY(β)RZ(γ)(2.9)

3. 旋转矩阵到欧拉角

已知旋转矩阵,如何求欧拉角呢?只需要对应到旋转矩阵,求反弦值即可。将式(2.9)两侧展开得: [ R 11 R 12 R 13 R 21 R 22 R 23 R 31 R 32 R 33 ] = [ cos ⁡ β cos ⁡ γ − cos ⁡ β sin ⁡ γ sin ⁡ β sin ⁡ α sin ⁡ β cos ⁡ γ + cos ⁡ α sin ⁡ γ − sin ⁡ α sin ⁡ β sin ⁡ γ + cos ⁡ α cos ⁡ γ − sin ⁡ α cos ⁡ β − cos ⁡ α sin ⁡ β cos ⁡ γ + sin ⁡ α sin ⁡ γ cos ⁡ α sin ⁡ β sin ⁡ γ + sin ⁡ α cos ⁡ γ cos ⁡ α cos ⁡ β ] (3.1) \begin{bmatrix} R_{11} & R_{12} & R_{13}\\ R_{21} & R_{22} & R_{23}\\ R_{31} & R_{32} & R_{33} \end{bmatrix}= \begin{bmatrix} \cos \beta\cos \gamma & -\cos \beta\sin \gamma & \sin \beta \\ \sin \alpha\sin \beta\cos \gamma +\cos \alpha\sin \gamma & -\sin \alpha\sin \beta\sin \gamma +\cos \alpha\cos \gamma & -\sin \alpha\cos \beta\\ -\cos \alpha\sin \beta\cos \gamma +\sin \alpha\sin \gamma & \cos \alpha\sin \beta\sin \gamma +\sin \alpha\cos \gamma & \cos \alpha\cos \beta \end{bmatrix}\tag{3.1} R11R21R31R12R22R32R13R23R33 = cosβcosγsinαsinβcosγ+cosαsinγcosαsinβcosγ+sinαsinγcosβsinγsinαsinβsinγ+cosαcosγcosαsinβsinγ+sinαcosγsinβsinαcosβcosαcosβ (3.1)
因此推得: { α = arctan ⁡ ( − R 23 R 33 ) β = arcsin ⁡ R 13 γ = arctan ⁡ ( − R 12 R 11 ) (3.2) \left\{\begin{matrix} \alpha=\arctan (-\frac{R_{23}}{R_{33}})\\ \beta=\arcsin R_{13}\\ \gamma=\arctan (-\frac{R_{12}}{R_{11}}) \end{matrix}\right.\tag{3.2} α=arctan(R33R23)β=arcsinR13γ=arctan(R11R12)(3.2)由上面推导,引出万向锁问题。

4. 万向锁

4.1 定义

万向锁问题:欧拉角的一个重大缺点就是会碰到著名的万向锁问题(Gimbal Lock),比如对于rpy,当仰俯角为 ± 9 0 ∘ \pm 90^{\circ } ±90时,第一次旋转与第三次旋转使用同一个轴,使得系统丢失了一个自由度(由3次旋转变成了两次旋转),这被称为奇异性问题,或万向锁,其它顺规形式的欧拉角也同样存在奇异性问题。

理论上可以证明,只要想用3个实数来表达三维旋转,都不可避免的碰到奇异性问题。由于这种原理,欧拉角不适用于插值和迭代,往往只用于人机交互中,因此我们很少在SLAM程序、滤波以及优化中使用欧拉角表达旋转。

4.2 顺规ZYX的万向锁

万向锁理论性描述讲完了,相信大部分人都没理解,网文解释的也比较模糊。以外国小哥的视频:欧拉旋转,配合截图,试讲解如下:

以欧拉角的顺规ZYX为例,如下图所示:图中蓝色为Z轴,绿色为Y轴,红色为X轴,它们有一定的层级关系,Z为Y的父级,Y为X的父级。当绕Z轴旋转时,它的子级Y轴和X轴也会跟着移动;当绕Y轴旋转时,X轴会随之移动,Z轴不发生改变;当绕X轴旋转时,Z轴和Y轴都不会发生改变。层级关系是理解万向节锁问题的关键。初始状态如图所示:
图4.1 ZYX欧拉旋转-1
图4.1 ZYX欧拉旋转-1
箭头绕Z轴旋转,此时Y轴和X轴也跟着变化,如图所示:
图4.2 ZYX欧拉旋转-2
图4.2 ZYX欧拉旋转-2
箭头继续绕Y轴旋转,此时Z轴固定不变,X轴随之变化,如图所示:
图4.3 ZYX欧拉旋转-3图4.3 ZYX欧拉旋转-3
当Y轴旋转角度为 ± 9 0 ∘ \pm 90^{\circ } ±90时,X轴和Z轴重合共面,丢失了一个自由度。三次旋转变换仅仅覆盖了两个外部轴的旋转,一个自由度就这样丢失了,这也就导致了 Gimbal Lock 的现象。Gimbal Lock 问题的核心还是在于我们采用了固定的旋转顺序。对于其它5种组合的顺规,当中间的轴正好旋转 ± 9 0 ∘ \pm 90^{\circ } ±90或两侧轴重合时,同样会产生万向锁现象。
这个变换用公式来理解的话,则是这样(你可以自己来代入验证一下),将 ( α , π 2 , γ ) (\alpha,\frac{\pi }{2} ,\gamma ) (α,2π,γ)代入式(3.1)得: R Z Y X ( α , π 2 , γ ) = R X ( γ ) R Y ( π 2 ) R Z ( α ) = [ 0 0 1 sin ⁡ α cos ⁡ γ + cos ⁡ α sin ⁡ γ − sin ⁡ α sin ⁡ γ + cos ⁡ α cos ⁡ γ 0 − cos ⁡ α cos ⁡ γ + sin ⁡ α sin ⁡ γ cos ⁡ α sin ⁡ γ + sin ⁡ α cos ⁡ γ 0 ] = [ 0 0 1 sin ⁡ ( α + γ ) cos ⁡ ( α + γ ) 0 − cos ⁡ ( α + γ ) sin ⁡ ( α + γ ) 0 ] = R Y ( π 2 ) R Z ( α + γ ) (4.1) \begin{aligned} R_{ZYX}(\alpha,\frac{\pi }{2} ,\gamma ) &= R_{X}(\gamma )R_{Y}(\frac{\pi }{2})R_{Z}(\alpha)\\ &= \begin{bmatrix} 0 & 0 & 1\\ \sin \alpha\cos \gamma +\cos \alpha\sin \gamma & -\sin \alpha\sin \gamma +\cos\alpha\cos \gamma & 0\\ -\cos \alpha\cos \gamma +\sin \alpha\sin \gamma & \cos \alpha\sin \gamma +\sin \alpha\cos \gamma & 0 \end{bmatrix}\\&= \begin{bmatrix} 0 & 0 & 1\\ \sin (\alpha+ \gamma ) & \cos (\alpha+ \gamma ) & 0\\ -\cos (\alpha+ \gamma ) & \sin (\alpha+ \gamma ) & 0 \end{bmatrix}\\&=R_{Y}(\frac{\pi }{2})R_{Z}(\alpha+ \gamma ) \end{aligned}\tag{4.1} RZYX(α,2π,γ)=RX(γ)RY(2π)RZ(α)= 0sinαcosγ+cosαsinγcosαcosγ+sinαsinγ0sinαsinγ+cosαcosγcosαsinγ+sinαcosγ100 = 0sin(α+γ)cos(α+γ)0cos(α+γ)sin(α+γ)100 =RY(2π)RZ(α+γ)(4.1)
利用一些三角恒等式,将原本由三个旋转矩阵所组成的变换化简成了两个变换矩阵。即便我们分别对 Z − Y − X Z-Y-X ZYX三轴进行了旋转,实际上这个矩阵仅仅旋转了 Z − Y Z-Y ZY两轴,它并没有对初始的X 轴进行变换。 R Z ( γ ) R_{Z}(\gamma ) RZ(γ) R X ( α ) R_{X}(\alpha) RX(α)这两个变换被合并为单独的一个 R Z ( γ + α ) R_{Z}(\gamma + \alpha ) RZ(γ+α)变换(因为化简完之后变换顺序不一样了,严格来说并不是合并,只不过是能够使用一步来完成)。
注意,我们在这里化简的并不是单独的一个变换,而是一系列变换。因为它对任意 α \alpha α γ \gamma γ角都是成立的。也就是说,一旦 轴上的变换角将这两个旋转轴对齐,我们就没有任何办法对最初的 X X X轴进行旋转了。无论 X X X轴与 Z Z Z轴的旋转角是多少,变换都会丧失一个自由度。

4.3 解决方法

笔者收集到两种解决办法,第一种会尽大可能规避,但无法避免;第二种能彻底解决,但会耗费一定内存。如有其它方法,请留言告知。

  1. 在三维空间中,无法完全避免,窍门是找到物体不太可能指向的方向,可以尽量规避这种现象。如对于相机模型,想象人持相机,建立右手坐标系,正前方为Y轴正向,左手侧为X轴正向,头顶为Z轴正向。则最常用的方向为正向握持,即Y轴正向,将相机树立对应X轴,此种操作相对较少,而朝天或朝地下拍摄,对应Z轴,即相机向上或倒放的操作最少,同时相机侧躺的情况也较少发生,因此确立适合相机的顺规为ZXY的欧拉旋转,可以最大可能规避万向锁问题,但无法完全避免。
  2. 另外一种彻底解决的办法是,将欧拉角转换为四元数,对四元数进行slerp即球面线性插值,再将这一系列四元数转换为对应的欧拉角,而后作用于角色。缺点是耗费一定的内存,但角色可以任意旋转,灵活度高。四元数部分详见下一篇博客。

至此,推导部分结束,让我们练练手吧。

5. 实践:Eigen几何模块

现在,我们实际演练前面讲到的各种旋转表达方式。将在Eigen中使用旋转向量、欧拉角和旋转矩阵,演示它们之间的变换方式。
代码可在博客资源中下载,截取部分如下(已加注释):

#include<iostream>
#include<cmath>
using namespace std;

#include<eigen3/Eigen/Core>
#include<eigen3/Eigen/Geometry>

using namespace Eigen;

//本程序演示了Eigen几何模块的使用方法
int main(int argc, char **argv){
    
    //Eigen/Geometry模块提供了各种旋转和平移的表示,3D旋转矩阵直接使用Matrix3d或Matrix3f
    Matrix3d rotation_matrix = Matrix3d::Identity();
    //旋转向量使用AngleAxisd,它底层不直接是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);
    Vector3d v_rotated = rotation_vector * v;
    cout<<"(1,0,0) after rotation (by angle axis) = "<<v_rotated.transpose()<<endl;
    //用旋转矩阵进行坐标变换
    v_rotated = rotation_matrix * v;
    cout<<"(1,0,0) after rotation (by matrix) = "<<v_rotated.transpose()<<endl;

    //欧拉角:可以将旋转矩阵直接转换成欧拉角
    Vector3d euler_angles = rotation_matrix.eulerAngles(2,1,0);
    cout<<"yaw pitch roll = "<<euler_angles.transpose()<<endl;
    //cout<<"euler angles matrix= "<<euler_angles.matrix()<<endl;
    
    //欧式变换矩阵使用Eigen::Isometry3d
    Isometry3d T = Isometry3d::Identity();
    cout<<"Isometry3d T = \n"<<T.matrix()<<endl; 
    T.rotate(rotation_vector);
    cout<<"T rotate rotation_vector = \n"<<T.matrix()<<endl; 
    T.pretranslate(Vector3d(1,3,4));
    cout<<"Transform matrix = \n"<<T.matrix()<<endl; 
    
    //用变换矩阵进行坐标变换
    Vector3d v_transformed = T * v;
    cout<<"v = "<<v.transpose()<<endl;
    cout<<"v transofrmed = "<<v_transformed.transpose()<<endl;
}

运行结果如下:

rotation matrix =
1 0 0
0 1 0
0 0 1
rotation vector =
 0.707 -0.707      0
 0.707  0.707      0
     0      0      1
rotation vector to Matrix =
 0.707 -0.707      0
 0.707  0.707      0
     0      0      1
(1,0,0) after rotation (by angle axis) = 0.707 0.707     0
(1,0,0) after rotation (by matrix) = 0.707 0.707     0
yaw pitch roll = 0.785    -0     0
Isometry3d T = 
1 0 0 0
0 1 0 0
0 0 1 0
0 0 0 1
T rotate rotation_vector = 
 0.707 -0.707      0      0
 0.707  0.707      0      0
     0      0      1      0
     0      0      0      1
Transform matrix = 
 0.707 -0.707      0      1
 0.707  0.707      0      3
     0      0      1      4
     0      0      0      1
v = 1 0 0
v transofrmed = 1.71 3.71    4

本文基于《视觉SLAM十四讲:从理论到实践》和《Quaternions, Interpolation and Animation》编写,但相对于原文会适当精简,同时为便于全面理解,会收集其他网络好文,根据作者理解,加入一些注解和扩展知识点,如果您觉得还不错,请一键四连(点赞关注收藏评论),让更多的人看到。

参考文献:

  1. 《视觉SLAM十四讲:从理论到实践》,高翔、张涛等著,中国工信出版社
  2. 四元数与欧拉角(RPY角)的相互转换
  3. 图形变换之旋转变换公式推导
  4. Bonus: Gimbal Lock By Krasjet
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/shao918516/article/details/105172829

智能推荐

ESP32 (UART 接收发送)-串口之接收发送通讯(4)_esp32接收发送16进制指令-程序员宅基地

文章浏览阅读9.7k次,点赞8次,收藏54次。提示:本博客作为学习笔记,有错误的地方希望指正文章目录一、ESP32串口介绍二、硬件设计三、实现代码四、串口实验演示结果五、ESP32串口函数API5.1、uart_types.h文件中的内容的API5.2、在uart.h文件中的内容的API一、ESP32串口介绍  UART 是一种以字符为导向的通用数据链,可以实现设备间的通信。异步传输的意思是不需要在发送数据上添加时钟信息。这也要求发送端和接收端的速率、停止位、奇偶校验位等都要相同,通信才能成功。  一个典型的 UART 帧开始于一个起始位,紧接_esp32接收发送16进制指令

第4章 学习Shader所需的数学基础(下)(坐标空间及其变换)_扩展到齐次坐标空间-程序员宅基地

文章浏览阅读580次。版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。 ..._扩展到齐次坐标空间

插入排序、冒泡排序、选择排序、快速排序、归并排序、堆排序-程序员宅基地

文章浏览阅读345次。插入排序、冒泡排序、选择排序、快速排序

【Linux】Bonding配置,管理-程序员宅基地

文章浏览阅读217次。1 通过Ifenslave手动配置Bonding该方法适用于某些发行包,它们的网络初始化脚本(sysconfig或initscripts包)没有bonding相关的知识。SuSE Linux Enterprise Server 版本8就是这样的一个发行包。对于这些系统一般的方法是,把bonding模块的参数放进/etc/modules.conf或..._echo "all" > /sys/class/net/bond0/bonding/arp_validate echo "100" > /sys

pip更新或安装包的时候出现错误:拒绝访问_pip23.3.2安装包时拒绝访问-程序员宅基地

文章浏览阅读4.7k次,点赞3次,收藏8次。pip更新安装包的时候出现错误,如下图所示:解决方法是:pip install --user [要安装的包] #加上一个–user就好了_pip23.3.2安装包时拒绝访问

随便推点

Kibana:创建你的第一个仪表板_kinaba仪表盘-程序员宅基地

文章浏览阅读2.8k次,点赞4次,收藏4次。了解从你自己的数据创建仪表板的最常用方法。本教程将从分析师查看网站日志的角度使用示例数据,但这种类型的仪表板适用于任何类型的数据。完成后,你将全面了解示例 Web 日志数据。在本次的展示中,我将使用最新的 Elastic Stack 8.7.1 来进行展示。_kinaba仪表盘

layui table表格带图片,图片显示不全问题_layui表格图片显示不全-程序员宅基地

文章浏览阅读9.8k次,点赞10次,收藏30次。这个平时没有注意过,今天有人问到,就记录一下吧layui的表格使用非常简单,layui文档中已经非常详细,下面直接上代码了1.jsp代码 <div class="demoTable"> <button class="layui-btn" data-type="publish">发布Banner</button> </..._layui表格图片显示不全

Android音视频开发 -> fdk-aac解码eld-aac为pcm_android aac音频解码-程序员宅基地

文章浏览阅读1.1k次。大体实例fdk-aac 解码初始化fdk-aac 开始解码公共变量//解码器对象实例HANDLE_AACDECODER aacDecoder;fdk-aac解码初始化int FdkAacDecode::fdkAacDecodeInit(JNIEnv *env) { //Java方法初始化 aacDecodeClass = env->FindClass("com/zkzj/aaclib/AacUtil"); aacDecodeId = env->GetM_android aac音频解码

基于Sphinx+MySQL的千万级数据全文检索(搜索引擎)架构设计-程序员宅基地

文章浏览阅读3.6k次。来自:http://blog.zyan.cc/post/360/前言:本文阐述的是一款经过生产环境检验的千万级数据全文检索(搜索引擎)架构。本文只列出前几章的内容节选,不提供全文内容。  在DELL PowerEdge 6850服务器(四颗64 位Inter Xeon MP 7110N处理器 / 8GB内存)、RedHat AS4 Linux操作系统、MySQL 5.1.26、MyIS

HTML嵌入JavaScript代码的三种方式_24、在html中,可以引入javascrint代码方式(3分)是()。a、a、行内式b、b、内嵌-程序员宅基地

文章浏览阅读5.4k次,点赞2次,收藏11次。HTML嵌入JavaScript代码的三种方式_24、在html中,可以引入javascrint代码方式(3分)是()。a、a、行内式b、b、内嵌

edge等浏览器打开开发者工具(F12)之后在NetWork看不到请求头等信息_浏览器开发者工具 console没有请求信息-程序员宅基地

文章浏览阅读6w次,点赞73次,收藏50次。问题打开调试器,F5刷新页面后出现下面这种情况没有出现资源等想要的信息(注:从参考1里面得到如下图)解决方法1、打开Edge浏览器里面的调试器的设置2、重置默认并刷新即可注:chrome浏览器的开发者工具的设置也在类似位置参考1、edge等浏览器打开开发者工具(F12)之后在NetWork看不到请求头等信息..._浏览器开发者工具 console没有请求信息

推荐文章

热门文章

相关标签