Зачем объединяться через накопление?

14

Я начинаю изучать физику DIY, и у меня есть вопрос о реализации интеграции на самом базовом уровне (т.е. это не вопрос Эйлера против РК4).

Почти каждый пример, с которым я сталкиваюсь, имеет некоторую integrate()функцию, которая получает временной шаг с момента последнего обновления и обновляет ускорение (и / или скорость и / или положение) с момента последнего обновления.

В простейшем виде: position += velocity * deltaTime

Тем не менее, я не понимаю, почему он накапливается так, когда его можно так же легко получить, изменив функцию . Например: getPosition = makeNewFunction()который может возвращать то, что имеет сигнатуру Time -> Position, и внутренняя работа этой функции генерируется с помощью соответствующей математической формулы.

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

Мое понимание новичка заключается в том, что это также позволит избежать ошибок, возникающих из-за накопления ... так почему это не работает, что я пропускаю?

(FWIW я сделал собрать основные доказательства концепции этого idea- , хотя это также тестирование несколько других вещей , в то же время , так что это не самый чистый пример: https://github.com/dakom/ball-bounce-frp )

РЕДАКТИРОВАТЬ 1: как упомянуто в комментариях, вероятно, важно отметить, что я еще не узнал об изменении ускорения или о работе с рывками и другими вещами, которые требуют интеграции более высокого порядка, чем постоянное ускорение.

EDIT 2: здесь некоторые основные примеры кода идеи, и псевдо Javascript синтаксис - обратите внимание , что getKinematicPositionэто частично применяется так оно возвращает новую функцию только время -> Положение:

Я придерживаюсь позиции здесь, но это может быть что-то еще, например getVelocity, я думаю ...

getKinematicPosition = initialVelocity => acceleration => time => 
  ((.5 *acceleration) * (time * time)) + (initialVelocity * time);

getPosition = getKinematicPosition ([0,0,0]) (GRAVITY);

onTick = totalTime => {
   position = getPosition (totalTime);
   onCollision = () => {
     getPosition = changeTheFunction(totalTime);
     //changeTheFunction uses totalTime to base updates from 0
     //it could use getKinematicPosition or something else entirely
   }
}
davidkomer
источник
1
Что бы вы делали, если бы у вас была непостоянная скорость / ускорение?
Линэйт
Я понятия не имею! : D Если это и есть причина - например, я еще не дошел до изменения ускорения, я был бы очень признателен за более полное объяснение того, где это сломалось бы в качестве ответа (в противном случае я могу пойти по этому функциональному пути и зайти в тупик !)
Давидкомер
6
Хорошо, если ваш объект просто вращается по кругу, тогда конечно ... как насчет того, когда игрок нажимает на коробку? Когда вы вызываете getPosition (сейчас + 100), предсказывает ли он будущее, чтобы знать, когда игрок перестанет его толкать? Когда вы вызываете getPosition (now-1000), нужно ли вспоминать прошлое?
user253751

Ответы:

34

... внутренняя работа этой функции генерируется с помощью соответствующей математической формулы ...

Это будет работать для определенных классов задач, и ключевая фраза для поиска - это решение в закрытой форме . Например, в космической программе «Кербал» движение космического корабля на орбите рассчитывается таким образом. К сожалению, большинство нетривиальных задач (например, выход в атмосферу упомянутого космического корабля) не имеют известного решения в замкнутой форме. Таким образом, потребность в математически более простых числовых приближениях (то есть integrate()во времени).

MooseBoys
источник
Ааа ... круто! Если у вас есть минутка - что вы думаете о стремлении к этому функциональному подходу, а затем прибегните к накоплению, если я не могу понять, как заставить его работать (например, если я имею дело с проблемой не закрытой формы или Я не могу понять, как превратить его в один)? Мне нравится идея генерации функций, поскольку она соответствует математике 1: 1 - но если я всегда буду неизбежно попадать в тупик, это может не стоить того ...
Давидкомер
8
@davidkomer Почему вы даже хотите продолжать генерировать функции? Если вы можете осуществить это, то вы можете просто предварительно рассчитать и записать всю траекторию! Конечно, люди уже делают это: это называется анимацией , и в ней есть свои тонкости.
Joker_vD
Функции меняются в зависимости от динамики времени выполнения ... см., Например, отскок мяча FRP
davidkomer
На самом деле мне нужно обновить этот пример, чтобы он был более похож на понг, с движущимися объектами, контролируемыми случайным образом / пользователем ...
Давидкомер
10

Проблема с вашим подходом в том, что у вас нет истории вашего объекта. Вы можете вычислить позицию, если будете двигаться в направлении, но что произойдет, если вы нажмете что-то и отскочите назад?
Если вы накапливаете с вашей последней известной позиции, вы можете справиться с ударом и идти дальше. Если вы попытаетесь рассчитать его с самого начала, вам придется каждый раз пересчитывать удар или устанавливать его в качестве новой стартовой позиции.

Ваш пример напомнил мне гоночную игру. (Я не знаю, будет ли положение контролироваться физическим двигателем, но я думаю, что это прекрасно работает, чтобы объяснить)
Если вы едете на машине, вы можете ускоряться и замедляться. Вы не можете рассчитать свою позицию, не зная, как профиль скорости вашего автомобиля выглядел с самого начала и до настоящего времени. Накопление расстояния намного проще, чем сохранение скорости, которую вы имели в каждом кадре от начала до настоящего момента.

Отказ от ответственности: я до сих пор не написал физику игры, просто я вижу проблему.

Изменить:
На этой диаграмме вы можете увидеть, как значения меняются со временем.
красный = ускорение (от начала ускорения к наклону вниз)
зеленый = скорость (от начала до остановки)
синий = путь, которым вы пошли.

Общая скорость - это интеграл ускорения от начальной точки до фактической регистрации. (Область между линией и осью)
Путь является интегралом вашей скорости.
Если вам известны значения вашего ускорения, вы можете рассчитать другие значения. Но если я не ошибаюсь, интегралы также рассчитываются путем накопления на ПК. И это намного сложнее хранить все значения ускорения.
Кроме того, это, вероятно, слишком много, чтобы рассчитывать каждый кадр.

диаграмма ускорения / скорости / пути-времени

Я знаю, у меня отличные навыки рисования. ;)

