2015-06-27 8 views
23

Sto lavorando su simulatore di movimento con 2 DOF (rullo &). Sto leggendo la matrice di trasformazione dal gioco e ho bisogno di ottenere gli angoli e inviare all'hardware per guidare i motori. Poiché gli angoli di Eulero hanno singolarità, non posso davvero usarli. Si comporta in questo modo:Ottieni intonazione e rotazione da matrice senza singolarità

quando dovrebbe in questo modo:

ho preparato esempio online per mostrare meglio la questione:

// Get euler angles from model matrix 
var mat = model.matrix; 
mat.transpose(); 

var e = new THREE.Euler(); 
e.setFromRotationMatrix(mat, 'XZY'); 
var v = e.toVector3(); 

var pitch = -v.z; 
var roll = -v.x; 

http://jsfiddle.net/qajro0ny/3/

Per quanto ho capito, ci sono due problemi qui.

  1. Non esiste alcun asse di imbardata sul simulatore.
  2. Anche se ci fosse l'asse di imbardata, i motori semplicemente non si comportano come computer grafica, cioè hanno bisogno di tempo per raggiungere la posizione di destinazione.

Ho letto di gimbal lock e anche implementato il filtro euler, ma non ha funzionato come previsto. La maggior parte dei consigli sul blocco del giunto cardanico consisteva nell'utilizzare i quaternioni, ma non posso guidare il motore fisico con il quaternione (o posso?).

L'ordine degli assi non ha molta importanza qui, perché cambiando si sposta solo la singolarità da un asse all'altro.

Ho bisogno di gestirlo in un altro modo.

Ho provato a moltiplicare i vettori di assi per matrice e quindi utilizzando prodotti a croce e punto per ottenere angoli, ma anche quello non è riuscito. Penso che dovrebbe esserci anche una riprogettazione degli assi per farlo correttamente, ma non riuscivo a capirlo. Ma qualcosa mi dice che questo è il modo giusto per farlo. Era qualcosa del genere: http://jsfiddle.net/qajro0ny/53/

Poi ho avuto un'idea diversa. So posizione precedente, quindi forse effettuare le seguenti operazioni: a matrice

  1. Converti in quaternione
  2. differenza Compute tra il quaternione corrente e precedente
  3. Converti risultante quaternione di angoli di Eulero
  4. Aggiungi quegli angoli di campo statico, variabili roll and yaw.

Quindi l'ho provato e ... ha funzionato! Nessuna singolarità in nessuna delle direzioni, perfetta rotazione di 360 gradi in pitch, roll e imbardata. La soluzione perfetta! Tranne ... non lo è. I frame non si sincronizzarono, quindi dopo un po 'gli angoli erano lontani da quello che dovrebbero essere. Ho pensato a una sorta di meccanismo di sincronizzazione, ma ho pensato che non fosse la strada giusta.

Si presentava così: http://jsfiddle.net/qajro0ny/52/

E la stessa logica, ma direttamente con le matrici: http://jsfiddle.net/qajro0ny/54/

Ho cercato web ad alta e bassa, ho letto decine di documenti e altre domande/messaggi e non riesco a credere che nulla funzioni davvero per il mio caso.

potrei non capire o manca qualcosa, ecco tutto quello che ho trovato e provato:

vicini: http://pastebin.com/3G0dYLvu

Codice: http://pastebin.com/PiZKwE2t (ho messo tutto insieme in modo che sia disordinato)

Mi manca qualcosa, o sto guardando questo da un angolo sbagliato.

risposta

7

