Girando vector3 por um quaternion

25

Estou tentando girar um vetor3 por um determinado quaternário.

Eu sei que isso é verdade

v=qvq1

Eu sei que q1 é o inverso que apenasqmagnitude(q) , mas como faço para mapear a multiplicação do vetor para o quaternion para voltar um vetor?

Descobri que você pode tratar v como uma matriz e converter q e q em matrizes e depois converter v de uma matriz em um vetor, mas isso parece um pouco exagerado apenas para obter um vetor. Existe uma implementação mais limpa que eu possa usar?

gardian06
fonte

Respostas:

36

Como Nathan Reed e teodron expuseram, a receita para rotacionar um vetor v por um quaternário de comprimento unitário q é:

1) Crie um quaternion puro p fora de v . Isso significa simplesmente adicionar uma quarta coordenada de 0:

p=(vx,vy,vz,0)p=(v,0)

2) Pré-multiplique-o com q e pós-multiplique-o com o conjugado q * :

p=q×p×q

3) Isso resultará em outro quaternion puro que pode ser retornado a um vetor:

v=(px,py,pz)

Este vector v é v girado por q .


Isso está funcionando, mas está longe de ser o ideal . Multiplicações de Quaternion significam toneladas e toneladas de operações. Fiquei curioso sobre várias implementações como esta e decidi descobrir de onde elas vieram. Aqui estão minhas descobertas.

Também podemos descrever q como a combinação de um vetor tridimensional u e um escalar s :

q=(ux,uy,uz,s)q=(u,s)

Pelas regras de multiplicação de quaternion , e como o conjugado de um quaternion de comprimento de unidade é simplesmente inverso, obtemos:

p=qpq=(u,s)(v,0)(u,s)=(sv+u×v,uv)(u,s)=((uv)(u)+s(sv+u× v)+(sv+u×v)×(u),)=((uv)u+s2v+s(u×v)+sv×(u)+(u×v)×(u),)

A parte escalar (elipses) resulta em zero, conforme detalhado aqui . O interessante é a parte do vetor, também conhecido como vetor girado v ' . Pode ser simplificado usando algumas identidades vetoriais básicas :

v=(uv)u+s2v+s(u×v)+s(u×v)+u×(u×v)=(uv)u+s2v+2s(u×v)+(uv)u(uu)v=2(uv)u+(s2uu)v+2s(u×v)

Agora isso é muito mais ideal ; dois produtos pontuais, um produto cruzado e alguns extras: cerca de metade das operações. O que daria algo parecido com isso no código-fonte (assumindo alguma biblioteca matemática vetorial genérica):

void rotate_vector_by_quaternion(const Vector3& v, const Quaternion& q, Vector3& vprime)
{
    // Extract the vector part of the quaternion
    Vector3 u(q.x, q.y, q.z);

    // Extract the scalar part of the quaternion
    float s = q.w;

    // Do the math
    vprime = 2.0f * dot(u, v) * u
          + (s*s - dot(u, u)) * v
          + 2.0f * s * cross(u, v);
}
Laurent Couvidou
fonte
Hats off to a better written response. And considering that most performance freaks tend to use intrinsics to perform vector operations, you do get quite a speed-up (even for plain quaternion multiplication, especially on intel architectures).
teodron
The final result looks similar to Rodrigues' rotation formula - it has the same basis vectors, anyway; I'd have to dig into some trig identities to see if the coefficients match.
Nathan Reed
@NathanReed Esta parece ser outra maneira de chegar ao mesmo resultado. Eu também gostaria de saber se isso corresponde. Obrigado por apontar isso!
Laurent Couvidou
11
Eu estava checando a implementação do GLM e isso parece ter sido implementado de maneira um pouco diferente, a saber: a seguinte vprime = v + ((cross(u, v) * s) + cross(u, cross(u, v)) * 2.0fé uma otimização semelhante? Parece um pouco semelhante, mas não é o mesmo - ele usa apenas produtos cruzados, sem produtos pontuais. O código fonte original pode ser encontrado no arquivo type_quat.inl do repositório oficial do GLM, no operator*qual leva um quaternion e um vector ( vec<3, T, Q> operator*(qua<T, Q> const& q, vec<3, T, Q> const& v))
j00hi
7

Primeiro de tudo, q ^ (- 1) não é -q / magnitude (q); é q * / (magnitude (q)) ^ 2 (q * é o conjugado; isso nega todos os componentes, exceto o real). Obviamente, você pode deixar de fora a divisão pela magnitude se todos os seus quaternions já estiverem normalizados, o que normalmente seria em um sistema de rotação.

Quanto à multiplicação com um vetor, você apenas estende o vetor a um quaternion definindo o componente real de um quat como zero e seus componentes ijk como xyz do vetor. Então você faz as multiplicações do quaternion para obter v 'e extrai os componentes ijk novamente. (A parte real de v 'sempre deve sair zero, mais ou menos algum erro de ponto flutuante.)

Nathan Reed
fonte
5

Primeira observação: o inverso de qnão é-q/magnitude(q) , isso é completamente errado. Rotações com quaterniões implicam que esses equivalentes de números complexos em 4D tenham norma unitária; portanto, residem na esfera unitária S3 nesse espaço 4D. O fato de um quat ser unitário significa que sua norma é norm(q)^2=q*conjugate(q)=1e isso significa que o inverso do quat é seu conjugado.