Редактировать 2:
этот пример для линейного движения. Нарушение направления делает это еще более сложным.

Linaith
источник
msgstr "или установить его как новую стартовую позицию." - да, но я не вижу проблемы в этом :) Re: пример автомобиля ... У меня появляется сильное чувство, что мне действительно нужно начать играть с чем-то более сложным, чтобы интуитивно понять, где это не получается .. .
davidkomer
установка новой позиции, вероятно, не является проблемой. я редактировал автомобильную часть
Linaith
1
Я думаю, что в автомобильной игре ускорение будет еще более сложным. Вероятно, будут скачки и шипы, и это может зависеть от скорости. (Например, ускорение уменьшается до 0, когда автомобиль приближается к максимальной скорости.)
jpmc26
3
@davidkomer даже не беспокоится о машине (если вы этого не хотите), подойдет обычный платформер. Как работает mario.getPosition (Time) в Super Mario Bros?
user253751
8

Тем не менее, я не понимаю, почему он накапливается так, когда его можно так же легко получить, изменив функцию. Например: getPosition = makeNewFunction (), которая может возвращать что-то, имеющее сигнатуру Time -> Position, и внутренняя работа этой функции генерируется с помощью соответствующей математической формулы.

Вы можете!

Он вызывается с использованием аналитической или закрытой формы решения. Преимущество состоит в том, что он является более точным, поскольку ошибки округления, которые накапливаются с течением времени, отсутствуют.

Однако это работает тогда и только тогда, когда вы заранее знаете такую ​​закрытую форму. Для игр это довольно часто просто не так.

Движение игрока нестабильно и просто не может быть помещено в какую-то предварительно вычисленную функцию. Игрок может и будет менять свою скорость и ориентацию довольно часто.

NPC могут потенциально использовать решения в закрытой форме, и фактически они иногда используют. Однако у этого есть некоторые другие недостатки. Подумайте о простой гоночной игре. Каждый раз, когда ваш автомобиль сталкивается с другим транспортным средством, вы должны изменить свою функцию. Может быть, машина движется быстрее в зависимости от метро. Тогда найти такое решение в замкнутой форме будет довольно сложно. В действительности, вероятно, существует больше случаев, когда найти такую ​​замкнутую форму невозможно или настолько сложно, что это просто невозможно.

Отличным примером использования решения в закрытой форме является Kerbal Space Program. Как только ваша ракета находится на орбите и не находится под ударом, KSP может поставить ее «на рельсы». Орбиты предопределены в системе двух тел и являются периодическими. Пока ракета не применяет больше тяги, вы уже знаете, где будет находиться ракета, и вы можете просто позвонить getPositionAtTime(t)(она точно не названа, но вы понимаете).

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

