如何在3D空间中找到移动目标的交点坐标?

时间:2013-06-20 02:47:22

标签: math 3d position coordinates prediction

假设我有一艘宇宙飞船(source);小行星(target)就在附近。

我知道,在3D空间(XYZ向量):

  • 我的船的位置sourcePos)和速度sourceVel)。
  • 小行星的位置targetPos)和力度targetVel)。

(例如sourcePos = [30,20,10]; sourceVel = [30,20,10]; targetPos = [600,400,200]; {{ 1}} = [300,200,100]`)

我也知道:

  • 船舶的速度 常量
  • 小行星的速度 常数
  • 我的船的射弹速度targetVel常数
  • 射击后我的船的射弹轨迹线性 / straight )。

(例如projSpd = 2000.00)

如何计算我需要射击的拦截坐标才能击中小行星?


注意:

此问题基于this Yahoo - Answers page

我也在谷歌和SO搜索过类似的问题,但大多数答案都是针对2D空间的,而对于3D的少数答案,解释和伪代码都没有解释什么在做什么和/或者为什么,所以我不能真正理解将它们成功应用于我的代码。以下是我访问过的一些页面:

Danik Games DevlogBlitz3D Forums threadUnityAnswersStackOverflow #1StackOverflow #2


我真的无法弄清楚链接页面上的数学/执行流程,除非有人将其(进一步)分解为正在做什么,以及为什么;
提供一个经过适当评论的伪代码供我遵循;
或者至少指出我实际解释方程如何工作的链接,而不仅仅是在我已经混淆的心灵中抛出更多的随机数和不可跟随的方程式。

3 个答案:

答案 0 :(得分:10)

我找到了解决这些问题的最简单方法,首先要理解它们,并且拥有基本的高中数学水平也会有所帮助。

解决这个问题实际上是解决了2个方程式,其中包含2个您不知道的变量:

  1. 您要为您的射弹找到的矢量(V
  2. 影响时间(t
  3. 您知道的变量是:

    1. 目标的位置(P0
    2. 目标的向量(V0
    3. 目标的速度(s0
    4. 射弹的起源(P1
    5. 射弹的速度(s1
    6. 好的,所以第一个等式是基本的。目标和射弹的撞击点是相同的。它等于两个物体的起始点+沿着它们两个矢量线的一定长度。该长度由它们各自的速度和冲击时间表示。这是等式:

      P0 + (t * s0 * V0) = P1 + (t * s0 * V)
      

      请注意,这里有两个缺失的变量 - V& t,所以我们现在无法解决这个问题。到了第二个等式。

      第二个等式也非常直观。撞击点与射弹原点的距离等于射弹的速度乘以经过的时间:

      我们将从第一个等式中得出影响点的数学表达式:

      P0 + (t * s0 * V0) <-- point of impact
      

      原点是P1 这两者之间的距离必须等于射弹的速度乘以经过的时间(distance = speed * time)

      距离公式为:(x0 - x1)^2 + (y0 - y1)^2 = distance^2,因此公式如下:

      ((P0.x + s0 * t * V0.x) - P1.x)^2 + ((P0.y + s0 * t * V0.y) - P1.y)^2 = (s1 * t)^2 
      

      (您可以轻松地将其扩展为3维)

      请注意,在这里,您只有一个未知变量的等式:t !.我们可以在这里发现t是什么,然后将其放在上一个等式中并找到向量V

      让我为你解决一些痛苦(如果你真的想,你可以自己做)。

      a = (V0.x * V0.x) + (V0.y * V0.y) - (s1 * s1)
      b = 2 * ((P0.x * V0.x) + (P0.y * V0.y) - (P1.x * V0.x) - (P1.y * V0.y))
      c = (P0.x * P0.x) + (P0.y * P0.y) + (P1.x * P1.x) + (P1.y * P1.y) - (2 * P1.x * P0.x) - (2 * P1.y * P0.y)
      
      t1 = (-b + sqrt((b * b) - (4 * a * c))) / (2 * a)
      t2 = (-b - sqrt((b * b) - (4 * a * c))) / (2 * a)
      

      现在,注意 - 我们将在此处获得t的2个值。

      一个或两个可能是否定数字或无效数字。显然,由于t表示时间,而时间不能无效或为负,因此您需要放弃t的这些值。

      很可能两个t都不好(在这种情况下,射弹无法击中目标,因为它更快且超出范围)。也可能是t两个都是有效和正面的,在这种情况下你会想要选择两者中较小的一个(因为它最好是更快地击中目标而不是更晚)。

      t = smallestWhichIsntNegativeOrNan(t1, t2)
      

      现在我们已经找到了影响的时间,让我们看看射弹的飞行方向是什么。回到我们的第一个等式:

      P0 + (t * s0 * V0) = P1 + (t * s0 * V)
      

      现在,t不再是缺失的变量,因此我们可以很容易地解决这个问题。只需整理等式来隔离V

      V = (P0 - P1 + (t * s0 * V0)) / (t * s1)
      V.x = (P0.x - P1.x + (t * s0 * V0.x)) / (t * s1) 
      V.y = (P0.y - P1.y + (t * s0 * V0.y)) / (t * s1) 
      

      就是这样,你已经完成了! 将矢量V分配给射弹,它将移动到目标所在的位置而不是现在的位置。

      我真的很喜欢这个问题,因为它需要我们在高中学到的数学方程,每个人都说“为什么学习这个?我们永远不会在生活中使用它!”,并给它们一个非常棒的实用应用

      我希望这可以帮助您或其他任何试图解决此问题的人。

答案 1 :(得分:2)

我有一个解决方案。请注意,船舶位置和小行星线(位置和速度)定义了拦截点所在的3D平面。在下面的注释中,| [x,y,z] |表示向量的大小或Sqrt(x^2+y^2+z^2)

请注意,如果小行星与targetSpd = |[300,200,100]| = 374.17一起移动,那么到达拦截点(仍然未知,称为hitPos)将需要等于t = |hitPos-targetPos|/targetSpd的时间。这是射弹需要到达拦截点的同一时间,或t = |hitPos - sourcePos|/projSpd。这两个方程用于解决拦截时间

t = |targetPos-sourcePos|/(projSpd - targetSpd)
  = |[600,400,200]-[30,20,10]|/(2000 - |[300,200,100]|)
  = 710.81 / ( 2000-374.17 ) = 0.4372

现在可以通过

找到intetception点的位置
hitPos = targetPos + targetVel * t
       = [600,400,200] + [300,200,100] * 0.4372
       = [731.18, 487.45, 243.73 ]

现在我知道命中位置,我可以计算射弹的方向

projDir = (hitPos-sourcePos)/|hitPos-sourcePos|
        = [701.17, 467.45, 233.73]/874.52 = [0.8018, 0.5345, 0.2673]

projDirprojSpd一起定义了射弹速度矢量。

答案 2 :(得分:1)

如果你想要一颗射弹击中小行星,它应该在符合等式的点interceptionPos上射击:

  

| interceptionPos - sourcePos | / | interceptionPos - targetPos | = projSpd / targetVel
  其中|x|是向量x的长度。

换句话说,目标和射弹需要相等的时间才能达到这一点 这个问题可以通过几何和三角学来解决,所以让我们绘制它。 enter image description here
A 将是小行星位置, S - 发货, - 拦截点。 我们在这里:

  

AI = targetVel * t
  SI = projSpd * t
  AS = | targetPos - sourcePos |

矢量AS和AI方向已定义,因此您可以通过简单的矢量数学(从herehere获取定义)轻松计算SAI角的余弦。然后,您应该使用SAI t角度的Law of cosines。它将产生一个易于求解的变量t的二次方程(没有解决方案=你的射弹比小行星慢)。只需选择正面解决方案{{1}},您的傻瓜相机就会

  

targetPos + t * targetVel

我希望你能写一个代码来自己解决它。如果你不能得到什么,请在评论中提问。