问题 用物理学研究飞船的2d轨迹规划


我正在太空船上实施2D游戏。

为了做到这一点,我正在使用LÖVE,它将Box2D与Lua包装在一起。但我相信任何对物理学有更多了解的人都可以回答我的问题 - 所以伪代码被接受作为回应。

我的问题是我不知道如何在支持2D物理的世界中正确地移动我的宇宙飞船。更具体地说:

一艘大众船 m 位于初始位置 {x, y}。它的初始速度矢量为 {vx, vy} (可 {0,0})。

目标是一个点 {xo,yo}。船必须达到速度为的目标 {vxo, vyo} (或靠近它),遵循最短的轨迹。

有一个叫做的功能 update(dt) 经常调用(即每秒30次)。在此功能上,船舶可以通过对自身施加“冲动”来修改其位置和轨迹。脉冲的大小是二进制的:你可以在给定的方向上应用它,或者根本不应用它。在代码中,它看起来像这样:

function Ship:update(dt)
  m = self:getMass()
  x,y = self:getPosition()
  vx,vy = self:getLinearVelocity()
  xo,yo = self:getTargetPosition()
  vxo,vyo = self:getTargetVelocity()
  thrust = self:getThrust()

  if(???)
    angle = ???
    self:applyImpulse(math.sin(angle)*thrust, math.cos(angle)*thrust))
  end
end

首先 ??? 是否表明在某些情况下(我猜)最好“不要冲动”并让船“漂移”。第二 ??? 部分包括如何计算给定dt上的脉冲角。

我们在太空中,所以我们可以忽略空气摩擦之类的东西。

虽然它会非常好,但我不是在寻找有人为我编码;我把代码放在那里,所以我的问题清楚地被理解了。

我需要的是一种策略 - 一种攻击方式。我知道一些基本的物理,但我不是专家。例如,这个问题有名字吗?之类的东西。

非常感谢。

编辑:Beta为此提供了有效的策略,Judge在评论中直接在LÖVE中实施了它。

编辑2:经过更多谷歌搜索后我也发现了 openSteer。它是在C ++上,但它做了我假装的。对于达到这个问题的人来说,这可能会有所帮助。


4687
2018-04-01 13:52


起源

信不信由你有 mathoverflow.net,可能值得在那里张贴。 - T.J. Crowder
这不适用于MathOverflow。该网站用于研究级数学。但我会在这里采取行动。 - John
添加最终速度要求时,这是一个非常复杂的问题。如果最终速度是从目的地到起点,那么你必须超过目的地并向后推。 - Judge Maygarden
是否有限制?角度可以瞬间改变吗? - Judge Maygarden
@Judge:根据OP写和代码,是的,推力是预设常数,是的,角度可以瞬间改变。 - Chris Burt-Brown


答案:


它被称为运动规划,并不是微不足道的。

这是获得非最佳轨迹的简单方法:

  1. 停止。施加与速度方向相反的推力,直到速度为零。
  2. 计算最后一条腿,它将与第一条腿相反,从站立起点到达x0和v0的稳定推力。起点将是距离x0 | v0 | ^ 2 /(2 *推力)的距离。
  3. 到达那个起点(然后做最后一站)。从一个站点到另一个站点很容易:向前推进直到你到达那里,然后向后推,直到你停下来。

如果您想要快速而肮脏的方法来获得最佳轨迹,您可以使用迭代方法:从上面的非最优方法开始;这只是推力角的时间序列。现在尝试对该序列进行少量修改,保持一组接近目标的序列。拒绝最坏的情况,尝试最好的 - 如果你感觉大胆,你可以把它变成遗传算法 - 幸运的是它会开始绕过角落。

如果您想要确切的答案,请使用变量计算。我会解决这个问题,如果我成功了,我会在这里发布答案。

编辑: 这是一个简单问题的确切解决方案。

