矩阵、欧拉角和四元数的相互转换与比较

相互转换

这里只展示最终的转换结果,推导过程请参考《3D数学基础:图形与游戏开发》

欧拉角转换到矩阵

欧拉角描述了一个旋转序列,分别计算出给每个旋转的矩阵再将他们按照一定的顺序连接成一个矩阵,这个矩阵就代表了整个角位移:

矩阵转换到欧拉角

将矩阵表示的角位移转化为欧拉角的表示:

四元数转换到矩阵

四元数q(w,x,y,z)对应的旋转矩阵可以表示为如下形式:

矩阵转换到四元数

利用四元数转换的成的矩阵(上述的矩阵)计算对角线元素之和即可求的 w 值:

利用类似的方法可以计算 x,y,z 的值:

以上方式并不总是正常工作,因为平方根的结果的结果总是正值,同时利用一下技巧是检查相对于对角线的对称位置上元素的和与差:

因此,一旦用对角线元素和/差的平方根解的4个值中的一个,就能利用如下方式计算其他三个:

似乎最简单的策略就是总是先计算同一个分量,如 w,然后再计算 x,y,z 。这样的方式可能存在如下错误:如果 w=0,除法就没有意义(分母不能为0);如有w非常小,将会出现数值不稳定。有学者建议首先判断 w,x,y,z 中哪一个最大,就用对角线元素计算该元素,然后再通过它计算其他三个分量。

//输入矩阵
float m11,m12,m13;
float m21,m22,m23;
float m31,m32,m33;//输出的四元数
float w,x,y,z;//探测w,x,y,z 中的最大值
float fourWSquaredMinusl = m11+m22+m33;
float fourXSquaredMinusl = m11-m22-m33;
float fourYSquaredMinusl = m22-m11-m33;
float fourZSquaredMinusl = m33-m11-m22;int biggestIndex = 0;
float fourBiggestSqureMinus1 = fourWSquaredMinusl;
if(fourXSquaredMinusl>fourBiggestSqureMinus1){fourBiggestSqureMinus1 = fourXSquaredMinusl;biggestIndex =1;
}
if(fourYSquaredMinusl>fourBiggestSqureMinus1){fourBiggestSqureMinus1 = fourYSquaredMinusl;biggestIndex =2;
}
if(fourZSquaredMinusl>fourBiggestSqureMinus1){fourBiggestSqureMinus1 = fourZSquaredMinusl;biggestIndex =3;
} //计算平方根和除法
float biggestVal = sqrt(fourBiggestSqureMinus1+1.0f)*0.5f;
float mult = 0.25f/biggestVal;//计算四元数的值
switch(biggestIndex){case 0:w=biggestVal;x=(m23-m32)*mult;y=(m31-m13)*mult;z=(m12-m21)*mult;break;case 1:x = biggestVal;w =(m23-m32)*mult;y =(m12+m21)*mult;z =(m31+m13)*mult;break;case 2:y =biggestVal;w =(m31-m13)*mult;x =(m12+m21)*mult;z =(m23+m32)*mult;break;case 3:z =biggestVal;w =(m12-m21)*mult;x =(m31+m13)*mult;y =(m23+m32)*mult;break;
} 

欧拉角转换到四元数

给定一个欧拉旋转(Z-Y-X顺序),则对应的四元数为:q = (w , x, y, z )其中:

四元数转换到欧拉角

 根据上面的公式可以求出逆解,即由四元数q=(q0,q1,q2,q3)或q=(w,x,y,z)到欧拉角的转换为:

  由于arctan和arcsin的取值范围在−π2−π2和π2π2之间,只有180°,而绕某个轴旋转时范围是360°,因此要使用atan2函数代替arctan函数:

将四元数转为欧拉角可以参考下面的代码。需要注意欧拉角有12种旋转次序,而上面推导的公式是按照Z-Y-X顺序进行的,所以有时会在网上看到不同的转换公式(因为对应着不同的旋转次序),在使用时一定要注意旋转次序:

