2010-08-09 21 views
10

Estoy tratando de averiguar el ángulo (en grados) entre dos vectores 2D. Sé que necesito usar trigonometría, pero no soy muy bueno con eso. Esto es lo que estoy tratando de resolver (el eje Y aumenta hacia abajo): alt text http://i38.tinypic.com/2dcefch.png¿Cómo calcular el ángulo de un vector desde la vertical?

Estoy tratando de utilizar este código en el momento, pero que no funciona en absoluto (calcula ángulos aleatorios por alguna razón) :

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

Estos son mis resultados (Hay constante al proporcionar una posición constante, pero cuando cambio la posición, el ángulo cambia y no puedo encontrar ningún vínculo entre los dos ángulos):

posición 1: x: 100 y: 100 x1: 50 y1: 50 Ángulo: 45

Posición 2: x: 92 y: 85 x1: 24 y1: 16 Ángulo: 44,58

Posición 3: x: 44 y: 16 X1: 106 y1: 132 Ángulo: 28.12

Editar: ¡Gracias a todos los que me respondieron y me ayudaron a descubrir que estaba mal! Lo siento, el título y la pregunta fueron confusos.

+1

Dudo que sea al azar. ¿Podría publicar los valores de x1, x, y1, y? ¿La salida cambia incluso cuando la entrada es constante? – FrustratedWithFormsDesigner

+2

Su diagrama es incorrecto. Solo has definido 2 puntos, y no hay representación para el vector que crea el ángulo Theta. Usar, p1 y p2 como en este diagrama encontrará un ángulo muy diferente; el ángulo p1 y p2 hacen con el origen. – aepryus

+1

Usted dice que está tratando de calcular el ángulo entre dos vectores, pero el diagrama parece implicar que en realidad está tratando de obtener el ángulo entre un vector y el eje y. ¿Es eso correcto? – Troubadour

Respuesta

13

Aha! Resulta que solo necesitaba voltear mi ángulo y usar atan2. Este es mi código final:

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

Gracias a todos por ayudarme a resolver esto y también para ayudarme a entender lo que en realidad estoy haciendo! :)

+0

Entonces, tu coordinación se incrementa en el camino y quieres que el Norte esté recto. En lugar de restar 180, puede simplemente cambiar los signos de y, es decir, su segundo argumento para 'atan2' es por lo tanto' y-y1'. – Troubadour

+0

oohhh, gracias! – Niall

+0

advertencia: se producirá una división por cero cuando (y1 - y) == 0. – Leftium

0

Debería ser:

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

abs representa absoluta (para evitar valores negativos)

+1

No olvides el caso y1 = y. –

+1

Si lo haces de esa manera, solo obtienes un ángulo entre -pi/2 y pi/2. El OP pregunta por 'atan2'. – Troubadour

0

¿Está utilizando números enteros? Echa los argumentos como dobles, y usaría fabs en el resultado, no en los argumentos. El resultado será en radianes; para obtener grados, use:

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

+0

Quien haya votado negativamente esto debería darse cuenta de que fue publicado antes del código publicado OP que confirma el uso de la precisión de punto flotante. – Troubadour

+0

Gracias Trovador :-) – Jess

0

Mi primera conjetura sería para calcular el ángulo de cada vector con los ejes utilizando atan (y/x) y luego restar esos ángeles y tomar el valor absoluto, es decir:

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

14

Primero tiene que entender cómo calcular el ángulo entre dos vectores y hay varios de ellos. Te daré lo que creo que es lo más simple.

  1. Dada v1 y v2, su producto escalar es: v1x * v2x + v1y * v2y
  2. La norma de un vector v viene dada por: sqtr (vx^2 + vy^2)

Con esta información, por favor tome esta definición:

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

Ahora, a resolver por angle(v1, v2):

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

Por último, tomando las definiciones dadas al principio, y luego se termina con:

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

Una vez más, hay muchas maneras de hacer esto, pero yo como este, ya que es útil para el producto de puntos dado el ángulo y la norma, o el ángulo, dado vectores.

La respuesta será en radianes, pero sabes que pi radianes (es decir, 3,14 radianes) son 180 grados, por lo que simplemente se multiplica por el factor de conversión 180/pi.

+0

