2010-08-09 12 views
10

Próbuję znaleźć kąt (w stopniach) między dwoma wektorami 2D. Wiem, że potrzebuję używać trig, ale nie jestem z nim zbyt dobry. Właśnie to staram się wypracować (oś Y rośnie w dół): alt text http://i38.tinypic.com/2dcefch.pngJak obliczyć kąt wektora od pionu?

Próbuję użyć tego kodu w tej chwili, ale to nie działa w ogóle (oblicza przypadkowe kąty z jakiegoś powodu) :

private float calcAngle(float x, float y, float x1, float y1) 
{ 
    float _angle = (float)Math.toDegrees(Math.atan2(Math.abs(x1-x), Math.abs(y1-y))); 
    Log.d("Angle","Angle: "+_angle+" x: "+x+" y: "+y+" x1: "+x1+" y1: "+y1); 
    return _angle; 
}

są to moje wyniki (jest stała, gdy zapewnienie stałej pozycji, ale przy zmianie pozycji, zmiany kąta i nie mogę znaleźć żadnego związku między tymi dwoma kątami):

stanowisko 1: x: 100 y: 100 x1: 50 y1: 50 Kąt: 45

Pozycja 2: X: 92 r: 85 X1: 24 Y1 16 Kąt: 44,58

Pozycja 3: X: 44 r: 16 X1: 106 y1: 132 Kąt: 28.12

Edycja: Dziękuję wszystkim, którzy odpowiedzieli i pomogli mi dowiedzieć się, że było źle! Niestety tytuł i pytanie były mylące.

+1

wątpię, że to przypadkowe. Czy możesz podać wartości x1, x, y1, y? Czy wyjście zmienia się, nawet jeśli wejście jest stałe? – FrustratedWithFormsDesigner

+2

Twój diagram jest niepoprawny. Zdefiniowałeś tylko 2 punkty i nie ma reprezentacji dla wektora, który tworzy kąt Theta. Używając, p1 i p2 jak na tym diagramie znajdą bardzo różny kąt; kąt p1 i p2 tworzą się z punktem początkowym. – aepryus

+1

Mówisz, że próbujesz obliczyć kąt między dwoma wektorami, ale diagram zdaje się sugerować, że faktycznie próbujesz uzyskać kąt między jednym wektorem a osią Y. Czy to jest poprawne? – Troubadour

Odpowiedz

13

Aha! Okazuje się, że potrzebowałem odwrócić swój kąt i użyć atan2. To jest mój ostateczny kod:

private float calcAngle(float x, float y, float x1, float y1) 
{ 
    float _angle = (float)Math.toDegrees(Math.atan2(x1-x, y-y1)); 
    return _angle; 
}

Dzięki wszystkim za pomoc wymyślimy a także pomaga mi zrozumieć, co ja właściwie robi! :)

+0

Wasze współrzędne Y rosną w dół, a Ty chcesz, aby północ była prosto. Zamiast odejmować od 180 możesz po prostu odwrócić znaki y, tzn. Twój drugi argument na 'atan2' to zatem' y-y1'. – Troubadour

+0

oohhh, dzięki! – Niall

+0

ostrzeżenie: podział przez zero spowoduje, że (y1 - y) == 0. – Leftium

0

Powinno być:

atan(abs(x1 - x)/abs(y1 - y)) 

abs oznacza absolutny (aby uniknąć wartości ujemnych)

+1

Nie zapomnij o przypadku y1 = y. –

+1

Jeśli zrobisz to w ten sposób, uzyskasz kąt między -pi/2 a pi/2. OP pyta o 'atan2'. – Troubadour

0

Czy używasz liczb całkowitych? Rzuć argumenty jako podwójne, a ja użyłbym fabs na wyniku, a nie argumentów. Wynik będzie w radianach; aby uzyskać stopnie, użyj:

res * = (360,0/(2,0 * Math.PI));

+0

Ktokolwiek powinien to zrobić, powinien zdawać sobie sprawę, że został wysłany przed wydaniem kodu OP, który potwierdza użycie precyzji zmiennoprzecinkowej. – Troubadour

+0

Dzięki Troubadour :-) – Jess

0

My pierwsze przypuszczenie to obliczenie kąta każdego wektora z osiami za pomocą Atan (y/x), a potem odjąć anioły i ma wartość bezwzględną, która jest:

ABS (atan (r/x) - atan (y1/x1))

14

Najpierw musisz zrozumieć, jak obliczyć kąt między dwoma wektorami i jest ich kilka. Dam ci to, co moim zdaniem jest najprostsze.

  1. względu V1 i V2 ich punkt produkt: v1x * v2x + v1y * v2y
  2. Norma wektora V jest dana przez: sqtr (VX^2 + vy^2)

Dzięki tej informacji, proszę wziąć tę definicję:

dot(v1, v2) = norm(v1) * norm(v2) * cos(angle(v1, v2)) 

Teraz można rozwiązać za angle(v1, v2):

angle(v1, v2) = acos(dot(v1, v2)/(norm(v1) * norm(v2))) 

Wreszcie, biorąc definicje podane na początku, a potem kończy się z:

angle(v1, v2) = acos((v1x * v2x + v1y * v2y)/(sqrt(v1x^2+v1y^2) * sqrt(v2x^2+v2y^2))) 

Ponownie, istnieje wiele sposobów, aby to zrobić, ale ja jak ten, ponieważ jest pomocny dla iloczynu podanego kąta i normy produktu lub kąta, podanych wektorów.

Odpowiedź będzie podawana w radianach, ale wiadomo, że pi radians (czyli 3,14 radiana) ma 180 stopni, a więc po prostu pomnożenie przez współczynnik konwersji 180/pi.

+0

