在Python中计算两个向量之间的夹角

10
我正在尝试在Python中计算两条直线之间的夹角。我在互联网上搜索并找到了如何计算的方程式。但是,我并不总是得到准确的结果。有些结果明显是错误的,而其他结果似乎是正确的。
我的代码如下:
def angle(pt1, pt2):
    m1 = (pt1.getY() - pt1.getY())/1
    m2 = (pt2.getY() - pt1.getY())/(pt2.getX()-pt1.getX())

    tnAngle = (m1-m2) / (1 + (m1*m2))
    return math.atan(tnAngle)

def calculate(pt, ls):
    i = 2
    for x in ls:
        pt2 = point(x, i)
        i = i + 1
        ang = angle(pt, pt2)*180/math.pi
        ang = ang * (-1)
        print ang


pt = point(3, 1)
ls = [1, 7, 0, 4, 9, 6, 150]

calculate(pt, ls)

它产生的结果是:
45.0
0.0
45.0
-75.9637565321
0.0
-63.4349488229
0.0

问题在于我不明白为什么第二个结果、第五个结果和最后一个结果都是零。它们相交,因为它们共享一个点,而另一个点没有重复,因为数组中的值是不同的。

第二行非常可疑。这真的是你想做的吗? - Vincent Savard
3个回答

20

如果您的角度公式不起作用,那么

pt2.getX() == pt1.getX()

(也就是说,如果pt1和pt2在一条垂直于水平方向的直线上),因为您无法除以零。(m2,即斜率,会无限大。)

此外

m1 = (pt1.getY() - pt1.getY())/1

将始终为零。因此,至少可以简化您的公式为斜率的arctan。但是,我不会费心去做,因为该公式不适用于所有可能的点。

相反,计算两个向量(定向线段)之间的夹角的更强大的方法(确实是标准方法)是使用点积公式

enter image description here

其中如果 a=(x1, y1)b=(x2, y2),那么 <a,b> 等于 x1*x2 + y1*y2,且 ||a|| 是向量 a 的长度,即 sqrt(x1**2 + y1**2)


import math

def angle(vector1, vector2):
    x1, y1 = vector1
    x2, y2 = vector2
    inner_product = x1*x2 + y1*y2
    len1 = math.hypot(x1, y1)
    len2 = math.hypot(x2, y2)
    return math.acos(inner_product/(len1*len2))

def calculate(pt, ls):
    i = 2
    for x in ls:
        pt2 = (x, i)
        i += 1
        ang = math.degrees(angle(pt, pt2))
        ang = ang * (-1)
        print(ang)

pt = (3, 1)
ls = [1,7,0,4,9,6,150]

calculate(pt, ls)

math.hypot(x1, y1) 更加简洁。 - John La Rooy
2
应该引发异常,因为当任一向量长度为零时,没有定义的角度。 - unutbu
我可能错了,但我认为angle()返回结果的符号可能存在问题。如果第一个点是(1.0,0.0),第二个点是(1.0,-1.0),那么角度的符号不应该是负数(或315度)吗?我认为只需将最后一行替换为return math.copysign(math.acos(inner_product/(len1*len2)), y2)即可解决这个问题。 - Bill
@Bill:上面的函数计算两个向量之间的夹角。根据定义,该角度始终为0到π弧度之间的较小角度。它具有这样的特性,即在旋转下两个向量之间的角度不会改变。例如,如果我们将两个向量旋转180度,“angle((1,0), (1,-1))”仍然等于“angle((-1,0), (-1,1))”。如果我们将其更改为您的公式,则角度将更改符号。此外,“angle(A,B)==angle(B,A)” 。您的公式不是可交换的。没关系——这只是听起来像您正在寻找与两个向量之间的角度不同的东西。 - unutbu

14

这是我最终使用的代码,全部采用numpy编写,范围介于-到

import numpy as np
def get_angle(p0, p1=np.array([0,0]), p2=None):
    ''' compute angle (in degrees) for p0p1p2 corner
    Inputs:
        p0,p1,p2 - points in the form of [x,y]
    '''
    if p2 is None:
        p2 = p1 + np.array([1, 0])
    v0 = np.array(p0) - np.array(p1)
    v1 = np.array(p2) - np.array(p1)

    angle = np.math.atan2(np.linalg.det([v0,v1]),np.dot(v0,v1))
    return np.degrees(angle)

7

看起来你正在使用Python2,如果两个参数都是int类型,则/将执行整数除法。要获得与Python3相同的行为,你可以将以下代码放在文件顶部。

from __future__ import division

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接