2010-08-09 10 views
10

En essayant de trouver l'angle (en degrés) entre deux vecteurs 2D. Je sais que je dois utiliser trig mais je ne suis pas très bon avec ça. Voilà ce que je suis en train de travailler (l'axe Y augmente vers le bas): alt text http://i38.tinypic.com/2dcefch.pngComment calculer l'angle d'un vecteur à partir de la verticale?

J'essaie d'utiliser ce code pour le moment, mais il ne fonctionne pas du tout (angles aléatoires calcule pour une raison quelconque) :

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

Ce sont mes résultats (Il constante en fournissant une position constante, mais quand je change la position, l'angle change et je ne trouve aucun lien entre les deux angles):

Position 1: x: 100 y: 100 x1: 50 y1: 50 Angle: 45

Position 2: x: 92 y: 85 x1: 24 y1: 16 Angle: 44,58

Position 3: x: 44 ans: 16 x1: 106 y1: 132 Angle: 28.12

Editer: Merci à tous ceux qui ont répondu et m'a aidé à comprendre que c'était faux! Désolé le titre et la question était confuse.

+1

Je doute que ce soit au hasard. Pourriez-vous afficher les valeurs de x1, x, y1, y? La sortie change-t-elle même lorsque l'entrée est constante? – FrustratedWithFormsDesigner

+2

Votre diagramme est incorrect. Vous avez seulement défini 2 points, et il n'y a aucune représentation pour le vecteur qui crée l'angle Thêta. En utilisant, p1 et p2 comme dans ce diagramme, vous trouverez un angle très différent; l'angle p1 et p2 fait avec l'origine. – aepryus

+1

Vous dites que vous essayez de calculer l'angle entre deux vecteurs, mais le diagramme semble impliquer que vous essayez réellement d'obtenir l'angle entre un vecteur et l'axe des ordonnées. Est-ce exact? – Troubadour

Répondre

13

Aha! Il s'avère que j'avais juste besoin de retourner mon angle et d'utiliser atan2. Ceci est mon code 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; 
}

Merci à tous pour me aider comprendre cela et aussi pour me aider à comprendre ce que je suis en fait faire! :)

+0

Donc votre co-ord augmente alors et vous voulez que North soit tout droit. Au lieu de soustraire de 180, vous pouvez simplement retourner les signes de y, c'est-à-dire que votre deuxième argument à 'atan2' est donc' y-y1'. – Troubadour

+0

oohhh, merci !! – Niall

+0

avertissement: une division par zéro résultera quand (y1 - y) == 0. – Leftium

0

Il devrait être:

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

abs est synonyme de absolu (pour éviter les valeurs négatives)

+1

Ne pas oublier le cas y1 = y. –

+1

Si vous le faites de cette façon, vous obtenez seulement un angle entre -pi/2 et pi/2. L'OP demande à propos de 'atan2'. – Troubadour

0

Utilisez-vous des entiers? Cast les arguments en tant que doubles, et j'utiliserais fabs sur le résultat, pas les arguments. Le résultat sera en radians; pour obtenir des degrés, utiliser:

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

+0

Celui qui downvoted cela devrait se rendre compte qu'il a été posté _avant le code postal OP qui confirme l'utilisation de la précision en virgule flottante. – Troubadour

+0

Merci Troubadour :-) – Jess

0

Ma première hypothèse serait de calculer l'angle de chaque vecteur avec les axes en utilisant atan (y/x), puis soustraire les anges et prendre la valeur absolue, à savoir:

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

14

Vous devez d'abord comprendre comment calculer angle entre deux vecteurs et il y en a plusieurs. Je vais vous donner ce que je pense être le plus simple.

  1. Étant donné v1 et v2, leur produit scalaire est: v1x * v2x + v1y * v2y
  2. La norme d'un vecteur v est donnée par: sqtr (vx^2 + vy^2)

Avec ces informations, s'il vous plaît prendre cette définition:

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

Maintenant, vous résolvez pour angle(v1, v2):

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

Enfin, en prenant les définitions données au début, puis vous vous retrouvez avec:

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

Encore une fois, il y a plusieurs façons de le faire, mais je comme celui-ci, car il est utile pour le produit scalaire donné angle et la norme, ou l'angle, les vecteurs donnés. La réponse sera en radians, mais vous savez que les pi radians (c'est-à-dire 3,14 radians) sont de 180 degrés, donc vous multipliez simplement par le facteur de conversion 180/pi.

+0