///
// Quaternion to Euler
///
enum RotSeq{zyx, zyz, zxy, zxz, yxz, yxy, yzx, yzy, xyz, xyx, xzy,xzx};void twoaxisrot(double r11, double r12, double r21, double r31, double r32, double res[]){res[0] = atan2( r11, r12 );res[1] = acos ( r21 );res[2] = atan2( r31, r32 );
}void threeaxisrot(double r11, double r12, double r21, double r31, double r32, double res[]){res[0] = atan2( r31, r32 );res[1] = asin ( r21 );res[2] = atan2( r11, r12 );
}void quaternion2Euler(const Quaternion& q, double res[], RotSeq rotSeq)
{switch(rotSeq){case zyx:threeaxisrot( 2*(q.x*q.y + q.w*q.z),q.w*q.w + q.x*q.x - q.y*q.y - q.z*q.z,-2*(q.x*q.z - q.w*q.y),2*(q.y*q.z + q.w*q.x),q.w*q.w - q.x*q.x - q.y*q.y + q.z*q.z,res);break;case zyz:twoaxisrot( 2*(q.y*q.z - q.w*q.x),2*(q.x*q.z + q.w*q.y),q.w*q.w - q.x*q.x - q.y*q.y + q.z*q.z,2*(q.y*q.z + q.w*q.x),-2*(q.x*q.z - q.w*q.y),res);break;case zxy:threeaxisrot( -2*(q.x*q.y - q.w*q.z),q.w*q.w - q.x*q.x + q.y*q.y - q.z*q.z,2*(q.y*q.z + q.w*q.x),-2*(q.x*q.z - q.w*q.y),q.w*q.w - q.x*q.x - q.y*q.y + q.z*q.z,res);break;case zxz:twoaxisrot( 2*(q.x*q.z + q.w*q.y),-2*(q.y*q.z - q.w*q.x),q.w*q.w - q.x*q.x - q.y*q.y + q.z*q.z,2*(q.x*q.z - q.w*q.y),2*(q.y*q.z + q.w*q.x),res);break;case yxz:threeaxisrot( 2*(q.x*q.z + q.w*q.y),q.w*q.w - q.x*q.x - q.y*q.y + q.z*q.z,-2*(q.y*q.z - q.w*q.x),2*(q.x*q.y + q.w*q.z),q.w*q.w - q.x*q.x + q.y*q.y - q.z*q.z,res);break;case yxy:twoaxisrot( 2*(q.x*q.y - q.w*q.z),2*(q.y*q.z + q.w*q.x),q.w*q.w - q.x*q.x + q.y*q.y - q.z*q.z,2*(q.x*q.y + q.w*q.z),-2*(q.y*q.z - q.w*q.x),res);break;case yzx:threeaxisrot( -2*(q.x*q.z - q.w*q.y),q.w*q.w + q.x*q.x - q.y*q.y - q.z*q.z,2*(q.x*q.y + q.w*q.z),-2*(q.y*q.z - q.w*q.x),q.w*q.w - q.x*q.x + q.y*q.y - q.z*q.z,res);break;case yzy:twoaxisrot( 2*(q.y*q.z + q.w*q.x),-2*(q.x*q.y - q.w*q.z),q.w*q.w - q.x*q.x + q.y*q.y - q.z*q.z,2*(q.y*q.z - q.w*q.x),2*(q.x*q.y + q.w*q.z),res);break;case xyz:threeaxisrot( -2*(q.y*q.z - q.w*q.x),q.w*q.w - q.x*q.x - q.y*q.y + q.z*q.z,2*(q.x*q.z + q.w*q.y),-2*(q.x*q.y - q.w*q.z),q.w*q.w + q.x*q.x - q.y*q.y - q.z*q.z,res);break;case xyx:twoaxisrot( 2*(q.x*q.y + q.w*q.z),-2*(q.x*q.z - q.w*q.y),q.w*q.w + q.x*q.x - q.y*q.y - q.z*q.z,2*(q.x*q.y - q.w*q.z),2*(q.x*q.z + q.w*q.y),res);break;case xzy:threeaxisrot( 2*(q.y*q.z + q.w*q.x),q.w*q.w - q.x*q.x + q.y*q.y - q.z*q.z,-2*(q.x*q.y - q.w*q.z),2*(q.x*q.z + q.w*q.y),q.w*q.w + q.x*q.x - q.y*q.y - q.z*q.z,res);break;case xzx:twoaxisrot( 2*(q.x*q.z - q.w*q.y),2*(q.x*q.y + q.w*q.z),q.w*q.w + q.x*q.x - q.y*q.y - q.z*q.z,2*(q.x*q.z + q.w*q.y),-2*(q.x*q.y - q.w*q.z),res);break;default:std::cout << "Unknown rotation sequence" << std::endl;break;}
}

