Physique de jeu parfaite et à précision infinie en Python (Partie 1)
Avec une précision absolue, programmez "Newton's Cradle", "tennis ball & basketball drop", et plus encore
Ceci est le premier de quatre articles vous montrant comment programmer un moteur physique parfait en Python. C'est un tout petit pas dans ma grande ambition de transformer toute la physique, les mathématiques et même la philosophie en programmation. Grâce à ce projet, nous découvrirons des surprises, augmenterons notre compréhension et (je l'espère) nous amuserons. Tout le code est disponible sur GitHub .
Nous limiterons le moteur de plusieurs manières, par exemple, aux collisions newtoniennes entre des cercles et des lignes. Cependant, nous ne limiterons pas la précision du moteur. Il représentera tous les temps, positions et vitesses avec des expressions exactes telles que 8*sqrt(3)/3
. En d'autres termes, il évite toute approximation numérique.
Les résultats seront des simulations parfaites, par exemple, du jouet Newton's Cradle. (Pour rejouer/mettre en pause la vidéo, appuyez sur le bouton dans le coin inférieur gauche. Le son est utile.)
À quoi sert un moteur physique d'une précision absolue ? Entre autres choses, cela peut améliorer notre compréhension du monde physique. Dans cet article (Partie 1), nous verrons ces nouvelles découvertes :
- Limite de vitesse de la balle de tennis — Dans une démonstration de physique populaire, vous faites tomber une balle de tennis avec un ballon de basket. La balle de tennis rebondit beaucoup plus vite qu'elle ne tombe. Combien plus rapide? Nous verrons que la vitesse de « montée » n'est jamais supérieure à trois fois la vitesse de « descente ». Cela tient même si nous rendons le ballon de basket infiniment massif.
- Bonnes vibrations - Imaginez une balle de tennis en mouvement coincée entre un ballon de basket et un mur. Dans le court laps de temps entre deux images vidéo, la balle de tennis peut rebondir 80 fois. Un moteur utilisant des approximations pourrait rater certains de ces rebonds. Notre moteur compte tout le monde.
- Billiards Broken - Même avec une précision infinie, une pause au billard ne peut pratiquement pas être inversée. Nous verrons que même lorsque nous supprimons «l'effet papillon» et l'incertitude quantique, le monde est toujours aléatoire et non déterministe.
Construire le moteur
Supposons que nous ayons déjà des fonctions Python pour :
- Étant donné deux objets (éventuellement en mouvement) dans le monde, renvoyez le laps de temps exact jusqu'à ce qu'ils se touchent. La réponse peut être "jamais".
- Étant donné deux objets en collision, comment, exactement, la collision changera-t-elle leur vitesse et leur direction ?
Travaillons à travers une approche en examinant trois mondes physiques, en commençant par le berceau de Newton.
Berceau de Newton
L'idée générale de notre moteur est de trouver l'heure exacte jusqu'à la prochaine collision dans le monde. Avancez jusqu'à ce moment précis. Ajustez les vitesses des objets en collision. Répéter.
Pour le berceau de Newton, nous avons d'abord mis en place des cercles et des murs.
from perfect_physics import World, Circle, Wall
circle_list = [Circle(x=1, y=0, r=1, vx=1, vy=0, m=1)]
for i in range(1, 6):
circle_list.append(Circle(x=i * 2 + 4, y=0, r=1, vx=0, vy=0, m=1))
wall_list = [Wall(x0=0, y0=0, x1=0, y1=1), Wall(x0=20, y0=0, x1=20, y1=1)]
world = World(circle_list, wall_list, xlim=(-1, 21), ylim=(-2, 2))
world.show()
- Entre chaque paire d'objets, trouvez le laps de temps exact pendant lequel ils entrent en collision (le cas échéant). Ignorez les autres paires et les collisions. — Dans ce cas, le premier cercle (en mouvement) entrera en collision avec le deuxième cercle à l'intervalle de temps 3. En ignorant cette collision, il entrera également en collision avec le troisième cercle à l'intervalle de temps 5, etc. Il entrera en collision avec le mur du fond à l'instant durée 18.
- Trouvez l'intervalle de temps de la ou des premières collisions et avancez l'horloge mondiale exactement d'autant. Dans ce cas, 3.
- Ajustez les vitesses de tous les objets impliqués dans ces collisions. Comme prévu avec le berceau de Newton, le premier cercle devient stationnaire et le deuxième cercle commence à bouger.
- Répétez aussi longtemps que vous le souhaitez. Dans l'exemple, la deuxième et la troisième boule entreront ensuite en collision dans l'intervalle de temps 0. Cette vidéo montre l'action, événement par événement. (Un « événement » consiste soit à avancer dans le temps jusqu'à une collision, soit à ajuster les vitesses en fonction de la collision)
En général, pour chaque image, nous :
- Trouvez la valeur de l'horloge de simulation pour cette image.
- Trouvez l'événement de collision qui précède cette valeur d'horloge.
- En commençant par le monde à la collision précédente, déplacez les cercles vers la valeur d'horloge de l'image. (De par leur conception, les cercles n'entreront en collision avec rien pendant ce mouvement.)
Cercle et Triangle
Je prétends que les heures et les positions sont toutes exactes, mais me croyez-vous ? En voici une preuve via un cercle inscrit par un triangle. Tout d'abord, voici la vidéo montrant juste les événements. Vous pouvez voir l'horloge réglée sur des expressions telles que 8*sqrt(3)/3
.
Et voici la vidéo régulière pour le cercle dans un triangle :
Dans ce cas, la simulation tombe dans un modèle. Sans une précision infinie, le motif aurait pu être manqué.
Chute de balles de tennis et de basket-ball
Regardons un autre monde physique pour voir un autre avantage de la précision infinie. Ce monde comprend une balle de tennis et un ballon de basket se déplaçant vers un mur à la vitesse 1. La masse du ballon de basket est 1000 fois celle de la balle de tennis. (Nos mondes n'ont pas de gravité.)
from perfect_physics import World, Circle, Wall
big_radius = 10
world_width = 40
folder = root / f"part1/two_size{big_radius}"
big = Circle(x=world_width // 2, y=0, r=big_radius, vx=1, vy=0, m=big_radius**3)
little = Circle(x=big.x - big_radius - 1, y=0, r=1, vx=1, vy=0, m=1)
circle_list = [big, little]
wall_list = [Wall(x0=0, y0=0, x1=0, y1=1), Wall(x0=world_width, y0=0, x1=world_width, y1=1)]
world = World(circle_list, wall_list, xlim=(-1, world_width + 1), ylim=(-big_radius - 1, big_radius + 1))
world.run_in_place(2, show=True)
print([circle.vx for circle in world.circle_list])
Comme prévu (si vous avez déjà essayé cela dans le monde physique), la balle de tennis accélère. Cela m'a surpris, cependant, en n'allant qu'environ trois fois plus vite.
Avançons un instant et utilisons les outils que nous développerons dans la partie 3. Ce code Python montre l'effet sur les balles de tennis d'un ballon de basket avec une masse infinie. La balle de tennis entre avec une vitesse de 1 et ressort avec une vitesse d'exactement 3.
from sympy import limit, simplify, oo
from perfect_physics import load
cc_velocity_solution = load("data/cc_velocity_solution.sympy")
a_vx, a_vy, b_vx, b_vy = cc_velocity_solution.subs([("a_x", 10), ("a_y", 0), ("a_r", 10), ("a_vx", -1), ("a_vy", 0),
("b_x", -1), ("b_y", 0), ("b_r", 1), ("b_vx", 1), ("b_vy", 0), ("b_m", 1)])
print(simplify(b_vx))
limit(b_vx, "a_m", oo)
# prints (1 - 3*a_m)/(a_m + 1)
# returns -3
Le moteur m'a encore plus surpris quand je l'ai laissé tourner plus longtemps. Regardez ce qui se passe au temps vidéo 20 secondes (temps de simulation 200):
Le ballon de basket écrase la balle de tennis. Cela fait rebondir la balle de tennis plus de 80 fois dans une seule image vidéo. La balle de tennis atteint une vitesse supérieure à 31. Sur la piste audio de la vidéo, la vibration de la balle de tennis produit une tonalité de plus de 2000 Hz (battements/seconde).
Si au lieu d'un calcul exact, nous échantillonnions juste une fois par image, nous manquerions (ou mal calculerions) cette action rapide.
Résumé de la partie 1
Ça marche! Nous avons un moteur physique parfait. Nous avons transformé un peu de physique en programmation.
Nous avons supposé que nous avions commencé avec deux fonctions Python qui nous disaient - pour n'importe quelle paire d'objets - deux choses 1) Le temps jusqu'à leur prochaine collision (le cas échéant) et 2) L'effet de cette collision sur leurs vitesses. Nous avons vu comment transformer ces deux fonctions en un moteur physique parfait en : avançant dans le temps jusqu'à la ou les prochaines collisions et en ajustant les vitesses de tous les objets impliqués dans la ou les collisions.
Nous avons créé des vidéos en recherchant la collision qui a précédé une image vidéo, puis en avançant dans le temps (sans nous soucier des collisions) jusqu'à cette image.
Le berceau de Newton, le premier monde, s'est comporté comme prévu. Le deuxième monde, un cercle inscrit par un triangle, utilisant des expressions telles que 8*sqrt(3)/3
pour le temps, etc., a trouvé un modèle. La chute de la balle de tennis et du basket-ball a révélé une limite surprenante à la vitesse de la balle de tennis. De plus, il produisait des rebonds plus rapidement que des méthodes moins parfaites ne pouvaient le calculer.
L'article suivant, Partie 2 , simule une pause billard avec des résultats philosophiques qui me surprennent. Ensuite, dans la partie 3, nous verrons comment faire en sorte que l'ordinateur crée ces deux fonctions de départ. Enfin, dans la partie 4, nous allons accélérer un peu le moteur (mais pas assez) et discuter des limitations.
Si vous avez des idées de simulations que vous aimeriez que je réalise, n'hésitez pas à me les envoyer. Ils peuvent devenir la base d'une partie 5.
Vous pouvez télécharger ce code depuis CarlKCarlK/perfect-physics (github.com) . Faites-moi savoir s'il y a un intérêt et je créerai un programme d'installation plus agréable.
Suivez Carl M. Kadie - Medium pour les notifications des prochaines parties. Toutes les parties seront libres de lecture. Enfin, sur YouTube, j'ai des simulations de physique plus anciennes (approximatives) et des vidéos de nature humoristiques .