计算 3D 中两条线(线段)之间的最短距离

Posted

技术标签:

【中文标题】计算 3D 中两条线(线段)之间的最短距离【英文标题】:Calculating the shortest distance between two lines (line segments) in 3D 【发布时间】:2010-10-12 06:29:39 【问题描述】:

我有两条线段:X1,Y1,Z1 - X2,Y2,Z2 和 X3,Y3,Z3 - X4,Y4,Z4

我正在尝试找到两个段之间的最短距离。

我一直在寻找解决方案几个小时,但所有这些似乎都适用于线条而不是线段。

有任何想法如何解决这个问题,或者任何来源的furmulae?

【问题讨论】:

【参考方案1】:

我会用matlab来回答这个问题,但也可以使用其他编程环境。我要补充一点,这个解决方案对于解决任意数量的维度(> = 3)的问题都是有效的。

假设我们在空间中有两条线段,PQ 和 RS。这里有几组随机点。

> P = randn(1,3)
P =
     -0.43256      -1.6656      0.12533

> Q = randn(1,3)
Q =
      0.28768      -1.1465       1.1909

> R = randn(1,3)
R =
       1.1892    -0.037633      0.32729

> S = randn(1,3)
S =
      0.17464     -0.18671      0.72579

无限线 PQ(t) 很容易定义为

PQ(u) = P + u*(Q-P)

同样,我们有

RS(v) = R + v*(S-R)

看到对于每一行,当参数为 0 或 1 时,我们会在返回的行上获得原始端点之一。因此,我们知道 PQ(0) == P,PQ(1) == Q,RS(0) == R,和 RS(1) == S。

这种参数化定义线的方式在很多情况下都非常有用。

接下来,假设我们沿着 PQ 线向下看。我们能找到从线段 RS 到无限长线 PQ 的最小距离的点吗?这最容易通过投影到 PQ 行的零空间中来完成。

> N = null(P-Q)
N =
     -0.37428     -0.76828
       0.9078     -0.18927
     -0.18927      0.61149

因此,null(P-Q) 是一对基向量,它们跨越与线 PQ 正交的二维子空间。

> r = (R-P)*N
r =
      0.83265      -1.4306

> s = (S-P)*N
s =
       1.0016     -0.37923

基本上我们所做的是将向量 RS 投影到与线 PQ 正交的二维子空间(平面)中。通过减去P(PQ线上的一个点)得到r和s,我们保证了无限长的线在这个投影平面上通过原点。

实际上,我们已将其简化为找到从线 rs(v) 到投影平面中原点 (0,0) 的最小距离。回想一下,rs(v) 行由参数 v 定义为:

rs(v) = r + v*(s-r)

线 rs(v) 的法线向量将为我们提供我们需要的东西。由于原始空间是 3-d,因此我们已将其减少为 2 维,因此我们可以简单地做到这一点。否则,我只会再次使用 null 。这个小技巧适用于二维:

> n = (s - r)*[0 -1;1 0];
> n = n/norm(n);

n 现在是一个单位长度的向量。无限线rs(v)到原点的距离很简单。

> d = dot(n,r)
d =
       1.0491

看到我也可以使用 s 来获得相同的距离。实际距离是 abs(d),但事实证明,d 在这里无论如何都是正数。

> d = dot(n,s)
d =
       1.0491

我们可以由此确定 v 吗?是的。回想一下,原点距离连接点 r 和 s 的直线的距离为 d 个单位。因此,对于标量 v 的某个值,我们可以写为 dn = r + v(sr)。用向量 (sr) 形成该方程每一边的点积,并求解 v .

> v = dot(s-r,d*n-r)/dot(s-r,s-r)
v =
       1.2024

这告诉我们线段 rs 最接近原点的方法发生在线段的端点之外。所以实际上 rs 上离原点最近的点是点 rs(1) = s。

从投影中退出,这告诉我们线段RS上离无限线PQ最近的点是点S。

在分析中还有一个步骤要执行。线段 PQ 上最近的点是什么?这个点是在线段内,还是也在端点外?

我们将点 S 投影到直线 PQ 上。 (对于 u 的这个表达式很容易从与我之前所做的类似的逻辑中推导出来。请注意,我已经使用 \ 来完成这项工作。)

> u = (Q-P)'\((S - (S*N)*N') - P)'
u =
      0.95903

看到 u 位于区间 [0,1] 中。我们已经解决了这个问题。 PQ线上的点是

> P + u*(Q-P)
ans =
      0.25817      -1.1677       1.1473

而且,两条线段上最近点之间的距离是

> norm(P + u*(Q-P) - S)
ans =
        1.071

当然,所有这些都可以压缩成几行代码。但它有助于扩展它以了解其工作原理。

【讨论】:

【参考方案2】:

一种基本方法与计算两条线之间的最短距离相同,但有一个例外。

如果您查看大多数用于查找 2 条线之间最短距离的算法,您会发现它会找到每条线上最近的点,然后计算它们之间的距离。

将此扩展到线段(或射线)的技巧是查看该点是否超出线的端点之一,如果是,则使用端点而不是无限线上的实际最近点.

具体示例见:

http://softsurfer.com/Archive/algorithm_0106/algorithm_0106.htm

