欧拉角是一种常用的描述方位的方法。这项技术是著名数学家Leonhard Euler(1707~1783)的名字命名的,他证明了角位移序列等价于单个角位移。想详细了解的请点击维基百科:http://zh.wikipedia.org/wiki/%E6%AC%A7%E6%8B%89%E8%A7%92#.E5.88.A5.E7.A8.AE.E9.A0.86.E5.BA.8F
1,什么是欧拉角
欧拉角的基本思想是将角位移分解为绕三个互相垂直轴的三个旋转组成的序列。这听起来复杂,其实它是非常直观的。之所以有“角位移”的说法正是因为欧拉角能用来描述任意旋转,但最有意义的是使用笛卡尔坐标系并按照一定顺序所组成的旋转序列。最常用的约定,即所谓“heading-pitch-bank”约定。在这个系统中,一个方位被定义为一个heading角,一个pitch角,和一个bank角。它的基本思想就是让物体开始于“标准”方位——就是物体坐标轴和惯性坐标轴对齐。在标准方位上,让物体作heading,pitch,bank旋转,最后物体到达我们想要描述的方位。
在精确定义术语“heading”“pitch”“bank”前,先让我们简要回顾本书中使用的坐标空间约定。我们使用左手坐标系,+x向右,+y向上,+z向前。
heading为绕y轴的旋转量,向右旋转为正,旋转正方向是顺时针方向,经过heading旋转之后,pitch为绕x轴的旋转量,注意是物体坐标系的x轴,不是原惯性坐标系的x轴,依然遵守左手法则,向下旋转为正。最后,经过了heading,pitch后,bank为绕z轴的旋转量,依然是物体坐标系的z轴。
当我们说到旋转的顺序是heading-pitch-bank时,是指从惯性坐标系到物体坐标系,如果从物体坐标系到惯性坐标系则相反。
2,关于欧拉角的其他约定
前面曾提到过,heading-pitch-bank系统不是惟一的欧拉角系统,绕任意三个互相垂直轴的任意旋转序列都能定义一个方位。所以,多种选择导致了欧拉角约定的多样性:
1)heading-pitch-bank系统有两个名称,当然,不同的名字并不代表不同的约定,这其实并不重要,一组常用的术语是roll-pitch-yaw,其中的roll对应与bank,yaw对应于heading,它定义了从物体坐标系到惯性坐标系的旋转顺序
2)任意三个轴都能作为旋转轴,不一定必须是笛卡尔轴,但是用笛卡尔轴最有意义
3)也可以选用右手坐标规则
4)旋转可以以不同的顺序进行
3,优点:1)容易使用;2)表达简洁;3)任意三个角都是合法的
4,缺点:1)给定方位的表达方式不唯一;2)两个角度间求插值非常困难
采用限制欧拉角的方法来避免以上问题的出现:heading限制在+-180,pitch为+-90。
以上为欧拉角的定义。旋转的方法如下:
从欧拉角矢量转换很容易,困难的部分是转换回来。XNA提供了一个方法可以创建旋转矩阵,但它并没有提供转换回来的方法,因此我们将不得不自己实现。
首先,我们转换为旋转矩阵,Matrix.CreateFromYawPitchRoll()方法可以做到这一点。如果这里使用欧拉角,我们需要以以下顺序提供坐标:
- Yaw(偏航):欧拉角向量的y轴
- Pitch(俯仰):欧拉角向量的x轴
- Roll(翻滚): 欧拉角向量的z轴
想象一下飞机,yaw指水平方向的机头指向,它绕y轴旋转。Pitch指与水平方向的夹角,绕x轴旋转。Roll指飞机的翻滚,绕z轴旋转。下面的代码演示了这一过程:
这种方法可以提供一个表示旋转的矩阵。下面是最难的部分:将矩阵转换为欧拉角。
这个过程是将矩阵拆散:一个表示位置的Vecto3,另一个表示缩放,而四元数表示旋转。然后,我们必须将这个四元数转换为一个欧拉角矢量。我不打算详细讨论代码背后的数学原理,但如果你有兴趣,可以到前面链接中的网址上去看看。
下面是将旋转矩阵转换为欧拉角的代码。
<span style="font-family:KaiTi_GB2312;font-size:18px;">// Returns Euler angles that point from one point to another Vector3 AngleTo(Vector3 from, Vector3 location) { Vector3 angle = new Vector3(); Vector3 v3 = Vector3.Normalize(location - from); angle.X = (float)Math.Asin(v3.Y); angle.Y = (float)Math.Atan2((double)-v3.X, (double)-v3.Z); return angle; } // Converts a Quaternion to Euler angles (X = Yaw, Y = Pitch, Z = Roll) Vector3 QuaternionToEulerAngleVector3(Quaternion rotation) { Vector3 rotationaxes = new Vector3(); Vector3 forward = Vector3.Transform(Vector3.Forward, rotation); Vector3 up = Vector3.Transform(Vector3.Up, rotation); rotationaxes = AngleTo(new Vector3(), forward); if (rotationaxes.X == MathHelper.PiOver2) { rotationaxes.Y = (float)Math.Atan2((double)up.X, (double)up.Z); rotationaxes.Z = 0; } else if (rotationaxes.X == -MathHelper.PiOver2) { rotationaxes.Y = (float)Math.Atan2((double)-up.X, (double)-up.Z); rotationaxes.Z = 0; } else { up = Vector3.Transform(up, Matrix.CreateRotationY(-rotationaxes.Y)); up = Vector3.Transform(up, Matrix.CreateRotationX(-rotationaxes.X)); rotationaxes.Z = (float)Math.Atan2((double)-up.Z, (double)up.Y); } return rotationaxes; } // Converts a Rotation Matrix to a quaternion, then into a Vector3 containing // Euler angles (X: Pitch, Y: Yaw, Z: Roll) Vector3 MatrixToEulerAngleVector3(Matrix Rotation) { Vector3 translation, scale; Quaternion rotation; Rotation.Decompose(out scale, out rotation, out translation); Vector3 eulerVec = QuaternionToEulerAngleVector3(rotation); return eulerVec; }</span>
同时,欧拉角的顺序规定一共有12种,在转化的时候一定要弄清楚规定是怎样的。