arrow_upward

MrMineev

🌍 Simulating Gravity

§

Building Simulator

Let’s start by considering two bodies with the masses M1 and M2, then the resulting gravitational force is:

`F = G * (m_(1) * m_(2)) / r^2`

Where r is the distance between the center of masses of the two bodies, and G is the gravitational constant. Now let's write the Python class for the Body.

 import math

class Body:
    name = 'Body'
    mass = 0.0
    vx = vy = 0.0 # velocity
    px = py = 0.0 # position
    G = 6.67428e-11 # kg^(-2) m^2
    color = (0, 0, 0) # color of body for rendering 
    radius = 0

    def attraction(self, other):
        sx, sy = self.px, self.py
        ox, oy = other.px, other.py
        dx = (ox - sx) # difference in x
        dy = (oy - sy) # difference in y
        d = math.sqrt(dx ** 2 + dy ** 2) # distance betweeen bodies

        if d == 0:
            raise ValueError("Collision between objects %r and %r" % (self.name, other.name))

        f = self.G * self.mass * other.mass / (d ** 2) # gravitational force

        # calculating the force vector
        theta = math.atan2(dy, dx)
        fx = math.cos(theta) * f
        fy = math.sin(theta) * f

        return fx, fy

Now that we have written the Body class, we need to figure out to update the body's location. So we want to write the Gravity class to work with multiple instances of Body.

import math
from engine.body import Body

class Gravity:
  timestep = 1

  def __init__(self, bodies):
      self.bodies = bodies

  def add_body(self, body):
      self.bodies.append(body)

  def update(self):
      forces = []
      for body in self.bodies:
          total_fx = total_fy = 0.0
          for other in self.bodies:
              if body is other:
                  continue

              fx, fy = body.attraction(other)
              total_fx += fx
              total_fy += fy
          forces.append(
              (total_fx, total_fy)
          )
      for index in range(0, len(self.bodies)):
          fx, fy = forces[index]
          self.bodies[index].vx = fx / self.bodies[index].mass * self.timestep
          self.bodies[index].vy = fy / self.bodies[index].mass * self.timestep

          self.bodies[index].px += self.bodies[index].vx * self.timestep
          self.bodies[index].py += self.bodies[index].vy * self.timestep

Here, for each body, I loop through each other body and add the force between those to the total force being applied to the body. Now how can we apply the force to the body? Simple, we know that

`F = ma`

And that

`a = (Delta V) / t`

Now knowing these formulas, we can calculate the ΔV so that we can calculate the new location:

`\text{New Position} = \text{Previous Position} + Delta V * t`

And this works. I will spare the details about implementing the pygame application. But here is what we get out of these formulas:

I made the green circle with the characteristics of the moon and the blue circle with the features of Earth. This animation looks dull. The moon is getting attracted to the Earth. We need to give the moon velocity to make the moon orbit around the Earth. After a quick Google search, it is easy to find that the moon's velocity is about 1022 km/s. Applying that to the moon's velocity, we get:

Nice orbiting motion!

§

Adding the Rocket

To keep it simple the rocket is just an instance of the class Body with some additional force applied to fly. So all we need to do to add the rocket is just copy & paste previously written code with some slight changes to be able to steer the rocket by applying additional force:

Nice! Now we implemented the rocket into the simulator.

§

AI-Powered Rocket

Now to the so-called elephant in the room. How to train an AI to control the rocket? Well, my first thought was to record multiple “good flights” (by this I mean flights that made an orbit around the earth) and then create multiple duplicates of that flight just rotating by different angles to create more training data. I recorded all the positions, and velocities of each body at each point during the flight, and recorded the response I made: 0 - no action, 1 - turned rockets on 2 - turned rockets off. I used this TensorFlow model to train:

          import tensorflow as tf

# loading training data
# ...

# now to the model
x_train = tf.keras.utils.normalize(x_train, axis=1)

model = tf.keras.models.Sequential()

model.add(tf.keras.layers.Flatten())
model.add(tf.keras.layers.Dense(2048, activation = tf.nn.relu))
model.add(tf.keras.layers.Dense(516, activation = tf.nn.relu))
model.add(tf.keras.layers.Dense(216, activation = tf.nn.relu))
model.add(tf.keras.layers.Dense(64, activation = tf.nn.relu))
model.add(tf.keras.layers.Dense(5, activation = tf.nn.softmax))

model.compile(optimizer='adam',
              loss='sparse_categorical_crossentropy',
              metrics='accuracy')

model.fit(x_train, y_train, epochs=5)

model.save('rocket_alpha_1.model')

To put it lightly it didn’t work. The information i provided seems to be not enough for the model. What other information should i include?

To put it lightly, it didn’t work. The information I provided seems to be not enough for the model. What other information should I include? I tried adding some data about the trajectory and the current time. However, the AI model would pick one of the actions and just stick with it, thus correctly answering X% (where X is the percentage of training instances with the action the AI chose) of the training data. I even tried inflating the dataset with duplicates to make the number of examples with each action the same to reduce the correct answers and force the model to devise another strategy. However, the TensorFlow model would still not find a pattern in the data. After recording about 5-8 flights with about 5000 training instances for each flight and creating duplicates of each flight just rotated by different angles. In total, I had about 75000 training instances. I guess the data wasn’t diverse enough or had insufficient training instances. The point is training the Tensorflow model didn’t work.

So instead of creating the TensorFlow model, I would just write a piece of software that would steer the rocket with no Machine Learning involved. So I decided to launch the rocket and constantly steer it to the right while it is on, and when the rocket flies out of some area, I will turn it off. After some tries, I found these values. For every 80 seconds, I would turn right by 5°. When the rocket’s distance from the center of the Earth is 120% of the radius of the Earth, I will turn the rocket off. And this is what I get:

A nice looking orbit!

§

Links

  https://github.com/MrMineev/GravitySimulator


§

đź’¬ Comments