方位表示比较

矩阵

优点

  • 可以立即进行向量的旋转;
  • 矩阵的形式被图形API所使用;当和图形API交流时,最终必须用矩阵来描述所需的转换;
  • 矩阵的组合实现多个角位移的连接;
  • 逆矩阵就是“相反"的角位移,因为旋转矩阵是正交的,而正交矩阵的逆矩阵等于 转置矩阵。

缺点

  • 矩阵占用更多的空间,欧拉角只需要三个数字来表达方位,而矩阵需要9个;
  • 难于使用,由于矩阵很不直观;
  • 描述方位的矩阵必须满足限制条件:行必须是单位向量,并且各行相互垂直,但是矩阵可能是病态的(比如精度的缺失,导致不满足上述条件)。

欧拉角

优点:

  • 欧拉角中的数都是角度,很直观,所以欧拉角很容易使用;
  • 最简洁的表示方式,仅使用三个数来表达方位
  • 任意三个数都是合法的

缺点:

  • 给定方位的表达方式不唯一
  • 二个角度间的插值非常困难
  • 万向节死锁问题

四元数

优点

  • 四元数可以平滑插值(球面线性插值)
  • 四元数的叉乘能将角位移序列快速转换为单个角位移,并很容易实现角位移的求逆
  • 能和矩阵形式快速转换

缺点

  • 相比欧拉角数据量要大一些
  • 四元数可能出现不合法
  • 四元数是最难于使用的

总结

  • 欧拉角最容易使用:大大简化人机交互(包括直接的键盘输入方位、渲染中指定的摄像机、调试测试等);
  • 如果需要在坐标系之间转换向量,那么选择矩阵形式;
  • 当需要保存大量的方位数据时,使用欧拉角或者四元数;
  • 平滑的插值只能通过四元数来实现

以上参考:

  • 3D数学基础:图形于游戏开发
  • 游戏引擎架构
  • https://blog.csdn.net/loongkingwhat/article/details/88428310
  • https://www.cnblogs.com/21207-iHome/p/6894128.html

