本节摘要:显式欧拉用当前加速度更新速度、用当前速度更新位置,等于始终在力曲线外侧切一刀。简谐振子上每步都给系统偷偷加油,振幅越来越大,直到弹簧爆炸。固定 dt 也救不了这种结构性涨能,只能换积分器或加假阻尼——而假阻尼会把真实运动一起摁死。
阅读完本节,你应当能够:
把质量拴在原点弹簧上,无阻尼,解析解是等幅振荡。显式欧拉是:
a = -k / m * x v = v + a * dt x = x + v_old * dt # 注意用的是旧速度
有人把第二行写成新速度,那其实已经滑向半隐式,下节再谈。纯显式用旧速度更新位置。取 k=1000、m=1、dt=1/60,初值拉开 1 米。几十步后振幅明显变大,再过几秒位置变成天文数字。这不是实现写错,是方法在放大能量。
弹簧像钟表游丝。游丝该把摆轮拉回,显式欧拉却总在“已经该回头的点”还按旧方向加一把。每步加的那一点能量,正是截断误差的符号偏向。dt 越小,涨得越慢,但一直在涨。把 dt 改成 1/1000,作业里能撑久一点,产品里仍会在长会话后炸。
圆轨道更直观。向心力 a = -omega^2 * r,解析解是圆。显式欧拉每步速度垂直于半径加一个切向误差,半径向外螺旋。半隐式欧拉在这个例子上会走多边形,半径漂移小得多。画出来,面试都能讲。
| 现象 | 显式欧拉 | 原因一句话 |
|---|---|---|
| 弹簧振幅变大 | 必现 | 误差给系统做正功 |
| 圆轨道外旋 | 必现 | 位置用旧速度,落后于应有的向心 |
| 自由落体 | 看起来还行 | 加速度常数,误差不积累成指数 |
| 刚体堆箱子 | 接触一抖就飞 | 接触力像极硬弹簧 |
自由落体骗过很多人:重力常数,显式欧拉的位置误差是 dt 平方量级且不反馈,短时间看不出来。一加恢复力(弹簧、接触罚函数、布料边),反馈回路把误差符号锁成“加油”。所以“我用欧拉掉石头没问题”不能推出“我用欧拉做物理引擎没问题”。
数值课会画稳定域:dt 太大,解振荡发散。那是另一件事。显式欧拉对简谐振子的稳定条件大致是 dt < 2/omega,omega 是固有频率平方根。硬弹簧频率高,dt=1/60 直接出稳定域,一步就 NaN。即使缩进稳定域,能量仍单调涨,只是涨得像慢性病。
三个词不要混:
游戏要的往往是“看起来守恒”和“永不 NaN”,不是数学论文里的四阶精度。所以默认半隐式,而不是上 RK4。RK4 更准,但约束冲量插在子步骤里很别扭,计算量也大。刚体引擎的误差大头在接触,不在光滑力场的积分阶数。
罚函数接触把穿透当弹簧,刚度极高,频率极高,显式欧拉几乎必炸。这也是为什么现代引擎不用罚函数当主接触,而用冲量约束——约束不走这根硬弹簧的积分路径。若你坚持显式加罚函数,dt 可能要小到 1 毫秒以下,实时预算不够。
💡 关键直觉:接触若被建模成硬弹簧,你就在求一个你的积分器解决不了的刚性问题。换积分器或换接触模型,不要把弹簧刚度调到 1e8 再祈祷。
阻尼能掩盖涨能:每步 v *= 0.99。它也能把真实的弹性运动摁成一潭死水。用阻尼去“修欧拉”是在用错误补偿错误。可以当临时保险丝,不能当方案。正确做法是换半隐式或 Verlet,再加物理上站得住的阻尼(空气、摩擦)。
比较积分器必须固定:dt、初值、弹簧 k、质量、步数。只改更新公式。记录动能加势能。显式欧拉的总能应稳步上升;半隐式可能微涨;Verlet 应在真值附近振荡。把三条曲线画在一张图上,比任何教科书句子都有用。

