Come viene risolto il problema del gimbal bloccato usando trasformazioni cumulative di matrici


12

Sto leggendo il libro online "Learning Modern 3D Graphics Programming" di Jason L. McKesson

A partire da ora, sono all'altezza del problema del blocco del gimbal e di come risolverlo usando i quaternioni.

Comunque proprio qui, alla pagina dei Quaternioni .

Parte del problema è che stiamo cercando di memorizzare un orientamento come una serie di 3 rotazioni assiali accumulate. Gli orientamenti sono orientamenti, non rotazioni. E gli orientamenti non sono certamente una serie di rotazioni. Quindi dobbiamo considerare l'orientamento della nave come un orientamento, come una quantità specifica.

Immagino che questo sia il primo punto in cui comincio a confondermi, il motivo è perché non vedo la differenza drammatica tra orientamenti e rotazioni. Inoltre non capisco perché un orientamento non possa essere rappresentato da una serie di rotazioni ...

Anche:

Il primo pensiero a tal fine sarebbe quello di mantenere l'orientamento come matrice. Quando arriva il momento di modificare l'orientamento, applichiamo semplicemente una trasformazione a questa matrice, memorizzando il risultato come nuovo orientamento corrente.

Ciò significa che ogni imbardata, inclinazione e rollio applicati all'orientamento corrente saranno relativi a quell'orientamento corrente. È proprio quello di cui abbiamo bisogno. Se l'utente applica un'imbardata positiva, si desidera che quell'imbardata li ruoti rispetto al punto in cui sono puntati attualmente, non rispetto a un sistema di coordinate fisso.

Il concetto, lo capisco, tuttavia non capisco come se l'accumulo di trasformazioni di matrici sia una soluzione a questo problema, come il codice fornito nella pagina precedente non sia solo questo.

Ecco il codice:

void display()
{
    glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
    glClearDepth(1.0f);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    glutil::MatrixStack currMatrix;
    currMatrix.Translate(glm::vec3(0.0f, 0.0f, -200.0f));
    currMatrix.RotateX(g_angles.fAngleX);
    DrawGimbal(currMatrix, GIMBAL_X_AXIS, glm::vec4(0.4f, 0.4f, 1.0f, 1.0f));
    currMatrix.RotateY(g_angles.fAngleY);
    DrawGimbal(currMatrix, GIMBAL_Y_AXIS, glm::vec4(0.0f, 1.0f, 0.0f, 1.0f));
    currMatrix.RotateZ(g_angles.fAngleZ);
    DrawGimbal(currMatrix, GIMBAL_Z_AXIS, glm::vec4(1.0f, 0.3f, 0.3f, 1.0f));

    glUseProgram(theProgram);
    currMatrix.Scale(3.0, 3.0, 3.0);
    currMatrix.RotateX(-90);
    //Set the base color for this object.
    glUniform4f(baseColorUnif, 1.0, 1.0, 1.0, 1.0);
    glUniformMatrix4fv(modelToCameraMatrixUnif, 1, GL_FALSE, glm::value_ptr(currMatrix.Top()));

    g_pObject->Render("tint");

    glUseProgram(0);

    glutSwapBuffers();
}

Per quanto ne so, non è quello che sta facendo (modificando una matrice su una pila) considerato l'accumulo di matrici, poiché l'autore ha combinato tutte le singole trasformazioni di rotazione in una matrice che viene memorizzata nella parte superiore della pila.

La mia comprensione di una matrice è che vengono utilizzati per prendere un punto relativo a un'origine (diciamo ... il modello) e renderlo relativo a un'altra origine (la fotocamera). Sono abbastanza sicuro che questa sia una definizione sicura, tuttavia mi sembra che manchi qualcosa che mi sta impedendo di comprendere questo problema di blocco del gimbal.

Una cosa che non ha senso per me è: se una matrice determina la differenza relativa tra due "spazi", come mai una rotazione attorno all'asse Y per, diciamo, roll, non inserisce il punto in "roll space "che può quindi essere trasformato ancora una volta in relazione a questo rotolo ... In altre parole, nessuna ulteriore trasformazione a questo punto dovrebbe essere in relazione a questo nuovo" spazio del rotolo "e quindi non avere la rotazione rispetto al precedente" spazio modello "che sta causando il blocco del gimbal.

Ecco perché si verifica il blocco del gimbal giusto? È perché stiamo ruotando l'oggetto attorno agli assi X, Y e Z anziché ruotando l'oggetto attorno ai suoi assi relativi . O mi sbaglio?

