【Unity编程】欧拉角与万向节死锁(图文版)

万向节死锁(Gimbal Lock)问题

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

陀螺仪

首先。我们来了解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,此时有效,而且直接改动欧拉角的代码效率应该是比較高的。

posted @ 2017-08-11 12:34  lytwajue  阅读(952)  评论(0编辑  收藏  举报