Como o problema do bloqueio do cardan é resolvido usando transformações de matriz acumulativa

12

Estou lendo o livro on-line "Learning Modern 3D Graphics Programming" de Jason L. McKesson

A partir de agora, estou pronto para o problema do bloqueio do cardan e como resolvê-lo usando quaterniões.

No entanto, aqui mesmo, na página Quaternions .

Parte do problema é que estamos tentando armazenar uma orientação como uma série de três rotações axiais acumuladas. Orientações são orientações, não rotações. E as orientações certamente não são uma série de rotações. Portanto, precisamos tratar a orientação do navio como uma orientação, como uma quantidade específica.

Acho que esse é o primeiro ponto em que começo a ficar confuso, o motivo é que não vejo a diferença dramática entre orientações e rotações. Também não entendo por que uma orientação não pode ser representada por uma série de rotações ...

Além disso:

O primeiro pensamento nesse sentido seria manter a orientação como uma matriz. Quando chega a hora de modificar a orientação, simplesmente aplicamos uma transformação a essa matriz, armazenando o resultado como a nova orientação atual.

Isso significa que cada guinada, inclinação e rotação aplicada à orientação atual serão relativas a essa orientação atual. Qual é precisamente o que precisamos. Se o usuário aplicar uma guinada positiva, você deverá girá-la em relação ao ponto em que está apontando atualmente, e não em relação a algum sistema de coordenadas fixo.

Entendo o conceito, mas não entendo como se acumular transformações de matriz é uma solução para esse problema, como o código fornecido na página anterior não é apenas isso.

Aqui está o código:

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

Na minha opinião, o que ele está fazendo (modificar uma matriz em uma pilha) é considerado acumular matrizes, já que o autor combinou todas as transformações de rotação individuais em uma matriz que está sendo armazenada no topo da pilha.

Minha compreensão de uma matriz é que elas são usadas para pegar um ponto que é relativo a uma origem (digamos ... o modelo) e torná-lo relativo a outra origem (a câmera). Tenho certeza de que essa é uma definição segura, no entanto, sinto que falta algo que está me impedindo de entender esse problema de bloqueio do cardan.

Uma coisa que não faz sentido para mim é: se uma matriz determina a diferença relativa entre dois "espaços", como é que uma rotação em torno do eixo Y para, digamos, rolar, não coloca o ponto no "espaço do rolo" "que pode ser transformado mais uma vez em relação a este rolo ... Em outras palavras, nenhuma outra transformação nesse ponto deve ser em relação a esse novo" espaço de rolo "e, portanto, não deve ter a rotação relativa ao anterior" modelo de espaço "que está causando o bloqueio do cardan.

É por isso que o bloqueio do cardan ocorre certo? É porque estamos girando o objeto em torno dos eixos X, Y e Z em vez de girar o objeto em torno de seus próprios eixos relativos . Ou eu estou errado?

Como aparentemente esse código no qual eu vinculei não é um acúmulo de transformações de matriz, você pode dar um exemplo de solução usando este método.

Então, em resumo:

  • Qual é a diferença entre uma rotação e uma orientação?
  • Por que o código está vinculado não é um exemplo de acumulação de transformações de matriz?
  • Qual é o real e específico objetivo de uma matriz, se eu entendi errado?
  • Como uma solução para o problema do bloqueio do cardan pode ser implementada usando o acúmulo de transformações matriciais?
  • Além disso, como um bônus: por que as transformações após a rotação ainda são relativas ao "espaço do modelo?"
  • Outro bônus: estou errado ao supor que, após uma transformação, novas transformações ocorrerão em relação à corrente?

Além disso, se não estava implícito, estou usando OpenGL, GLSL, C ++ e GLM, portanto, exemplos e explicações em termos destes são muito apreciados, se não for necessário.

Quanto mais os detalhes, melhor!

Desde já, obrigado.

Luke San Antonio Bialecki
fonte

Respostas:

11

Não tenho certeza de uma boa maneira de preceder isso, exceto que espero que esteja bem ligado até o final. Dito isto, vamos mergulhar:

Uma rotação e uma orientação são diferentes porque o primeiro descreve uma transformação e o último descreve um estado. Uma rotação é como um objeto entra em uma orientação , e uma orientação é o espaço rotacionado local do objeto . Isso pode estar diretamente relacionado à forma como os dois são representados matematicamente: uma matriz armazena transformações de um espaço de coordenadas para outro (você acertou) e um quaternion descreve diretamente uma orientação. A matriz, portanto, só pode descrever como o objeto entra em uma orientação , através de uma série de rotações. O problema com isso, porém, é o Gimbal Lock.

O bloqueio do cardan demonstra a dificuldade de orientar um objeto usando uma série de rotações. O problema ocorre quando pelo menos dois dos eixos de rotação se alinham:

Imagem cortesia de deepmesh3d.com
Na imagem à esquerda acima, os eixos azul e laranja fazem a mesma rotação! Isso é um problema, porque isso significa que um dos três graus de liberdade foi perdido e rotações adicionais a partir deste ponto podem produzir resultados inesperados. O uso de quaternions resolve isso porque aplicar um quaternion para transformar a orientação de um objeto colocará o objeto diretamente em uma nova orientação (é a melhor maneira que posso dizer), em vez de dividir a transformação em operações de rotação, inclinação e guinada.

