¿Por qué integrar sobre acumulación?

14

Estoy empezando a aprender a hacer física de bricolaje, y tengo una pregunta sobre la implementación de la integración en el nivel más básico (es decir, esta no es una pregunta de Euler vs. RK4).

Casi todos los ejemplos que encuentro tienen alguna integrate()función que obtiene el paso de tiempo desde la última actualización, y actualiza la aceleración (y / o velocidad y / o posición) desde la última actualización.

En la forma más simple: position += velocity * deltaTime

Sin embargo, no entiendo por qué se acumula de esta manera cuando se podría obtener fácilmente cambiando una función . Por ejemplo: getPosition = makeNewFunction()qué podría devolver algo que tiene la firma de Time -> Position, y el funcionamiento interno de esa función se genera a través de la fórmula matemática adecuada.

De esa manera, no hay acumulación ... cada vez que se necesita obtener la posición, llama a esa función con la hora actual.

Mi comprensión de novato es que esto también evitaría los errores que provienen de la acumulación ... entonces, ¿por qué esto no funciona? ¿Qué me estoy perdiendo?

(fwiw me he reunido una prueba básica del concepto de esta idea-aunque también está probando algunas otras cosas al mismo tiempo, así que no es el ejemplo más limpio: https://github.com/dakom/ball-bounce-frp )

EDITAR 1: como se menciona en los comentarios, probablemente sea importante señalar que aún no he aprendido sobre el cambio de la aceleración, o sobre el manejo de tirones y otras cosas que requieren una integración de orden superior a la aceleración constante.

EDITAR 2: aquí hay un código de muestra básico de la idea y una sintaxis de pseudo javascript: tenga en cuenta que getKinematicPositionse aplica parcialmente, por lo que devuelve una nueva función de solo Tiempo -> Posición:

Me estoy quedando en la posición aquí, pero podría ser algo más, como getVelocity, supongo ...

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
fuente
1
¿Qué haría su función si tuviera una velocidad / aceleración no constante?
Linaith
¡No tengo idea! : D Si esa es la razón, por ejemplo, aún no he llegado a cambiar la aceleración, realmente agradecería una explicación más completa de dónde se rompería como respuesta (de lo contrario, podría seguir este camino funcional y llegar a un callejón sin salida !)
davidkomer
66
Bueno, si tu objeto simplemente gira en círculo, entonces seguro ... ¿qué pasa cuando es una caja que el jugador está empujando? Cuando llamas a getPosition (ahora + 100), ¿predice el futuro para saber cuándo el jugador dejará de presionarlo? Cuando llamas a getPosition (ahora-1000) ¿tiene que recordar el pasado?
user253751

Respuestas:

34

... el funcionamiento interno de esa función se genera a través de la fórmula matemática adecuada ...

Esto funcionará para ciertas clases de problemas, y la frase clave para buscar es una solución de forma cerrada . Por ejemplo, en Kerbal Space Program, el movimiento de una nave espacial en órbita se calcula de esta manera. Desafortunadamente, la mayoría de los problemas no triviales (por ejemplo, reentrada atmosférica de dicha nave espacial) no tienen una solución de forma cerrada conocida. De ahí la necesidad de aproximaciones numéricas matemáticamente más simples (es decir, a lo integrate()largo del tiempo).

MooseBoys
fuente
Ahh ... genial! Si tiene un minuto, ¿qué opina de apuntar a este enfoque funcional y luego recurrir a la acumulación si no puedo encontrar la manera de hacerlo funcionar (por ejemplo, si estoy tratando con un problema de forma no cerrada o No puedo entender cómo convertirlo en uno) Me gusta la idea de generar funciones ya que se ajusta a las matemáticas 1: 1, pero si siempre llego inevitablemente a un callejón sin salida, puede que no valga la pena ...
davidkomer
8
@davidkomer ¿Por qué quieres seguir generando funciones? Si puedes lograr esto, ¡entonces puedes precalcular y registrar toda la trayectoria! Por supuesto, la gente ya hace esto: se llama animación , y tiene su parte de sutilezas.
Joker_vD
Las funciones cambian en función de la dinámica de tiempo de ejecución ... vea el rebote de bolas FRP, por ejemplo
davidkomer
En realidad, necesitaré actualizar ese ejemplo para que sea algo más como pong, con objetos en movimiento controlados aleatoriamente / por el usuario ...
davidkomer
10

El problema con su enfoque es que no tiene un historial de su objeto. Puede calcular la posición si se mueve en una dirección, pero ¿qué sucede si golpea algo y se recupera?
Si acumula desde su última posición conocida, puede manejar el impacto y continuar desde allí. Si intenta calcularlo desde el principio, debe volver a calcular el impacto cada vez o establecerlo como una nueva posición de inicio.

Tu ejemplo me recordó a un juego de carreras. (No sé si la posición sería controlada por el motor de física, pero creo que funciona muy bien explicarlo)
Si conduce con su automóvil, puede acelerar y reducir la velocidad. No puede calcular su posición sin saber cómo se veía el perfil de velocidad de su automóvil desde el principio hasta ahora. Acumular la distancia es mucho más fácil que almacenar la velocidad que tenía en cada cuadro desde el principio hasta ahora.

Descargo de responsabilidad: no he escrito la física del juego por ahora, así es como veo el problema.

Editar:
en este diagrama puede ver cómo cambian los valores con el tiempo.
rojo = aceleración (desde comenzar a acelerar hasta descender)
verde = velocidad (desde comenzar a detenerse)
azul = el camino que siguió.

La velocidad total es la integral de la aceleración desde su punto de inicio hasta su registro real. (El área entre la línea y el eje)
El camino es la integral de su velocidad.
Si conoce los valores de su aceleración, puede calcular los otros valores. Pero si no me equivoco, las integrales también se calculan por acumulación en las PC. Y es mucho más sobrecarga tener almacenados todos los valores de aceleración.
Además, probablemente sea demasiado para calcular cada cuadro.

diagrama de aceleración / velocidad / tiempo de camino

Lo sé, mis habilidades de pintura son geniales. ;)

