上文中曾经说过,欧拉旋转的顺规和轴向定义,自然造就了“万向节死锁”问题。本文主要来探索它自然形成的原因。

陀螺仪
首先,我们来了解Gimbal 究竟是个什么玩意儿。下面来自维基百科中关于Gimbal的一段引述:

平衡环架(英语:Gimbal)为一具有枢纽的装置,使得一物体能以单一轴旋转。由彼此垂直的枢纽轴所组成的一组三只平衡环架,则可使架在最内的环架的物体维持旋转轴不变,而应用在船上的陀螺仪、罗盘、饮料杯架等用途上,而不受船体因波浪上下震动、船身转向的影响。

Gimbal

上图就是一个Gimbal装置了,它是一个陀螺仪。中间有一根竖轴,穿过一个金属圆盘。金属圆盘称为转子,竖轴称为旋转轴。转子用金属制成,应该是了增加质量,从而增大惯性。竖轴外侧是三层嵌套的圆环,它们互相交叉,带来了三个方向自由度的旋转。
看着不停转来转去,有点晕,接下来看两个静态的。这两张图来自百度百科。

陀螺仪中文注释: 陀螺仪

其中Gimbal只代表陀螺仪装置中的平衡环,显然维基百科上将它解释成“平衡环架”更为合理。

Pitch、Yaw、Roll
在解释陀螺仪的工作原理之前,我先介绍一些转动的术语。在飞行器的航行中,进行XYZ三个方向旋转的旋转有专业的术语,见下图:

Pitch、Yaw、Roll

沿着机身右方轴(Unity中的+X)进行旋转,称为pitch,中文叫俯仰。
沿着机头上方轴(Unity中的+Y)进行旋转,称为Yaw,中文叫偏航。
沿着机头前方轴(Unity中的+Z)进行旋转,称为Roll,中文叫桶滚。

陀螺仪的工作原理
我们知道陀螺仪使用来测量平衡和转速的工具,在载体高速转动的时候,陀螺仪始终要通过自我调节,使得转子保持原有的平衡,这一点是如何做到的?带着这个问题,我们来看一下这个古老而又神秘的装置的工作原理。

为了解释清楚问题,我自己画了一个简单的陀螺仪示意图。(金属圆盘我就省略了,丑点儿也就别管了。。)
陀螺仪示意图
这里,我把三个Gimbal环用不同的颜色做了标记,底部三个轴向,RGB分别对应XYZ。
假设现在这个陀螺仪被放在一艘船上,船头的方向沿着+Z轴,也就是右前方。

现在假设,船体发生了摇晃,是沿着前方进行旋转的摇晃,也就是桶滚。由于转子和旋转轴具有较大的惯性,只要没有直接施加扭矩,就会保持原有的姿态。由于上图中绿色的活动的连接头处是可以灵活转动的,此时将发生相对旋转,从而出现以下的情形:
桶滚平衡

再次假设,船体发生了pitch摇晃,也就是俯仰。同样,由于存在相应方向的可以相对旋转的连接头(红色连接头),转子和旋转轴将仍然保持平衡,如下图:
俯仰平衡

最后假设,船体发生了yaw摇晃,也就是偏航,此时船体在发生水平旋转。相对旋转发生在蓝色连接头。如下图:
偏航平衡

最终,在船体发生Pitch、Yaw、Roll的情况下,陀螺仪都可以通过自身的调节,而让转子和旋转轴保持平衡。

陀螺仪中的万向节死锁
现在看起来,这个陀螺仪一切正常,在船体发生任意方向摇晃都可以通过自身调节来应对。然而,真的是这样吗?

假如,船体发生了剧烈的变化,此时船首仰起了90度(这是要翻船的节奏。。。。),此时的陀螺仪调节状态如下图:
死锁开始

此时,船体再次发生转动,沿着当前世界坐标的+Z轴(蓝色轴,应该正指向船底)进行转动,那么来看看发生了什么情况。

死锁的陀螺仪

现在,转子不平衡了,陀螺仪的三板斧不起作用了。它失去了自身的调节能力。那么这是为什么呢?
之前陀螺仪之所以能通过自身调节,保持平衡,是因为存在可以相对旋转的连接头。在这种情况下,已经不存在可以相对旋转的连接头了。
那么连接头呢?去了哪里?显然,它还是在那里,只不过是,连接头可以旋转的相对方向不是现在需要的按着+Z轴方向。从上图中,我们清楚地看到:

