3D数学 欧拉角编程

欧拉角编程

1. 欧拉角转换到矩阵

欧拉角描述了一个旋转序列。分别计算出每个旋转的矩阵再将它们连成一个矩阵,这个矩阵就代表了整个角位移。注意,要区分物体-惯性矩阵还是惯性-物体矩阵,它们互逆(也互为转置矩阵)。

void RotationMatrix::setup(const EulerAngles& orientation)
{
    //计算角度的sin和cos值
    float sh, ch, sp, cp, sb, cb;
    sinCos(&sh,&ch,orientation.heading);
    sinCos(&sp,&cp,orientation.pitch);
    sinCos(&sb,&cb,orientation.bank);

    m11 = ch * cb + sh * sp * sb;
    m12 = -ch * sb + sh * sp * cb;
    m13 = sh * cp;

    m21 = sb * cp;
    m22 = cb * cp;
    m23 = -sp;

    m31 = -sh * cb + ch * sp * sb;
    m32 = sb * sh + ch * sp * cb;
    m33 = ch * cp;

}

2. 矩阵转换到欧拉角

注意以下几点:

  • 必须知道矩阵代表物体-惯性坐标系还是惯性-物体坐标系。
  • 因为“别名”问题,要限制欧拉角。
  • 要处理浮点数精度的误差。
  • 要处理万向锁。
  • 这里,只讨论旋转矩阵上的转换。
void EulerAngles::fromObjectToWorldMatrix(const Matrix4x3 &m)
{
    float sp = - m.m32;

    //检测万向锁
    if (fabs(sp)>0.99999f)
    {
        //向上看或向下看
        //将bank置为零,赋值给heading
        pitch = kPiOver2 * sp;
        //bank置为零,计算heading
        bank = 0.0f;
        heading = atan2(-m.m23, m.m11);
    }
    else
    {
        //计算角度
        heading = atan2(m.m31, m.m33);
        pitch = asin(sp);
        bank = atan2(m.m12, m.m22);
    }
}

void EulerAngles::fromWorldToBojectMatrix(const Matrix4x3 &m)
{
    //根据m32计算sin(pitch)
    float sp = -m.m32;

    //检查万向锁

    if (fabs(sp) > 0.99999f)
    {
        //向正上看或正下方看
        pitch = kPiOver2 * sp;
        //bank置零,计算heading
        bank = 0.0f;
        heading = atan2(-m.m31, m.m11);
    }
    else
    {
        //计算角度

        heading = atan2(m.m13,m.m33);
        pitch = asin(sp);
        bank = atan2(m.m21,m.m22);
    }

}

void EulerAngles::fromRotationMatrix(const RotationMatrix &m)
{
    //根据m32计算sin(pitch)
    float sp = -m.m32;

    //检查万向锁

    if (fabs(sp) > 0.99999f)
    {
        //向正上看或正下方看
        pitch = kPiOver2 * sp;
        //bank置零,计算heading
        bank = 0.0f;
        heading = atan2(-m.m31, m.m11);
    }
    else
    {
        //计算角度

        heading = atan2(m.m13,m.m33);
        pitch = asin(sp);
        bank = atan2(m.m21,m.m22);
    }
}

3. 欧拉角编程

/////////////////////////////////////////////////////////////////////////////
//
// 3D Math Primer for Games and Graphics Development
//
// EulerAngles.h - Declarations for class EulerAngles
//
// Visit gamemath.com for the latest version of this file.
//
// For more details, see EulerAngles.cpp
//
/////////////////////////////////////////////////////////////////////////////

#ifndef __EULERANGLES_H_INCLUDED__
#define __EULERANGLES_H_INCLUDED__

// Forward declarations

class Quaternion;
class Matrix4x3;
class RotationMatrix;

//---------------------------------------------------------------------------
// class EulerAngles
//
// This class represents a heading-pitch-bank Euler angle triple.

class EulerAngles {
public:

// Public data

    // Straightforward representation.  Store the three angles, in
    // radians

    float   heading;
    float   pitch;
    float   bank;

// Public operations

    // Default constructor does nothing

    EulerAngles() {}

    // Construct from three values

    EulerAngles(float h, float p, float b) :
        heading(h), pitch(p), bank(b) {}

    // Set to identity triple (all zeros)

    void    identity() { pitch = bank = heading = 0.0f; }

    // Determine "canonical" Euler angle triple

    void    canonize();

    // Convert the quaternion to Euler angle format.  The input quaternion
    // is assumed to perform the rotation from object-to-inertial
    // or inertial-to-object, as indicated.