假设我们有四个固定的推进器指向{+ X,+ Y,-X,-Y}方向,而不是我们可以指向任何方向的推力。在任何给定的时间,我们将最多发射一个+/- X和最多一个+/- Y(同时发射+ x和-X没有任何意义)。所以现在X和Y问题是独立的(它们不是原始问题,因为必须在X和Y之间共享推力)。我们现在必须解决一维问题 - 并应用两次。

事实证明,最好的轨迹包括向一个方向推进,然后向另一个方向推进,而不是再次回到第一个方向。 (只有当另一个轴的解决方案花费的时间比你的长,所以你有时间杀死时,滑行是有用的。)首先解决速度问题:假设(WLOG)你的目标速度大于你的初始速度。要达到目标速度,您需要一段持续时间(+)

T = (Vf - Vi)/a

(我使用的是Vf:最终速度,Vi:初始速度,a:推力的大小。)

我们注意到,如果这就是我们所做的一切,那么这个位置就不会正确。实际的最终位置将是

X = (Vi + Vf)T/2

所以我们必须添加更正

D = Xf - X = Xf -(Vi+Vf)T/2

现在为了使位置正确,我们在一个方向上添加一段推力 之前 那个,相反的方向相同的时期 。这将使最终速度不受干扰,但给我们一些位移。如果第一个周期(和第三个周期)的持续时间是t,那么我们得到的位移是

d = +/-(at^2 + atT)

+/-取决于我们是否推动+然后 - 或 - 然后+。假设它是+。 我们解决二次方:

t = (-aT + sqrt(a^2 T^2 + 4 a D))/2a

我们已经完成了。


8
2018-04-01 14:26



是的,完整的答案至少需要一台状态机,除非你是某种数学神。 - Judge Maygarden
嗨,谢谢你的慷慨解答!问题实际上是 更多 比这更复杂 - 在能够向正确的方向推进之前,船只在有限的时间内“使自己定向”。但这是我想要的第一个'咬'。现在我可以谷歌进行“运动规划”:D。 - kikito
@egarcia为了好玩,我做了一个Beta描述方法的LOVE2D演示。它工作得很好。在这里下载:(mediafire.com/file/m2tjdknwhwd/spacenav.love)。单击并拖动以设置新的目标位置/速度。小心地将蓝点方式从屏幕上以较大的最终速度发送出去。我完全没有处理。 ;) - Judge Maygarden
法官,你只是吹了我的脑海。谢谢你提出这个问题! - kikito
@JudgeMaygarden这是旧的,链接坏了,但我喜欢玩你的演示。你有没有机会重新发布? - k_day


在没有额外信息的情况下,我们可以假设有3种力量作用于宇宙飞船并最终决定其轨迹:

  • 冲动“:[用户/程序控制]强制。
    用户(或程序)似乎可以完全控制它,即它控制脉冲的方向及其推力(可能在0到最大范围内)
  • 一些外力称之为引力,不管......
    这种力量可以由几个来源驱动,但我们只对所产生的合力产生兴趣:在给定的时间和空间,这种外力以给定的强度和方向作用在船上。用户/程序无法控制这些。
  • 惯性:这与船舶的当前速度和方向有关。该力通常使船舶以其当前速度继续​​沿其当前方向行驶。可能存在控制惯性的其他[太空时代]参数,但通常,它与速度和船体质量成比例(直观地,如果当前速度较小和/或船舶将更容易停泊如果质量较小)

显然,用户/程序仅控制(在限制范围内)第一个力。
从问题来看,尚不清楚手头的问题是:

  • [问题A]编写一个程序,发现系统的动态(和/或适应改变这些动态)。
    要么..
  • [问题B]建议一个模型 - 一个公式 - 可以用来计算最终应用于船舶的合力:用户控制的脉冲和其他两个系统/物理驱动力的“加权”和。

后一个问题,问题B,更容易和简洁地解释,所以让我们建议以下模型:

Constant Parameters:
  ExternalForceX   = strength of the external force in the X direction
  ExternalForceY   = id. Y direction
  MassOfShip       = coeficient controlling 
