Por que integrar sobre a acumulação?

14

Estou começando a aprender a física de bricolage e tenho uma pergunta sobre a implementação da integração no nível mais básico (isto é, não é uma pergunta de Euler vs. RK4).

Quase todos os exemplos que encontrei têm alguma integrate()função que obtém o timestep desde a última atualização e atualiza a aceleração (e / ou velocidade e / ou posição) desde a última atualização.

Na forma mais simples: position += velocity * deltaTime

No entanto, eu não entendo por que ele é acumulado dessa forma quando pode ser obtido facilmente com a alteração de uma função . Por exemplo: o getPosition = makeNewFunction()que poderia retornar algo que possui a assinatura de Time -> Position, e o funcionamento interno dessa função é gerado por meio da fórmula matemática apropriada.

Dessa forma, não há acumulação ... sempre que a posição precisa ser obtida, ela chama essa função com a hora atual.

Meu entendimento para iniciantes é que isso também evitaria os erros resultantes da acumulação ... então, por que isso não funciona, o que estou perdendo?

(fwiw eu fiz reunir uma prova básica do conceito desta idéia-embora também está testando algumas outras coisas ao mesmo tempo, por isso não é o exemplo mais limpa: https://github.com/dakom/ball-bounce-frp )

EDIT 1: como mencionado nos comentários, provavelmente é importante salientar que eu tenho não aprendi sobre a alteração da aceleração ou sobre como lidar com o idiota e outras coisas que exigem integração de ordem superior à aceleração constante.

EDIT 2: aqui está um código de exemplo básico da ideia e sintaxe de pseudo-javascript - observe que getKinematicPositioné parcialmente aplicado e, portanto, está retornando uma nova função apenas de Time -> Position:

Estou mantendo a posição aqui, mas poderia ser outra coisa, tipo getVelocity, eu acho ...

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
fonte
1
O que sua função faria se você tivesse velocidade / aceleração não constante?
Linaith
Eu não faço ideia! : D Se esse for o motivo - por exemplo, eu ainda não mudei a aceleração, eu realmente apreciaria uma explicação mais completa de onde isso seria quebrado como resposta (caso contrário, eu posso seguir por esse caminho funcional e chegar a um beco sem saída !)
davidkomer
6
Bem, se o seu objeto gira em círculo, então ... e quando é uma caixa que o jogador está empurrando? Quando você chama getPosition (agora + 100), ele prevê o futuro para saber quando o jogador irá parar de pressioná-lo? Quando você chama getPosition (agora-1000), ele precisa se lembrar do passado?
user253751

Respostas:

34

... o funcionamento interno dessa função é gerado através da fórmula matemática apropriada ...

Isso funcionará para determinadas classes de problemas, e a frase-chave a ser pesquisada é uma solução em formato fechado . Por exemplo, no Kerbal Space Program, o movimento de uma espaçonave em órbita é calculado dessa maneira. Infelizmente, a maioria dos problemas não triviais (por exemplo, reentrada atmosférica da referida espaçonave) não tem solução de forma fechada conhecida. Daí a necessidade de aproximações numéricas matematicamente mais simples (isto é, ao integrate()longo do tempo).

MooseBoys
fonte
Ahh ... incrível! Se você tiver um minuto - o que você acha de apontar para essa abordagem funcional e depois voltar a acumular se eu não conseguir descobrir como fazê-la funcionar (por exemplo, se estou lidando com um problema de formulário não fechado ou Não consigo descobrir como transformá-lo em um)? Eu gosto da idéia de gerar funções, uma vez que se encaixa na matemática 1: 1 - mas se eu sempre atingir um beco sem saída, pode não valer a pena ...
davidkomer
8
@davidkomer Por que você quer continuar gerando funções? Se você conseguir fazer isso, basta pré-calcular e registrar toda a trajetória! Obviamente, as pessoas já fazem isso: chama-se animação e tem sua parcela de sutilezas.
Joker_vD 25/07/19
As funções mudam com base na dinâmica de tempo de execução ... ver a FRP bola-bounce por exemplo
davidkomer
Na verdade, vou precisar atualizar esse exemplo para ser algo mais parecido com pong, com objetos em movimento controlados aleatoriamente / pelo usuário ...
davidkomer
10

O problema com sua abordagem é que você não tem um histórico do seu objeto. Você pode calcular a posição se você se mover em uma direção, mas o que acontece se você bater em algo e se recuperar?
Se você acumular a partir da sua última posição conhecida, poderá lidar com o impacto e prosseguir a partir daí. Se você tentar calculá-lo desde o início, precisará recalcular o impacto toda vez ou defini-lo como nova posição inicial.

Seu exemplo me lembrou um jogo de corrida. (Não sei se a posição seria controlada pelo mecanismo de física, mas acho que funciona muito bem para explicar)
Se você dirige com o carro, pode acelerar e desacelerar. Você não pode calcular sua posição sem saber como era o perfil de velocidade do seu carro do começo ao agora. Acumular a distância é muito mais fácil do que armazenar a velocidade que você tinha em todos os quadros do início ao agora.

Disclaimer: Eu não escrevi física de jogos até agora, é assim que vejo o problema.

Editar:
neste diagrama, você pode ver como os valores mudam ao longo do tempo.
vermelho = aceleração (do início à aceleração até o movimento lento)
verde = velocidade (do início ao fim)
azul = o caminho a seguir.

A velocidade total é parte integrante da aceleração do seu ponto inicial até a sua logon real. (A área entre a linha e o eixo)
O caminho é parte integrante da sua velocidade.
Se você conhece os valores para sua aceleração, pode calcular os outros valores. Mas, se não me engano, as integrais também são calculadas pela acumulação nos PCs. E é muito mais caro ter todos os valores de aceleração armazenados.
Além disso, é provavelmente demais para ser calculado a cada quadro.

diagrama de aceleração / velocidade / tempo-caminho

Eu sei, minhas habilidades de pintura são ótimas. ;)

