Выбор шага и устойчивость: энергия как тест
Если симуляция «взрывается» — почти всегда виноваты слишком большой шаг или неустойчивый метод.
Устойчивость интегратора — свойство сохранять ограниченность решения: устойчивый метод не даёт энергии и амплитуде расти до бесконечности из-за дискретизации.
Энергия как универсальный тест
В замкнутой системе без трения полная механическая энергия $E = E_k + E_p$ сохраняется — это закон природы. Значит, если в симуляции энергия дрейфует, виноват не мир, а интегратор. Это даёт бесценный инструмент проверки: посчитайте энергию на каждом шаге и посмотрите на её график. Растёт — метод накачивает систему (типично для явного Эйлера); падает — численно затухает; держится — метод корректен. Сохранение энергии — самый честный тест качества движка, и мы посвятим ему отдельный раздел в конце курса.
Устойчивость зависит от шага
Прогоним осциллятор $20$ секунд при разных $\Delta t$ и сравним, во сколько раз изменилась энергия, для явного и полу-неявного Эйлера.
def ratio(method, dt, T=20.0):
x, v = 1.0, 0.0
for _ in range(int(T/dt)):
a = -x
if method == "euler":
xn = x + v*dt; v = v + a*dt; x = xn
else: # semi
v = v + a*dt; x = x + v*dt
return (0.5*v*v + 0.5*x*x) / 0.5
for dt in (0.5, 0.2, 0.1, 0.05):
e = ratio("euler", dt)
s = ratio("semi", dt)
print(f"dt={dt:.2f} явный×{e:10.3f} полу-неявный×{s:.3f}")
Вывод:
dt=0.50 явный× 7523.164 полу-неявный×1.024 dt=0.20 явный× 50.505 полу-неявный×0.947 dt=0.10 явный× 7.316 полу-неявный×0.967 dt=0.05 явный× 2.715 полу-неявный×0.982
Картина говорит сама за себя. У явного Эйлера при крупном шаге энергия вырастает в тысячи раз — система буквально взрывается. Уменьшение шага помогает, но медленно (вдвое меньше шаг — примерно вдвое меньше раздувание). А полу-неявный метод при любом из этих шагов держит энергию около единицы. Вывод инженера: лучший способ ускорить симуляцию — не уменьшать шаг у плохого метода, а взять хороший метод.
Фиксированный шаг — это контракт
В играх возникает соблазн привязать $\Delta t$ к реальному времени кадра (так называемый variable timestep). Это опасно: на лагающем компьютере кадр длиннее, шаг больше, и физика может стать неустойчивой именно в самый неподходящий момент. Стандартное решение — фиксированный шаг: физику считают с постоянным маленьким $\Delta t$ (например, $1/120$ с), накапливая «долг времени» и догоняя его нужным числом шагов за кадр. Тогда поведение мира одинаково на любом железе и воспроизводимо — критично для сетевых игр и реплеев.
Как работает под капотом
У каждого явного метода есть порог устойчивости по шагу: для осциллятора с частотой $\omega$ явный Эйлер устойчив лишь приближённо и всегда понемногу накачивает энергию, а такие методы, как Рунге — Кутты 4-го порядка, устойчивы до некоторого $\Delta t \cdot \omega$. Чем «жёстче» система (чем выше частоты или сильнее силы), тем меньше допустимый шаг. Поэтому жёсткие пружины и тяжёлые массы требуют либо крошечного шага, либо специальных неявных методов, решающих уравнение относительно будущего состояния.
Частые ошибки
- Гонять явный метод с большим шагом. Это прямой путь к «взрыву» симуляции — частая причина того, что объекты улетают в бесконечность.
- Variable timestep в физике. Привязка $\Delta t$ к FPS делает мир невоспроизводимым и нестабильным при лагах.
- Не мониторить энергию. Без контроля энергии вы не отличите физический эффект от численного артефакта.
Итог
- Сохранение энергии — лучший тест корректности интегратора.
- У явного Эйлера раздувание энергии резко растёт с $\Delta t$; у симплектических методов — нет.
- Выгоднее сменить метод, чем уменьшать шаг плохого метода.
- Физику считают с фиксированным шагом, независимым от частоты кадров.