Dzięki za wyjaśnienie! Próbuję twojego algorytmu, ale kiedy kąt jest rozwarty, muszę go odbić, a kiedy nie, muszę dodać 45 stopni. Czy jest jakikolwiek powód? Mam na myśli, że nic mi nie zrobi prostego zdania, jeśli jeszcze inaczej, ale chciałbym wiedzieć dlaczego to robię :) – Niall

+0

Nie martw się, po prostu zorientowałem się, że muszę użyć atan2 i zwierciadłem kąt. Dziękuję za odpowiedź i za wyjaśnienie! :) – Niall

+0

Tak. Jeśli myślisz o iloczynie dwóch wektorów, zrozumiesz, dlaczego arcus tangens rozwiązuje problem kwadrantu. Innymi słowy, wybiera właściwy znak dla kąta. Powodzenia. – Escualo

1

wierzę równanie dla kąta między dwoma wektorami powinien wyglądać bardziej jak:

toDegrees(acos((x*x1+y*y1)/(sqrt(x*x+y*y)*sqrt(x1*x1+y1*y1)))) 

Twój powyższe równanie będzie obliczyć kąt wykonany pomiędzy wektorem P1-P2 i linii wykonane przez rozciągając ortogonalne od punktu p2 do wektora p1.

Iloczyn iloczynowy dwóch wektorów V1 i V2 jest równy | V1 | * | V2 | cos (theta). Dlatego teta jest równa acos ((V1 kropka V2)/(| V1 | | V2 |)). V1 dot V2 to V1.x V2.x + V1.y V2.y. Wielkość V (tj. | | | | |) To twierdzenie patogorejskie ... sqrt (V.x^2 + V.y^2)

5

Nie należy przyjmować wartości bezwzględnej argumentów do atan2. Cały punkt atan2 polega na tym, że używa znaków swoich argumentów, aby ustalić, który kąt jest w danym położeniu. Przyjmując bezwzględne wartości, zmuszasz atan2 do zwracania wartości od 0 do pi/2 zamiast od -pi do pi.

+1

Dzięki za wyjaśnienie, dlaczego nie powinienem przyjmować bezwzględnych wartości przy użyciu atan2 :) W końcu okazało się, że po prostu musiałem odzwierciedlać kąt (np. + (- toDegrees (atan2 (x1-x, y1-y)))) – Niall

+0

@Niall: Cieszę się, że mogę pomóc. :) Zobacz mój komentarz do swojej odpowiedzi. Ponadto, ponieważ faktycznie próbujesz ustalić kąt między ujemną osią y a pojedynczym wektorem, być może powinieneś edytować tytuł pytania, ponieważ obecnie jest on bardzo mylący. Coś w stylu "Jak obliczyć kąt wektora z pionu?" i wskaż w pytaniu, że y zwiększa się w dół w twoim systemie. – Troubadour

+0

Jasne, po prostu to teraz zmienię. :) – Niall

2

Wygląda na to, że Niall to wymyślił, ale i tak skończę moje wyjaśnienia.Oprócz wyjaśnienia dlaczego rozwiązanie działa, moje rozwiązanie ma dwie zalety:

  • Potencjalne dzielenie przez zero w atan2() jest uniknąć
  • Zwracana wartość jest zawsze dodatnia w przedziale od 0 do 360 stopni

atan2() zwraca kąt przeciwny do ruchu wskazówek zegara względem dodatniej osi X. Niall szukał kąta zgodnego z ruchem wskazówek zegara względem dodatniej osi Y (między wektorem utworzonym przez dwa punkty i dodatnią oś Y).

Poniższa funkcja jest dostosowany od mojego asteroidy gry gdzie chciałem obliczyć kierunek wektorze statek/prędkość była „wskazującym”

// Calculate angle between vector from (x1,y1) to (x2,y2) & +Y axis in degrees. 
// Essentially gives a compass reading, where N is 0 degrees and E is 90 degrees. 

double bearing(double x1, double y1, double x2, double y2) 
{ 
    // x and y args to atan2() swapped to rotate resulting angle 90 degrees 
    // (Thus angle in respect to +Y axis instead of +X axis) 
    double angle = Math.toDegrees(atan2(x1 - x2, y2 - y1)); 

    // Ensure result is in interval [0, 360) 
    // Subtract because positive degree angles go clockwise 
    return (360 - angle) % 360; 
} 
+0

Dzięki za pomoc w zrozumieniu, dlaczego to działa! :) Niestety obecnie pracuję w Javie (głównie jestem programistą C & Obj-C, ale zawsze dobrze jest się uczyć nowych rzeczy! Zacząłem od Java'a już kilka lat temu, więc nie jest to zbyt trudne :)), więc Nie mogę użyć #define. Myślisz, że powinienem użyć podwójnego zamiast używać float? Ponieważ wszystkie funkcje matematyczne w Javie używają podwójnego, ale miałem kiedyś problem z double vs float (prawdopodobnie po prostu coś innego w moim kodzie) i od tego czasu zawsze używałem float. – Niall

+1

Zmieniłem kod, aby był bardziej podobny do Javy: 'Math.toDegrees()' jest efektywnie taki sam jak mnożenie przez poprzednią stałą DEG_PER_RAD. (W przeciwnym razie możesz utworzyć stałą Java z 'statycznym ostatnim podwójnym '). Float ma mniejszą precyzję niż podwójne i najprawdopodobniej poniesie karę za wydajność, ponieważ wymagane będą konwersje pomiędzy floatem i double. O ile oszczędzanie pamięci nie jest dużym problemem, zawsze używałbym podwójnego. Również Troubadour zwrócił uwagę, że atan2 nie musi być chroniony przed dzieleniem przez zero. – Leftium