Solo un pequeño descargo de responsabilidad de antemano: nunca he estudiado astronomía ni ninguna ciencia exacta (ni siquiera TI), por lo que estoy tratando de llenar este vacío mediante la autoeducación. La astronomía es una de las áreas que más me ha llamado la atención y mi idea de la autoeducación es directa con un enfoque aplicado. Entonces, directo al grano: este es un modelo de simulación orbital en el que estoy trabajando casualmente cuando tengo tiempo/animo. Mi objetivo principal es crear un sistema solar completo en movimiento y la capacidad de planificar lanzamientos de naves espaciales a otros planetas.
¡Todos son libres de retomar este proyecto en cualquier momento y divertirse experimentando!
¡¡¡actualizar!!! (10 de noviembre)
Básicamente, ahora es posible "lanzar" una nave espacial desde la superficie de la Tierra y trazar una misión a la Luna haciendo correcciones del vector deltaV a través de giveMotion(). Lo siguiente en la línea es intentar 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 para mejorar 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:
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.Craft.giveMotion(deltaV=8500.0, motionDirection=100, time=130000)
obviamente requiere Craft = Object(...)
ser creado en primer lugar como se mencionó en el punto anterior. Los parámetros aquí están deltaV
en m/s (tenga en cuenta que por ahora la aceleración es instantánea), motionDirection
es la dirección de deltaV en grados (desde la posición actual, imagine un círculo de 360 grados alrededor del objeto, por lo que la dirección es un punto en ese círculo) y finalmente el parámetro time
es cuántos segundos después de que la trayectoria de empuje deltaV del objeto sea monitoreada. Las subsiguientes giveMotion()
comienzan desde la última posición de la anterior giveMotion()
.Preguntas:
Básicamente, mi objetivo es iniciar una discusión sobre el tema y ver a dónde conduce. Y, si es posible, aprenda (o mejor aún, 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 encendidos, uno progrado en la órbita terrestre y otro retrógrado en la órbita lunar, logré una órbita lunar estable. ¿Están cerca de los valores teóricos esperados?
Ejercicio sugerido: Pruébelo en 3 encendidos: órbita terrestre estable desde la superficie de la Tierra, encendido progresivo para llegar a la Luna, encendido retrógrado para estabilizar la órbita alrededor de la Luna. Luego intente minimizar deltaV.
Nota: Planeo actualizar el código con extensos comentarios para aquellos que no están familiarizados con la sintaxis de python3.
Que los dos cuerpos involucrados tengan masas . Comience con la segunda ley de Newton
dónde es el vector de posición relativa para los dos cuerpos en cuestión. La fuerza sobre el cuerpo 1 del cuerpo dos es, por supuesto desde . Si denotamos las posiciones por y , después
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 de Runge-Kutta o de varios pasos para resolverlas.
Si aplica algo simple como Euler hacia adelante o Euler 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 de Runge-Kutta de cuarto orden, encontrará que se mantiene cerca de una órbita real por un tiempo, pero eventualmente se mueve en espiral hacia adentro (siempre que el paso de tiempo sea lo suficientemente pequeño) o va al infinito (si el paso de tiempo es demasiado grande). El enfoque correcto es utilizar un método simpléctico, que mantendrá a la Tierra en la órbita correcta, aunque su fase seguirá estando desfasada debido a errores numéricos.
Para el problema de 2 cuerpos, es posible derivar un sistema más simple al basar 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.
usuario2449
espacio de Estados
usuario21
espacio de Estados
usuario21
espacio de Estados
espacio de Estados