Apenas um pouco de aviso prévio: nunca estudei astronomia ou ciências exatas para esse assunto (nem mesmo TI), por isso estou tentando preencher essa lacuna com a auto-educação. A astronomia é uma das áreas que chamou minha atenção e minha ideia de auto-educação é de abordagem aplicada. Então, direto ao ponto - este é o modelo de simulação orbital em que estou trabalhando casualmente quando tenho tempo / humor. Meu principal objetivo é criar um sistema solar completo em movimento e capacidade de planejar lançamentos de naves espaciais para outros planetas.
Você é livre para escolher este projeto a qualquer momento e se divertir experimentando!
atualizar!!! (Nov10)
- velocidade agora é deltaV adequada e, dando movimento adicional, agora calcula o vetor soma da velocidade
- você pode colocar quantos objetos estáticos quiser, em todas as vezes que o objeto da unidade em movimento verificar os vetores de gravidade de todas as fontes (e verificar a colisão)
- melhorou bastante o desempenho dos cálculos
- uma correção para considerar o mod interativo no matplotlib. Parece que esta é a opção padrão apenas para ipython. Python3 regular requer essa declaração explicitamente.
Basicamente agora é possível "lançar" uma espaçonave da superfície da Terra e planejar uma missão para a Lua, fazendo correções de vetores deltaV via giveMotion (). O próximo na fila é tentar implementar a variável de tempo global para permitir movimento simultâneo, por exemplo, a Lua orbita a Terra enquanto a sonda tenta uma manobra de auxílio à gravidade.
Comentários e sugestões de melhorias são sempre bem-vindos!
Feito em Python3 com a 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)
Como funciona
Tudo se resume a duas coisas:
- Criando objetos como
Earth = Object(name="Earth", position=(50.0, 50.0), radius=6.371, mass=5.972e24)
com parâmetros de posição na grade (1 unidade da grade é 1000 km por padrão, mas isso também pode ser alterado), raio nas unidades da grade e massa em kg. - Dando ao objeto algum deltaV, como
Craft.giveMotion(deltaV=8500.0, motionDirection=100, time=130000)
obviamente ele precisaCraft = Object(...)
ser criado em primeiro lugar, como mencionado no ponto anterior. Os parâmetros aqui estãodeltaV
em m / s (observe que, por enquanto, a aceleração é instantânea),motionDirection
é a direção do deltaV em graus (da posição atual, imagine um círculo de 360 graus em torno do objeto, então a direção é um ponto nesse círculo) e finalmente o parâmetrotime
é quantos segundos após a trajetória deltaV push do objeto será monitorada. Partida subsequentegiveMotion()
da última posição da anteriorgiveMotion()
.
Questões:
- Este é um algoritmo válido para calcular órbitas?
- Quais são as melhorias óbvias a serem feitas?
- Eu tenho considerado a variável "timeScale" que otimizará os cálculos, pois pode não ser necessário recalcular vetores e posições a cada segundo. Alguma idéia de como deve ser implementada ou geralmente é uma boa ideia? (perda de precisão versus desempenho aprimorado)
Basicamente, meu objetivo é iniciar uma discussão sobre o tópico e ver aonde ele leva. E, se possível, aprenda (ou melhor ainda - ensine) algo novo e interessante.
Sinta-se livre para experimentar!
Tente 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)
Com duas queimaduras - uma prograde na órbita da Terra e uma retrógrada na órbita da Lua, consegui uma órbita lunar estável. Estes valores estão próximos dos valores teóricos esperados?
Exercício sugerido: experimente em 3 queimaduras - órbita terrestre estável da superfície da Terra, prograde a queima para alcançar a Lua, queima retrógrada para estabilizar a órbita em torno da Lua. Então tente minimizar o deltaV.
Nota: Pretendo atualizar o código com comentários extensos para aqueles que não estão familiarizados com a sintaxe python3.
fonte
Respostas:
Deixe os dois corpos envolvidos terem massas . Comece com a segunda lei de Newton onde é aceleração. A força gravitacional no corpo 2 do corpo 1 é dada porm1, m2
e
Juntamente com as posições e velocidades iniciais, este sistema de equações diferenciais ordinárias (EDOs) compreende um problema de valor inicial. A abordagem usual é escrever isso como um sistema de 8 equações de primeira ordem e aplicar um método Runge-Kutta ou várias etapas para resolvê-las.
Se você aplicar algo simples como Euler dianteiro ou retrógrado, verá a Terra espiralar para o infinito ou em direção ao sol, respectivamente, mas isso é um efeito dos erros numéricos. Se você usar um método mais preciso, como o clássico método Runge-Kutta de 4ª ordem, descobrirá que ele permanece próximo de uma órbita verdadeira por um tempo, mas ainda acaba indo para o infinito. A abordagem correta é usar um método simplético, que manterá a Terra na órbita correta - embora sua fase ainda esteja desativada devido a erros numéricos.
Para o problema de dois corpos, é possível derivar um sistema mais simples baseando seu sistema de coordenadas em torno do centro de massa. Mas acho que a formulação acima é mais clara se isso é novo para você.
fonte