【问题标题】:How to convert Euler angles to directional vector?如何将欧拉角转换为方向向量?
【发布时间】:2010-12-06 19:35:05
【问题描述】:

我有俯仰角、滚动角和偏航角。我如何将这些转换为方向向量?

如果你能给我看一个四元数和/或矩阵表示,那就太酷了!

【问题讨论】:

    标签: c++ math game-physics


    【解决方案1】:

    不幸的是,关于如何定义这些东西有不同的约定(滚动、俯仰、偏航与欧拉角并不完全相同),所以你必须小心。

    如果我们将 pitch=0 定义为水平 (z=0) 并将 yaw 定义为从 x 轴逆时针方向,那么方向向量将为

    x = cos(偏航)*cos(俯仰) y = sin(yaw)*cos(pitch) z = sin(音高)

    请注意,我没有使用滚动;这是方向单位向量,它不指定姿态。编写一个旋转矩阵将物体带入飞行物体的框架很容易(例如,如果您想知道左翼尖指向的位置),但首先指定约定确实是个好主意。你能告诉我们更多关于这个问题的信息吗?

    编辑: (两年半以来我一直想回到这个问题。)

    对于完整的旋转矩阵,如果我们使用上面的约定并且我们希望向量先偏航,然后俯仰,然后滚动,为了得到世界坐标系中的最终坐标,我们必须在倒序。

    第一卷:

    | 1    0          0      |
    | 0 cos(roll) -sin(roll) |
    | 0 sin(roll)  cos(roll) |
    

    然后间距:

    | cos(pitch) 0 -sin(pitch) |
    |     0      1      0      |
    | sin(pitch) 0  cos(pitch) |
    

    然后偏航:

    | cos(yaw) -sin(yaw) 0 |
    | sin(yaw)  cos(yaw) 0 |
    |    0         0     1 |
    

    将它们组合起来,总的旋转矩阵为:

    | cos(yaw)cos(pitch) -cos(yaw)sin(pitch)sin(roll)-sin(yaw)cos(roll) -cos(yaw)sin(pitch)cos(roll)+sin(yaw)sin(roll)|
    | sin(yaw)cos(pitch) -sin(yaw)sin(pitch)sin(roll)+cos(yaw)cos(roll) -sin(yaw)sin(pitch)cos(roll)-cos(yaw)sin(roll)|
    | sin(pitch)          cos(pitch)sin(roll)                            cos(pitch)sin(roll)|
    

    所以对于从 x 轴开始的单位向量,最终坐标为:

    x = cos(yaw)cos(pitch)
    y = sin(yaw)cos(pitch)
    z = sin(pitch)
    

    对于从 y 轴(左翼尖)开始的单位向量,最终坐标为:

    x = -cos(yaw)sin(pitch)sin(roll)-sin(yaw)cos(roll)
    y = -sin(yaw)sin(pitch)sin(roll)+cos(yaw)cos(roll)
    z =  cos(pitch)sin(roll)
    

    【讨论】:

    • FWIW,归一化(从 -PI 到 PI)欧拉解决方案有六种不同的公式,具体取决于旋转顺序(考虑到角度环绕在 2*PI 处,还有更多变体)。根据您的用例,您可能必须使用其中一种替代公式来获得所需的结果。
    • 如果我们确实需要滚动,方程式会是什么?
    • @ApproachingDarknessFish 方程将完全相同,因为滚动会导致矢量围绕自身旋转,这对其没有影响。
    • @Beta,我在哪里可以找到这个公式的数学推导? wiki 太宽泛了,我需要推导方面的帮助。
    • @Beta,是否可以转换回 Pitch Yaw Roll? XYZ -> Pitch Yaw Roll?
    【解决方案2】:

    根据应用的顺序,有六种不同的方法可以将三个欧拉角转换为矩阵:

    typedef float Matrix[3][3];
    struct EulerAngle { float X,Y,Z; };
    
    // Euler Order enum.
    enum EEulerOrder
    {
        ORDER_XYZ,
        ORDER_YZX,
        ORDER_ZXY,
        ORDER_ZYX,
        ORDER_YXZ,
        ORDER_XZY
    };
    
    
    Matrix EulerAnglesToMatrix(const EulerAngle &inEulerAngle,EEulerOrder EulerOrder)
    {
        // Convert Euler Angles passed in a vector of Radians
        // into a rotation matrix.  The individual Euler Angles are
        // processed in the order requested.
        Matrix Mx;
    
        const FLOAT    Sx    = sinf(inEulerAngle.X);
        const FLOAT    Sy    = sinf(inEulerAngle.Y);
        const FLOAT    Sz    = sinf(inEulerAngle.Z);
        const FLOAT    Cx    = cosf(inEulerAngle.X);
        const FLOAT    Cy    = cosf(inEulerAngle.Y);
        const FLOAT    Cz    = cosf(inEulerAngle.Z);
    
        switch(EulerOrder)
        {
        case ORDER_XYZ:
            Mx.M[0][0]=Cy*Cz;
            Mx.M[0][1]=-Cy*Sz;
            Mx.M[0][2]=Sy;
            Mx.M[1][0]=Cz*Sx*Sy+Cx*Sz;
            Mx.M[1][1]=Cx*Cz-Sx*Sy*Sz;
            Mx.M[1][2]=-Cy*Sx;
            Mx.M[2][0]=-Cx*Cz*Sy+Sx*Sz;
            Mx.M[2][1]=Cz*Sx+Cx*Sy*Sz;
            Mx.M[2][2]=Cx*Cy;
            break;
    
        case ORDER_YZX:
            Mx.M[0][0]=Cy*Cz;
            Mx.M[0][1]=Sx*Sy-Cx*Cy*Sz;
            Mx.M[0][2]=Cx*Sy+Cy*Sx*Sz;
            Mx.M[1][0]=Sz;
            Mx.M[1][1]=Cx*Cz;
            Mx.M[1][2]=-Cz*Sx;
            Mx.M[2][0]=-Cz*Sy;
            Mx.M[2][1]=Cy*Sx+Cx*Sy*Sz;
            Mx.M[2][2]=Cx*Cy-Sx*Sy*Sz;
            break;
    
        case ORDER_ZXY:
            Mx.M[0][0]=Cy*Cz-Sx*Sy*Sz;
            Mx.M[0][1]=-Cx*Sz;
            Mx.M[0][2]=Cz*Sy+Cy*Sx*Sz;
            Mx.M[1][0]=Cz*Sx*Sy+Cy*Sz;
            Mx.M[1][1]=Cx*Cz;
            Mx.M[1][2]=-Cy*Cz*Sx+Sy*Sz;
            Mx.M[2][0]=-Cx*Sy;
            Mx.M[2][1]=Sx;
            Mx.M[2][2]=Cx*Cy;
            break;
    
        case ORDER_ZYX:
            Mx.M[0][0]=Cy*Cz;
            Mx.M[0][1]=Cz*Sx*Sy-Cx*Sz;
            Mx.M[0][2]=Cx*Cz*Sy+Sx*Sz;
            Mx.M[1][0]=Cy*Sz;
            Mx.M[1][1]=Cx*Cz+Sx*Sy*Sz;
            Mx.M[1][2]=-Cz*Sx+Cx*Sy*Sz;
            Mx.M[2][0]=-Sy;
            Mx.M[2][1]=Cy*Sx;
            Mx.M[2][2]=Cx*Cy;
            break;
    
        case ORDER_YXZ:
            Mx.M[0][0]=Cy*Cz+Sx*Sy*Sz;
            Mx.M[0][1]=Cz*Sx*Sy-Cy*Sz;
            Mx.M[0][2]=Cx*Sy;
            Mx.M[1][0]=Cx*Sz;
            Mx.M[1][1]=Cx*Cz;
            Mx.M[1][2]=-Sx;
            Mx.M[2][0]=-Cz*Sy+Cy*Sx*Sz;
            Mx.M[2][1]=Cy*Cz*Sx+Sy*Sz;
            Mx.M[2][2]=Cx*Cy;
            break;
    
        case ORDER_XZY:
            Mx.M[0][0]=Cy*Cz;
            Mx.M[0][1]=-Sz;
            Mx.M[0][2]=Cz*Sy;
            Mx.M[1][0]=Sx*Sy+Cx*Cy*Sz;
            Mx.M[1][1]=Cx*Cz;
            Mx.M[1][2]=-Cy*Sx+Cx*Sy*Sz;
            Mx.M[2][0]=-Cx*Sy+Cy*Sx*Sz;
            Mx.M[2][1]=Cz*Sx;
            Mx.M[2][2]=Cx*Cy+Sx*Sy*Sz;
            break;
        }
        return(Mx);
    }
    

    FWIW,一些 CPU 可以同时计算 Sin 和 Cos(例如 x86 上的 fsincos)。如果这样做,您可以通过 3 次调用而不是 6 次调用来更快地计算初始 sin 和 cos 值。

    更新:实际上有 12 种方法,具体取决于您想要右手还是左手结果 - 您可以通过否定角度来更改“手性”。

    【讨论】:

      【解决方案3】:

      测试版拯救了我的一天。但是,我使用的参考坐标系略有不同,我对音高的定义是向上\向下(点头表示同意),其中 音高会导致 y -零件。我的参考向量是 OpenGl 样式(沿着 -z 轴),所以当 yaw=0, pitch=0 时,得到的单位向量应该等于 (0, 0, -1)。 如果有人看到这篇文章并且在将 Beta 的公式转换为这个特定系统时遇到困难,我使用的公式是:

      vDir->X = sin(yaw);
      vDir->Y = -(sin(pitch)*cos(yaw));
      vDir->Z = -(cos(pitch)*cos(yaw));
      

      注意符号变化和偏航 俯仰交换。希望这可以节省一些时间。

      【讨论】:

        【解决方案4】:

        您需要在此处明确定义 - 特别是,您想要的向量是什么?如果它是飞机指向的方向,滚动甚至不会影响它,而您只是在使用spherical coordinates(可能轴/角度已置换)。

        另一方面,如果您想获取一个给定的向量并通过这些角度对其进行变换,那么您正在寻找一个旋转矩阵。旋转矩阵上的wiki article 包含一个基于 xyz 旋转矩阵的偏航俯仰滚动旋转公式。考虑到涉及的希腊字母和矩阵,我不会尝试在此处输入它。

        【讨论】:

          【解决方案5】:

          如果有人偶然发现要在 FreeCAD 中实现。

          import FreeCAD, FreeCADGui
          from FreeCAD import Vector
          from math import sin, cos, pi
          
          cr = FreeCADGui.ActiveDocument.ActiveView.getCameraOrientation().toEuler()
          crx = cr[2] # Roll
          cry = cr[1] # Pitch
          crz = cr[0] # Yaw
          
          crx = crx * pi / 180.0
          cry = cry * pi / 180.0
          crz = crz * pi / 180.0
          
          x = sin(crz)
          y = -(sin(crx) * cos(crz))
          z = cos(crx) * cos(cry)
          
          view = Vector(x, y, z)
          

          【讨论】:

          • 问题是 C/C++ 而不是 Python
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-02-25
          • 1970-01-01
          • 2015-07-28
          • 1970-01-01
          • 2017-02-08
          相关资源
          最近更新 更多