Dal momento che apparentemente questo codice che ho collegato non è un accumulo di trasformazioni di matrice, puoi per favore dare un esempio di una soluzione usando questo metodo.

Quindi in sintesi:

  • Qual è la differenza tra una rotazione e un orientamento?
  • Perché il codice non è collegato in un esempio di accumulo di trasformazioni di matrici?
  • Qual è il vero scopo specifico di una matrice, se ho sbagliato?
  • Come potrebbe essere implementata una soluzione al problema del blocco gimbal usando l'accumulo di trasformazioni di matrice?
  • Inoltre, come bonus: perché le trasformazioni dopo la rotazione sono ancora relative allo "spazio modello?"
  • Un altro vantaggio: sbaglio nell'ipotesi che dopo una trasformazione si verificheranno ulteriori trasformazioni rispetto alla corrente?

Inoltre, se non fosse implicito, sto usando OpenGL, GLSL, C ++ e GLM, quindi esempi e spiegazioni in questi termini sono molto apprezzati, se non necessari.

Più sono i dettagli, meglio è!

Grazie in anticipo.

Risposte:


11

Non sono sicuro di un buon modo per prefigurarlo, a parte il fatto che spero che si leghi bene alla fine. Detto questo, tuffiamoci in:

Una rotazione e un orientamento sono diversi perché il primo descrive una trasformazione e il secondo descrive uno stato. Una rotazione è il modo in cui un oggetto entra in un orientamento e un orientamento è lo spazio ruotato locale dell'oggetto . Questo può essere direttamente correlato al modo in cui i due sono rappresentati matematicamente: una matrice memorizza le trasformazioni da uno spazio di coordinate a un altro (che avevi quello corretto) e un quaternione descrive direttamente un orientamento. La matrice, quindi, può solo descrivere come l'oggetto si trova in un orientamento , attraverso una serie di rotazioni. Il problema con questo, tuttavia, è Gimbal Lock.

Il blocco cardanico dimostra la difficoltà di ottenere un oggetto in un orientamento usando una serie di rotazioni. Il problema si verifica quando almeno due degli assi di rotazione si allineano:

Immagine gentilmente concessa da deepmesh3d.com
Nell'immagine a sinistra sopra, gli assi blu e arancione fanno la stessa rotazione! Questo è un problema, perché significa che uno dei tre gradi di libertà è stato perso e ulteriori rotazioni da questo punto possono produrre risultati imprevisti. L'uso dei quaternioni risolve questo problema perché l'applicazione di un quaternione per trasformare l'orientamento di un oggetto metterà direttamente l'oggetto in un nuovo orientamento (è il modo migliore in cui posso dirlo), piuttosto che suddividere la trasformazione in operazioni di rollio, beccheggio e imbardata.

Ora, in realtà sono scettico sul fatto che accumulare matrici sia una soluzione completa a questo, perché accumulare matrici (quindi accumulare rotazioni) sono esattamente ciò che può causare il problema del blocco cardanico in primo luogo. Il modo corretto di gestire la trasformazione da parte di un quaternione è eseguire una multiplazione di quaternioni su un punto:

pTransformed = q * pAsQuaternion * qConjugate

o convertendo il quaternione in una matrice e trasformando il punto usando quella matrice.