Agora, eu sou realmente cético sobre o fato de acumular matrizes ser uma solução completa para isso, porque matrizes acumulativas (portanto acumulando rotações) são exatamente o que pode causar o problema da trava Gimbal em primeiro lugar. A maneira correta de lidar com a transformação por um quaternion é executar a multiplicação de quaternion em um ponto:

pTransformed = q * pAsQuaternion * qConjugate

ou convertendo o quaternion em uma matriz e transformando o ponto usando essa matriz.

Uma rotação de matriz simples (como uma guinada de 45 graus) sempre será definida no espaço global. Se você deseja aplicar a transformação no espaço local, teria que transformar sua transformação nesse espaço local de objetos. Parece estranho, então vou elaborar. É aqui que entra a importância da ordem das rotações. Eu recomendo pegar um livro aqui para que você possa acompanhar as transformações.

Comece com o livro plano, com a capa voltada para o teto, orientada como se você estivesse prestes a abri-lo e começar a ler. Agora incline a frente do livro 45 graus (a tampa frontal deve estar voltada para você):

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

Agora, digamos que você queira ajustar a guinada do livro em 45 graus (acho que estou assumindo um sistema de coordenadas para a mão direita, portanto isso mudará de direção para a esquerda) e você deseja que isso se aplique ao local do livro coordenar o espaço, de modo que a capa do livro ainda esteja voltada para você:

bookMatrix.RotateY(45);

O problema é que essa rotação ocorre no espaço global das coordenadas, portanto a capa do livro terminará voltada para o ombro direito. Para que essa alteração no cabeçalho ocorra no espaço de coordenadas local, você deve aplicá-la primeiro!

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

Experimente! Comece o livro voltado para cima no teto novamente. Altere sua guinada em 45 graus e, em seguida, incline-a 45 graus no eixo X global (da esquerda para a direita). Essa é a orientação que você esperava com um passo de 45 e uma guinada de 45 no espaço local do livro.

O que isto significa? Tudo o que realmente se resume é que a ordem das operações é importante. As transformações feitas primeiro se tornam transformações locais no contexto das transformações feitas posteriormente. Torna-se muito difícil de entender, e é assim que os quaternions economizam muitos problemas. Ignora todo o material dependente do pedido.

A outra grande vantagem que os quaternions oferecem é que eles permitem a interpolação de orientações. Tentar interpolar entre os ângulos de Euler é quase impossível por causa das dependências da ordem. As propriedades matemáticas do quaternion permitem uma interpolação linear esférica bem definida entre eles.

Para finalizar e resolver sua pergunta original: transformações de matriz acumulativa realmente não resolverão o problema do bloqueio do cardan, a menos que as transformações sejam cuidadosamente escolhidas e aplicadas em uma ordem precisa. Portanto, sempre use quaternions e aplique quaternions em pontos usando a multiplicação de quaternions.

Espero que isto ajude :)

kevintodisco
fonte
4
apenas para constar, os quaternions ainda podem introduzir o bloqueio do cardan se descrito através dos ângulos de Euler; como você vai fazer o mesmo cálculo de uma maneira diferente (quaternions em vez de matrizes)
concept3d
1
@ concept3d - parabéns por mencionar isso! É importante entender o que torna o mecanismo de cardan propenso a perder um grau de liberdade: é como uma articulação robótica que descreve inerentemente um sistema de equações sobredeterminado. Se você criar esse mecanismo com quaternions, matrizes ou magia, ainda terá ambigüidades - entender e não usá-lo em primeiro lugar é uma solução real (a menos que você precise usá-lo para fins demonstrativos ou técnicos) .
Teodron #
é difícil imaginar quaternions, do jeito que sempre penso é que eles (quaternions de unidades) representam um espaço de 3 esferas, portanto podem representar qualquer orientação, enquanto eu entendo que os ângulos de Euler representam círculos / turos, portanto, não uma esfera completa. não é forma muito precisa para representar orientação (3 círculos / toro realmente não pode gerar todas as orientações possíveis a menos que eles giram de forma independente que não é possível no caso de ângulos de Euler), não tenho certeza se eu explicasse com precisão :)
concept3d
1

As acumulações de matrizes podem, de fato, resolver o bloqueio do cardan. Ao acumular rotações, você adiciona cardan, permitindo qualquer rotação arbitrária. O diagrama fornecido pela ktodisco mostra uma trava de cardan no diagrama esquerdo. A matriz para esta orientação pode ser definida como:

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

Devido à rotação do cardan y, os cardan X e Z agora estão bloqueados, então perdemos um grau de movimento. Neste ponto, não temos guinada (y local, z global) usando esses três cardan. Mas adicionando outro cardan, eu posso girar localmente em torno do y:

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

Para cada novo lançamento, inclinação e guinada, basta adicionar outro cardan, ACUMULANDO-os em uma matriz. Portanto, toda vez que é necessária outra rotação local, uma rotação é criada e multiplicada para a matriz de acumulação. Como o capítulo menciona, ainda existem problemas, mas o bloqueio do cardan não é um deles.

Justin Ehrlich
fonte