Gracias por explicarlo! Estoy probando tu algoritmo, pero cuando el ángulo es obtuso tengo que duplicarlo y cuando no es así, debo agregar 45 grados. Hay alguna razón para esto? Quiero decir, estoy bien haciendo una declaración simple si no, pero me gustaría saber por qué lo estoy haciendo :) – Niall

+0

No se preocupe, simplemente descubrí que tenía que usar atan2 y reflejar el ángulo. ¡Gracias por su respuesta y por explicarla! :) – Niall

+0

Sí. Si piensas en el producto escalar de dos vectores, te darás cuenta de por qué el arcotangente resuelve el problema del cuadrante. En otras palabras, elige el signo correcto para el ángulo. Buena suerte. – Escualo

1

creo que la ecuación para el ángulo entre dos vectores debería parecerse más a:

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

Su ecuación anterior calculará el ángulo formado entre el vector P1-P2 y la línea hecha por extendiendo una ortogonal desde el punto p2 al vector p1.

El producto escalar de dos vectores V1 y V2 es igual a | V1 | * | V2 | cos (theta). Por lo tanto, theta es igual a acos ((V1 punto V2)/(| V1 | | V2 |)). V1 dot V2 es V1.x V2.x + V1.y V2.y. La magnitud de V (es decir, | V |) es el teorema patogórico ... sqrt (V.x^2 + V.y^2)

5

No tome el valor absoluto de los argumentos en atan2. El punto entero de atan2 es que usa los signos de sus argumentos para determinar en qué intervalo se encuentra el ángulo. Al tomar los valores absolutos, forzará atan2 para que solo devuelva valores entre 0 y pi/2 en lugar de -pi a pi.

+1

Gracias por explicar por qué no debo tomar los valores absolutos cuando uso atan2 :) Resulta que al final solo tuve que reflejar el ángulo (Ej. 180 + (- toDegrees (atan2 (x1-x, y1-y)))) – Niall

+0

@Niall: Me alegra ayudar. :) Mira mi comentario a tu respuesta. Además, dado que en realidad estás tratando de calcular el ángulo entre el eje y negativo y un único vector, quizás debas editar el título de la pregunta, ya que es muy engañoso en este momento. Algo así como "¿Cómo calcular el ángulo de un vector desde la vertical?" y señale en la pregunta que y aumenta hacia abajo en su sistema. – Troubadour

+0

Claro, lo editaré ahora. :) – Niall

2

Parece que Niall lo descubrió, pero terminaré mi explicación, de todos modos.Además de explicar por qué la solución funciona, mi solución tiene dos ventajas:

  • potencial división por cero dentro atan2() se evita
  • valor de retorno es siempre positivo en el rango de 0 a 360 grados

atan2() devuelve el ángulo en sentido antihorario con respecto al eje X positivo. Niall estaba buscando el ángulo en el sentido de las agujas del reloj con respecto al eje Y positivo (entre el vector formado por los dos puntos y el eje Y positivo).

La siguiente función es una adaptación de mi asteroides juego en el que quería calcular la dirección de un vector de la nave/velocidad era "apunta:"

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

¡Gracias por ayudarme a entender por qué funciona! :) Lamentablemente, actualmente estoy trabajando en Java (soy desarrollador de C & Obj-C principalmente, pero siempre es bueno aprender cosas nuevas. Empecé con Java hace años, así que no es demasiado difícil :)) entonces No puedo usar #define. Además, ¿crees que debería usar double en lugar de usar float? Como todas las funciones de Matemáticas en Java usan el doble, pero una vez tuve un problema con el doble frente al float (probablemente solo algo más en mi código) y desde entonces siempre he usado float. – Niall

+1

Cambié el código para que sea más parecido a Java: 'Math.toDegrees()' es en realidad lo mismo que multiplicar por la constante DEG_PER_RAD previa. (De lo contrario, puede crear una constante Java con 'static final double'). Float tiene menos precisión que el doble, y probablemente incurrirá en una penalización de rendimiento ya que se requerirán conversiones entre float y double. A menos que ahorrar memoria sea una gran preocupación, siempre usaría el doble. También Troubadour señaló que atan2 no necesita protección contra la división por cero. – Leftium

Cuestiones relacionadas