红色连接头:可以给予一个相对俯仰的自由度。
绿色连接头:可以给予一个相对偏航的自由度。
蓝色连接头:可以给予一个相对偏航的自由度。
没错,三个连接头,提供的自由度只对应了俯仰和偏航两个自由度,桶滚自由度丢失了。这就是陀螺仪上的“万向节死锁”问题。

用小程序来重现万向节死锁问题
首先,预设一下接下来的欧拉角变化顺序。见下图:
预设欧拉旋转

上图中,红色框内的部分的列表,记录了接下来欧拉角的增长变化过程。即它会从(0,0,0)变化到(90,0,0),再变化到(90,90,0),再变化到(90,180,0),再变化到(90,180,90),再变化到(90,180,180)。下图是变化的过程演示。

YZ轴死锁

现在可以看到:
- 当先执行X轴旋转90度,此时在执行Pitch(俯仰)变化。
- 再在Y轴进行变化0-180度,此时在执行相对自身的Roll(桶滚)变化。
- 再在Z轴进行变化0-180度,此时仍在执行相对自身的Roll(桶滚)变化。

这里所说的俯仰、桶滚、偏航都是相对自己局部坐标系的。这与上述的陀螺仪中出现的问题是一样的,万向节死锁。也就是尽管欧拉角在XYZ三个轴向进行进动(持续增长或者减少),但是影响最终的结果,只对应了两个轴向。

死锁的过程解析
在《Unity中欧拉旋转》一文中我曾提到,是欧拉角顺规和轴向的定义方式,造就了“万向节死锁”问题的自然形成。通过上述的例子,这里作个详细解释。

首先我们知道,由于Unity中欧拉旋转的顺规的定义,围绕Z轴的进动最先执行,所以,Z轴是“严格保护”的一个轴,就是说,当先沿着Z轴进行进动时,无论此时的XY是什么值,最终的结果,围绕Z轴的进动始终造成相对自身执行桶滚变化。
然而X、Y轴就不同了,我们先不考虑Y轴,假设其一直为0,先说X轴。如果Z轴也是保持为0,那么围绕X轴进动,最终的影响是预期的俯仰变化。如下图:

YZ为0的X进动

然而当Z轴为90度时,围绕X轴进动变成了偏航变化,如下图:

Z先进动90的X进动

也就是说,欧拉角的X轴进动造成最后的变化结果,受到到了预先执行的Z轴进动的影响,它仍然会造成某个相对自身的轴向的变化,但是结果不唯一;同样,欧拉角的Y轴进动,则受到了Z轴和X轴的影响,结果更加不唯一。

然而,以上的过程执行,都是严格遵守欧拉角的顺规和轴向定义的。某些时刻,这种不确定的结果,就可能造成某个轴向自由度的丢失。
就拿下图来说:
YZ轴死锁

欧拉角Z轴的进动,最先执行,造成桶滚,这个没问题。
欧拉角Y轴的进动,最后执行,造成沿着欧拉旋转前的Y轴旋转,这也是根据定义执行。然而现在这种沿着Y轴的旋转,同样也被映射到了物体的桶滚变化。

总结
总结来说,欧拉角的“万向节死锁”问题,是由于欧拉旋转定义本身造成的。这种围绕选旋转前固定轴的先Z、再X、再Y的旋转操作,与其最终所预期的三个轴向可以旋转的结果并非一定是一对一的映射。某些情况下是多对一的映射,造成一些旋转自由度的缺失,也就是“死锁”。

建议
对于写代码来说,你直接去改变Transform的欧拉角显然是不合适的,通过本文也可以看到,这种结果几乎是不可预测的。但是某些情况下,却是可以预期的,就是你仅仅在一个轴向进动,其它两个轴向保持为0,此时有效,并且直接修改欧拉角的代码效率应该是比较高的。