Variable Parameters:
  ImpulseAngle     = direction of impulse
  ImpulseThrust    = force of thrust
Formula:
  Vx[new] = (cos(ImpulseAngle) * ImpulseThrust) + ExternalForceX  + (MassOfShip * Vx[current])
  Vy[new] = (sin(ImpulseAngle) * ImpulseThrust) + ExternalForceY  + (MassOfShip * Vy[current])

注意,上述模型假设一个恒定的外力(在强度和方向上都是常数);也就是说:类似于相对远离所显示区域的引力场(就像说地球引力,在足球场的范围内考虑)。如果显示区域的比例相对于外力源较大,则应修改上述公式的中间项,以包括:基于源中心与电流之间角度的三角因子基于源中心与当前位置之间的距离的位置和/或[反向]比例因子。
类似地,假设船的质量保持不变,它可能是一个变量,基于空的船的质量,当游戏进行时燃料的重量被移除/添加。

现在...以上所有假设系统的动态由游戏设计师控制:基本上为所提到的参数选择一组值,并可能在公式的数学中增加一点复杂性(并确保适当的缩放)通常将船“保持”在显示区域内)。

相反,如果相反,系统动力学很容易被编程到游戏中(并假设是隐藏/随机),而手头的任务是编写一个程序,该程序将逐步决定冲动的方向和推力值以驱动船到它的目标目的地,其目标的速度尽可能接近getTargetVelocity()?这就是“问题A”。

这种类型的问题可以用a来解决 PID控制器。简而言之,这样一个控制器“决定”哪个动作量(在这个游戏的情况下=哪个脉冲角和施加的推力量),基于三个,称重的因子,松散地定义如下:

  • 我们距离“设定点”的当前值有多远:这是PID的P =比例部分
  • 我们接近“设定点”的速度有多快:这是PID的D =微分部分
  • 我们离开“设定点”多长时间和多少:这是PID的I =整数部分

例如,不太复杂的控制器可以仅使用比例因子。这会导致振荡,有时在设定点的两侧都有很大的振幅(“我距离我应该的位置是X单位:让我拉动方向盘并按下气体”)。这种设定点的超调受到衍生因素的影响(“是的,我仍然不是我应该在的地方,但是自从我上一次检查以来我取得的进步非常大:更好地放慢一点”) 。最后,积分部分考虑到所有事物在比例和导数部分方面相同的事实,根据我们是否已经长时间“偏离轨道”而不是更小或更大的行动是合适的。并且我们一直都在偏离轨道(例如,“最近我们一直在追踪到我们应该去的地方,没有必要做出轻率的动作”)

我们可以讨论实现PID控制器的细节,以满足太空船游戏的特定需求,如果这是有效的需要。这个想法是为了提供可以做的事情。


3
2018-04-01 16:15



感谢您的广泛回复。你提到“振荡”很有趣 - 我已经在我的测试中看到过这种情况。我肯定会看你提到的那些PID。不幸的是,我只能将一个答案标记为有效,Beta的响应似乎更准确一些(“运动规划”正是我所寻找的术语)。所以我会给你+1。谢谢你们! - kikito
@egarcia:没有必要为接受一个答案道歉;-)“运动规划”算法将有效地帮助你, 提供 系统的动态是恒定的(或最不确定的)。即使系统的动态是随机的(或者如果它们是确定性的但不易于处理的话),PID控制器也会有所帮助。无论是使用MP还是PID,您尝试解决的问题都很困难,因为在通过目标点时需要特定的速度和方向:不能使用起点和目标之间的线作为理想轨迹...... - mjv


要以初始速度从当前位置到达目的地,然后沿最短路径和当前速度之间的归一化差值施加推力。你实际上并不需要角度。

-- shortest path minus initial velocity
dx,dy = x0 - x - vx, y0 - y - vy

-- normalize the direction vector
magnitude = sqrt(dx*dx + dy*dy)
dx,dy = dx/magnitude, dy/mangitude