Merci de nous l'expliquer! J'essaie ton algorithme, mais quand l'angle est obtus, je dois le refléter et quand ce n'est pas le cas, je dois ajouter 45 degrés. Y a-t-il une raison quelconque pour cela? Je veux dire, je vais bien faire une simple déclaration sinon d'autre, mais j'aimerais savoir pourquoi je le fais :) – Niall

+0

Ne vous inquiétez pas, je me suis dit que je devais utiliser atan2 et refléter l'angle. Merci pour votre réponse et pour l'avoir expliqué! :) – Niall

+0

Ouais. Si vous pensez au produit scalaire de deux vecteurs, vous comprendrez pourquoi l'arctangente résout le problème du quadrant. En d'autres termes, il choisit le bon signe pour l'angle. Bonne chance. – Escualo

1

je crois que l'équation de l'angle entre deux vecteurs devrait ressembler davantage:

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

Votre équation ci-dessus calcule l'angle formé entre le vecteur p1-p2 et la ligne faite par étendant un orthogonal du point p2 au vecteur p1.

Le produit scalaire de deux vecteurs V1 et V2 est égal à | V1 | * | V2 | cos (thêta). Par conséquent, thêta est égal à acos ((V1 point V2)/(| V1 | | V2 |)). V1 point V2 est V1.x V2.x + V1.y V2.y. L'amplitude de V (c'est-à-dire | V |) est le théorème pathogoréen ... sqrt (V.x^2 + V.y^2)

5

Ne prenez pas la valeur absolue des arguments à atan2. Le point entier de atan2 est qu'il utilise les signes de ses arguments pour déterminer quel angle est l'angle. En prenant les valeurs absolues, vous forcez atan2 à retourner seulement des valeurs entre 0 et pi/2 au lieu de -pi à pi.

+1

Merci d'expliquer pourquoi je ne devrais pas prendre les valeurs absolues lors de l'utilisation atan2 :) S'avère à la fin je devais juste pour refléter l'angle (par exemple 180 + (- àDegrees (atan2 (x1-x, y1-y)))) – Niall

+0

@Niall: Heureux d'aider. :) Voir mon commentaire à votre réponse. En outre, puisque vous essayez réellement de calculer l'angle entre l'axe Y négatif et un seul vecteur, vous devriez peut-être éditer le titre de la question car il est très trompeur pour le moment. Quelque chose comme "Comment calculer l'angle d'un vecteur de la verticale?" et signalez dans la question que y augmente dans votre système. – Troubadour

+0

Bien sûr, je vais juste l'éditer maintenant. :) – Niall

2

On dirait que Niall a compris, mais je vais finir mon explication, de toute façon.En plus d'expliquer pourquoi la solution fonctionne, ma solution présente deux avantages:

  • division potentielle par zéro dans atan2() est évité
  • Valeur de retour est toujours positif dans la plage 0 à 360 degrés

atan2() renvoie l'angle anti-horaire par rapport à l'axe X positif. Niall cherchait l'angle dans le sens des aiguilles d'une montre par rapport à l'axe Y positif (entre le vecteur formé par les deux points et l'axe Y positve).

La fonction suivante est adaptée de mon astéroïdes jeu où je voulais calculer la direction un vecteur navire/vitesse était « pointage: »

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

Merci de m'avoir aidé à comprendre pourquoi cela fonctionne! :) Malheureusement, je travaille actuellement en Java (je suis principalement développeur C & Obj-C, mais il est toujours bon d'apprendre de nouvelles choses!) J'ai commencé avec Java il y a quelques années, donc ce n'est pas trop difficile :)) Je ne peux pas utiliser #define. Aussi, pensez-vous que je devrais utiliser double au lieu d'utiliser float? Comme toutes les fonctions mathématiques en Java utilisent le double, mais j'ai eu un problème avec le double vs float (probablement juste quelque chose d'autre dans mon code) et depuis lors j'ai toujours utilisé float. – Niall

+1

J'ai changé le code pour qu'il ressemble plus à Java: 'Math.toDegrees()' est en fait la même chose que la multiplication par la constante précédente DEG_PER_RAD. (Sinon, vous pouvez créer une constante Java avec 'static final double'). Float a moins de précision que le double, et va probablement subir une pénalité de performance puisque les conversions entre float et double seront nécessaires. Sauf si l'économie de mémoire est une grande préoccupation, j'utiliserais toujours le double. Aussi Troubadour a souligné atan2 n'a pas besoin d'être protégé contre la division par zéro. – Leftium

Questions connexes