Vector3 rotante di un quaternione


25

Sto tentando di ruotare un vettore3 di un dato quaternione.

So che questo è vero

v'=qvq-1

So che q-1 è l'inverso che solo -qmun'gniotude(q) , ma come posso mappare la moltiplicazione del vettore al quaternione per ottenere un vettore?

Ho scoperto che puoi trattare v come una matrice e convertire q e q' in matrici, quindi convertire v' da una matrice in un vettore, ma questo sembra un po' esagerato solo per ottenere un vettore. Esiste un'implementazione più pulita che potrei usare?

Risposte:


36

Come esposto Nathan Reed e teodron, la ricetta per ruotare un vettore v di un quaternione di lunghezza unitaria q è:

1) Creare un quaternione puro p di v . Questo significa semplicemente aggiungere una quarta coordinata di 0:

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

2) Pre-moltiplicarlo con q e post-moltiplicarlo con il coniugato q * :

p'=q×p×q*

3) Ciò si tradurrà in un altro quaternione puro che può essere convertito in un vettore:

v'=(pX',py',pz')

Questo vettore v' è v ruotato di q .


Funziona ma non è ottimale . Le moltiplicazioni del quaternione significano tonnellate e tonnellate di operazioni. Ero curioso di conoscere varie implementazioni come questa e ho deciso di trovare da dove provenissero. Ecco i miei risultati.

Possiamo anche descrivere q come la combinazione di un vettore tridimensionale u con uno scalare s :

q=(uX,uy,uz,S)q=(u,S)

Secondo le regole della moltiplicazione del quaternione , e poiché il coniugato di un quaternione di lunghezza unitaria è semplicemente inverso, otteniamo:

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

La parte scalare (ellissi) risulta in zero, come dettagliato qui . La cosa interessante è la parte vettoriale, AKA il nostro vettore ruotato v ' . Può essere semplificato utilizzando alcune identità vettoriali di base :

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+(S2-uu)v+2S(u×v)

Questo è ora molto più ottimale ; due prodotti a punti, un prodotto incrociato e alcuni extra: circa la metà delle operazioni. Che darebbe qualcosa del genere nel codice sorgente (supponendo una libreria matematica vettoriale generica):

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

Tanto di cappello per una migliore risposta scritta. E considerando che la maggior parte dei maniaci delle prestazioni tendono ad usare i intrinseci per eseguire operazioni vettoriali, si ottiene abbastanza una maggiore velocità (anche per la semplice moltiplicazione del quaternione, specialmente sulle architetture Intel).
teodron,

Il risultato finale è simile alla formula di rotazione di Rodrigues - ha comunque gli stessi vettori di base; Dovrei scavare in alcune identità di trigoni per vedere se i coefficienti corrispondono.
Nathan Reed,

@NathanReed Questo sembra essere un altro modo per ottenere lo stesso risultato. Vorrei anche sapere se questo corrisponde. Grazie per la segnalazione!
Laurent Couvidou,

1
Stavo verificando l'implementazione di GLM di questo e sembra essere implementato in modo leggermente diverso, vale a dire come segue: vprime = v + ((cross(u, v) * s) + cross(u, cross(u, v)) * 2.0fè un'ottimizzazione simile? Sembra un po 'simile, ma non è lo stesso: usa solo prodotti incrociati, nessun prodotto punto. Il codice sorgente originale può essere trovato nel file type_quat.inl del repository GLM ufficiale nel operator*quale prende un quaternione e un vettore ( vec<3, T, Q> operator*(qua<T, Q> const& q, vec<3, T, Q> const& v))
j00hi,

7

Prima di tutto, q ^ (- 1) non è -q / magnitudo (q); è q * / (magnitudine (q)) ^ 2 (q * è il coniugato; ciò nega tutti i componenti tranne quello reale). Ovviamente, puoi lasciare la divisione per grandezza se tutti i tuoi quaternioni sono già normalizzati, cosa che normalmente farebbero in un sistema di rotazione.

Per quanto riguarda la moltiplicazione con un vettore, è sufficiente estendere il vettore a un quaternione impostando il componente reale di un quat su zero e i suoi componenti ijk sullo xyz del vettore. Quindi esegui le moltiplicazioni del quaternione per ottenere v ', quindi estrai nuovamente i componenti ijk. (La parte reale di v 'dovrebbe sempre risultare zero, più o meno un errore in virgola mobile.)


5

Prima osservazione: l'inverso di qnon lo è -q/magnitude(q), è completamente sbagliato. Le rotazioni con quaternioni implicano che questi equivalenti di numeri complessi 4D abbiano una norma unitaria, quindi si trovano sulla sfera dell'unità S3 in quello spazio 4D. Il fatto che un quat sia unitario significa che la sua norma è norm(q)^2=q*conjugate(q)=1e ciò significa che l'inverso del quat è il suo coniugato.