-- apply the thrust in the direction we just calculated
self:applyImpulse(thrust*dx, thrust*dy)

请注意,这不会考虑目标速度,因为这非常复杂。

我有一个非常小的Lua模块用于处理2D矢量 这个粘贴箱。欢迎您使用它。上面的代码将减少为:

d = destination - position - velocity
d:normalize()
d = d * thrust
self:applyImpulse(d.x, d.y)

1
2018-04-01 14:24



“你实际上并不需要角度。”除了,大概是在游戏中,为了让船将目标推向正确的方向向量,他无论如何都需要将其转化为一个角度。 (大多数船只不能沿任何方向加速......只能沿着它们的主轴)。 - Beska
那么,从方向矢量获得角度是一个微不足道的操作(例如acos(dx),asin(dy),atan2(dx,dy))。然而,在他的例子中没有真正需要它,因为他立即使用trig函数将其转换为方向向量。 - Judge Maygarden
@Beska:实际上,通过旋转内部飞轮,大多数航天器都可以轻松旋转。无需消耗反应质量和任意低的净能量消耗。 - Beta
最终的速度实际上是“有趣的”部分。我已经找到了你的算法。但是我的船只试图以一半的声速降落在太空港上:D - 所以我真的需要先减速它们。无论如何,谢谢你的回答。 - kikito
这是一个“有趣”的问题。祝你好运,我很想知道你的进步! - Judge Maygarden


你在榨油吗?如果你是的话,你的质量会随着时间而改变。

推力是一种反作用力。它是质量的变化率,乘以相对于宇宙飞船的排气速度。

你有外力吗?如果这样做,则需要输入您的冲动计算。

让我们假设一个神奇的推力,没有质量被驱逐,没有外力。

冲动有动量单位。随着时间的推移,它是力量的积分。

首先,你需要弄清楚API究竟叫什么“推力”和冲动。如果你用一个标量(数字)加上一个推力,那么applyImpulse必须对你的输入做一些其他事情,以便能够将它用作冲动,因为单位不匹配。

假设你的“推力”是一个力,那么你将该推力乘以时间间隔(1/30秒)来获得冲动,并打破组件。

不知道我是否在回答你的问题,但希望这有助于你理解物理学。


0
2018-04-01 14:40



你好!推力是我发明的东西。是冲动的大小。我觉得代码很清楚,也许我应该解释一下。这里的“冲动”意味着“在给定时刻施加的分立器具”。而且你没有假设油耗是正确的 - 它不会增加游戏的乐趣并且会使微积分复杂化。谢谢回答。 - kikito


如果将船的速度分成组件,则更容易考虑, 平行 和 垂直 到目标速度矢量。

沿着垂直轴考虑,船舶希望尽快与目标位置一致,然后停留在那里。

沿着平行轴,它应该在任何方向上加速,使其接近目标速度。 (显然,如果加速采取它  从目标点来看,你需要决定做什么。飞过点然后再回来?)

我会分别处理它们中的两个,并且可能首先垂直。一旦它工作,如果这不够好,你可以开始考虑是否有办法让船在垂直和平行之间发射智能角度。

(编辑:另外,我忘了提一下,这将需要一些调整来处理你在垂直方向上偏移很多但在平行方向上偏差很大的情况。这里重要的一课是采取组件,这给出了你有用的数字作为决定的依据。)


0
2018-04-01 14:53



对不起,但你的答案对我没有多大帮助 - 我不明白为什么将速度分成“两个组成部分”会对我有所帮助。但无论如何,谢谢你的回答。 - kikito
关于我的答案的一点是你试图解决一个复杂的二维问题,而我建议解决两个更简单的一维问题。 - Chris Burt-Brown


您的角度是相反/相邻的反向切线

所以angle = InvTan(VY / VX)

不确定你在谈论想要漂移?


-1
2018-04-01 14:06



嗯,谢谢你,但我已经想到了那一个。我在这里谈论的是另一个问题。 - kikito