欧拉法为什么会把能量越加越多
一句话总结
从加速度求速度、从速度求位置有多种方法。显式欧拉法在振荡系统中会每一步都增加少量能量;只改变计算顺序的半隐式欧拉法则不会。
为什么需要它
物理引擎求解的是微分方程。力决定加速度,对加速度随时间积分得到速度,再对速度积分得到位置。计算机无法进行连续积分,只能把时间切成小段进行近似。
问题在于,这种近似不仅产生误差,还会改变系统本身的性质。悬挂在弹簧上的重物本应始终以相同幅度振荡,但最简单的计算方法会让振幅不断增大,几秒后飞出屏幕。错误不在力的计算,而在积分方法。
如何工作
显式欧拉法只使用一步开始时的值。
x_new = x + v * dt
v_new = v + a(x) * dt
对于弹簧(a = -x,质量与刚度均为 1),一步可表示为线性变换,并能精确计算其放大率。能量 E = (x² + v²)/2 每一步都会精确乘以 (1 + dt²)。当 dt=0.05、共 400 步时,结果约为 1.0025^400 ≈ 2.715 倍。这不是随机误差,而是系统性增长。减小 dt 只会减慢增长,不会改变方向。
半隐式(辛)欧拉法只改变顺序。
v_new = v + a(x) * dt
x_new = x + v_new * dt # 방금 구한 새 속도를 쓴다
只交换一行,性质就完全不同。这种方法会精确保留一个非常接近原能量的量,因此能量只在小范围振荡而不会发散。几乎所有游戏物理引擎都使用它,计算成本与显式欧拉法相同。
速度 Verlet在一步中使用两次加速度。
x_new = x + v * dt + 0.5 * a * dt²
a_new = a(x_new)
v_new = v + 0.5 * (a + a_new) * dt
它的精度高一个阶次(dt 的二阶),同样 dt 下误差小得多,同时保持辛性质。需要长期精确模拟的分子动力学中,它是标准方法;代价是每一步多计算一次力。
这里最重要的是:精度与稳定性是两个不同维度。 显式欧拉的问题不仅是“不够准确”,而是误差随时间沿固定方向恶化。
值得进一步理解,为什么只换一个顺序会产生巨大差异。把显式欧拉的一步写成矩阵,其行列式为 1 + dt²,大于 1。行列式表示相空间面积的放大倍数,因此每一步都会让状态空间略微膨胀。半隐式欧拉矩阵的行列式恰好为 1,保留面积,因此不会发散。“辛”所表达的正是这一性质。
还要澄清一个误解:辛积分器保留的不是能量本身。它精确保留另一个非常接近原能量的量,因此能量会小幅上下波动,却不会单向漂移。所以实验中半隐式欧拉的能量曲线不是完全平直,而是一条细窄的波动带,这是正常现象。
在实际项目中
可变时间间隔容易引发事故。某帧延迟导致 dt 突然增大,物体可能在这一步穿墙,或约束系统直接爆炸。因此,物理引擎会把渲染与物理分离,以固定 dt运行物理,再根据剩余时间调整步数;不是放大 dt,而是多走几步。
另一种常见做法是每步把速度乘以 0.99 来模拟阻尼。它简单,也确实有助于稳定,但阻尼量会随 dt 改变,导致帧率不同的设备上物体运动不同。物理上正确的方法是把阻尼作为力加入。
还有一条关于执行顺序的实务规则。一个物理步中,施加力、更新速度、求解碰撞、更新位置必须有固定顺序。如果代码不同位置采用略有差异的顺序,结果就无法复现。相同输入不能产生相同结果的模拟几乎无法调试,因为缺陷本身无法重现。网络游戏要求服务器与客户端执行相同计算时,可复现性更是明确需求。
选择积分器的实用标准如下:游戏只需看起来合理并能长期稳定运行,半隐式欧拉几乎总是正确选择,因为不发散远比数值绝对精确重要;轨道计算或分子动力学中,数值本身就是结果,则应使用 Verlet 或更高级方法。
下一实验要做什么
用三种积分器对同一个弹簧运行 400 步,把各自能量保存为 CSV,再把三条曲线画到同一张 PNG 上,直观看到只有欧拉法向上发散。随后改变 dt,确认能量增长率与闭式公式 (1 + dt²)^n 一致,最后把阻尼作为力加入,观察能量单调下降。