【问题标题】:2d trajectory planning of a spaceship with physics宇宙飞船的二维轨迹规划与物理
【发布时间】:2010-04-01 13:52:11
【问题描述】:

我正在实现一个带有太空飞船的 2D 游戏。

为了做到这一点,我使用了 LÖVE,它用 Lua 封装了 Box2D。但我相信任何比我更了解物理的人都可以回答我的问题——因此接受伪代码作为回答。

我的问题是我不知道如何在启用了 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 中的 cmets 中实施了它。

EDIT2:经过更多谷歌搜索后,我还找到了openSteer。它在 C++ 上,但它做了我假装的事情。这可能对任何遇到这个问题的人都有帮助。

【问题讨论】:

  • 信不信由你mathoverflow.net,可能值得在那里发帖。
  • 这不适用于 MathOverflow。该站点用于研究级数学。但我会在这里尝试一下。
  • 当您添加最终速度要求时,这是一个非常复杂的问题。如果最终速度是从目的地向起点,那么您将不得不越过目的地并向后推。
  • 推力有界吗?可以瞬间改变角度吗?
  • @Judge:根据OP写的和代码,是的,推力是一个预设常数,是的,角度可以瞬间改变。

标签: lua 2d physics box2d


【解决方案1】:

这叫做运动规划,它不是微不足道的。

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

  1. 停止。施加与速度方向相反的推力,直到速度为零。
  2. 计算最后一段,这将与第一段相反,从站立开始的稳定推力使船到达 x0 和 v0。起点将在距离 x0 的 |v0|^2/(2*thrust) 处。
  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

我们完成了。

【讨论】:

  • 对,完整的答案至少需要一个状态机,除非你是某种数学神。
  • 您好,感谢您的详细回答!问题实际上比这复杂 - 涉及船舶在有限时间内以扭矩“定位自身”,然后才能朝正确的方向推进。但这是我一直在寻找的第一个“咬”。现在我可以用谷歌搜索“运动规划”了:D。
  • @egarcia 只是为了好玩,我对 Beta 描述的方法做了一个 LOVE2D 演示。它工作得很好。在此处下载:(mediafire.com/file/m2tjdknwhwd/spacenav.love)。单击并拖动以设置新的目标位置/速度。小心以较大的最终速度将蓝点从屏幕上发送出去。我根本没有处理。 ;)
  • @JudgeMaygarden 这是旧的,链接已损坏,但我很想玩你的演示。你有机会转发吗?
  • @k_day 我看看能不能把它挖出来找个更永久的宿主。
【解决方案2】:

在没有其他信息的情况下,我们可以假设有 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”。

此类问题可以通过PID Controller 解决。简而言之,这样的控制器“决定”采取何种行动量(在本游戏的情况下 = 要应用的脉冲角度和推力量),基于三个加权的因素,松散定义如下:

  • 我们与“设定点”的当前值相差多远:这是 PID 的 P=比例部分
  • 我们以多快的速度接近“设定点”:这是 PID 的 D=Derivative 部分
  • 我们距离“设定点”有多长时间和多少:这是 PID 的 I=Intergral 部分

一个不太复杂的控制器可以例如只使用比例因子。这会导致振荡,有时在设定点的任一侧都有很大的幅度(“我离我应该在的位置有 X 个单位:让我猛拉方向盘并按下油门”)。设定点的这种超调会受到微分因素的影响(“是的,我仍然不在我应该在的地方,但自上次检查以来我取得的进步非常大:最好慢一点”) .最后,积分部分考虑到这样一个事实,即对于组合的比例和微分部分,所有事物都是平等的,根据我们是否已经“偏离轨道”很长时间,更小或更大的动作是合适的并且我们一直以来都偏离轨道(例如,“最近我们一直在追踪我们应该在的地方,没有必要轻率行事”)

我们可以讨论实现 PID 控制器以满足太空船游戏的特定需求的细节,如果这实际上是需要的话。这个想法是提供一种可以做的事情的味道。

【讨论】:

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

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

-- 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 模块,用于处理 this paste bin 中的二维向量。欢迎您使用它。上面的代码将简化为:

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

【讨论】:

  • “您实际上并不需要角度。”除了,大概在游戏中,为了让船将推力对准正确的方向矢量,他无论如何都需要将其转换为角度。 (大多数飞船不能向任何方向加速……只能沿着它们的主轴)。
  • 嗯,从方向向量获取角度是一个简单的操作(例如 acos(dx)、asin(dy)、atan2(dx,dy))。但是,在他的示例中并没有真正需要它,因为他立即使用三角函数将其转换为方向向量。
  • @Beska:实际上,大多数航天器都非常容易旋转,只需旋转内部飞轮即可。无需消耗反应质量,任意低的净能量消耗。
  • 最终速度实际上是“有趣”的部分。我已经弄清楚了你的算法。但是我有飞船试图以一半的音速降落在太空港上:D - 所以我真的需要先让它们减速。无论如何感谢您的回答。
  • 这是一个“有趣”的问题。祝你好运,我很想听听你的进展!
【解决方案4】:

你在排放燃料吗?如果你是,你的体重会随着时间而改变。

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

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

让我们假设一个魔法推力,没有物质被排出,也没有外力。

Impulse 有动量单位。它是一种力量随时间的积分。

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

假设您的“推力”是一种力,那么您将该推力乘以时间间隔(1/30 秒)以获得冲量,然后分解组件。

不知道我是否在回答你的问题,但希望这能帮助你稍微了解物理学。

【讨论】:

  • 您好!推力是我发明的。是冲量的大小。我认为代码上很清楚,也许我应该更多地解释一下。这里的“脉冲”是指“在给定时刻施加的力的离散装置”。你没有假设油耗是对的——它不会给游戏增加乐趣,也会使计算复杂化。感谢您的回答。
【解决方案5】:

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

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

沿着平行轴,它应该在任何方向上加速,使其接近目标速度。 (很明显,如果加速度将其 远离 目标点,您需要决定要做什么。飞过该点然后双回?)

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

(编辑:另外,我忘了提一下,这将需要一些调整来处理您在垂直方向偏移很多但在平行方向偏移不多的情况。这里的重要教训是获取组件,它会为您提供有用的数字,作为决策的依据。)

【讨论】:

  • 对不起,但你的回答对我没有多大帮助 - 我不明白为什么将速度分成“两个分量”会对我有所帮助。不过还是谢谢你的回答。
  • 我的回答的重点是你试图解决一个复杂的二维问题,而我建议解决两个更简单的一维问题。
【解决方案6】:

你的角度是相反/相邻的反正切

所以角度 = InvTan(VY/VX)

不知道你在说什么想要漂移??

【讨论】:

  • 嗯,谢谢,但我已经想通了。我在这里谈论的是另一个问题。
猜你喜欢
  • 2011-01-30
  • 1970-01-01
  • 1970-01-01
  • 2012-12-14
  • 2021-07-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多