others - 数学 - 利用atan2求两个矢量的夹角

我知道:

atan2(vector.y, vector.x) =向量与X轴之间的角度。

但是我想知道如何使用atan2获得两个矢量之间的角度,我找到了这个解决方案:


atan2(vector1.y - vector2.y, vector1.x - vector2.x)



我的问题很简单:

下列两个公式是否产生相同的数字?

  • atan2(vector1.y - vector2.y, vector1.x - vector2.x)

  • atan2(vector2.y - vector1.y, vector2.x - vector1.x)

如果没有:我该如何知道哪个向量在衰减中首先出现?

谢谢

时间:


 atan2(vector1.y - vector2.y, vector1.x - vector2.x)



是矢量差(连接矢量2和矢量1)和x轴之间的角度,这可能不是你的意思。

从vector1到vector2的(定向)角度可以计算为


angle = atan2(vector2.y, vector2.x) - atan2(vector1.y, vector1.x);



你可能想把它归一化到范围 [0 ,2 π):


if (angle < 0) { angle += 2 * M_PI; }



或者到范围(-π,π] :


if (angle > M_PI) { angle -= 2 * M_PI; }


else if (angle <= -M_PI) { angle += 2 * M_PI; }



正确的方法是找到角度的正弦值,使用点积找到角度的余弦值,并将两者与Atan2()函数结合起来。

在C#中,


public struct Vector2


{


 public double X, Y;



 /// <summary>


 /// Returns the angle between two vectos


 /// </summary>


 public static double GetAngle(Vector2 A, Vector2 B)


 {


 // |A·B| = |A| |B| COS(θ)


 // |A×B| = |A| |B| SIN(θ)



 return Math.Atan2(Cross(A,B), Dot(A,B));


 }



 public double Magnitude { get { return Math.Sqrt(Dot(this,this)); } }



 public static double Dot(Vector2 A, Vector2 B)


 {


 return A.X*B.X+A.Y*B.Y;


 }


 public static double Cross(Vector2 A, Vector2 B)


 {


 return A.X*B.Y-A.Y*B.X;


 }


}


class Program


{


 static void Main(string[] args)


 {


 Vector2 A=new Vector2() { X=5.45, Y=1.12};


 Vector2 B=new Vector2() { X=-3.86, Y=4.32 };



 double angle=Vector2.GetAngle(A, B) * 180/Math.PI;


 // angle = 120.16850967865749


 }


}



请参阅GeoGebra中的测试用例。

GeoGebra

这里有一个更好的公式: http://www.mathworks.com/matlabcentral/answers/16243-angle-between-two-vectors-in-3d


angle = atan2(norm(cross(a,b)), dot(a,b))



所以这个公式在2或3维中有效,对于2维,该公式简化为上述公式。

如果你关心小角度的精度,你需要使用以下方法:

angle = 2*atan2(|| ||b||a - ||a||b || ,|| ||b||a ||a||b || )

其中"||"表示绝对值,即"向量的长度"

然而,这有一个缺点,在两维中,它丢失了角度的符号。

你不必使用atan2来计算两个矢量之间的角度。 如果你想要最快的方法,你可以用dot(v1, v2)=|v1|*|v2|*cos A来实现,


A = Math.acos( dot(v1, v2)/(v1.length()*v2.length()) );



可以对arcus tangens使用和差公式。


angle = atan2(vec2.y, vec2.x) - atan2(vec1.y, vec1.x);


angle = -atan2(vec1.x * vec2.y - vec1.y * vec2.x, dot(vec1, vec2))


 where dot = vec1.x * vec2.x + vec1.y * vec2.y



  • 警告1:确保角度保持在-pi. . . +pi
  • 警告2:当向量变得非常相似时,可能会在第一个参数中消失,导致数值错误,


angle(vector.b,vector.a)=pi/2*((1+sgn(xa))*(1-sgn(ya^2))-(1+sgn(xb))*(1-sgn(yb^2)))



+pi/4*((2+sgn(xa))*sgn(ya)-(2+sgn(xb))*sgn(yb))



+sgn(xa*ya)*atan((abs(xa)-abs(ya))/(abs(xa)+abs(ya)))



-sgn(xb*yb)*atan((abs(xb)-abs(yb))/(abs(xb)+abs(yb)))



xb,yb和xa,是两个向量的坐标

这里是Python中的一个小程序,它使用向量之间的角度来确定某个点是在某个多边形的内部还是外部,


import sys


import numpy as np


import matplotlib.pyplot as plt


import matplotlib.patches as patches


from shapely.geometry import Point, Polygon


from pprint import pprint



# Plot variables


x_min, x_max = -6, 12


y_min, y_max = -3, 8


tick_interval = 1


FIG_SIZE = (10, 10)


DELTA_ERROR = 0.00001


IN_BOX_COLOR = 'yellow'


OUT_BOX_COLOR = 'black'



def angle_between(v1, v2):


""" Returns the angle in radians between vectors 'v1' and 'v2'


 The sign of the angle is dependent on the order of v1 and v2


 so acos(norm(dot(v1, v2))) does not work and atan2 has to be used, see:


 https://stackoverflow.com/questions/21483999/using-atan2-to-find-angle-between-two-vectors


"""


 arg1 = np.cross(v1, v2)


 arg2 = np.dot(v1, v2)


 angle = np.arctan2(arg1, arg2)


 return angle