高速子弹打薄板,显式欧拉可能整步穿过,根本没采样到板的力。这是隧道效应,积分阶数救不了,要连续碰撞或更小 dt。别指望“换成 RK4 就能打中薄板”。检测不到,积分再准也是在真空里飞。
刚体若只用重力加冲量接触,没有罚弹簧,显式欧拉有时能撑——因为接触不走硬弹簧。即便如此,仍建议半隐式:多写一个用新速度更新位置,稳定性明显好,成本为零。没有理由在新产品里默认纯显式。
⚠️ 常见坑:在变 dt 下测欧拉,卡顿帧看起来像“偶发爆炸”,于是去查碰撞。先把第 1.2 节的固定步套上,再给欧拉定罪。否则你在给抖动的尺子做实验。
能量曲线要对数坐标,线性坐标前期像平的,突然一根竖线,你会以为是某一帧碰撞写错。对数上一开始就是斜线,那是方法本身。
把弹跳球的积分改回纯显式、去掉地面,只留弹簧挂着球,看曲线。再改回半隐式。两张图存下来。第 2.3 节所有推荐都以这两张图为证据,而不是“业界都这么做”。业界这么做,是因为这张图在每个工作室都画过一遍。
还可以测相位误差:同样 10 秒,解析解振荡多少次,数值解慢了还是快了。显式欧拉通常周期也会漂。游戏里周期漂一点不致命,振幅漂会致命。角色跳跃高度越跳越高,玩家能立刻感觉到。所以能量行为的优先级高于相位精度。这是实时引擎和科学计算选积分器时分道的地方:科学计算在乎相位和长期定性;游戏在乎别炸、别穿、别抖。
若你在做轨道力学或长时机器人仿真,欧拉更不能用,那是另一本教程的辛积分和变步长 ODE。本教程默认实时、固定步、刚体加接触。工具选得和问题匹配,比追求“更高阶”重要。
实验标题:简谐振子能量漂移。参数:m=1,k=400,x0=1,v0=0,dt=1/60,步数 600。记录每步 E=0.5*m*v^2+0.5*k*x^2。显式欧拉结束时 E 应明显高于初值;若你的曲线下降,多半把公式写成了半隐式(位置用了新速度)或忘了弹簧力符号。把 CSV 留下来,第 2.3 节直接叠上去。
第二实验:圆轨道。向心加速度 omega^2=4,半径 1,dt=1/60。显式欧拉 10 秒后半径应明显大于 1。半隐式半径漂移小。不要用碰撞“帮忙”把点拉回圆上,那不是积分器实验。
第三:把 dt 改成 1/15,显式欧拉可能直接出稳定域变 NaN。这是刚性:硬弹簧加长步长。记下 k 与最大稳定 dt 的粗关系,后面选接触模型时你就知道为什么罚函数弹簧不能配大 dt。
课堂记录的意义是:以后调接触爆炸,先问“我是不是又在用显式加硬弹簧”。有自己的图,不必背书。
先查是不是其实写成了半隐式,或弹簧符号把力写成了阻尼。纯显式振子应涨能。下降可能是 bug 碰巧耗散,换参数就会涨。不要把 bug 当特性。
慢性化不是治愈。长会话仍涨。产品还要弹簧和接触,欧拉更脆弱。没有理由默认纯显式。
只当保险丝。真运动会一起被摁死,手感发黏。正确路径是换半隐式。阻尼补偿会让你调两个错误互相抵消,下个场景就穿帮。
接触被当成极硬弹簧,频率极高,一步就出稳定域。改冲量模型,不要把罚刚度调到天文数字再配欧拉。
不能当通行证。一加弹簧就涨能。
慢性涨能,长会话仍炸。换半隐式成本为零。
前期像平的然后竖线。用对数。
卡顿像偶发爆炸。先钉固定步再定罪。
检测不到就打不中。隧道不是阶数问题。
定时炸弹。改冲量模型。
真运动一起死。只当临时保险丝。
外旋是最好懂的图。留下截图给后继者。
游戏可接受。振幅漂不可接受。优先级如此。
把「02-显式欧拉为什么会飞」相关的失败做成最小复现:关掉渲染特效、关掉音频、只留固定 dt 的 step 和一份输入日志。能在十秒内重放出来的 bug 才叫被抓住。不能重放就先补录制,再谈修。很多人在这一步省时间,后面用几天陪着偶发抖动。 本条对应「02-显式欧拉为什么会飞」第 1 号备忘,和相邻条目不要合并成一句空话,分开验收。
在「02-显式欧拉为什么会飞」路径上加计数器要进 step 末尾快照,不要在热循环里格式化字符串。计数器本身若分配内存,你会优化一个被测量污染的世界。发布版可用宏剥掉绘制,但计数器的定义要保留,方便线上开一个极轻的统计开关。 本条对应「02-显式欧拉为什么会飞」第 2 号备忘,和相邻条目不要合并成一句空话,分开验收。
下一节给出默认该写的半隐式欧拉,以及布料爱用的 Verlet,还有软体大步长才请出的隐式方法。