63

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

如果你能向我展示这个的四元数和/或矩阵表示,那就太酷了!

4

5 回答 5

100

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

如果我们将 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)
于 2009-10-14T20:11:00.887 回答
28

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

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 种方法,具体取决于您想要右手还是左手的结果——您可以通过否定角度来改变“手性”。

于 2009-10-15T01:45:04.107 回答
9

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));

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

于 2012-09-04T17:08:07.813 回答
2

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

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

于 2009-10-14T20:07:52.723 回答
-1

如果有人偶然发现在 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)
于 2017-06-20T12:51:01.807 回答