Числовая точность и накопление ошибок

Cannon.js — это физический движок на JavaScript, реализующий динамическую симуляцию тел в 3D. Все расчёты в нём выполняются с использованием чисел с плавающей запятой двойной точности (float64). Несмотря на высокую точность, при длительных симуляциях или при работе с малыми величинами накапливаются ошибки округления, что может приводить к нестабильности и неточностям в симуляции.

Ключевой аспект: в Cannon.js позиции и скорости объектов обновляются через интеграторы (например, Euler, Verlet). Любая числовая ошибка при каждом шаге интеграции постепенно накапливается, особенно при большом числе шагов или при высокой частоте обновления.


Интеграторы и накопление ошибок

Cannon.js использует несколько методов интеграции движения тел:

  1. Explicit Euler Обновление выполняется по формулам:

    v_new = v_old + a * dt
    x_new = x_old + v_new * dt
    • Простая и быстрая схема
    • Ошибки интеграции линейно накапливаются с шагом времени dt
    • Может приводить к «раздутию» энергии системы
  2. Semi-implicit Euler (или Symplectic Euler)

    v_new = v_old + a * dt
    x_new = x_old + v_new * dt
    • Более стабильная для симуляции механики
    • Сохраняет энергию лучше, чем Explicit Euler
    • Ошибки всё ещё присутствуют, но накапливаются медленнее
  3. Verlet

    x_new = 2*x_current - x_previous + a * dt^2
    • Отличается высокой стабильностью при сохранении энергии
    • Хорошо подходит для тел с жёсткими соединениями
    • Числовые ошибки концентрируются в позициях, а не скоростях

Важно: выбор интегратора напрямую влияет на скорость накопления ошибок и устойчивость симуляции.


Проблемы с накоплением ошибок

  1. Дрейф тел в пространстве Даже при полностью статической сцепке объектов можно наблюдать постепенное смещение координат. Это особенно заметно при симуляции большого количества объектов или при длительных симуляциях.

  2. Нарушение ограничений Ограничения (constraints) и соединения могут «прыгать» или растягиваться, если ошибки в позициях и скоростях накопились. В Cannon.js это проявляется через небольшой разрыв между телами, несмотря на то, что они должны оставаться соединёнными.

  3. Энергетические артефакты Из-за ошибок численного интегрирования кинетическая энергия тел может постепенно увеличиваться или уменьшаться, создавая нестабильность. Особенно критично для систем с высокочастотными колебаниями (например, подвесные конструкции).


Методы уменьшения ошибок

  1. Уменьшение шага симуляции (dt)

    • Чем меньше шаг интеграции, тем меньше величина ошибки за один цикл
    • Рекомендуется адаптивно подбирать dt для динамичных сцен
  2. Повышение точности ограничений

    • Cannon.js использует итерационный метод решения ограничений (solver.iterations)
    • Увеличение числа итераций уменьшает накопление ошибок в соединениях, но увеличивает нагрузку на процессор
  3. Регулярная корректировка позиций

    • Для статических или квазистатических объектов полезно применять «снап» к исходным координатам
    • Позволяет компенсировать дрейф из-за накопленных ошибок
  4. Использование меньших масс и нормализация силы

    • Большие массы или неадекватно рассчитанные силы усиливают влияние числовых ошибок
    • Нормализация силы и момента уменьшает вероятность неустойчивости

Примеры проявления ошибок

  • При симуляции сцепленных тел (цепь, маятник) с малым dt = 0.016 и стандартным числом итераций solver (10) через несколько секунд наблюдается постепенное растяжение соединений.
  • При симуляции башни из кубов каждый куб начинает «дрейфовать», создавая эффект просачивания под действием гравитации, несмотря на идеальное столкновение.

Вывод: ошибка интеграции и ограничений — не баг Cannon.js, а фундаментальное следствие работы чисел с плавающей запятой и дискретной интеграции.


Практические рекомендации

  • Для длительных симуляций использовать semi-implicit Euler или Verlet.
  • Настраивать solver.iterations ≥ 20 для сложных constraint-систем.
  • Минимизировать шаг времени при симуляциях с быстрыми объектами.
  • Применять корректирующие методы к статическим телам для уменьшения накопленного дрейфа.
  • Следить за масштабом сцены: слишком большие координаты увеличивают относительную погрешность чисел с плавающей запятой.

Особенности JavaScript и числовых типов

Cannon.js опирается на стандартный Number в Jav * aScript:

  • 64-битная IEEE-754 точность
  • Максимальная безопасная целая величина: 2^53 - 1
  • Ошибки накапливаются быстрее при работе с координатами > 1e7
  • Любые операции с малыми числами (1e-7) могут потерять значимость

Практическое правило: поддерживать координаты и размеры объектов в диапазоне 1e-31e5 для стабильной симуляции.


Влияние масштабирования на числовую стабильность

  • Масштаб сцены: крупные модели увеличивают риск накопления ошибок
  • Масштаб силы: слишком большие или слишком маленькие значения приводят к переполнению или потере точности
  • Единицы измерения: рекомендуется использовать метрическую систему, избегая очень малых единиц (мм) в больших сценах

Правильное масштабирование объектов и сил снижает эффект дрейфа и нестабильности.


Итоговое понимание

Накопление ошибок в Cannon.js — системное явление, обусловленное:

  • дискретной интеграцией
  • ограниченной точностью чисел с плавающей запятой
  • итерационными методами решения ограничений

Эффективная стратегия работы с этим феноменом заключается в выборе подходящего интегратора, уменьшении шага времени, увеличении числа итераций solver и корректном масштабировании сцены и сил. Это позволяет добиться стабильной и реалистичной симуляции длительных динамических сцен.