3D数学基础——矩阵、欧拉角和四元数的相互转换与比较相关推荐

  1. 3D数学基础——矩阵的介绍与使用

    矩阵 矩阵就是一个矩形的数字.符号或表达式数组.矩阵中每一项叫做矩阵的元素(Element).下面是一个2×3矩阵的例子: 矩阵可以通过(i, j)进行索引,i是行,j是列,这就是上面的矩阵叫做2×3 ...

  2. 学习 3D数学基础 (矩阵1)

    矩阵 一个4x3矩阵(4行,3列) 方阵 行数和列数相同的矩阵 对角矩阵 所有非对角元素为0 单位矩阵 向量作为矩阵 一个n维向量能被当做1xN矩阵或Nx1矩阵. 矩阵的转置 沿着矩阵的对角线翻转 向 ...

  3. 四元数与矩阵欧拉角之间的相互转换

    四元数转矩阵 void fromInertialToObjectQuaternion(const Quaternion &q);void fromObjectToInertialQuatern ...

  4. 3D 中的方位与角位移(旋转矩阵、欧拉角、四元数)

    文章目录 一.3D 中的方位与角位移 1. 欧拉角 (Euler angles) 2. 四元数的相关知识 2.1 复数 2.2 欧拉旋转定理 2.3 三维空间旋转的拆分 3. 四元数 (Quatern ...

  5. 3D数学之方位与角位移、欧拉角与四元数

    3D物体如何描述方位: 1.方向 2.角位移 3.旋转 方位≠方向 当向量自转时,不会改变向量的属性 但是物体自转时,物体的方位就变化了 表示方法: 位置:使用相对于参考系的位移 方位:相对于已知方位 ...

  6. matlab和Eigen库中的一些旋转矩阵(方向余弦矩阵)、四元数和欧拉角之间的转换和绘图的注意事项

    最近用matlab和Eigen库中的一些旋转矩阵(方向余弦矩阵).四元数和欧拉角之间的转换和绘图,弄得我有些头疼,把遇到的问题记录一下,以防以后又脑阔疼....有不同的理解可以再评论区批评指正- 主要 ...

  7. PSINS中欧拉角、方向余弦矩阵与姿态四元数的转换公式与代码

    文章目录 欧拉角转四元数 公式 代码 欧拉角转方向余弦矩阵 公式 代码 四元数转方向余弦矩阵 公式 代码 方向余弦矩阵转欧拉角 公式 代码 方向余弦矩阵转四元数 公式 代码 提示:代码只给出PSINS ...

  8. 一文搞懂什么VR,什么是6Dof,欧拉角,四元数转视图矩阵

    目录 一.什么是VR 二.什么是3Dof,6Dof, 9Dof 三.欧拉角(姿态角) 四.Android手机的欧拉角与坐标系 五.安卓坐标系转换欧拉角 六.根据姿态四元数求视图矩阵 一文搞懂什么VR, ...

  9. 3d数学基础:图形和游戏开发(第2版)_游戏引擎编程需要哪些基本数学知识?

    现今,想要从头写一个功能强大的3D引擎,个人的力量恐怕难以胜任,即使能力足够,时间恐怕也不允许.在这个美好的开源时代,你只需具备修改各种引擎的能力便足以满足开发游戏的各项需求.现代游戏引擎的复杂级别已 ...

最新文章

  1. DataRow判断列名是否存在
  2. mfc c语言 编辑器,语法高亮编辑控件Scintilla在MFC中的简单使用
  3. 视觉与图像系列 几何光学I 近轴光学1 Fermat原理
  4. 联发科mtk手机处理器怎么样_5G手机价格将再探新低!联发科天玑700芯片发布:入门级5G处理器...
  5. html不用点击自动执行,页面自动执行(加载)js的几种方法
  6. linux常用的文件操作命令大全,(办公)记事本_Linux常用的文件操作命令
  7. 构造函数 返回值_JavaScript构造函数的简单介绍
  8. python2 与 python3的区别
  9. 最小二乘法计算CCM
  10. 淘宝 喵铺脚本自动签到脚本
  11. access_token(接口访问凭证)
  12. python 读取gif_如何使用opencv(python)从url读取gif
  13. AI基础:机器学习库Scikit-learn的使用
  14. plc和c语言和cnc,CNC是什么意思? CNC 和 PLC的区别?
  15. cad调了比例因子没反应_大神们都在用的9个CAD制图技巧,你会用几个?
  16. flex 垂直方向 两端对齐
  17. C编程标准:GNU C 、ANSI C、标准C、标准c++区别和联系
  18. 给女朋友的微信小程序之情侣手账本(含源码)
  19. cad模型轻量化_碳纤维泡沫三明治夹层复合材料汽车扰流板的轻量化设计
  20. Codeforces Global Round 2(CF1119)

热门文章

  1. 购买商品复选框全选 单选
  2. [C#]获取窗口客户区域、标题栏高度、菜单栏高度、边框宽度正确方法
  3. Android 蓝牙开发(扫描设备、绑定、解绑)Kotlin版
  4. ESET(NOD32) ESS/EAV 4.0.424.0 BE 32位 汉化正式版
  5. 【Python实战】“特种兵”们的专属游戏助手,助你吃鸡:极品小助手也是棒呆了~(“大吉大利,今W吃鸡”)
  6. 【Try to Hack】Cobalt Strike(一)
  7. python底层是用什么语言实现的_我为何说Python是全栈式开发语言?
  8. IDEA创建maven项目时的plugins、dependencies飘红问题
  9. oracle_day5_程序包
  10. C# 面向对象(多态)