Una rotazione a matrice semplice (come un'imbardata di 45 gradi) sarà sempre definita nello spazio globale. Se si desidera applicare la trasformazione nello spazio locale, è necessario trasformare la trasformazione nello spazio locale di tali oggetti. Sembra strano, quindi elaborerò. È qui che entra in gioco l'importanza dell'ordine delle rotazioni. Consiglio di prendere un libro qui in modo da poter seguire le trasformazioni.

Inizia con il libro piatto, con la copertina rivolta verso l'alto, orientata come se stessi per aprirlo e iniziare a leggere. Ora inclina la parte anteriore del libro di 45 gradi (la copertina dovrebbe essere rivolta verso di te):

glutil::MatrixStack bookMatrix;
bookMatrix.RotateX(45);

Ora, supponiamo che tu voglia regolare l'angolazione del libro di 45 gradi (penso che sto assumendo un sistema di coordinate per la mano destra, quindi questo cambierà la direzione a sinistra), e vuoi che questo si applichi al locale del libro coordinare lo spazio, in modo che la copertina del libro ti affronti ancora:

bookMatrix.RotateY(45);

Il problema è che questa rotazione si verifica nello spazio delle coordinate globali, quindi la copertina del libro finirà rivolta verso la spalla destra. Per fare in modo che questo cambiamento di rotta si verifichi nello spazio delle coordinate locali, è necessario applicarlo prima!

glutil::MatrixStack bookMatrix;
bookMatrix.RotateY(45);
bookMatrix.RotateX(45);

Provalo! Inizia di nuovo il libro rivolto verso l'alto sul soffitto. Cambia la sua imbardata di 45 gradi, quindi inclinala di 45 gradi lungo l'asse X globale (correndo da sinistra a destra). Questo è l'orientamento che ti aspettavi con un passo di 45 e un'imbardata di 45 nello spazio locale del libro.

Cosa significa questo? Tutto ciò che si riduce davvero è che l'ordine delle operazioni conta. Le trasformazioni eseguite per prime diventano trasformazioni locali nel contesto delle trasformazioni eseguite successivamente. Diventa molto difficile avvolgere la testa, ed è così che i quaternioni risparmiano molti problemi. Salta tutte le cose dipendenti dall'ordine.

L'altro enorme vantaggio offerto dai quaternioni è che consentono l'interpolazione degli orientamenti. Cercare di interpolare tra gli angoli di Eulero è quasi impossibile a causa delle dipendenze dell'ordine. Le proprietà matematiche del quaternione consentono un'interpolazione lineare sferica ben definita tra di loro.

Per concludere e affrontare la tua domanda originale: le trasformazioni cumulative della matrice non risolveranno davvero il problema del blocco del giunto cardanico, a meno che le trasformazioni non vengano scelte e applicate con cura in un ordine preciso. Pertanto, usa sempre i quaternioni e applica i quaternioni ai punti usando la moltiplicazione dei quaternioni.

Spero che sia di aiuto :)


4
solo per la cronaca, i quaternioni possono ancora introdurre il blocco del gimbal se descritto tramite gli angoli di Eulero; poiché eseguirai lo stesso calcolo in modo diverso (quaternioni anziché matrici)
concept3d

1
@ concept3d - congratulazioni per averlo menzionato! È importante capire cosa rende il meccanismo del gimbal incline a perdere un certo grado di libertà: è come un giunto robotico che descrive intrinsecamente un sistema di equazioni troppo determinato. Se costruisci questo meccanismo con quaternioni, matrici o magie, finisci ancora con le ambiguità: è capirlo e non usarlo in primo luogo è una vera soluzione (a meno che non ti venga richiesto di usarlo per scopi dimostrativi o tecnici) .
teodron,

i quaternioni sono difficili da immaginare, il modo in cui ci penso sempre è che essi (unità quaternioni) rappresentano uno spazio a 3 sfere, quindi possono rappresentare qualsiasi orientamento, mentre capisco che gli angoli di Eulero rappresentano ciascuno cerchi / turos, quindi non una sfera completa questo non è un modo molto preciso di rappresentare l'orientamento (3 cerchi / tori non possono realmente generare ogni possibile orientamento a meno che non ruotino in modo indipendente, il che non è possibile nel caso di angoli di eulero), non sono sicuro se ho spiegato con precisione :)
concept3d

1

Gli accumuli di matrice possono infatti risolvere il blocco del giunto cardanico. Accumulando rotazioni, si stanno aggiungendo elementi cardanici, consentendo qualsiasi rotazione arbitraria. Il diagramma fornito da ktodisco mostra un blocco gimbal nel diagramma di sinistra. La matrice per questo orientamento può essere definita come:

glutil::MatrixStack bookMatrix;
bookMatrix.RotateX(90);
bookMatrix.RotateY(90);
bookMatrix.RotateZ(90);

A causa della rotazione del gimbal y, i gimbal X e Z ora sono bloccati, quindi abbiamo perso un grado di movimento. A questo punto non abbiamo imbardata (y locale, z globale) usando questi tre gimbali. Ma aggiungendo un altro gimbal, posso ruotare localmente attorno alla y:

glutil::MatrixStack bookMatrix;
bookMatrix.RotateX(90);
bookMatrix.RotateY(90);
bookMatrix.RotateZ(90);
bookMatrix.RotateY(90);

Per ogni nuovo rollio, beccheggio e imbardata, basta aggiungere un altro gimbal, ACCUMULANDO in una matrice. Quindi ogni volta che è necessaria un'altra rotazione locale, una rotazione viene creata e moltiplicata per la matrice di accumulo. Come menzionato nel capitolo, ci sono ancora problemi, ma il blocco del gimbal non è uno di questi.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.