Se si sa che la matrice contiene solo le due rotazioni (nell'ordine dato T = RZ * RX), allora si può fare qualcosa di simile al seguente:

L'asse x locale non è influenzato dalla seconda rotazione. Quindi puoi calcolare il primo angolo con solo l'asse x locale. Quindi è possibile rimuovere questa rotazione dalla matrice e calcolare l'angolo restante da uno qualsiasi degli altri due assi:

function calculateAngles() { 
    var mat = model.matrix; 

    //calculates the angle from the local x-axis 
    var pitch = Math.atan2(mat.elements[1], mat.elements[0]); 

    //this matrix is used to remove the first rotation 
    var invPitch = new THREE.Matrix4(); 
    invPitch.makeRotationZ(-pitch); 

    //this matrix will only contain the roll rotation 
    // rollRot = RZ^-1 * T 
    //   = RZ^-1 * RZ * RX 
    //   = RX 
    var rollRot = new THREE.Matrix4(); 
    rollRot.multiplyMatrices(invPitch, mat); 

    //calculate the remaining angle from the local y-axis 
    var roll = Math.atan2(rollRot.elements[6], rollRot.elements[5]); 

    updateSimAngles(pitch, roll); 
} 

Questo ha ovviamente funzionano solo se la matrice indicata corrisponde ai requisiti. Non deve contenere una terza rotazione. Altrimenti, probabilmente avrai bisogno di trovare una soluzione di minimi quadrati non lineare.

+0

Grazie, ma purtroppo la mia matrice contiene la terza rotazione. Potresti approfondire la questione sulla soluzione dei minimi quadrati non lineari? Ho letto sull'algoritmo di Levenberg-Marquardt, ma non ho idea di come usarlo per il mio problema. – AdrianEddy

+0

Levenberg Marquardt è probabilmente una buona scelta. Fondamentalmente, vuoi trovare il minimizzatore di '|| T - RZ (pitch) * RX (roll) || _2' su 'pitch' e' roll'. La maggior parte delle librerie ti consente di collegare tale definizione in modo più o meno diretto. Ma non conosco nessuna libreria JavaScript per questo. Inoltre, otterrete sempre gli orientamenti flipping se la terza rotazione diventa relativamente grande. Potresti provare ad aggirare questo problema con i termini di regolarizzazione (differenza tra l'ultima e la soluzione corrente). Ma non sono sicuro che questo farà il lavoro. –

2

La tua idea sull'utilizzo dei delta della rotazione sembra davvero promettente.

Non sono abbastanza sicuro di cosa intendi con "i frame non sincronizzati". Potrei immaginare che i tuoi calcoli con i quaternioni potrebbero non essere esatti al 100% (usi i punti fluttuanti?), Il che si traduce in piccoli errori che si accumulano e che infine generano movimenti asincroni.

Il punto è che si suppone di utilizzare quaternioni unità per la rappresentazione delle rotazioni. Puoi farlo in un modello teorico, ma se rappresenti i quaternioni di 4 numeri in virgola mobile, i tuoi quaternioni non saranno unità quaternioni nella maggior parte dei casi, ma saranno solo molto vicini (la loro norma è 1+e per alcuni piccoli - e probabilmente negativa - valore e). Questo va bene dal momento che non noterai queste piccole differenze, tuttavia se stai lanciando tonnellate di operazioni ai tuoi quaternari (che stai facendo instradando costantemente il tuo modello e calcolando i delta), questi piccoli errori si accumuleranno. Pertanto, è necessario rinormalizzare costantemente i quaternioni per mantenerli il più vicino possibile ai quaternioni unitari in modo che i calcoli, in particolare la conversione agli angoli di Eulero, rimangano (quasi) esatti.

+0

Anche se sto operando esclusivamente su doppi 64 bit (inclusa la matrice iniziale del gioco) e normalizzando i quaternioni dopo ogni calcolo, gli angoli sono ancora considerevolmente spenti dopo un paio di minuti di movimento costante. Sto leggendo la matrice 50 volte al secondo. Sto cercando ora di creare una sorta di soluzione ibrida, usando i quaternioni per tracciare l'orientamento corretto ma calcolando gli angoli finali dagli angoli di Eulero. – AdrianEddy

+0

Puoi vederlo in azione in questo violino: http://jsfiddle.net/qajro0ny/52/. Forse ho fatto un errore da qualche parte? Naturalmente questo è solo un esempio in JS, ma il comportamento è praticamente lo stesso in C++ – AdrianEddy

2

Ho aggiunto i miei due centesimi in http://jsfiddle.net/qajro0ny/58/, applicando fondamentalmente i lavori di http://blogs.msdn.com/b/mikepelton/archive/2004/10/29/249501.aspx che mi sono imbattuto in qualche anno fa. Si riduce sostanzialmente verso

var a = THREE.Math.clamp(-mat.elements[6], -1, 1); 
var xPitch = Math.asin(a); 
var yYaw = 0; 
var zRoll = 0; 
if (Math.cos(xPitch) > 0.0001) 
{ 
    zRoll = -Math.atan2(mat.elements[4], mat.elements[5]); 
    yYaw = -Math.atan2(mat.elements[2], mat.elements[10]); 
} 
else 
{ 
    zRoll = -Math.atan2(-mat.elements[1], mat.elements[0]); 
} 

ho notato che l'imbardata mappatura assunto, beccheggio, rollio assi (y, x, z) come usato in DirectX mancini sistema di coordinate differisce l'da quello che si sta utilizzando OpenGL/WebGL, quindi forse questo deve essere finalmente risolto.

Spero che questo aiuti.

+0

Grande esempio di BTW. – mkoertgen

1

Immagino che in realtà potresti guidare motori fisici con quaternioni. Ricorda che quaternion rappresenta un asse di rotazione (x, y, z) e un angolo. Suppongo che tu abbia tre motori (per ogni asse), velocità di rotazione di cui puoi scalare. Ora, ridimensionando la velocità di rotazione di questi tre motori è possibile impostare un asse di rotazione specifico e misurando attentamente il tempo è possibile impostare un angolo di rotazione specifico. Dovrebbe essere più facile della conversione in Euler angoli delta.

+0

Il fatto è che non ho un terzo motore, non con [quel design] (https://www.youtube.com/watch?v=tVhFCJWn_xw). Avrei bisogno di cancellare la terza rotazione in qualche modo. Ma se potessi cancellare in modo affidabile la terza rotazione, potrei facilmente calcolare gli angoli di Eulero da quello. – AdrianEddy

+0

Ok. Quindi, supponiamo di avere un quaternario "obiettivo" - una rotazione che si desidera simulare. E tu hai due motori, che applicano le rotazioni in alcuni assi. È possibile definire due quaternioni che corrispondono a questi assi noti ma con angoli sconosciuti. La moltiplicazione di questi quaternioni deve essere la più vicina possibile al quaternion "bersaglio", che può darti angoli sconosciuti ... – dragn

+0

Anche se questo implica una matematica complicata ... :) – dragn

Problemi correlati