更具体地说:

http://softsurfer.com/Archive/algorithm_0106/algorithm_0106.htm#dist3D_Segment_to_Segment()

【讨论】:

SoftSurfer 代码相当不错。几乎平行的线有问题。我最终为“几乎平行”的线写了一张支票。一旦我这样做了,它工作得很好。我不确定为什么对 SoftSurfer 中内置的“几乎平行”线的检查没有成功。只是觉得下一个用户想知道.... 我发现了和@TimPerry 一样的东西。我使用了这个并行检查: bool parallel = dot(u, v) / (u.length() * v.length()) > 1 - SMALL_NUM;为您的矢量库进行修改。 @ReedCopsey 我尝试了 dist3D_Segment_to_Segment() 与以下线段: LineSegment lineSegmentA; lineSegmentA.startPoint = PointType(0.,0.,0.); lineSegmentA.endPoint = PointType(1.,0.,0.);线段线段B; lineSegmentB.startPoint = PointType(.5,0.2,0.); lineSegmentB.endPoint = PointType(.5,1.2,0.);最近的距离应该是从 lineSegmentB 的端点到 lineSegmentA 的中心点,它的值应该是“0.2”。但是,该函数将距离返回为“0.538”。有什么想法吗? @DavidDoria 您几乎可以肯定地错误地转录了该函数。如果选择了 D < SMALL_NUM 路由,这就是您获得的值。它不应该被选择,而且确实没有被选择。您需要确保正确转录代码。 那些链接已经失效。您可以获取新链接吗?有新的可以链接的来源吗?【参考方案3】:

我会将两个线段参数化为每个使用一个参数,范围在 0 和 1 之间,包括 0 和 1。然后找出两个线函数之间的差异,并将其用作以参数为变量的线性优化问题中的目标函数。

假设您有一条从 (0,0,0) 到 (1,0,0) 的线和另一条从 (0,1,0) 到 (0,0,0) 的线(是的,我正在使用容易的)。这些线可以像 (1*t,0*t,0*t) 一样参数化,其中 t 位于 [0,1] 和 (0*s,1*s,0*s) ,其中 s 位于 [0,1 ],与 t 无关。

那么你需要最小化 ||(1*t,1*s,0)||其中 t, s 位于 [0,1] 中。这是一个很容易解决的问题。

【讨论】:

给定从 p1 到 p2 和从 q1 到 q2 的线段,您需要计算以下所有距离并取最小值:(line1, line2), (p1, line2), (p1, q1 ), (p1, q2), (p2, line2), (p2, q1), (p2, q2), (line1, q1), (line1, q2)。 (或者也许你可以用数学方法证明一些可以被消除。)【参考方案4】:

如何将线段延伸成无限的线并找到两条线之间的最短距离。然后找到每条线上作为最短距离线段端点的点。

如果每条线的点都在原始线段上,那么你就有答案了。如果每条线的一个点不在原始线段上,则该点是原始线段的端点之一。

【讨论】:

【参考方案5】:

根据找到两条无限线之间的距离来寻找两条有限线之间的距离,然后将无限线绑定到有限线并不总是有效。例如试试这个点

Q=[5 2 0]
P=[2 2 0]
S=[3 3.25 0]
R=[0 3 0]

基于无限逼近算法选择R和P进行距离计算(距离=2.2361),但是在R和S中间的某个地方距离P点的距离更近。显然,从 R 到 S 线选择 P 和 [2 3.166] 的距离较小,为 1.1666。通过精确计算并找到从 P 到 R S 线的正交线,即使这个答案也可以变得更好。

【讨论】:

我尝试了更多不同的点和方法,这是我到目前为止的发现。当您使用投影方法来查找两条有限线之间的距离时,您必须在任一侧执行投影。这意味着您必须先将 RS 投影到 PQ,然后反之亦然才能得到正确的答案。在这种方法中,计算了两种不同的距离,而最低的距离就是您要寻找的距离。【参考方案6】:

首先,找到在它们的延长线之间桥接的最接近线段。我们称之为 LineSeg BR。

如果 BR.endPt1 落在 LS1 上,而 BR.endPt2 落在 LS2 上,你就完成了……只需计算 BR 的长度。

如果网桥 BR 与 LS1 相交但不与 LS2 相交,则使用以下两个距离中较短的一个:smallerOf(dist(BR.endPt1, LS2.endPt1), dist(BR.endPt1, LS2.endPt2))

如果网桥 BR 与 LS2 相交但不与 LS1 相交,则使用以下两个距离中较短的一个:smallerOf(dist(BR.endPt2, LS1.endPt1), dist(BR.endPt2, LS1.endPt2))

如果这些条件都不成立,则最近的距离是相对线段上最近的一对端点。

【讨论】:

以上是关于计算 3D 中两条线(线段)之间的最短距离的主要内容,如果未能解决你的问题,请参考以下文章

两条线段之间的最短距离

创建滑块以动态显示图像中两条线的距离

已知两个线段的端点坐标如何用MATLAB求解他们之间的最短距离。要求的是线段啊,不是直线。

CAD多线,怎么调整两条线之间的距离呢

cad中怎么测量两条线间垂直距离

点到线段的最短距离