Edit 2:
Este exemplo é para movimento linear. A direção do canto torna isso ainda mais difícil.

Linaith
fonte
"ou defina-a como nova posição inicial." - sim, mas não vejo problema com isso :) Re: exemplo de carro ... Estou com a forte sensação de que realmente preciso começar a brincar com algo mais complexo como esse para entender intuitivamente onde isso falha .. .
davidkomer
definir uma nova posição provavelmente não é um problema. eu editei a peça do carro
Linaith
1
Em um jogo de carros, imagino que a aceleração seria ainda mais complexa. Provavelmente haveria saltos e picos, e isso pode depender da velocidade. (Por exemplo, a aceleração diminui para 0, quando o carro se aproxima da velocidade de topo.)
jpmc26
3
@davidkomer nem se preocupa com um carro (a menos que você queira), um jogo de plataforma básico o fará. Como o mario.getPosition (Time) funciona no Super Mario Bros?
user253751
8

No entanto, eu não entendo por que ele é acumulado dessa forma quando pode ser obtido facilmente com a alteração de uma função. Por exemplo: getPosition = makeNewFunction () que pode retornar algo que possui a assinatura Time -> Position, e o funcionamento interno dessa função é gerado por meio da fórmula matemática apropriada.

Você pode!

É chamado usando uma solução analítica ou de formulário fechado . Tem o benefício de ser mais preciso, pois os erros de arredondamento que se acumulam ao longo do tempo são inexistentes.

No entanto, isso funciona se e somente se você conhecer um formulário fechado com antecedência. Para jogos, isso geralmente não é o caso.

O movimento do jogador é irregular e simplesmente não pode ser colocado em alguma função pré-calculada. O jogador pode e irá mudar sua velocidade e orientação com bastante frequência.

Os NPCs poderiam potencialmente utilizar soluções de formulário fechado e, de fato, às vezes o fazem. Isso tem outras desvantagens, no entanto. Pense em um jogo de corrida simples. Sempre que seu veículo colidir com outro veículo, você precisará alterar sua função. Talvez o carro se mova mais rápido, dependendo do metrô. Então, encontrar uma solução em formato fechado será bastante difícil. De fato, há provavelmente mais casos em que encontrar uma forma tão fechada é impossível ou tão complicado que simplesmente não é viável.

Um ótimo exemplo de uso de uma solução fechada é o Kerbal Space Program. Assim que seu foguete estiver em órbita e não estiver sob pressão, o KSP poderá colocá-lo "nos trilhos". As órbitas são pré-determinadas no sistema de dois corpos e são periódicas. Contanto que o foguete não aplique mais força, você já sabe onde o foguete estará e pode simplesmente ligar getPositionAtTime(t)(não é exatamente o mesmo, mas você entendeu).

Na prática, no entanto, o simples uso da integração passo a passo costuma ser muito mais prático. Mas quando você vir uma situação em que existe uma solução de formulário fechado e é fácil de calcular, vá em frente! Não há razão para não usá-lo.

Por exemplo, se seu personagem está mirando um canhão, você pode mostrar facilmente o ponto de impacto previsto da bola de canhão usando uma solução de forma fechada. E, se o seu jogo não permitir que o curso da bola de canhão seja alterado (sem vento, por exemplo), você pode até usá-lo para mover a bola de canhão. Observe que você precisa tomar um cuidado especial com os obstáculos que se movem no caminho da sua bola de canhão.

