首页 > 代码库 > 欧拉角之万向节死锁

欧拉角之万向节死锁

  最近阅读《3D数学基础:图形与游戏开发》,看到欧拉角一节,对万向锁感到难以理解。先看一段视频。

  欧拉旋转

  什么是欧拉角?欧拉角是一种描述物体旋转的方式,使用三个值表示物体在三个互相垂直的轴上的旋转。这里的三个轴是任意相互垂直的三个轴,旋转顺序也是随意的。我们可以按照一定的约定来对物体进行欧拉角旋转。此处使用heading(沿Y轴旋转)—pitch(X轴)—bank(Z轴)的旋转顺序,旋转正方向为从坐标轴正方向望去的顺时针方向。

  什么是别名?用欧拉角来表示方位(orientation)的缺点是指定方位的表达式不唯一,也就是别名现象。比如角度加360°,值改变了但是方位并没有变。另一种别名问题是由于一种角度的旋转可以用另一种角度的旋转来表示。例如沿着X轴(pitch)旋转120°,等价于先沿着Y轴(heading)旋转180°,再沿着X轴(pitch)旋转60°,再沿着Z轴(bank)旋转180°(沿物体坐标系)。常用的解决问题的办法是限制三个角度的度数。

  万向锁是一种著名的欧拉角别名问题。当pitch为90度时,heading X°,bank 0° 等价于 heading 0°,bank X°。

  假设一架飞机停在地上,头朝前为+Z,右侧机翼为+X,从地平面向上的方向为+Y。对飞机进行欧拉角旋转,当pitch的度数为90°时,无论heading和bank取什么值,飞机的旋转结果都只有一种情况,那就是头上脚下的结果。欧拉角的三个值应该代表三个维度的旋转,但是只要pitch的度数为±90°,无论heading和bank取什么值,飞机只有2个维度的自由了!或者说,飞机的形态被锁住了,这是与预期不符的——我们丢了了一个维度。无论使用什么样的约定,都会出现某种形态的万向锁,这是无法避免的。

  解决别名问题可以规定,当pitch为90°时,bank为零,使用heading做完所有竖直方向旋转。但是万向锁还会导致另一个问题,那就是坐标突变。当我们使用差值来描述动画时,一旦碰到万向锁,可能导致不符合预期的旋转。举个网上的例子。我们拿着望远镜追踪一个飞行物,使用x°来表示望远镜水平方向的旋转,使用y°来表示望远镜与地平面的角度。使用(x,y)可以描述任何一个角度来操纵望远镜追踪飞行物。假设正北方向x为零,水平方向y为零。当飞行物在正东方向某高度,x为90度,假设飞行物朝望远镜飞来,y值增加,直到飞行物飞临头顶,此时x=90°,y=90°。事实是,无论x等于多少度,我们都只能望向头顶方向。此时,旋转x实际上已经无法再像预期一样实现旋转望远镜的目的。假设飞机连续的向南飞行,y值是连续的差值,但是x值突变成了180°。我们人可以直接完成旋转,但是计算机只懂得机械的按照关键帧和差值进行运算,此时必定会出错。当y等于90度时,我们只有一个维度的自由了(意思是x不再负责一个维度的旋转,x值变动也没用了。因为没有Z轴,所以左右旋转不算一个维度,3D就是加一个Z轴,是一样的)。我的理解是,这种维度的丢失,坐标的突变,轴的重合,就是所谓的万向锁。

  在《3D数学基础》一书中,欧拉角主要用来描述物体的方位状态,至于旋转差值,这个可以用四元数来做,这样就不会出现死锁的情况了。

欧拉角之万向节死锁