2010-08-09 5 views
10

Sto cercando di scoprire l'angolo (in gradi) tra due vettori 2D. So che ho bisogno di usare trig ma non ne sono troppo bravo. Questo è quello che sto cercando di capire (l'asse Y aumenta verso il basso): alt text http://i38.tinypic.com/2dcefch.pngCome calcolare l'angolo di un vettore dalla verticale?

Sto cercando di utilizzare questo codice al momento, ma non sta funzionando affatto (calcola gli angoli casuali per qualche motivo) :

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

Questi sono i miei risultati (ci costante nel fornire una posizione costante, ma quando cambio la posizione, l'angolazione cambia e non riesco a trovare alcun collegamento tra i due angoli):

posizione 1: x: 100 y: 100 x1: 50 y1: 50.210 Angolo: 45

montaggio 2: x: 92 y: 85 x1: 24 y1: 16 Angolo: 44.58

posizione 3: x: 44 Y: 16 x1: 106 y1: 132 Angolo: 28,12

Modifica: Grazie a tutti quelli che hanno risposto e mi hanno aiutato a capire che era sbagliato! Scusa il titolo e la domanda è stata confusa.

+1

Dubito che sia casuale. Potresti postare i valori di x1, x, y1, y? L'uscita cambia anche quando l'ingresso è costante? – FrustratedWithFormsDesigner

+2

Il tuo diagramma non è corretto. Hai definito solo 2 punti e non esiste alcuna rappresentazione per il vettore che crea l'angolo Theta. Usando, p1 e p2 come in questo diagramma troverai un angolo molto diverso; l'angolo p1 e p2 fanno con l'origine. – aepryus

+1

Si dice che si sta tentando di calcolare l'angolo tra due vettori ma il diagramma sembra implicare che si stia effettivamente cercando di ottenere l'angolo tra un vettore e l'asse y. È corretto? – Troubadour

risposta

13

Aha! Ho scoperto che avevo bisogno di capovolgere la mia angolazione e usare atan2. Questo è il mio codice finale:

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

Grazie a tutti per avermi aiutato a capire questo e anche per avermi aiutato a capire quello che sto effettivamente facendo! :)

+0

Quindi il tuo ordine aumenta in quel momento e vuoi che il Nord sia diritto. Invece di sottrarre da 180 puoi semplicemente capovolgere i segni di y, cioè il tuo secondo argomento di 'atan2' è quindi' y-y1'. – Troubadour

+0

oohhh, grazie !! Avviso – Niall

+0

: verrà generata una divisione per zero quando (y1 - y) == 0. – Leftium

0

Dovrebbe essere:

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

abs sta per assoluta (per evitare valori negativi)

+1

Non dimenticare il caso y1 = y. –

+1

Se lo fai in questo modo ottieni solo un angolo tra -pi/2 e pi/2. L'OP sta chiedendo di 'atan2'. – Troubadour

0

Si sta utilizzando numeri interi? Esegui il cast degli argomenti come double e userei fabs sul risultato, non sugli argomenti. Il risultato sarà in radianti; per ottenere i gradi, utilizzare:

res * = (360.0/(2.0 * Math.PI));

+0

Chiunque abbia downvotato questo dovrebbe rendersi conto che è stato pubblicato _prima_ il codice postato OP che conferma l'uso della precisione in virgola mobile. – Troubadour

+0

Grazie Troubadour :-) – Jess

0

mia prima ipotesi è calcolare l'angolo di ogni vettore con gli assi usando atan (y/x) e quindi sottrarre quegli angeli e prendere il valore assoluto, cioè:

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

14

Devi prima capire come calcolare l'angolo tra due vettori e ce ne sono diversi. Ti darò quello che penso sia il più semplice.

  1. Dato v1 e v2, il loro prodotto scalare è: v1x * v2x + v1y * v2y
  2. La norma di un vettore v è dato da: sqtr (vx^2 + vy^2)

Con queste informazioni, si prega di prendere questa definizione:

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

Ora, a risolvere per angle(v1, v2):

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

Infine, prendendo le definizioni date all'inizio, poi si finisce con:

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

Anche in questo caso, ci sono molti modi per farlo, ma io come questo perché è utile per l'angolo e la norma dati dal punto del prodotto, o l'angolo, dati i vettori.

La risposta sarà in radianti, ma si sa che i radianti pi (ovvero 3,14 radianti) sono 180 gradi, quindi è sufficiente moltiplicare per il fattore di conversione 180/pi.

