我想在游戏中实现物理引擎,以便计算施加力的物体的轨迹。 该引擎将根据其先前的状态计算对象的每个状态。当然,这意味着在两个时间单位之间进行大量计算才能足够精确。
为了做到这一点,我首先想知道这种获取位置的方法与运动方程之间的差异有多大。 所以我制作了这个代码,用于存储模拟和方程式给出的位置(x,y,z)。
def quantize(x):
# Your vector quantization code
model.add(Lambda(quantize))
问题在于,对于简单的数字(比如在-9.81引力场中的简单下降),我得到了不错的位置,但是如果数字更大(且非常随机),我会得到不准确的位置。
这是浮点问题吗?
以下是结果,但有相对错误。 (注意:标签轴为法语,Temps = Time)。
图表
答案 0 :(得分:6)
这不是浮点问题。事实上,即使你使用精确的算术,你也会看到同样的问题。
这个错误对于数值积分本身以及您正在使用的特定方法和您正在解决的ODE来说是非常重要的。
在这种情况下,您使用的是称为Forward Euler的集成方案。这可能是解决一阶ODE的最简单方法。当然,这会留下一些不良特征。
首先,它在每一步都引入了错误。错误的大小为O(Δt²)
。这意味着单个时间步长的误差大致与时间步长的平方成正比。因此,如果将时间步长减半,大致可以将增量误差降低到值的1/4。
但是,由于您减少了时间步长,因此您必须采取更多步骤来模拟相同的时间。所以你要添加更多但更小的错误。这就是累积误差为O(Δt)
的原因。因此,如果你采取一半大的时间步长,那么在整个模拟时间内,你会得到一半的累积误差。
最终这个累积错误就是你所看到的。您可以在错误图中看到,每次将时间步长增加10倍时,最终错误最终会减少大约10倍:因为时间步长小10倍,所以总错误结束大约小10倍。
另一个问题是前向欧拉表现出所谓的条件稳定性。这意味着在某些情况下累积误差可能无限制地增长。为了了解原因,让我们看一个简单的ODE:
x' = -k * x
其中k是常数。该ODE的确切解是x(t) = x(0) * exp( -k * t )
。因此,只要k为正数,x
就会随着时间的推移而趋于0
。
但是,如果我们尝试使用Forward Euler来估算它,我们得到的结果如下:
x(t + Δt) = x(t) + Δt * ( -k * x[n] )
= ( 1 - k * Δt ) * x(t)
这是我们可以解决的简单递归关系:
x(t) = ( 1 - k * Δt )^(t / Δt) * x(0)
现在,我们知道当t
变大时,确切的解决方案为十到零。但是,前向欧拉解决方案只在|1 - k * Δt| < 1
时才这样做。请注意该表达式如何取决于步长以及ODE中的k
项。如果k
真的很大,我们需要一个非常小的时间步骤来防止解决方案爆炸。这就是为什么它拥有所谓的条件稳定性:解决方案的稳定性取决于时间步长。
还有其他一些问题,但这是一个广泛的主题,我无法在一个答案中涵盖所有内容。