Math 使用atan2查找两个向量之间的角度

Math 使用atan2查找两个向量之间的角度,math,vector,geometry,Math,Vector,Geometry,我明白: 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

我明白:

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轴之间的角度, 这可能不是你的意思

从矢量1到矢量2的(定向)角度可计算为

angle = atan2(vector2.y, vector2.x) - atan2(vector1.y, vector1.x);
您可能希望将其规格化为范围[0,2π):

if(角度<0){angle+=2*M_-PI;}
或到范围(-π,π]:

if (angle > M_PI)        { angle -= 2 * M_PI; }
else if (angle <= -M_PI) { angle += 2 * M_PI; }
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
    }
}
公共结构向量2
{
公共双X,Y;
/// 
///返回两个向量之间的角度
/// 
公共静态双GetAngle(矢量2A,矢量2B)
{
//|A·B |=|A | B | COS(θ)
//|A×B |=|A | B | SIN(θ)
返回Math.Atan2(交叉(A,B),点(A,B));
}
公共双量级{get{return Math.Sqrt(Dot(this,this));}
公共静态双点(矢量2A,矢量2B)
{
返回A.X*B.X+A.Y*B.Y;
}
公共静态双交叉(矢量2 A、矢量2 B)
{
返回A.X*B.Y-A.Y*B.X;
}
}
班级计划
{
静态void Main(字符串[]参数)
{
Vector2A=新Vector2(){X=5.45,Y=1.12};
vector2b=newvector2(){X=-3.86,Y=4.32};
双角度=矢量2.GetAngle(A,B)*180/Math.PI;
//角度=120.16850967865749
}
}
参见GeoGebra中的上述测试用例


没有人指出,如果你有一个向量,并且想要找到向量相对于X轴的角度,你可以利用atan2()的参数实际上是直线的斜率,或者(δY/δX)。因此,如果你知道斜率,你可以做以下事情:

鉴于:

A=要确定的矢量/线的角度(从X轴)

m=向量/直线的有符号斜率

然后:

A=atan2(m,1)


非常有用!

我认为这里发布了一个更好的公式:

所以这个公式适用于2维或3维。
对于2维,此公式简化为上述公式。

如果您关心小角度的精度,请使用以下公式:

角度=2*atan2(| | | | | | | b | | a-| | a | | | b | | | | | | | b |)

其中“| |”表示绝对值,也称为“向量长度”。请参阅

但是,这样做的缺点是,在二维空间中,它会丢失角度符号。

你不必使用atan2来计算两个向量之间的角度。如果你只需要最快的方法,你可以使用
点(v1,v2)=| v1 |*| v2 |*cos A
得到


作为对@martin-r答案的补充,我们应该注意,可以使用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:当心向量变得非常相似时,您可能会在第一个参数中消失,从而导致数值不准确
xb,yb和xa,ya是两个向量的坐标

我发送的公式,
角度(vector.b,vector.a)
,给出结果

在四个象限中,对于任何坐标
xa,ya
xb,yb

对于坐标
xa=ya=0
xb=yb=0
未定义的

角度可以大于或小于pi,并且可以为正


或者是负数。

这里有一个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)
导入系统 将numpy作为np导入 将matplotlib.pyplot作为plt导入 将matplotlib.patches导入为修补程序 从shapely.geometry导入点,多边形 从pprint导入pprint #绘图变量 最小x_,最大x_=-6,12 y_最小值,y_最大值=-3,8 勾选间隔=1 图_尺寸=(10,10) DELTA_误差=0.00001 盒内颜色='黄色' 外盒颜色=‘黑色’ (v1,v2)之间的def角度_: “”“返回向量“v1”和“v2”之间的角度(以弧度为单位) 角度的符号取决于v1和v2的顺序 因此acos(norm(dot(v1,v2))不起作用,必须使用atan2,请参见: https://stackoverflow.com/questions/21483999/using-atan2-to-find-angle-between-two-vectors """ arg1=np.交叉(v1,v2) arg2=np.点(v1,v2) 角度=np.arctan2(arg1,arg2) 返回角 def点_内部(点、边框): “”“如果点位于边界多边形内,则返回True;否则返回False。” 论据: :点:x,y在shapely.geometry.point类型中 :边框:[x1 y1,x2 y2,…,xn yn]为shapely.geomettry.Polygon类型 """ 断言len(border.external.coords)>2\ '多边形中的点数必须大于2' 点=np.数组(点) side1=np.array(border.external.coords[0])-点 和角=0 对于border.exterior.coords[1:]: side2=np.数组(边界点)-点 角度=之间的角度_(侧1,侧2) 角度总和+=角度 侧1=侧2 #如果wn为1,则该点位于内部 wn=角度总和/2/np.pi 如果abs(wn-1)A = Math.acos( dot(v1, v2)/(v1.length()*v2.length()) );
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
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)))
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)