def point_inside(point, border):


""" Returns True if point is inside border polygon and False if not


 Arguments:


 :point: x, y in shapely.geometry.Point type


 :border: [x1 y1, x2 y2, ... , xn yn] in shapely.geomettry.Polygon type


""" 


 assert len(border.exterior.coords) > 2,


 'number of points in the polygon must be > 2'



 point = np.array(point)


 side1 = np.array(border.exterior.coords[0]) - point


 sum_angles = 0


 for border_point in border.exterior.coords[1:]:


 side2 = np.array(border_point) - point


 angle = angle_between(side1, side2)


 sum_angles += angle


 side1 = side2



 # if wn is 1 then the point is inside


 wn = sum_angles / 2 / np.pi


 if abs(wn - 1) < DELTA_ERROR:


 return True


 else:


 return False



class MainMap():



 @classmethod


 def settings(cls, fig_size):


 # set the plot outline, including axes going through the origin


 cls.fig, cls.ax = plt.subplots(figsize=fig_size)


 cls.ax.set_xlim(-x_min, x_max)


 cls.ax.set_ylim(-y_min, y_max)


 cls.ax.set_aspect(1)


 tick_range_x = np.arange(round(x_min + (10*(x_max - x_min) % tick_interval)/10, 1),


 x_max + 0.1, step=tick_interval)


 tick_range_y = np.arange(round(y_min + (10*(y_max - y_min) % tick_interval)/10, 1), 


 y_max + 0.1, step=tick_interval)


 cls.ax.set_xticks(tick_range_x)


 cls.ax.set_yticks(tick_range_y)


 cls.ax.tick_params(axis='both', which='major', labelsize=6)


 cls.ax.spines['left'].set_position('zero')


 cls.ax.spines['right'].set_color('none')


 cls.ax.spines['bottom'].set_position('zero')


 cls.ax.spines['top'].set_color('none')



 @classmethod


 def get_ax(cls):


 return cls.ax



 @staticmethod


 def plot():


 plt.tight_layout()


 plt.show()



class PlotPointandRectangle(MainMap):



 def __init__(self, start_point, rectangle_polygon, tolerance=0):



 self.current_object = None


 self.currently_dragging = False


 self.fig.canvas.mpl_connect('key_press_event', self.on_key)


 self.plot_types = ['o', 'o-']


 self.plot_type = 1


 self.rectangle = rectangle_polygon



 # define a point that can be moved around


 self.point = patches.Circle((start_point.x, start_point.y), 0.10,


 alpha=1)


 if point_inside(start_point, self.rectangle):


 _color = IN_BOX_COLOR


 else:


 _color = OUT_BOX_COLOR


 self.point.set_color(_color)


 self.ax.add_patch(self.point)


 self.point.set_picker(tolerance)


 cv_point = self.point.figure.canvas


 cv_point.mpl_connect('button_release_event', self.on_release)


 cv_point.mpl_connect('pick_event', self.on_pick)


 cv_point.mpl_connect('motion_notify_event', self.on_motion)



 self.plot_rectangle()



 def plot_rectangle(self):


 x = [point[0] for point in self.rectangle.exterior.coords]


 y = [point[1] for point in self.rectangle.exterior.coords]


 # y = self.rectangle.y


 self.rectangle_plot, = self.ax.plot(x, y,


 self.plot_types[self.plot_type], color='r', lw=0.4, markersize=2)



 def on_release(self, event):


 self.current_object = None


 self.currently_dragging = False



 def on_pick(self, event):


 self.currently_dragging = True


 self.current_object = event.artist



 def on_motion(self, event):


 if not self.currently_dragging:


 return


 if self.current_object == None:


 return



 point = Point(event.xdata, event.ydata)


 self.current_object.center = point.x, point.y


 if point_inside(point, self.rectangle):


 _color = IN_BOX_COLOR


 else:


 _color = OUT_BOX_COLOR


 self.current_object.set_color(_color)



 self.point.figure.canvas.draw()



 def remove_rectangle_from_plot(self):


 try:


 self.rectangle_plot.remove()


 except ValueError:


 pass



 def on_key(self, event):


 # with 'space' toggle between just points or points connected with


 # lines


 if event.key == ' ':


 self.plot_type = (self.plot_type + 1) % 2


 self.remove_rectangle_from_plot()


 self.plot_rectangle()


 self.point.figure.canvas.draw()



def main(start_point, rectangle):



 MainMap.settings(FIG_SIZE)


 plt_me = PlotPointandRectangle(start_point, rectangle) #pylint: disable=unused-variable


 MainMap.plot()



if __name__ =="__main__":


 try:


 start_point = Point([float(val) for val in sys.argv[1].split()])


 except IndexError:


 start_point= Point(0, 0)



 border_points = [(-2, -2),


 (1, 1),


 (3, -1),


 (3, 3.5),


 (4, 1),


 (5, 1),


 (4, 3.5),


 (5, 6),


 (3, 4),


 (3, 5),


 (-0.5, 1),


 (-3, 1),


 (-1, -0.5),


 ] 



 border_points_polygon = Polygon(border_points)


 main(start_point, border_points_polygon)



...