2009-10-14 6 views

risposta

59

Sfortunatamente ci sono diverse convenzioni su come definire queste cose (e il rollio, il beccheggio, l'imbardata non sono proprio gli angoli di Eulero), quindi dovrete stare attenti.

Se definiamo passo = 0 per orizzontale (z = 0) e di imbardata come senso antiorario dall'asse x, allora il vettore di direzione sarà

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

noti che non ho usato rotolo; questo è il vettore di unità di direzione, non specifica l'atteggiamento. È abbastanza facile scrivere una matrice di rotazione che trasporterà le cose nella cornice dell'oggetto volante (se vuoi sapere, per esempio, dove punta la punta dell'ala sinistra sta puntando), ma è davvero una buona idea specificare prima le convenzioni. Puoi dirci di più sul problema?

EDIT: (ho in mente di tornare a questa domanda per due anni e mezzo.)

Per la matrice rotazione completa, se usiamo la convenzione di cui sopra e vogliamo che il vettore prima di imbardare, poi lanciare, poi rotolare, per ottenere le coordinate finali nel frame delle coordinate del mondo, dobbiamo applicare le matrici di rotazione nell'ordine inverso.

primo rullo:

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

poi passo:

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

poi imbardata:

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

Combinateli, e la matrice di rotazione totale è:

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

Così per un vettore unitario che inizia in corrispondenza dell'asse x, le coordinate finali saranno:

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

E per il versore che inizia in corrispondenza dell'asse y (ala-punta sinistra), le coordinate finali sarà:

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, ci sono sei formula diversa per normalizzati (da -PI a PI) soluzioni Eulero seconda ordine rotazione (e infinito più varianti considerando angoli avvolgere intorno a 2 * PI). A seconda del caso d'uso, potrebbe essere necessario utilizzare una delle formule alternative per ottenere il risultato desiderato. – Adisak

+7

Quali sarebbero le equazioni se avessimo bisogno di rollare? – ApproachingDarknessFish

+1

@ApproachingDarknessFish le equazioni sarebbero esattamente le stesse, poiché il rotolo fa ruotare il vettore su se stesso, che non ha alcun effetto su di esso. –

1

Hai bisogno di essere chiaro sulle tue definizioni qui - in particolare, qual è il vettore che vuoi? Se è la direzione che un aereo sta puntando, il tiro non ha nemmeno effetto, e stai usando solo spherical coordinates (probabilmente con assi/angoli permutati).

Se invece si desidera prendere un determinato vettore e trasformarlo da questi angoli, si sta cercando una matrice di rotazione. Le matrici di rotazione wiki article contengono una formula per una rotazione di imbardata, basata sulle matrici di rotazione xyz. Non ho intenzione di tentare di inserirlo qui, date le lettere greche e le matrici coinvolte.

18

ci sono sei modi diversi per convertire tre angoli di Eulero in una matrice a seconda dell'ordine che vengono applicati:

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, alcune CPU possono calcolare Sin & Cos simultaneamente (ad esempio fsincos su x86). Se lo fai, puoi renderlo un po 'più veloce con tre chiamate anziché 6 per calcolare il valore iniziale di & cos.

Update: In realtà ci sono 12 modi a seconda se si desidera che i risultati destri o mancini - è possibile modificare la "manualità" negando gli angoli.

+0

BTW, puoi cambiare la "mano" negando gli angoli. – Adisak

+0

È destrorso o mancino? – ciyo

5

Beta ha salvato la giornata. Comunque sto usando un riferimento leggermente diverso sistema di coordinate e la mia definizione di passo è su \ giù (annuendo la testa d'accordo) dove un positivi risultati campo in una negativo y-componente. Il mio vettore di riferimento è in stile OpenGl (lungo l'asse -z), quindi con yaw = 0, pitch = 0 il vettore risultante dell'unità dovrebbe essere uguale (0, 0, -1). Se qualcuno si imbatte in questo post e ha difficoltà di tradurre le formule di beta a questo particolare sistema, le equazioni che uso sono:

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

Nota il cambio di segno e l'imbardata < -> passo swap. Spero che questo salvi qualcuno prima o poi.

0

Se qualcuno si imbatte in cerca dell'implementazione in 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)