我有 2 个点 (x1,y1) & (x2,y2) 和一个圆,假设这 2 个点之间有一条线,我需要检查是否与圆发生碰撞

I have 2 points (x1,y1) & (x2,y2) and a circle, suppose there is a line between the 2 points I need to check if there is collision with the circle

正如我在标题中提到的,假设我有一条从点 1 到点 2 的线段,并且有一个圆心和半径我需要检查是否会与圆发生碰撞使用代码。这就是我的进展。 但是,closestX 和 closestY 存在问题,因为我需要检查它们是否在从点 1 到点 2 的线段上,因为如果它们不在线段上,则不会发生碰撞。可悲的是,虽然我被困在这里,但我无法想出一种方法来检查它们是否在线段上。请帮忙谢谢。

import math
p=2
obsHeight=200
DroneHeight=150
cx=3
cy=3
r=1
x1=1
y1=1
x2=1.5
y2=1.5


if DroneHeight<=obsHeight:
    distX= x1 - x2
    distY= y1 - y2
    length=math.sqrt((distX*distX) + (distY*distY ))
    dot= (((cx-x1)*(x2-x1)) + ((cy-y1)*(y2-y1)) )/(math.pow(length,p))
    closestX=x1+( dot * (x2-x1))
    closestY=y1+( dot * (y2-y1))
    print(" Closest x: ",closestX)
    print(" Closest y: ",closestY)
    distX=closestX-cx
    distY= closestY-cy
    distance= math.sqrt((distX*distX) + (distY*distY ))
    print("The distance is: ", distance)
    print("The length is: ", length)
    if (r==distance):
        print("Touching")
    elif (distance<r):
        print("COLLIDING")
    else:
        print("Will not collide")
else:
    print(" Will not collide, the drone is higher than the obstacle")


可以通过

计算圆心到直线的平方距离
d2 = ((y1-y2)*(cx-x1)+(x2-x1)*(cy-y1))**2/((x2-x1)**2+(y2-y1)**2)

现在只需将该值与平方半径进行比较。如果 d2

编辑: 我认为您已经非常接近正确的解决方案了。在计算点积的行中,您应该除以线段的长度,而不是除以其平方长度。只需在 math.pow 表达式周围放置 (...)**0.5 即可给出正确的值。

注意: Phython 有一个名为 ** 的内置幂运算符,因此无需使用 math.pow

忽略代码的特殊性,假设您有一条线段、一个中心和一个半径。下面写一个N维的线段是否与N维的超球面相交的通解。这将为我们在 2D 的特殊情况下为您的问题提供正确的解决方案。

您的函数签名如下所示:

def intersects(p1, p2, c, r):

p1p2 是长度为 N 的向量。在您的例子中,p1 = np.array([1, 1])p2 = np.array([1.5, 1.5])c是等长向量(c = np.array([3, 3])),r是标量半径(r = 1)。我强烈建议您使用 numpy 数组进行数学运算,因为如果您正确使用它会更快,并且您可以在不使用循环的情况下对数组应用逐元素操作(例如 p2 - p1)。

穿过p1p2的线可以参数化为p = p1 + t * (p2 - p1)。直线 p 上的每个点对应参数 t 的某个值。具体来说,t == 0对应p = p1t == 1对应p = p2。这意味着你可以通过检查一个点的参数是否在 [0, 1].

范围内来知道它是否在线段上

然后问题就变成了找到 t 的值,使得 p 最接近 c。如果 t < 0t > 1,则您知道线段的极值位于端点。否则,您需要比较两个端点的距离和您找到的 p

有几种不同的方法可以提出解决方案。几何方法利用了这样一个事实,即最近的方法发生在从 c 到直线的垂线上。微分方法找到长度的导数为零的位置。我会在这里展示前者。

查看图表,您有以下等式:

(c - p).dot(p2 - p1) == 0
(c - p1 - t * (p2 - p1)).dot(p2 - p1) == 0
(c - p1).dot(p2 - p1) - t * (p2 - p1).dot(p2 - p1) == 0
t == (c - p1).dot(p2 - p1) / (p2 - p1).dot(p2 - p1)

您现在可以像这样编写您的函数:

def intersects(p1, p2, c, r):
    c1 = np.subtract(c, p1)
    c2 = np.subtract(c, p2)

    dist1 = np.linalg.norm(c1)
    dist2 = np.linalg.norm(c2)

    # If point are on opposite sides of circle, intersects
    if (r - dist1) * (r - dist2) < 0:
        return True

    # If both on inside, does not intersect
    if r > dist1:
        return False

    dp = np.subtract(p2, p1)
    t = dp.dot(c1) / dp.dot(dp)

    # If closest approach is outside segment, does not intersect
    # convince yourself of this (use symmetry about the line c-p)
    if t < 0 or t > 1:
        return False

    cp = np.subtract(p1 + t * dp, c)
    distp = np.linalg.norm(cp)
    # only other possibility of approach is when closest point is inside radius
    return distp <= r

在 Stack Overflow 上和我的应用程序中多次出现查找点和线之间距离的问题,因此我最近将它添加到我维护的实用程序库中,haggis. You can build a solution using haggis.math.segment_distance 逻辑非常相似。为此,我特地让函数以线段或全线模式运行:

def intersects(p1, p2, c, r):
    dist1 = np.linalg.norm(c1 := np.subtract(c, p1))
    dist2 = np.linalg.norm(c2 := np.subtract(c, p2))
    if (r - dist1) * (r - dist2) < 0: # Opposite sides of circle
        return True
    if r > dist1:                     # Both inside circle
        return False
    d = segment_distance(c, p1, p2)
    return d < r

您可以将最后两行改写如下:

    d, t = segment_distance(c, p1, p2, segment=False, return_t=True)
    return d < r and 0 <= t <= 1