Например, если ваш персонаж нацеливает пушку, вы можете легко показать прогнозируемую точку удара пушечного ядра, используя решение в замкнутой форме. И, если ваша игра не позволяет изменить ход пушечного ядра (например, без ветра), вы даже можете использовать его для перемещения пушечного ядра. Обратите внимание, что тогда вам нужно особенно внимательно следить за препятствиями, движущимися на пути вашего ядра.

Есть много похожих ситуаций. Если вы строите игру на основе раундов, то, вероятно, будет гораздо больше доступных решений закрытых форм, чем при создании игры RTS, поскольку вы заранее знаете все параметры и можете с уверенностью сказать, что они не меняются (ничего не происходит внезапно) на этот путь, например).

Обратите внимание, что существуют методы борьбы с числовыми неточностями ступенчатой ​​интеграции. Например, вы можете отслеживать накопленную ошибку и применять корректирующий термин для контроля ошибки, например, суммирование по Кахану.

Polygnome
источник
8

В случае простого прыгающего мяча найти решение в закрытой форме легко. Однако более сложные системы, как правило, требуют решения обыкновенного дифференциального уравнения (ОДУ). Числовые решатели необходимы для обработки всех случаев, кроме самых простых.

Действительно, есть два класса числовых решателей ODE: явный и неявный. Явные решатели обеспечивают приближенную форму для вашего следующего состояния, в то время как неявные решатели требуют решения уравнения для этого. То, что вы описываете для своего прыгающего мяча, на самом деле является неявным решателем ODE, знаете ли вы об этом или нет!

Преимущество неявных решателей заключается в возможности использовать намного большие временные шаги. Для вашего алгоритма прыгающего мяча ваш временной шаг может быть как минимум таким же, как и продолжительность до следующего столкновения (что изменило бы вашу функцию). Это может заставить вашу программу работать намного быстрее. Однако, в общем, мы не всегда можем найти хорошие неявные решения для интересующих нас ODE. Когда мы не можем, мы возвращаемся к явной интеграции.

Большое преимущество, которое я вижу в явной интеграции, состоит в том, что ошибки хорошо известны. Вы можете открыть любой учебник 60-х годов и прочитать все, что вам нужно знать о небольших причудах, возникающих при использовании определенных методов интеграции. Таким образом, разработчик изучает эти навыки один раз, и им никогда не придется изучать их снова. Если вы делаете неявную интеграцию, каждый сценарий использования немного отличается, с немного отличающимися ошибками. Немного сложнее применить то, что вы узнали из одной задачи, к следующей.

Корт Аммон
источник
1

pos (t) = v (t) * t

работает только если pos (0) = 0 и v (t) = k

Вы не можете связать положение со временем без знания начального условия и всей функции скорости, поэтому уравнение является приближением интеграла

pos (t) = интеграл от v (t) dt от 0 до t

РЕДАКТИРОВАТЬ _________

Вот небольшое доказательство в комментариях (при условии pos (0) = 0)

пусть v (t) = 4

уравнение 1: pos (t) = 4 * t (правильно)

уравнение 2: pos (t) = c + 4 * t от 0 до t = 4 * t (правильно)

пусть v (t) = 2 * t

уравнение 1: pos (t) = 2 * t ^ 2 (неверно)

уравнение 2: pos (t) = c + t ^ 2 от 0 до t = t ^ 2 (правильно)

Я должен добавить, что ваше уравнение уже учитывает постоянное ускорение (т.е. ваше уравнение равно 2, где v (t) = v0 + a * t и пределы интегрирования равны t0 и t), поэтому ваше уравнение должно работать до тех пор, пока вы обновляете начальное положение, начальная скорость и ускорение остаются постоянными.

РЕДАКТИРОВАТЬ 2 ________

Я также должен добавить, что вы также можете рассчитать положение с начальным положением, начальной скоростью, начальным ускорением и постоянным рывком. Другими словами, вы можете создать функцию, основанную на уравнении 2, которая представляет позицию в зависимости от времени, разделив ее на ее производные: скорость, рывок, что будет дальше, и т. Д., И т. Д., И т. Д., Но вы будете точны в своем уравнении, только если v (t) можно смоделировать таким образом. Если v (t) не может быть смоделировано только с помощью скорости, ускорения, постоянного рывка и т. Д., То вам нужно вернуться к приближению 2, что обычно происходит, когда у вас есть прыгающие вещи, сопротивление воздуха, ветер и т. Д. ,

Kyy13
источник
постоянная. это просто означает, что v (t) не должно меняться со временем
Kyy13
Я должен буду с этим смириться и разобраться, почему это правда ... пока что голосую :) Я разместил пример кода в вопросе на случай, если что-то изменится
davidkomer
нет проблем, обновлено снова с лучшими словами :)
Kyy13