Edición 2:
este ejemplo es para movimiento lineal. La dirección de arrastre hace que esto sea aún más difícil.

Linaith
fuente
"o establecerlo como nueva posición de inicio". - Sí, pero no veo el problema con eso :) Ejemplo de re: car ... Tengo la fuerte sensación de que realmente necesito comenzar a jugar con algo más complejo como eso para comprender intuitivamente dónde falla esto ... .
davidkomer
Establecer una nueva posición probablemente no sea un problema.
edité la
1
En un juego de autos, imagino que la aceleración sería aún más compleja. Probablemente habría saltos y picos, y puede depender de la velocidad. (Por ejemplo, la aceleración disminuye a 0 cuando el automóvil se acerca a la velocidad máxima.)
jpmc26
3
@davidkomer ni siquiera se moleste con un automóvil (a menos que lo desee), un juego de plataformas básico lo hará. ¿Cómo funciona mario.getPosition (Time) en Super Mario Bros?
user253751
8

Sin embargo, no entiendo por qué se acumula de esta manera cuando se podría obtener fácilmente cambiando una función. Por ejemplo: getPosition = makeNewFunction () que podría devolver algo que tiene la firma de Tiempo -> Posición, y el funcionamiento interno de esa función se genera a través de la fórmula matemática adecuada.

¡Usted puede!

Se llama usando un solución analítica o de forma cerrada . Tiene el beneficio de que es más preciso, ya que los errores de redondeo que se acumulan con el tiempo son inexistentes.

Sin embargo, esto funciona si y solo si conoce de antemano una forma tan cerrada. Para los juegos, esto no suele ser el caso.

El movimiento del jugador es errático y simplemente no se puede poner en alguna función precalculada. El jugador puede y cambiará su velocidad y orientación con bastante frecuencia.

Los NPC podrían utilizar soluciones de forma cerrada, y de hecho a veces lo hacen. Sin embargo, esto tiene otros inconvenientes. Piensa en un simple juego de carreras. Cada vez que su vehículo colisiona con otro vehículo, debe cambiar su función. Quizás el auto se mueva más rápido dependiendo del metro. Entonces encontrar una solución tan cerrada será bastante difícil. De hecho, es probable que haya más casos en los que encontrar una forma tan cerrada sea imposible o tan complicado que simplemente no sea factible.

Un gran ejemplo de uso de una solución de forma cerrada es Kerbal Space Program. Tan pronto como su cohete esté en órbita y no bajo empuje, KSP puede ponerlo "sobre rieles". Las órbitas están predeterminadas en el sistema de dos cuerpos y son periódicas. Mientras el cohete no aplique más empuje, ya sabe dónde estará el cohete, y simplemente puede llamargetPositionAtTime(t) (no se llama exactamente eso, pero entiendes la idea).

En la práctica, sin embargo, el uso de la integración paso a paso suele ser mucho más práctico. Pero cuando ve una situación en la que existe una solución de forma cerrada y es fácil de calcular, ¡adelante! No hay razón para no usarlo.

Por ejemplo, si tu personaje apunta a un cañón, puedes mostrar fácilmente el punto de impacto previsto de la bola de cañón usando una solución de forma cerrada. Y, si su juego no permite alterar el curso de la bola de cañón (sin viento, por ejemplo), incluso puede usarlo para mover la bola de cañón. Tenga en cuenta que debe tener especial cuidado con los obstáculos que se mueven en el camino de su bala de cañón, entonces.

