Solo un pequeño descargo de responsabilidad de antemano: nunca he estudiado astronomía o ciencias exactas para ese asunto (ni siquiera TI), así que estoy tratando de llenar este vacío mediante la autoeducación. La astronomía es una de las áreas que ha captado mi atención y mi idea de la autoeducación es de enfoque aplicado. Entonces, directamente al grano: este es un modelo de simulación orbital en el que estoy trabajando casualmente cuando tengo tiempo / estado de ánimo. Mi objetivo principal es crear un sistema solar completo en movimiento y la capacidad de planificar el lanzamiento de naves espaciales a otros planetas.
¡Todos ustedes son libres de elegir este proyecto en cualquier momento y divertirse experimentando!
¡¡¡actualizar!!! (10 de noviembre)
- la velocidad ahora es deltaV adecuada y al dar un movimiento adicional ahora se calcula el vector suma de velocidad
- puede colocar tantos objetos estáticos como desee, en cada objeto de unidad en movimiento, verifique los vectores de gravedad de todas las fuentes (y verifique la colisión)
- mejoró enormemente el rendimiento de los cálculos
- Una solución para tener en cuenta el mod interactivo en matplotlib. Parece que esta es la opción predeterminada solo para ipython. Python3 regular requiere esa declaración explícitamente.
Básicamente, ahora es posible "lanzar" una nave espacial desde la superficie de la Tierra y planear una misión a la Luna haciendo correcciones vectoriales deltaV a través de giveMotion (). El siguiente en la línea está tratando de implementar una variable de tiempo global para permitir el movimiento simultáneo, por ejemplo, la Luna orbita la Tierra mientras la nave espacial prueba una maniobra de asistencia por gravedad.
¡Los comentarios y sugerencias de mejoras son siempre bienvenidos!
Hecho en Python3 con la biblioteca matplotlib
import matplotlib.pyplot as plt
import math
plt.ion()
G = 6.673e-11 # gravity constant
gridArea = [0, 200, 0, 200] # margins of the coordinate grid
gridScale = 1000000 # 1 unit of grid equals 1000000m or 1000km
plt.clf() # clear plot area
plt.axis(gridArea) # create new coordinate grid
plt.grid(b="on") # place grid
class Object:
_instances = []
def __init__(self, name, position, radius, mass):
self.name = name
self.position = position
self.radius = radius # in grid values
self.mass = mass
self.placeObject()
self.velocity = 0
Object._instances.append(self)
def placeObject(self):
drawObject = plt.Circle(self.position, radius=self.radius, fill=False, color="black")
plt.gca().add_patch(drawObject)
plt.show()
def giveMotion(self, deltaV, motionDirection, time):
if self.velocity != 0:
x_comp = math.sin(math.radians(self.motionDirection))*self.velocity
y_comp = math.cos(math.radians(self.motionDirection))*self.velocity
x_comp += math.sin(math.radians(motionDirection))*deltaV
y_comp += math.cos(math.radians(motionDirection))*deltaV
self.velocity = math.sqrt((x_comp**2)+(y_comp**2))
if x_comp > 0 and y_comp > 0: # calculate degrees depending on the coordinate quadrant
self.motionDirection = math.degrees(math.asin(abs(x_comp)/self.velocity)) # update motion direction
elif x_comp > 0 and y_comp < 0:
self.motionDirection = math.degrees(math.asin(abs(y_comp)/self.velocity)) + 90
elif x_comp < 0 and y_comp < 0:
self.motionDirection = math.degrees(math.asin(abs(x_comp)/self.velocity)) + 180
else:
self.motionDirection = math.degrees(math.asin(abs(y_comp)/self.velocity)) + 270
else:
self.velocity = self.velocity + deltaV # in m/s
self.motionDirection = motionDirection # degrees
self.time = time # in seconds
self.vectorUpdate()
def vectorUpdate(self):
self.placeObject()
data = []
for t in range(self.time):
motionForce = self.mass * self.velocity # F = m * v
x_net = 0
y_net = 0
for x in [y for y in Object._instances if y is not self]:
distance = math.sqrt(((self.position[0]-x.position[0])**2) +
(self.position[1]-x.position[1])**2)
gravityForce = G*(self.mass * x.mass)/((distance*gridScale)**2)
x_pos = self.position[0] - x.position[0]
y_pos = self.position[1] - x.position[1]
if x_pos <= 0 and y_pos > 0: # calculate degrees depending on the coordinate quadrant
gravityDirection = math.degrees(math.asin(abs(y_pos)/distance))+90
elif x_pos > 0 and y_pos >= 0:
gravityDirection = math.degrees(math.asin(abs(x_pos)/distance))+180
elif x_pos >= 0 and y_pos < 0:
gravityDirection = math.degrees(math.asin(abs(y_pos)/distance))+270
else:
gravityDirection = math.degrees(math.asin(abs(x_pos)/distance))
x_gF = gravityForce * math.sin(math.radians(gravityDirection)) # x component of vector
y_gF = gravityForce * math.cos(math.radians(gravityDirection)) # y component of vector
x_net += x_gF
y_net += y_gF
x_mF = motionForce * math.sin(math.radians(self.motionDirection))
y_mF = motionForce * math.cos(math.radians(self.motionDirection))
x_net += x_mF
y_net += y_mF
netForce = math.sqrt((x_net**2)+(y_net**2))
if x_net > 0 and y_net > 0: # calculate degrees depending on the coordinate quadrant
self.motionDirection = math.degrees(math.asin(abs(x_net)/netForce)) # update motion direction
elif x_net > 0 and y_net < 0:
self.motionDirection = math.degrees(math.asin(abs(y_net)/netForce)) + 90
elif x_net < 0 and y_net < 0:
self.motionDirection = math.degrees(math.asin(abs(x_net)/netForce)) + 180
else:
self.motionDirection = math.degrees(math.asin(abs(y_net)/netForce)) + 270
self.velocity = netForce/self.mass # update velocity
traveled = self.velocity/gridScale # grid distance traveled per 1 sec
self.position = (self.position[0] + math.sin(math.radians(self.motionDirection))*traveled,
self.position[1] + math.cos(math.radians(self.motionDirection))*traveled) # update pos
data.append([self.position[0], self.position[1]])
collision = 0
for x in [y for y in Object._instances if y is not self]:
if (self.position[0] - x.position[0])**2 + (self.position[1] - x.position[1])**2 <= x.radius**2:
collision = 1
break
if collision != 0:
print("Collision!")
break
plt.plot([x[0] for x in data], [x[1] for x in data])
Earth = Object(name="Earth", position=(50.0, 50.0), radius=6.371, mass=5.972e24)
Moon = Object(name="Moon", position=(100.0, 100.0), radius=1.737, mass = 7.347e22) # position not to real scale
Craft = Object(name="SpaceCraft", position=(49.0, 40.0), radius=1, mass=1.0e4)
Craft.giveMotion(deltaV=8500.0, motionDirection=100, time=130000)
Craft.giveMotion(deltaV=2000.0, motionDirection=90, time=60000)
plt.show(block=True)
Cómo funciona
Todo se reduce a dos cosas:
- Crear objetos como
Earth = Object(name="Earth", position=(50.0, 50.0), radius=6.371, mass=5.972e24)
con parámetros de posición en la cuadrícula (1 unidad de cuadrícula es 1000 km por defecto, pero esto también se puede cambiar), radio en unidades de cuadrícula y masa en kg. - Darle al objeto algo deltaV, como
Craft.giveMotion(deltaV=8500.0, motionDirection=100, time=130000)
obviamente requiereCraft = Object(...)
que se cree en primer lugar como se menciona en el punto anterior. Los parámetros aquí estándeltaV
en m / s (tenga en cuenta que por ahora la aceleración es instantánea),motionDirection
es la dirección del deltaV en grados (desde la posición actual imagine un círculo de 360 grados alrededor del objeto, entonces la dirección es un punto en ese círculo) y finalmente el parámetrotime
es cuántos segundos después de la trayectoria de empuje deltaV del objeto será monitoreado.giveMotion()
Comienzo posterior desde la última posición de la anteriorgiveMotion()
.
Preguntas:
- ¿Es este un algoritmo válido para calcular órbitas?
- ¿Cuáles son las mejoras obvias que se harán?
- He estado considerando la variable "timeScale" que optimizará los cálculos, ya que podría no ser necesario volver a calcular los vectores y las posiciones por cada segundo. ¿Alguna idea sobre cómo debería implementarse o es generalmente una buena idea? (pérdida de precisión versus rendimiento mejorado)
Básicamente, mi objetivo es comenzar una discusión sobre el tema y ver a dónde conduce. Y, si es posible, aprenda (o incluso mejor, enseñe) algo nuevo e interesante.
¡Siéntete libre de experimentar!
Intenta usar:
Earth = Object(name="Earth", position=(50.0, 100.0), radius=6.371, mass=5.972e24)
Moon = Object(name="Moon", position=(434.0, 100.0), radius=1.737, mass = 7.347e22)
Craft = Object(name="SpaceCraft", position=(43.0, 100.0), radius=1, mass=1.0e4)
Craft.giveMotion(deltaV=10575.0, motionDirection=180, time=322000)
Craft.giveMotion(deltaV=400.0, motionDirection=180, time=50000)
Con dos quemaduras: un programa en órbita terrestre y otro retrógrado en órbita lunar, logré una órbita lunar estable. ¿Están estos cerca de los valores teóricamente esperados?
Ejercicio sugerido: Pruébelo en 3 quemaduras: órbita estable de la Tierra desde la superficie de la Tierra, programe quemaduras para llegar a la Luna, quemadura retrógrada para estabilizar la órbita alrededor de la Luna. Luego intente minimizar deltaV.
Nota: planeo actualizar el código con comentarios extensos para aquellos que no están familiarizados con la sintaxis de python3.
fuente
Respuestas:
y
Junto con las posiciones y velocidades iniciales, este sistema de ecuaciones diferenciales ordinarias (EDO) comprende un problema de valor inicial. El enfoque habitual es escribir esto como un sistema de primer orden de 8 ecuaciones y aplicar un método Runge-Kutta o multipaso para resolverlas.
Si aplica algo simple como Euler hacia adelante o hacia atrás, verá que la Tierra gira en espiral hacia el infinito o hacia el sol, respectivamente, pero eso es un efecto de los errores numéricos. Si usa un método más preciso, como el método clásico Runge-Kutta de 4to orden, encontrará que permanece cerca de una órbita verdadera por un tiempo, pero aún así se va al infinito. El enfoque correcto es utilizar un método simpléctico, que mantendrá a la Tierra en la órbita correcta, aunque su fase seguirá estando apagada debido a errores numéricos.
Para el problema de los 2 cuerpos, es posible derivar un sistema más simple basando su sistema de coordenadas alrededor del centro de masa. Pero creo que la formulación anterior es más clara si esto es nuevo para usted.
fuente