Se um quaternião unitário é escrito como q=(w,x,y,z)= (cos (t), sin (t) v ), então seu conjugado é conjugate(q)=(w,-x,-y,-z)= (cos (t), - sin (t) v ), onde t é metade do ângulo de rotação e ev é o eixo de rotação (como um vetor unitário, é claro).

Quando aquele cara de Hamilton decidiu brincar com equivalentes numéricos complexos em dimensões mais altas, ele também encontrou algumas boas propriedades. Por exemplo, se você emprega um quaternion completamente puro q=(0,x,y,z)(sem parte escalar w !), Pode considerar essa porcaria como um vetor (na verdade, é um quat do que as pessoas podem chamar de equador da esfera S3, que é uma esfera S2! ! - coisas preocupantes, se considerarmos o quão tecnicamente prejudicada as pessoas no século 19 nos parecem caçadores de olhos nos telefones hoje em dia). Então Hamilton pegou esse vetor em sua forma quat: v=(0,x,y,z)e fez uma série de experimentos considerando as propriedades geométricas dos quats. Resumindo a história:

INPUT: _v=(x,y,z)_ a random 3D vector to rotate about an __u__ unit axis by an angle of _theta_

OUTPUT: q*(0,_v_)*conjugate(q)

Onde

 q = (cos(theta/2), sin(theta/2)*u)
 conjugate(q) = inverse(q) = (cos(theta/2), -sin(theta/2)*u)
 norm(q)=magnitude(q)=|q|=1

Observação: o q * (0, v) * conj (q) deve ser outro quat da forma (0, v '). Não analisarei toda a explicação aparentemente complicada de por que isso acontece, mas se você girar um quaternário imaginário puro (ou um vetor no nosso caso!) Através desse método, deverá obter um tipo de objeto semelhante: quat imaginário puro. e você assume sua parte imaginária como resultado. Aí está, o maravilhoso mundo das rotações com quaternions em uma casca de noz (ty).

OBSERVAÇÃO : para quem se interessa por essa frase exagerada: os quats são bons porque os evitam o bloqueio do cardan ... devem desbloquear sua imaginação primeiro! Os quats são um mero aparato matemático "elegante" e podem ser evitados por meio de outras abordagens, a que acho completamente equivalente geometricamente sendo a abordagem do ângulo do eixo.

CÓDIGO : a biblioteca C ++ que eu imagino é bastante simplista, mas possui todas as operações de matriz, vetor e quat que um experimentalista de gráficos 3D deve precisar sem ter que perder mais de 15 minutos para aprendê-lo. Você pode testar as coisas que escrevi aqui usando esse em 15 minutos se você não é um novato em C ++. Boa sorte!

teodron
fonte
+1 para sua anotação. Aposto que a maioria das pessoas não conseguiria o bloqueio real do cardan se tentassem.
Steve H
A maioria das pessoas não consegue criar um mecanismo adequado de cardan e pensa que, se unirem 3 matrizes de rotações, acabam automaticamente com a representação "ângulos de Euler". articulações que podem sofrer redundância ao tentar executar cinemática inversa (ela tem mais graus de liberdade do que realmente precisa para produzir a orientação desejada). Oh, bem, isso é outro assunto, mas achei que é bom para ficar longe do hype esta questão "lendário" tem gerado entre os programadores de computação gráfica ..
teodron
Nitpickery: enquanto o ângulo do eixo é equivalente em que ambas as representações podem representar todas as rotações em SO (3) de forma única (ok, modulo a habitual capa dupla) e, é claro, há uma transformação quase trivial entre elas, os quaternions têm a vantagem de ser muito mais fácil de compor do que todas as outras representações não matriciais.
Steven Stadnicki
Eles têm a vantagem de serem mais fáceis de compor devido ao seu bom comportamento em qualquer linguagem de programação orientada a objetos, especialmente ao usar sobrecarga de operador. Não tenho certeza, mas talvez até suas propriedades de interpolação esférica preservem o ângulo do eixo (exceto SQUAD, talvez ?!).
Teodron
2

Aqui está uma maneira alternativa de transformar um vetor por um quaternion. É a maneira como a MS faz isso na estrutura xna. http://pastebin.com/fAFp6NnN

Steve H
fonte
-1

Eu tentei resolver isso manualmente, e criei a seguinte equação / método:

// inside quaterion class
// quaternion defined as (r, i, j, k)
Vector3 rotateVector(const Vector3 & _V)const{
    Vector3 vec();   // any constructor will do
    vec.x = 2*(r*_V.z*j + i*_V.z*k - r*_V.y*k + i*_V.y*j) + _V.x*(r*r + i*i - j*j - k*k);
    vec.y = 2*(r*_V.x*k + i*_V.x*j - r*_V.z*i + j*_V.z*k) + _V.y*(r*r - i*i + j*j - k*k);
    vec.z = 2*(r*_V.y*i - r*_V.x*j + i*_V.x*k + j*_V.y*k) + _V.z*(r*r - i*i - j*j + k*k);
    return vec;
}

Eu gostaria que alguém olhasse sobre a derivação do mt que eu usei http://pastebin.com/8QHQqGbv. Eu sugiro que copie para um editor de texto que suporte rolagem lateral

na minha notação, usei q ^ (- 1) para significar conjugados, e não inversos, e identificadores diferentes, mas espero que sejam seguidos. Eu acho que a maioria está certa, especialmente onde provar a parte real do vetor desapareceria.

gardian06
fonte