Obracanie wektora3 o ćwiartkę

25

Próbuję obrócić wektor3 o dany czwartorzęd.

Wiem, że to prawda

v=qvq-1

Wiem, że jest odwrotnością, która po prostu , ale jak mam odwzorować mnożenie wektora na czwartorzęd, aby odzyskać wektor?q-1qmagnituremi(q)

Odkryłem, że można traktować jak macierz i konwertować i na macierze, a następnie konwertować z macierzy na wektor, ale wydaje się to nieco przesadne, aby uzyskać wektor. Czy istnieje czystsza implementacja, której mógłbym użyć?vqqv

gardian06
źródło

Odpowiedzi:

36

Jak ujawnili Nathan Reed i teodron, receptą na obrót wektora v o czwartorzędową jednostkę długości q jest:

1) Utwórz czysty czwartorzęd p z v . Oznacza to po prostu dodanie czwartej współrzędnej 0:

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

2) Pomnóż go wstępnie przez q, a następnie pomnóż go przez koniugat q * :

p=q×p×q

3) Spowoduje to powstanie kolejnego czystego czwartorzędu, który można odwrócić do wektora:

v=(px,py,pz)

Ten wektor jest obrócony o .vvq


To działa, ale dalekie od optymalnego . Mnożenia czwartorzędu oznaczają tony operacji. Byłem ciekawy różnych wdrożeń, takich jak ten , i postanowiłem dowiedzieć się, skąd one pochodzą. Oto moje ustalenia.

Możemy również opisać q jako połączenie trójwymiarowego wektora u i skalara s :

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

Zgodnie z regułami mnożenia czwartorzędu i ponieważ koniugat czwartorzędu o długości jednostki jest po prostu odwrotny, otrzymujemy:

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

Część skalarna (elipsy) daje zero, jak wyszczególniono tutaj . Interesująca jest część wektorowa, AKA nasz obrócony wektor v ' . Można to uprościć za pomocą podstawowych tożsamości wektorowych :

v=(uv)u+s2)v+s(u×v)+s(u×v)+u×(u×v)=(uv)u+s2)v+2)s(u×v)+(uv)u-(uu)v=2)(uv)u+(s2)-uu)v+2)s(u×v)

Jest to teraz o wiele bardziej optymalne ; dwa produkty kropkowe, produkt krzyżowy i kilka dodatków: około połowa operacji. Co dałoby coś takiego w kodzie źródłowym (przy założeniu jakiejś ogólnej biblioteki matematycznej wektorowej):

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
źródło
Czapki z głów przed lepszą pisemną odpowiedzią. Biorąc pod uwagę, że większość maniaków wydajności zwykle używa wewnętrznych funkcji do wykonywania operacji wektorowych, dostajesz całkiem przyspieszenie (nawet w przypadku zwykłego mnożenia czwartorzędu, szczególnie w przypadku architektur Intel).
teodron
Wynik końcowy wygląda podobnie do wzoru rotacji Rodriguesa - i tak ma wektory bazowe; Musiałbym zagłębić się w niektóre tożsamości wyzwalaczy, aby sprawdzić, czy współczynniki się zgadzają.
Nathan Reed
@NathanReed Wydaje się, że to kolejny sposób na osiągnięcie tego samego rezultatu. Chciałbym również wiedzieć, czy to pasuje. Dzięki za zwrócenie na to uwagi!
Laurent Couvidou
1
Sprawdzałem implementację tego przez GLM i wydaje się, że jest ona implementowana nieco inaczej, a mianowicie: vprime = v + ((cross(u, v) * s) + cross(u, cross(u, v)) * 2.0fczy to podobna optymalizacja? Wygląda nieco podobnie, ale nie jest taki sam - używa tylko produktów krzyżowych, bez produktów kropkowych. Oryginalny kod źródłowy można znaleźć w pliku type_quat.inl oficjalnego repozytorium GLM, w operator*którym znajduje się czwartorzęd i wektor ( vec<3, T, Q> operator*(qua<T, Q> const& q, vec<3, T, Q> const& v))
j00hi
7

Przede wszystkim q ^ (-1) nie jest -q / magnituda (q); to q * / (magnituda (q)) ^ 2 (q * to koniugat; który neguje wszystkie składniki oprócz rzeczywistego). Oczywiście możesz pominąć podział według wielkości, jeśli wszystkie twoje czwartorzędy są już znormalizowane, co zwykle byłoby w systemie rotacji.

Jeśli chodzi o mnożenie przez wektor, wystarczy rozszerzyć wektor do czwartorzędu, ustawiając rzeczywisty komponent quatu na zero, a jego komponenty ijk na xyz wektora. Następnie wykonujesz mnożenie czwartorzędu, aby uzyskać v ', a następnie ponownie rozpakuj komponenty ijk. (Rzeczywista część v 'powinna zawsze wychodzić na zero, plus lub minus jakiś błąd zmiennoprzecinkowy.)

Nathan Reed
źródło
5

Pierwsza obserwacja: Odwrotność qnie jest -q/magnitude(q), to jest całkowicie błędne. Obroty z czwartorzędami sugerują, że te ekwiwalenty liczby zespolonej 4D mają normę jednostkową, a zatem leżą na sferze jednostki S3 w tej przestrzeni 4D. Fakt, że quat jest jednolity, oznacza, że ​​jego normą jest, norm(q)^2=q*conjugate(q)=1a to oznacza, że ​​odwrotność quatu jest jego koniugatem.