Existem muitas situações semelhantes. Se você estiver construindo um jogo baseado em rodadas, é provável que haja muito mais soluções em formato fechado disponíveis do que quando estiver construindo um jogo RTS, já que você conhece todos os parâmetros de antemão e pode dizer com certeza que eles não mudam (nada se move repentinamente) nesse caminho, por exemplo).

Observe que existem técnicas para combater as imprecisões numéricas da integração por etapas. Por exemplo, você pode acompanhar o erro acumulado e aplicar um termo corretivo para manter o erro sob controle, por exemplo, Kahan Summation

Polygnome
fonte
8

No caso de apenas uma bola quicando simples, é fácil criar soluções de forma fechada. No entanto, os sistemas mais complexos tendem a exigir a resolução de uma Equação Diferencial Ordinária (ODE). Solucionadores numéricos são necessários para lidar com todos, exceto nos casos mais simples.

De fato, existem duas classes de solucionadores numéricos de EDO: explícitos e implícitos. Os solucionadores explícitos fornecem uma aproximação de forma fechada para o seu próximo estado, enquanto os solucionadores implícitos exigem a resolução de uma equação para fazê-lo. O que você descreve para a sua bola quicando é na verdade um solucionador implícito de ODE, quer você saiba ou não!

Os solucionadores implícitos têm a vantagem de poder usar etapas de tempo muito maiores. Para o seu algoritmo de bola quicando, seu passo no tempo pode ser pelo menos tão grande quanto a duração até a próxima colisão (o que mudaria sua função). Isso pode fazer com que seu programa seja executado muito mais rápido. No entanto, em geral, nem sempre podemos encontrar boas soluções implícitas para os ODEs nos quais estamos interessados. Quando não conseguimos, recorremos à integração explícita.

A grande vantagem que vejo com a integração explícita é que as dicas são bem conhecidas. Você pode abrir qualquer livro didático dos anos 60 e ler tudo o que precisa saber sobre as pequenas peculiaridades que surgem com determinadas técnicas de integração. Assim, um desenvolvedor aprende essas habilidades uma vez e nunca precisa aprendê-las novamente. Se você estiver fazendo integração implícita, cada caso de uso será um pouco diferente, com dicas um pouco diferentes. É um pouco mais difícil aplicar o que você aprendeu de uma tarefa para a seguinte.

Cort Ammon - Restabelecer Monica
fonte
1

pos (t) = v (t) * t

só funciona se pos (0) = 0 e v (t) = k

você não pode relacionar posição ao tempo sem o conhecimento da condição inicial e de toda a função de velocidade; portanto, a equação é uma aproximação da integral

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

EDITAR _________

Heres uma pequena prova pelos comentários (assumindo pos (0) = 0)

Seja v (t) = 4

eqn 1: pos (t) = 4 * t (correto)

eqn 2: pos (t) = c + 4 * t de 0 a t = 4 * t (correto)

Seja v (t) = 2 * t

eqn 1: pos (t) = 2 * t ^ 2 (errado)

eqn 2: pos (t) = c + t ^ 2 de 0 a t = t ^ 2 (correto)

Devo acrescentar que sua equação já é fator de aceleração constante (ou seja, sua equação é a eqn 2, em que v (t) = v0 + a * t e os limites de integração são t0 e t), portanto sua equação deve funcionar desde que você atualize a posição inicial, velocidade inicial e a aceleração permanecem constantes.

EDIT2 ________

Devo acrescentar também que você também pode calcular a posição com pos inicial, velocidade inicial, aceleração inicial e empurrão constante. Em outras palavras, você pode criar uma função baseada na equação 2 que representa a posição versus o tempo, separando-a em suas derivadas, como velocidade, empurrão, o que vier a seguir, etc, etc, etc, mas você só será preciso em sua equação se v (t) pode ser modelado dessa maneira. Se v (t) não puder ser modelado apenas com velocidade, aceleração, solavanco constante, etc, será necessário voltar a uma aproximação da eqn 2, o que tende a acontecer quando há quedas de coisas, resistência do ar, vento, etc. .

Kyy13
fonte
uma constante. significa apenas que v (t) não deve variar ao longo do tempo
Kyy13
Eu vou ter que sentar-se com isso e trabalhar para fora porque é verdade ... upvoting por agora :) Eu postei uma amostra de código em questão no caso em que muda as coisas
davidkomer
não tem problema, atualizado novamente com melhores palavras :)
Kyy13