如何将欧拉角转换为方向向量?

Pol*_*878 46 c++ math game-physics

我有俯仰,滚转和偏航角度.我如何将这些转换为方向向量?

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

Bet*_*eta 74

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

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

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

请注意,我没有使用过roll; 这是方向单位向量,它没有指定态度.编写一个旋转矩阵很容易,它将物体带入飞行物体的框架中(如果你想知道,比如左翼尖指向的位置),但最好先指定惯例.你能告诉我们更多这个问题吗?

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

对于完整旋转矩阵,如果我们使用上面的约定并且我们希望矢量首先偏转,然后俯仰,然后滚动,以便在世界坐标系中获得最终坐标,我们必须以相反的顺序应用旋转矩阵.

第一卷:

| 1    0          0      |
| 0 cos(roll) -sin(roll) |
| 0 sin(roll)  cos(roll) |
Run Code Online (Sandbox Code Playgroud)

投球:

| cos(pitch) 0 -sin(pitch) |
|     0      1      0      |
| sin(pitch) 0  cos(pitch) |
Run Code Online (Sandbox Code Playgroud)

偏航:

| cos(yaw) -sin(yaw) 0 |
| sin(yaw)  cos(yaw) 0 |
|    0         0     1 |
Run Code Online (Sandbox Code Playgroud)

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

| 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)|
Run Code Online (Sandbox Code Playgroud)

因此,对于从x轴开始的单位矢量,最终坐标将是:

x = cos(yaw)cos(pitch)
y = sin(yaw)cos(pitch)
z = sin(pitch)
Run Code Online (Sandbox Code Playgroud)

对于从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)
Run Code Online (Sandbox Code Playgroud)

  • 如果我们_did需要滚动,那么方程式是什么? (7认同)
  • FWIW,归一化(从-PI到PI)Euler解决方案有六种不同的公式,取决于旋转顺序(无限多种变体,考虑角度环绕在2*PI).根据您的使用情况,您可能必须使用备用公式之一来获得所需的结果. (2认同)
  • @ApproachingDarknessFish 方程将完全相同,因为 roll 导致向量围绕自身旋转,这对其没有影响。 (2认同)

Adi*_*sak 20

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

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);
}
Run Code Online (Sandbox Code Playgroud)

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

更新:如果您想要右手或左手结果,实际上有12种方式取决于您 - 您可以通过否定角度来改变"手性".


pau*_*s86 7

Beta节省了我的一天.不过我使用了一个稍微不同的参考坐标系与我的间距的定义是向上\向下(点头你的头在协议)其中一个积极的间距导致 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));
Run Code Online (Sandbox Code Playgroud)

注意符号变化和偏航< - >音高交换.希望这能节省一些时间.