+0

Grazie per averlo spiegato! Sto provando il tuo algoritmo, ma quando l'angolo è ottuso devo specchiarlo e quando non lo è, devo aggiungere 45 gradi. c'è qualche ragione per questo? Voglio dire, sto bene facendo una semplice dichiarazione if, ma mi piacerebbe sapere perché sto facendo :) – Niall

+0

Non preoccuparti, ho capito che dovevo usare atan2 e rispecchiare l'angolo. Grazie per la tua risposta e per averlo spiegato! :) – Niall

+0

Sì. Se pensi al prodotto punto di due vettori, capirai perché l'arcotangente risolve il problema del quadrante. In altre parole, seleziona il segno corretto per l'angolo. In bocca al lupo. – Escualo

1

credo che l'equazione per l'angolo tra due vettori dovrebbe essere più simile a:

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

vostro sopra equazione calcola l'angolo fatta tra il vettore P1-P2 e la linea fatta da estendere un ortogonale dal punto p2 al vettore p1.

Il prodotto punto di due vettori V1 e V2 è uguale a | V1 | * | V2 | cos (theta). Pertanto, theta è uguale a aco ((V1 punto V2)/(| V1 | | V2 |)). V1 punto V2 è V1.x V2.x + V1.y V2.y. La magnitudine di V (cioè, | V |) è il teorema del pathogorean ... sqrt (V.x^2 + V.y^2)

5

Non assumere il valore assoluto degli argomenti su atan2. L'intero punto di atan2 è che utilizza i segni dei suoi argomenti per capire quale qaudrant è l'angolo. Prendendo i valori assoluti stai forzando atan2 a restituire solo valori tra 0 e pi/2 invece di -pi a pi.

+1

Grazie per aver spiegato perché non dovrei prendere i valori assoluti quando uso atan2 :) Alla fine ho dovuto solo specchiare l'angolo (Eg 180 + (- toDegrees (atan2 (x1-x, y1-y)))) – Niall

+0

@Niall: Felice di aiutare. :) Vedi il mio commento alla tua risposta. Inoltre, dal momento che stai effettivamente cercando di elaborare l'angolo tra l'asse y negativo e un singolo vettore, forse dovresti modificare il titolo della domanda in quanto è molto fuorviante al momento. Qualcosa come "Come calcolare l'angolo di un vettore dalla verticale?" e sottolinea nella domanda che si aumenta verso il basso nel sistema. – Troubadour

+0

Certo, lo modificherò ora. :) – Niall

2

Sembra che Niall l'abbia capito, ma finirò comunque la mia spiegazione.Oltre a spiegare perché la soluzione funziona, la soluzione presenta due vantaggi:

  • divisione potenziale per zero entro atan2() è evitata
  • valore di ritorno è sempre positiva nell'intervallo da 0 a 360 gradi

atan2() restituisce l'angolo in senso antiorario rispetto all'asse X positivo. Niall stava cercando l'angolo in senso orario rispetto all'asse Y positivo (tra il vettore formato dai due punti e l'asse Y positivo).

La funzione seguente è adattato dal mio asteroidi gioco in cui ho voluto calcolare la direzione di un vettore di nave/velocità era "di puntamento:"

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

Grazie per avermi aiutato a capire perché funziona! :) Purtroppo sto lavorando in Java (sono principalmente uno sviluppatore C & Obj-C, ma è sempre bello imparare cose nuove! Ho iniziato con Java anni fa, quindi non è troppo difficile :)) quindi Non posso usare #define. Inoltre, pensi che dovrei usare double invece di usare float? Come tutte le funzioni Math in Java usano il doppio, ma una volta ho avuto un problema con double vs float (probabilmente solo qualcos'altro nel mio codice) e da allora ho sempre usato float. – Niall

+1

Ho cambiato il codice in modo che fosse più simile a Java: 'Math.toDegrees()' è effettivamente lo stesso di moltiplicare per la costante DEG_PER_RAD precedente. (Altrimenti puoi creare una costante Java con 'static double double'). Float ha meno precisione del doppio e probabilmente incorre in una penalizzazione delle prestazioni poiché saranno richieste conversioni tra float e double. A meno che il salvataggio della memoria non sia un problema, utilizzerei sempre il doppio. Anche il Troubadour ha sottolineato che atan2 non ha bisogno di essere protetto dalla divisione per zero. – Leftium