2009-10-14 3 views

ответ

59

К сожалению, существуют разные соглашения о том, как определять эти вещи (и рулон, шаг, рыскание не совсем такие же, как углы Эйлера), поэтому вам нужно быть осторожным.

Если мы определим шаг = 0 в качестве горизонтальной оси (Z = 0) и рыскания в направлении против часовой стрелки от оси х, то направление вектора будет

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

Обратите внимание, что я не использовал рулон; это вектор единицы направления, он не указывает отношения. Достаточно легко написать матрицу вращения, которая будет переносить вещи в кадр летающего объекта (если вы хотите знать, скажем, где указывает левый крыло), но это действительно хорошая идея, чтобы сначала определить соглашения. Не могли бы вы рассказать нам больше о проблеме?

EDIT: (я был смысл, чтобы вернуться к этому вопросу в течение двух с половиной лет.)

Для полной матрицы вращения, если мы используем вышеупомянутую конвенцию, и мы хотим, чтобы вектор сначала зацепиться, затем шаг, затем сверните, чтобы получить конечные координаты в мировой системе координат, мы должны применить матрицы вращения в обратном порядке.

Первый рулон:

| 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 = cos(yaw)cos(pitch) 
y = sin(yaw)cos(pitch) 
z = sin(pitch) 

А для единичного вектора, который начинается на оси у (левое крыло наконечника), конечные координаты будет:

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) 
+2

FWIW, есть шесть различных формула для нормализованных (от -PI к PI) решений Эйлера в зависимости от заказа вращения (и бесконечность более вариантов с учетом углов обернуть вокруг на 2 * PI). В зависимости от вашего варианта использования вам может потребоваться использовать одну из альтернативных формул для получения желаемого результата. – Adisak

+7

Каковы были бы уравнения, если бы мы _did_ нуждались в рулоне? – ApproachingDarknessFish

+1

@ApproachingDarknessFish уравнения будут точно такими же, поскольку roll заставляет вектор вращаться вокруг себя, что не влияет на него. –

1

Здесь необходимо четко указать свои определения - в частности, какой вектор вы хотите? Если это направление, на которое указывает самолет, рулон даже не влияет на него, и вы просто используете spherical coordinates (возможно, с перестановкой осей/углов).

Если, с другой стороны, вы хотите взять данный вектор и преобразовать его под эти углы, вы ищете матрицу вращения. wiki article на матрицах вращения содержит формулу для поворота поворотного вала, основанного на матрицах вращения xyz. Я не собираюсь вводить его здесь, учитывая связанные с ним греческие буквы и матрицы.

18

Есть шесть различных способов преобразования три угла Эйлера в матрице в зависимости от порядка, что они применяются:

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, некоторые процессоры могут вычислить Sin & Cos одновременно (например, fsincos на x86). Если вы это сделаете, вы можете сделать это немного быстрее с тремя вызовами, а не с 6, чтобы вычислить начальные значения sin &.

Обновление: на самом деле существует 12 способов в зависимости от того, хотите ли вы иметь правовые или левые результаты - вы можете изменить «ручность», отрицая углы.

+0

Кстати, вы можете изменить «ручность», отрицая углы. – Adisak

+0

Это правая или левая? – ciyo

5

Бета спасла мой день. Тем не менее, я использую немного другую систему координат, и мое определение высоты тона вверх/вниз (кивая головой в согласии), где положительный шаг приводит к отрицательным y-компонентам. Мой опорный вектор - это стиль OpenGl (вниз по оси -z), поэтому с yaw = 0, pitch = 0 результирующий единичный вектор должен быть равен (0, 0, -1). Если кто приходит на этот пост и имеет трудности перевод формулы Бета к данной системе уравнений, которые я использую:

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

Обратите внимание на изменение знака и рыскания < -> шаг подкачки. Надеюсь, это немного сэкономит.

0

Если кто-то наткнулся на поиск в 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)