    void    fromObjectToInertialQuaternion(const Quaternion &q);
    void    fromInertialToObjectQuaternion(const Quaternion &q);

    // Convert the transform matrix to Euler angle format.  The input
    // matrix is assumed to perform the transformation from
    // object-to-world, or world-to-object, as indicated.  The
    // translation portion of the matrix is ignored.  The
    // matrix is assumed to be orthogonal.

    void    fromObjectToWorldMatrix(const Matrix4x3 &m);
    void    fromWorldToObjectMatrix(const Matrix4x3 &m);

    // Convert a rotation matrix to Euler Angle form.

    void    fromRotationMatrix(const RotationMatrix &m);
};

// A global "identity" Euler angle constant

extern const EulerAngles kEulerAnglesIdentity;

/////////////////////////////////////////////////////////////////////////////
#endif // #ifndef __EULERANGLES_H_INCLUDED__
/////////////////////////////////////////////////////////////////////////////
//
// 3D Math Primer for Games and Graphics Development
//
// EulerAngles.cpp - Implementation of class EulerAngles
//
// Visit gamemath.com for the latest version of this file.
//
/////////////////////////////////////////////////////////////////////////////

#include <math.h>

#include "EulerAngles.h"
#include "Quaternion.h"
#include "MathUtil.h"
#include "Matrix4x3.h"
#include "RotationMatrix.h"

/////////////////////////////////////////////////////////////////////////////
//
// Notes:
//
// See Chapter 11 for more information on class design decisions.
//
// See section 10.3 for more information on the Euler angle conventions
// assumed.
//
/////////////////////////////////////////////////////////////////////////////

/////////////////////////////////////////////////////////////////////////////
//
// global data
//
/////////////////////////////////////////////////////////////////////////////

// The global "identity" Euler angle constant.  Now we may not know exactly
// when this object may get constructed, in relation to other objects, so
// it is possible for the object to be referenced before it is initialized.
// However, on most implementations, it will be zero-initialized at program
// startup anyway, before any other objects are constructed.

const EulerAngles kEulerAnglesIdentity(0.0f, 0.0f, 0.0f);

/////////////////////////////////////////////////////////////////////////////
//
// class EulerAngles Implementation
//
/////////////////////////////////////////////////////////////////////////////

//---------------------------------------------------------------------------
// EulerAngles::canonize
//
// Set the Euler angle triple to its "canonical" value.  This does not change
// the meaning of the Euler angles as a representation of Orientation in 3D,
// but if the angles are for other purposes such as angular velocities, etc,
// then the operation might not be valid.
//
// See section 10.3 for more information.

void    EulerAngles::canonize() {

    // First, wrap pitch in range -pi ... pi

    pitch = wrapPi(pitch);

    // Now, check for "the back side" of the matrix, pitch outside
    // the canonical range of -pi/2 ... pi/2

    if (pitch < -kPiOver2) {
        pitch = -kPi - pitch;
        heading += kPi;
        bank += kPi;
    } else if (pitch > kPiOver2) {
        pitch = kPi - pitch;
        heading += kPi;
        bank += kPi;
    }

    // OK, now check for the gimbel lock case (within a slight
    // tolerance)

    if (fabs(pitch) > kPiOver2 - 1e-4) {

        // We are in gimbel lock.  Assign all rotation
        // about the vertical axis to heading

        heading += bank;
        bank = 0.0f;

    } else {

        // Not in gimbel lock.  Wrap the bank angle in
        // canonical range 

        bank = wrapPi(bank);
    }

    // Wrap heading in canonical range

    heading = wrapPi(heading);
}

//---------------------------------------------------------------------------
// EulerAngles::fromObjectToInertialQuaternion
//
// Setup the Euler angles, given an object->inertial rotation quaternion
//
// See 10.6.6 for more information.

void    EulerAngles::fromObjectToInertialQuaternion(const Quaternion &q) {

    // Extract sin(pitch)

    float sp = -2.0f * (q.y*q.z - q.w*q.x);

    // Check for Gimbel lock, giving slight tolerance for numerical imprecision

    if (fabs(sp) > 0.9999f) {

        // Looking straight up or down

        pitch = kPiOver2 * sp;

        // Compute heading, slam bank to zero

        heading = atan2(-q.x*q.z + q.w*q.y, 0.5f - q.y*q.y - q.z*q.z);
        bank = 0.0f;

    } else {

        // Compute angles.  We don't have to use the "safe" asin
        // function because we already checked for range errors when
        // checking for Gimbel lock

        pitch   = asin(sp);
        heading = atan2(q.x*q.z + q.w*q.y, 0.5f - q.x*q.x - q.y*q.y);
        bank    = atan2(q.x*q.y + q.w*q.z, 0.5f - q.x*q.x - q.z*q.z);
    }
}