Se un'unità quaternione è scritta come q=(w,x,y,z)= (cos (t), sin (t) v ), allora il suo coniugato è conjugate(q)=(w,-x,-y,-z)= (cos (t), - sin (t) v ), dove t è la metà dell'angolo di rotazione e v è l'asse di rotazione (come vettore unitario, ovviamente).

Quando quel tipo di Hamilton decise di giocare con numeri equivalenti complessi in dimensioni superiori, inciampò anche in alcune belle proprietà. Ad esempio, se impieghi un quaternione completamente puro q=(0,x,y,z)(nessuna parte scalare w !), Puoi considerare quella merda come un vettore (in realtà è un quat su ciò che le persone potrebbero chiamare l'equatore della sfera S3, che è una sfera S2! ! - mente piegando le cose se consideriamo quanto tecnicamente le persone del 19 ° secolo ci sembrano dei cowboy di EyePhone al giorno d'oggi). Quindi Hamilton ha preso quel vettore nella sua forma quat: v=(0,x,y,z)e ha fatto una serie di esperimenti considerando le proprietà geometriche dei quat. Per farla breve:

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)

dove

 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

Osservazione: q * (0, v) * conj (q) deve essere un altro quat della forma (0, v '). Non esaminerò tutta quella spiegazione apparentemente complicata del perché ciò accada, ma se ruotate un quaternione immaginario puro (o un vettore nel nostro caso!) Attraverso questo metodo, dovete ottenere un tipo simile di oggetto: puro quat immaginario. e prendi la sua parte immaginaria come risultato. Ecco qua, il meraviglioso mondo delle rotazioni con quaternioni in un guscio di noce.

NOTA : a chiunque salti dentro con quella frase abusata: i quat sono buoni perché evitano il blocco del gimbal .. dovrebbero prima sbloccare la loro immaginazione !! I quat sono un semplice apparato matematico "elegante" e possono essere evitati del tutto usando altri approcci, quello che trovo completamente geometricamente equivalente è l'approccio dell'angolo dell'asse.

CODICE : la libreria C ++ che immagino è piuttosto semplicistica, ma ha tutte le operazioni di matrice, vettoriale e quat di cui uno sperimentatore di grafica 3D dovrebbe aver bisogno senza dover perdere più di 15 minuti per impararlo .. Puoi testare le cose che ho scritto qui usando quello tra 15 minuti se non sei un principiante C ++. In bocca al lupo!


+1 per la tua nota. Scommetto che la maggior parte della gente non potrebbe ottenere il vero blocco del gimbal se ci provasse. È diventata una frase universale per qualsiasi comportamento imprevisto durante l'esecuzione delle rotazioni.
Steve H,

La maggior parte delle persone non è in grado di costruire un meccanismo gimbal adeguato e pensa che se si uniscono in 3 matrici di rotazioni, finiscono automaticamente con la rappresentazione "Angoli di Eulero". La cosa gimbal è solo uno dei più semplici tipi di rotazione robot-braccio articolazioni che possono sperimentare ridondanza quando si tenta di eseguire la cinematica inversa (ha più gradi di libertà di quanti ne abbia effettivamente bisogno per produrre l'orientamento desiderato). Vabbè, questo è un altro argomento, ma ho pensato che fosse bello stare lontano
dall'hype

Nitpickery: mentre l'angolo dell'asse è equivalente in quanto entrambe le rappresentazioni possono rappresentare tutte le rotazioni in SO (3) in modo univoco (ok, modulo la solita doppia copertina) e ovviamente c'è una trasformazione quasi banale avanti e indietro tra di loro, i quaternioni hanno il vantaggio di essere molto più facile da comporre rispetto a tutte le altre rappresentazioni non a matrice.
Steven Stadnicki,

Hanno il vantaggio di essere più facili da comporre grazie al loro comportamento piacevole in qualsiasi linguaggio di programmazione orientato agli oggetti, specialmente quando si utilizza il sovraccarico dell'operatore. Non ne sono sicuro, ma forse anche le loro proprietà di interpolazione sferica conservano l'angolo dell'asse (tranne forse SQUAD ?!).
teodron,


-1

Ho provato a risolverlo manualmente e ho trovato la seguente equazione / metodo:

// 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;
}

Gradirei se qualcuno esaminasse la derivazione mt che ho usato http://pastebin.com/8QHQqGbv suggerirei di copiare in un editor di testo che supporta lo scorrimento laterale

nella mia notazione ho usato q ^ (- 1) per indicare coniugato, e non inverso, e identificatori diversi, ma spero che sia seguibile. Penso che la maggioranza abbia ragione, specialmente dove, dimostrando che la parte reale del vettore sparirebbe.

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.