Hay muchas situaciones similares. Si está construyendo un juego basado en rondas, entonces es probable que haya muchas más soluciones de forma cerrada disponibles que cuando construye un juego RTS, ya que conoce todos los parámetros de antemano y puede decir con certeza que no cambian (nada se mueve de repente en ese camino, por ejemplo).

Tenga en cuenta que existen técnicas para combatir las imprecisiones numéricas de la integración escalonada. Por ejemplo, puede realizar un seguimiento del error acumulado y aplicar un término correctivo para mantener el error bajo control, por ejemplo, Kahan Summation

Poligoma
fuente
8

En el caso de una simple pelota que rebota, es fácil encontrar soluciones de forma cerrada. Sin embargo, los sistemas más complejos tienden a requerir la resolución de una ecuación diferencial ordinaria (EDO). Se requieren solucionadores numéricos para manejar todos menos los casos más simples.

De hecho, hay dos clases de solucionadores numéricos de EDO: explícito e implícito. Los solucionadores explícitos proporcionan una aproximación de forma cerrada para su próximo estado, mientras que los solucionadores implícitos requieren resolver una ecuación para hacerlo. ¡Lo que usted describe para su pelota que rebota es en realidad un solucionador implícito de ODE, lo supiera o no!

Los solucionadores implícitos tienen la ventaja de poder usar pasos de tiempo mucho más largos. Para su algoritmo de pelota que rebota, su paso de tiempo puede ser al menos tan largo como la duración hasta la próxima colisión (lo que cambiaría su función). Esto puede hacer que su programa se ejecute mucho más rápido. Sin embargo, en general, no siempre podemos encontrar buenas soluciones implícitas a las EDO que nos interesan. Cuando no podemos, recurrimos a la integración explícita.

La gran ventaja que veo con la integración explícita es que las trampas son bien conocidas. Puede abrir cualquier libro de texto de los años 60 y leer todo lo que necesita saber sobre las pequeñas peculiaridades que surgen con técnicas de integración particulares. Por lo tanto, un desarrollador aprende estas habilidades una vez y nunca más tiene que aprenderlas. Si está haciendo una integración implícita, cada caso de uso es ligeramente diferente, con problemas ligeramente diferentes. Es un poco más difícil aplicar lo que aprendiste de una tarea a la siguiente.

Cort Ammon - Restablece a Monica
fuente
1

pos (t) = v (t) * t

solo funciona si pos (0) = 0 y v (t) = k

no se puede relacionar la posición con el tiempo sin conocer la condición inicial y la función de velocidad completa, por lo que la ecuación es como una aproximación de la integral

pos (t) = integral de v (t) dt de 0 a t

EDITAR _________

Aquí hay una pequeña prueba por los comentarios (suponiendo pos (0) = 0)

dejar v (t) = 4

ecuación 1: pos (t) = 4 * t (correcto)

ecuación 2: pos (t) = c + 4 * t de 0 a t = 4 * t (correcto)

dejar v (t) = 2 * t

ecuación 1: pos (t) = 2 * t ^ 2 (incorrecto)

ecuación 2: pos (t) = c + t ^ 2 de 0 a t = t ^ 2 (correcto)

Debo agregar que su ecuación ya tiene en cuenta la aceleración constante (es decir, su ecuación es eqn 2 donde v (t) = v0 + a * t y los límites de integración son t0 y t), por lo que su ecuación debería funcionar siempre que actualice La posición inicial, la velocidad inicial y la aceleración permanecen constantes.

EDITAR2 ________

También debo agregar que también puedes calcular la posición con la posición inicial, la velocidad inicial, la aceleración inicial y la sacudida constante. En otras palabras, puede crear una función basada en la ecuación 2 que represente la posición frente al tiempo separándola en sus derivadas, es decir, velocidad, tirón, lo que venga después, etc., etc., pero solo será precisa en su ecuación si v (t) se puede modelar de esa manera. Si v (t) no se puede modelar solo con velocidad, aceleración, sacudidas constantes, etc., entonces debe volver a una aproximación de la ecuación 2, que tiende a suceder cuando tiene cosas que rebotan, resistencia al aire, viento, etc. .

Kyy13
fuente
una constante. solo significa que v (t) no debe variar con el tiempo
Kyy13
Tendré que sentarme con esto y averiguar por qué es verdad ... votar por ahora :) Publiqué una muestra de código en la pregunta en caso de que eso cambie las cosas
davidkomer
no hay problema, actualizado nuevamente con mejores palabras :)
Kyy13