Навіщо інтегруватись над накопиченням?


14

Я починаю вивчати, як робити самостійно фізику, і у мене виникає питання щодо інтеграції на самому базовому рівні (тобто це не питання Ейлера проти RK4).

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

У найпростішій формі: position += velocity * deltaTime

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

Таким чином, немає накопичення ... щоразу, коли потрібно отримати позицію, вона викликає цю функцію з поточним часом.

Моя новачка розуміє, що це також дозволить уникнути помилок, що виникають через накопичення ... так чому це не працює, чого я пропускаю?

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

EDIT 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
   }
}

1
Що б зробила ваша функція, якщо у вас непостійна швидкість / прискорення?
Лінаїт

Я поняття не маю! : D Якщо це причина - наприклад, я ще не домігся змінити прискорення, я б дуже цінував більш повне пояснення, де це може зламатися як відповідь (інакше я можу піти по цій функціональній дорозі і потрапити в тупик !)
давидкомер

6
Добре, якщо ваш об'єкт просто обходить коло, тоді впевнений ... а як бути, коли це гра, яку гравець штовхає? Коли ви зателефонуєте на getPosition (зараз + 100), чи передбачає це майбутнє, щоб знати, коли гравець перестане його натискати? Коли ви телефонуєте getPosition (зараз-1000), чи має воно пам'ятати минуле?
користувач253751

Відповіді:


34

... внутрішня робота цієї функції генерується за допомогою відповідної математичної формули ...

Це буде працювати для певних класів проблем, і ключовою фразою для пошуку є рішення закритої форми . Наприклад, у космічній програмі Кербал рух космічного корабля по орбіті обчислюється таким чином. На жаль, у більшості нетривіальних проблем (наприклад, атмосферне повторне спостереження згаданих космічних апаратів) немає відомого рішення закритої форми. Таким чином, необхідність математично спрощених числових наближень (тобто integrate()з часом).


Ааа ... дивовижно! Якщо у вас є хвилина - що ви думаєте про націлення на цей функціональний підхід, а потім відкидаєтесь на накопичення, якщо я не можу зрозуміти, як змусити його працювати (наприклад, якщо я маю справу з проблемою незакритої форми або Не можу зрозуміти, як перетворити його в одне)? Мені подобається ідея генерування функцій, оскільки вона відповідає математиці 1: 1 - але якщо я завжди неминуче потрапляю в глухий кут, можливо, це не варто буде ...
davidkomer

8
@davidkomer Чому ви навіть хочете продовжувати генерувати функції? Якщо ви можете зняти це, то ви можете просто попередньо обчислити і записати всю траєкторію! Звичайно, люди вже роблять це: це називається анімацією , і вона має свою частку тонкощів.
Joker_vD

Функції змінюються залежно від динаміки виконання ... див. Для прикладу FRP ball-bounce
davidkomer

Насправді мені потрібно буде оновити цей приклад, щоб бути чимось більше схожим на понг, з рухомими об'єктами, що контролюються випадковим чином / користувачем ...
davidkomer

10

Проблема вашого підходу полягає в тому, що у вас немає історії вашого об'єкта. Ви можете обчислити положення, якщо рухаєтесь у напрямку, але що станеться, якщо ви щось вдаритесь і відскочите назад?
Якщо ви накопичитеся з останньої відомої позиції, ви можете впоратися з впливом і продовжити звідти. Якщо ви намагаєтеся обчислити його з самого початку, вам доведеться кожен раз перераховувати вплив або встановлювати його як нове стартове положення.

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

Відмова: Я досі не писав фізику гри, саме так я бачу проблему.

Редагувати:
На цій діаграмі ви бачите, як значення змінюються з часом.
червоний = прискорення (від початку прискорення до нахилу вниз)
зелений = швидкість (від початку до зупинки)
синій = шлях, який ви пройшли.

Загальна швидкість - це невід'ємна частина прискорення від стартової точки до фактичної логанізації. (Область між лінією та віссю)
Шлях є інтегралом вашої швидкості.
Якщо ви знаєте значення для свого прискорення, ви можете обчислити інші значення. Але якщо я не помиляюся, інтеграли також обчислюються накопиченням на ПК. І це набагато більше витрат, щоб зберігати всі значення прискорення.
Плюс це, мабуть, занадто багато, щоб обчислити кожен кадр.

діаграма прискорення / швидкості / шляху / часу

Я знаю, мої навички фарбування чудові. ;)

Редагувати 2:
Цей приклад для лінійного руху. Напрямок накачування робить це ще складніше.


"або встановіть його як нове початкове положення." - так, але я не бачу проблем з цим :) Re: приклад автомобіля ... У мене виникає сильне відчуття, що мені справді потрібно почати грати з чимось складнішим таким, щоб інтуїтивно зрозуміти, де це не вдається .. .
davidkomer

встановлення нової посади, мабуть, не є проблемою. я відредагував частину автомобіля
Лінаїт

1
У автомобільній грі я думаю, що прискорення було б ще складніше. Ймовірно, були б стрибки і шипи, і це може залежати від швидкості. (Наприклад, при наближенні автомобіля до максимальної швидкості прискорення зменшується до 0)
jpmc26

3
@davidkomer навіть не турбується з автомобілем (якщо ви цього не хочете), це зробить базовий платформер. Як працює mario.getPosition (Час) у Super Mario Bros?
користувач253751

8

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

Ти можеш!

Він називається за допомогою аналітичного чи закритого форми . Це вигідно, що воно є більш точним, оскільки помилки округлення, що накопичуються з часом, відсутні.