数组.列表.集合.应用相关推荐

  1. Java ArrayList和Vector、LinkedList与ArrayList、数组(Array)和列表集合(ArrayList)的区别...

    ArrayList和Vector的区别 ArrayList与Vector主要从二方面来说. 一.同步性: Vector是线程安全的,也就是说是同步的,而ArrayList是线程序不安全的,不是同步的. ...

  2. java集合——数组列表(ArrayList)+散列集(HashSet)

    [0]README 0.1) 本文描述+源代码均 转自 core java volume 1, 旨在理解 java集合--数组列表(ArrayList)+散列集(HashSet) 的相关知识: 0.2 ...

  3. 死磕算法第一弹——数组、集合与散列表

    本文整理来源 <轻松学算法--互联网算法面试宝典>/赵烨 编著 数组 自我解读 数组是一堆数据按照顺序放入的固定长度空间. 数组的长度固定,所以在声明时需要指定数组长度.如果长度不够用,也 ...

  4. 转载--编写高质量代码:改善Java程序的151个建议(第5章:数组和集合___建议60~64)

    阅读目录 建议60:性能考虑,数组是首选 建议61:若有必要,使用变长数组 建议62:警惕数组的浅拷贝 建议63:在明确的场景下,为集合指定初始容量 建议64:多种最值算法,适时选择 噢,它明白了,河 ...

  5. 二维数组,锯齿数组和集合 C# 一维数组、二维数组(矩形数组)、交错数组(锯齿数组)的使用 C# 数组、多维数组(矩形数组)、锯齿数组(交叉数组)...

    二维数组,锯齿数组和集合 一.二维数组 二维数组: 一维数组----豆角 二维数组----表格 定义: 1.一维数组: 数据类型[] 数组变量名 = new 数据类型[数组长度]; 数据类型[] 数组 ...

  6. vue实现两重列表集合,点击显示,点击隐藏的折叠效果,(默认显示集合最新一条数据,点击展开,显示集合所有数据)...

    效果图: 默认显示最新一条数据: 点击显示所有数据: 代码: 说明:这里主要是 这块用来控制显示或者隐藏 根据当前点击的  这个方法里传递的index 对应  isShow 数组里的index  ,对 ...

  7. C# 笔记2 - 数组、集合与与文本文件处理

    C# 笔记2 - 数组.集合.文本文件处理 正在做笔记中- 任何软件编程语言都会涉及到这些基本内容--栈.队列.列表.数组.散列表等等,没有它们几乎无法完成任何工作.只是不同语言对某些数据结构由不同的 ...

  8. java入门~第十六天 对象数组以及集合和相关数据结构

    1.对象数组 ​ 就是数组中的元素都是一个个的对象,但是并不是指对象本身,而是指的是对象的引用,即指向对象的那个指针,而这个指针是存放在虚拟机内存的栈中. ​ 对象数组的创建需要两个步骤: 1:创建一 ...

  9. Java中数组转集合总结

    一.使用Arrays.asList() 方法 package com.joshua317;import java.util.*;public class Main {public static voi ...

最新文章

  1. appendChild append insertBefore prepend
  2. 自定义Realm实现认证
  3. mac好用大java_2020 最后,搞个 Mac 玩玩
  4. android rfid 读写sdk,Android-SDK-1.0.0-STD android手机调用RFID模块读取电子标签Demo - 下载 - 搜珍网...
  5. 面试必备:多线程学习(一)
  6. 细说ReactiveCocoa的冷信号与热信号(一)
  7. 指针大小为什么与类型无关?
  8. 【Spring框架】mvc:default-servlet-handler/的作用
  9. java项目管理工具
  10. 图片怎么压缩成指定大小?怎么能把图片压缩到200k?
  11. ssm毕设项目基于远程协作的汽车故障诊断系统t6ipg(java+VUE+Mybatis+Maven+Mysql+sprnig)
  12. 日本語 IME输入法(Microsoft 输入法)切换问题
  13. JS数组方法shift()、unshift()用法实例分析
  14. C#操作Excel文件(读取Excel,写入Excel)
  15. HTML5+CSS3制作透视正方体
  16. 论文解读| NeurIPS 2022:面向科学任务的图神经网络设计
  17. nrf52x 二 GPIOTE
  18. 人脸扫描建模_人脸识别中的特征建模方法与流程
  19. 什么是SQL注入式攻击,如何去防范SQL注入式攻击
  20. BP_TOOLS_MODE[] = bp-tools

热门文章

  1. 计算机网络专业函授,函授计算机专业都考什么课程
  2. Java学习路线以及方法推荐
  3. terminal命令下安装python_Windows Terminal-Windows Terminal命令行安装包下载 官方最新版 - 安下载...
  4. Debian10 Centos7 ProxmoxVE 虚拟硬盘格式转换
  5. element plus 表格合计
  6. 微信公众号生成个性化菜单
  7. 从零开始搭建创业公司全新技术栈
  8. 非隔离开关电源相关知识理解汇总
  9. 华盛顿大学计算机专业硕士申请,华盛顿大学计算机科学与系统理学硕士研究生申请要求及申请材料要求清单...
  10. Cyclone V LAB ALM结构