Jeśli czwartorzędowa jednostka jest zapisana jako q=(w,x,y,z)= (cos (t), sin (t) v ), to jej sprzężeniem jest conjugate(q)=(w,-x,-y,-z)= (cos (t), - sin (t) v ), gdzie t jest połową kąta obrotu v jest osią obrotu (oczywiście jako wektor jednostkowy).

Kiedy ten koleś z Hamilton zdecydował się bawić z ekwiwalentami liczb zespolonych w wyższych wymiarach, natknął się również na kilka dobrych właściwości. Na przykład, jeśli zastosujesz całkowicie czysty czwartorzęd q=(0,x,y,z)(bez części skalarnej w !), Możesz uznać to badziewie za wektor (to tak naprawdę quat na temat tego, co ludzie mogliby nazwać równikiem sfery S3, która jest sferą S2! ! - zginanie rzeczy, jeśli weźmiemy pod uwagę fakt, że ludzie w XIX wieku upośledzeni technicznie wydają się nam w dzisiejszych czasach kowbojami EyePhone). Hamilton przyjął ten wektor w swojej quat: v=(0,x,y,z)przeprowadził serię eksperymentów, biorąc pod uwagę właściwości geometryczne quatów. Krótko mówiąc:

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)

gdzie

 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

Uwaga: sprzężenie q * (0, v) * (q) musi być kolejnym quatem formy (0, v '). Nie będę omawiał tego pozornie skomplikowanego wyjaśnienia, dlaczego tak się dzieje, ale jeśli obrócisz za pomocą tej metody czysty wyimaginowany czwartorzęd (lub w naszym przypadku wektor!), Musisz uzyskać podobny rodzaj przedmiotu: czysty wyimaginowany quat. i bierzesz jego wymyśloną część jako wynik. Oto wspaniały świat rotacji z czwartorzędami w łupinie orzecha (ty).

UWAGA : ktokolwiek wskakuje z tą nadużywaną frazą: quaty są dobre, ponieważ unikają blokady gimbala ... powinni najpierw odblokować swoją wyobraźnię !! Quaty są jedynie „eleganckim” aparatem matematycznym, którego można całkowicie uniknąć, stosując inne podejścia, przy czym moim zdaniem całkowicie geometrycznie równoważne jest podejście do kąta osi.

KOD : biblioteka C ++, którą według mnie jest dość uproszczona, ale ma wszystkie operacje na matrycy, wektorze i quatach, których powinien potrzebować eksperymentator grafiki 3D bez konieczności marnowania więcej niż 15 minut na naukę. Za pomocą tego możesz przetestować to, co tu napisałem w 15 minut, jeśli nie jesteś nowicjuszem w C ++. Powodzenia!

teodron
źródło
+1 za notatkę. Założę się, że większość ludzi nie byłaby w stanie osiągnąć prawdziwej blokady gimbala, gdyby próbowali. Stało się to zwrotem dla każdego nieoczekiwanego zachowania podczas wykonywania obrotów.
Steve H
Większość ludzi nie jest w stanie zbudować odpowiedniego mechanizmu gimbala i myśli, że jeśli połączą razem 3 macierze rotacji, automatycznie otrzymają reprezentację „kątów Eulera”. Gimbal jest tylko jednym z najprostszych rotacyjnych robotów stawy, które mogą doświadczyć nadmiarowości podczas próby wykonania kinematyki odwrotnej (ma więcej stopni swobody niż faktycznie potrzebuje do uzyskania pożądanej orientacji). No cóż, to inny temat, ale pomyślałem, że fajnie jest trzymać się z dala od szumu, jaki ten „legendarny” problem wygenerował wśród programistów CG ..
teodron
Nitpickery: podczas gdy kąt osi jest równoważny, ponieważ obie reprezentacje mogą jednoznacznie reprezentować wszystkie obroty w SO (3) (dobrze, modulo zwykłą podwójną okładkę) i oczywiście istnieje prawie trywialna transformacja tam iz powrotem, ćwiartki mają zaletą jest to, że jest znacznie łatwiejsze do skomponowania niż wszystkie inne reprezentacje nie-macierzowe.
Steven Stadnicki
Mają tę zaletę, że łatwiej je komponować ze względu na ich miłe zachowanie w dowolnym języku programowania obiektowego, szczególnie w przypadku przeciążenia operatora. Nie jestem pewien, ale może nawet ich właściwości interpolacji sferycznej zachowują kąt osi (może z wyjątkiem SQUAD ?!).
teodron
2

Oto alternatywny sposób transformacji wektora o czwartorzędu. Jest to sposób, w jaki MS robi to w środowisku xna. http://pastebin.com/fAFp6NnN

Steve H.
źródło
-1

Próbowałem to rozwiązać ręcznie i wymyśliłem następujące równanie / metodę:

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

Byłbym wdzięczny, gdyby ktoś spojrzał na pochodzenie mt, którego użyłem http://pastebin.com/8QHQqGbv Sugerowałbym skopiowanie do edytora tekstowego, który obsługuje przewijanie boczne

w mojej notacji użyłem q ^ (- 1), aby oznaczać koniugat, a nie odwrotność i różne identyfikatory, ale mam nadzieję, że można go śledzić. Myślę, że większość ma rację, szczególnie gdy udowadnianie, że prawdziwa część wektora zniknie.

gardian06
źródło