Однак це спрацьовує, якщо і лише тоді, якщо ви заздалегідь знаєте таку закриту форму. Для ігор це досить часто просто не так.

Рух гравця нестабільний і просто не може бути введений в якусь попередньо обчислену функцію. Гравець може і дуже часто може змінювати свою швидкість і орієнтацію.

НПК можуть потенційно використовувати рішення закритої форми, а насправді вони іноді це роблять. Однак є і деякі інші недоліки. Подумайте про просту гоночну гру. Кожен раз, коли ваш автомобіль стикається з іншим транспортним засобом, ви повинні змінити свою функцію. Можливо, машина рухається швидше залежно від метро. Тоді знайти таке рішення закритої форми буде досить складно. Насправді, ймовірно, більше випадків, коли знайти таку закриту форму або неможливо, або настільки складно, що просто неможливо.

Чудовим прикладом використання рішення закритої форми є космічна програма Kerbal. Як тільки ваша ракета перебуває на орбіті і не знаходиться під тягою, КСП може поставити її "на рейки". Орбіти заздалегідь визначаються в системі двох тіл і є періодичними. Поки ракета не застосовує більше тяги, ви вже знаєте, де буде знаходитися ракета, і ви можете просто зателефонувати getPositionAtTime(t)(її точно не названо, але ви отримаєте ідею).

На практиці, однак, використання поетапної інтеграції часто набагато практичніше. Але коли ви бачите ситуацію, коли рішення закритої форми існує і його легко обчислити, перейдіть до цього! Немає причин не використовувати його.

Наприклад, якщо ваш персонаж націлений на гармати, ви можете легко показати прогнозовану точку удару від гарматної кулі, використовуючи рішення закритої форми. І якщо ваша гра не дозволяє змінювати хід гарматного м’яча (наприклад, немає вітру), ви навіть можете використовувати його для переміщення гарматного м'яча. Зверніть увагу, що вам потрібно особливо подбати про перешкоди, що рухаються на шляху вашої гарматної кулі.

Подібних ситуацій багато. Якщо ви будуєте гру на основі круглої форми, то, ймовірно, буде набагато більше закритих рішень, ніж тоді, коли будуєте гру RTS, оскільки ви заздалегідь знаєте всі параметри і можете з упевненістю сказати, що вони не змінюються (нічого не рухається раптово наприклад, у цей шлях).

Зауважте, що існують методи боротьби з чисельними неточностями ступінчастої інтеграції. Наприклад, ви можете відслідковувати накопичену помилку і застосувати коригувальний термін, щоб тримати помилку під контролем, наприклад, підсумок Kahan


8

У випадку простого підстрибуючого м'яча, придумати рішення закритої форми легко. Однак більш складні системи, як правило, вимагають вирішення звичайного диференціального рівняння (ODE). Чисельні розв'язувачі потрібні для обробки всіх, крім найпростіших випадків.

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

Неявні вирішувачі мають перевагу в тому, що вони можуть використовувати набагато більші часові кроки. Для алгоритму підстрибування кулі ваш часовий крок може бути принаймні таким же великим, як і тривалість до наступного зіткнення (що змінило б вашу функцію). Це може змусити вашу програму працювати набагато швидше. Однак, в цілому, ми не завжди можемо знайти хороші неявні рішення ОДЕ, які нас цікавлять. Коли ми не можемо, ми повертаємося до явної інтеграції.

Великою перевагою, яку я бачу при явній інтеграції, є те, що ґетчі добре відомі. Ви можете відкрити будь-який підручник з 60-х років і прочитати все, що вам потрібно знати про маленькі химерності, які виникають із певними методами інтеграції. Таким чином, розробник засвоює ці навички один раз, і їм більше ніколи не доведеться їх вивчати. Якщо ви робите неявну інтеграцію, кожен випадок використання дещо інший, із дещо різними дітьми. Трохи складніше застосувати те, що ви навчилися від одного завдання, до іншого.


1

pos (t) = v (t) * t

працює лише, якщо pos (0) = 0 і v (t) = k

не можна співвідносити положення з часом без знання початкової умови та всієї функції швидкості, тому рівняння є наближенням інтеграла

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

EDIT _________

Ось невеликий доказ на коментарі (припустимо, що позиція (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 (правильно)

Додам, що ваше рівняння вже має чинники постійного прискорення (тобто ваше рівняння є eqn 2, де v (t) = v0 + a * t, а межі інтеграції t0 і t), тому ваше рівняння має працювати до тих пір, поки ви оновите початкове положення, початкова швидкість і прискорення залишаються постійними.

EDIT2 ________

Я також повинен додати, що ви також можете обчислити положення з початковою позицією, початковою швидкістю, початковим прискоренням і постійним ривком. Іншими словами, ви можете створити функцію на основі рівняння 2, яка представляє позицію проти часу, розділивши її на її похідні, тобто швидкість, ривок, що буде далі, і т. Д., І т. Д., І т. Д., Але ви будете точні у своєму рівнянні, лише якщо v (t) можна моделювати таким чином. Якщо v (t) неможливо моделювати зі швидкістю, прискоренням, постійним ривком тощо, тоді вам потрібно повернутися до наближення рівня 2, яке, як правило, відбувається, коли у вас є речі, що підстрибують, опір повітря, вітер тощо .


константа. це просто означає, що v (t) не повинен змінюватися з часом
Kyy13

Мені доведеться посидіти з цим і розібратися, чому це правда ... зараз наголосив :) Я розмістив зразок коду у запитанні на випадок, якщо зміниться щось
davidkomer

немає проблем, оновлено знову кращими словами :)
Kyy13
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.