计算 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 中两条线(线段)之间的最短距离的主要内容,如果未能解决你的问题,请参考以下文章