//---------------------------------------------------------------------------
// EulerAngles::fromInertialToObjectQuaternion
//
// Setup the Euler angles, given an inertial->object rotation quaternion
//
// See 10.6.6 for more information.

void    EulerAngles::fromInertialToObjectQuaternion(const Quaternion &q) {

    // Extract sin(pitch)

    float sp = -2.0f * (q.y*q.z + q.w*q.x);

    // Check for Gimbel lock, giving slight tolerance for numerical imprecision

    if (fabs(sp) > 0.9999f) {

        // Looking straight up or down

        pitch = kPiOver2 * sp;

        // Compute heading, slam bank to zero

        heading = atan2(-q.x*q.z - q.w*q.y, 0.5f - q.y*q.y - q.z*q.z);
        bank = 0.0f;

    } else {

        // Compute angles.  We don't have to use the "safe" asin
        // function because we already checked for range errors when
        // checking for Gimbel lock

        pitch   = asin(sp);
        heading = atan2(q.x*q.z - q.w*q.y, 0.5f - q.x*q.x - q.y*q.y);
        bank    = atan2(q.x*q.y - q.w*q.z, 0.5f - q.x*q.x - q.z*q.z);
    }
}

//---------------------------------------------------------------------------
// EulerAngles::fromObjectToWorldMatrix
//
// Setup the Euler angles, given an object->world transformation matrix.
//
// The matrix is assumed to be orthogonal.  The translation portion is
// ignored.
//
// See 10.6.2 for more information.

void    EulerAngles::fromObjectToWorldMatrix(const Matrix4x3 &m) {

    // Extract sin(pitch) from m32.

    float   sp = -m.m32;

    // Check for Gimbel lock

    if (fabs(sp) > 9.99999f) {

        // Looking straight up or down

        pitch = kPiOver2 * sp;

        // Compute heading, slam bank to zero

        heading = atan2(-m.m23, m.m11);
        bank = 0.0f;

    } else {

        // Compute angles.  We don't have to use the "safe" asin
        // function because we already checked for range errors when
        // checking for Gimbel lock

        heading = atan2(m.m31, m.m33);
        pitch = asin(sp);
        bank = atan2(m.m12, m.m22);
    }
}

//---------------------------------------------------------------------------
// EulerAngles::fromWorldToObjectMatrix
//
// Setup the Euler angles, given a world->object transformation matrix.
//
// The matrix is assumed to be orthogonal.  The translation portion is
// ignored.
//
// See 10.6.2 for more information.

void    EulerAngles::fromWorldToObjectMatrix(const Matrix4x3 &m) {

    // Extract sin(pitch) from m23.

    float   sp = -m.m23;

    // Check for Gimbel lock

    if (fabs(sp) > 9.99999f) {

        // Looking straight up or down

        pitch = kPiOver2 * sp;

        // Compute heading, slam bank to zero

        heading = atan2(-m.m31, m.m11);
        bank = 0.0f;

    } else {

        // Compute angles.  We don't have to use the "safe" asin
        // function because we already checked for range errors when
        // checking for Gimbel lock

        heading = atan2(m.m13, m.m33);
        pitch = asin(sp);
        bank = atan2(m.m21, m.m22);
    }
}

//---------------------------------------------------------------------------
// EulerAngles::fromRotationMatrix
//
// Setup the Euler angles, given a rotation matrix.
//
// See 10.6.2 for more information.

void    EulerAngles::fromRotationMatrix(const RotationMatrix &m) {

    // Extract sin(pitch) from m23.

    float   sp = -m.m23;

    // Check for Gimbel lock

    if (fabs(sp) > 9.99999f) {

        // Looking straight up or down

        pitch = kPiOver2 * sp;

        // Compute heading, slam bank to zero

        heading = atan2(-m.m31, m.m11);
        bank = 0.0f;

    } else {

        // Compute angles.  We don't have to use the "safe" asin
        // function because we already checked for range errors when
        // checking for Gimbel lock

        heading = atan2(m.m13, m.m33);
        pitch = asin(sp);
        bank = atan2(m.m21, m.m22);
    }
}
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值