Skip to content

Commit 2323358

Browse files
committed
Trained the Model
1 parent ef11b8f commit 2323358

8 files changed

Lines changed: 39968 additions & 9067 deletions

Data Sets/TLE/propagated_orbits.csv

Lines changed: 13281 additions & 3001 deletions
Large diffs are not rendered by default.

Data Sets/TLE/tle_propagate.py

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,14 @@
1515
from datetime import datetime, timedelta, timezone
1616
import numpy as np
1717
import matplotlib.pyplot as plt
18+
import math
1819

1920
#Load the dataframe
2021
df = pd.read_csv("tle_data.csv")
2122

2223

2324

24-
def propagate_orbit(tle1, tle2, start_time, duration=3600, step=60):
25+
def propagate_orbit(tle1, tle2, start_time, duration=8100, step=30):
2526
'''
2627
2728
:param tle1: The first line of the TLE
@@ -42,7 +43,7 @@ def propagate_orbit(tle1, tle2, start_time, duration=3600, step=60):
4243
#Creates a list of timestamps starting from the start time, incremented of "step" seconds until the defined duration.
4344
#This represents the time points at which the satellite position will be predicted.
4445
timestamps = []
45-
for i in range(0, duration, step):
46+
for i in range(0, duration + step, step):
4647
timestamps.append(start_time + timedelta(seconds=i))
4748

4849
#We will store the positions and velocities at each time step here
@@ -61,6 +62,11 @@ def propagate_orbit(tle1, tle2, start_time, duration=3600, step=60):
6162
'''
6263
e, position, velocity = sat.sgp4(julian_date, fraction)
6364

65+
if any(np.isnan(position)) or any(np.isnan(velocity)):
66+
print(f"Bad propagation at {t} for object: {tle1[2:7]} - skipping satellite")
67+
return []
68+
69+
6470
if (e == 0):
6571
#Append the current time stamp, position, and velocity
6672
results.append({
@@ -72,11 +78,13 @@ def propagate_orbit(tle1, tle2, start_time, duration=3600, step=60):
7278
"velocity_y": velocity[1],
7379
"velocity_z": velocity[2]
7480
})
75-
76-
81+
if math.isnan(position[0]) or math.isnan(velocity[0]):
82+
print(f"NaN detected at time {t}")
7783
return results
7884

7985
def propagate_row(row):
86+
object_id = row["tle_line1"][2:7]
87+
print(f"Propagating satellite {object_id}")
8088
return propagate_orbit(row["tle_line1"], row["tle_line2"], start_time)
8189

8290
def graph_positions(df):
@@ -165,9 +173,21 @@ def parse_data(propagated_data):
165173
array = np.array([[sv["position_x"], sv["position_y"], sv["position_z"], sv["velocity_x"], sv["velocity_y"], sv["velocity_z"]] for sv in propagated_data])
166174
return array
167175

168-
def data_formatting(df):
176+
def data_formatting(df, duration=8100, steps=30):
169177
# Process each row into a list of sequences
170-
sequences = [parse_data(row["propagated"]) for i, row in df.iterrows()]
178+
179+
expected_timesteps = int(duration / steps) + 1
180+
181+
sequences = []
182+
#Drop any invalid propagated sequences
183+
for i, row in df.iterrows():
184+
parsed = parse_data(row["propagated"])
185+
if parsed.shape == (expected_timesteps, 6):
186+
sequences.append(parsed)
187+
else:
188+
print(f"Dropping incomplete sequence {i}: shape {parsed.shape}")
189+
190+
171191
# Convert to (B, T, F) NumPy array
172192
data_array = np.array(sequences) # Shape: (B, T, 6)
173193

Data Sets/TLE/training_data.csv

Lines changed: 13280 additions & 3000 deletions
Large diffs are not rendered by default.

src/Models/OrbitAITransformer.py

Lines changed: 7 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,5 @@
11
import torch
22
import torch.nn as nn
3-
import torch.nn.functional as F
4-
import pandas as pd
5-
from torch.nn.functional import dropout
6-
from sklearn.preprocessing import StandardScaler
73

84
#Hyperparameters:
95
'''
@@ -21,19 +17,6 @@
2117
2218
'''
2319

24-
INPUT_DIM = 7 #The dimensions/neurons for the input layer: time, pos_x, pos_y, pos_z, vel_x, vel_y, vel_z
25-
EMBED_DIM = 128 #Embedding Dimension for input vectors.
26-
NUM_HEADS = 8 #Number of attention heads in multi-head attention block
27-
NUM_LAYERS = 6 #Number of encoder layers
28-
FEED_FORWARD_DIM = 256 #Size of feedforward layers within the Transformer's MLP
29-
OUTPUT_DIM = 6 #Predicting the 6 dimensional outputs (the next state vectors)
30-
SEQ_LENGTH = 10 #Length of the input sequences
31-
LEARNING_RATE = 0.001 #The learning rate for the optimizer function
32-
BATCH_SIZE = 32 #Number of sequences per batch
33-
EPOCHS = 50 #Number of training iterations
34-
DROPOUT = 0.1 #Overfitting prevention
35-
#Add another parameter, dropout, if experiencing overfitting
36-
3720
'''
3821
Embedding Layer: Since our input consists of 6 continuous features:
3922
(pos_x, pos_y, pos_z, vel_x, vel_y, vel_z) we will project this into a higher-dimensional space using a fully connected
@@ -97,13 +80,10 @@ def forward(self, src):
9780
src: input data, tensor of shape (batch_size, sequence_length, embedding_dimensions)
9881
returns: (batch_size, sequence_length, embedding_dimension)
9982
'''
100-
#Transpose for the transformer so that sequence_length comes first in the tensor
101-
#x = x.transpose(0,1)
10283

10384
#Pass through transformer encoder for prediction
10485
encoded_data = self.encoder(src)
10586

106-
#Transpose back to having batch first in the tensor
10787
return encoded_data
10888
'''
10989
Transformer Decoder
@@ -128,15 +108,12 @@ def forward(self, tgt, memory):
128108
tgt: a typical name for the input sequence being sent into a decoder, short for target
129109
memory: (batch, src_sequence_length, embed_dim) - this is output from the encoder
130110
'''
131-
#Transpose to match transformer input (seq_len, batch, embed_dim), optional
132-
#tgt = tgt.transpose(0,1)
133-
#memory = memory.transpose(0,1)
111+
tgt = tgt
112+
memory = memory
134113

135114
#Decode!
136115
out = self.decoder(tgt=tgt, memory=memory)
137116

138-
#Transpose back, optional
139-
#out = out.transpose(0,1)
140117
return out
141118
'''
142119
Output Layer
@@ -156,14 +133,15 @@ def forward(self,x):
156133
OrbitAI Transformer Model
157134
'''
158135
class OrbitAI(nn.Module):
159-
def __init__(self, input_dim, embed_dim, output_dim, num_heads, feedforward_dim, num_layers, dropout, seq_len):
136+
def __init__(self, input_dim, embed_dim, output_dim, num_heads, feedforward_dim, num_layers, dropout, seq_len, pred_len):
160137
super(OrbitAI, self).__init__()
161138

162139
self.embedding = InputEmbedding(
163140
input_dim = input_dim,
164141
embed_dim = embed_dim
165142
)
166-
self.positional_encoding = LearnedPositionalEncoding(seq_len, embed_dim)
143+
self.src_encoded = LearnedPositionalEncoding(seq_len, embed_dim)
144+
self.tgt_encoded = LearnedPositionalEncoding(pred_len, embed_dim)
167145

168146
self.encoder = TransformerEncoder(
169147
embed_dim = embed_dim,
@@ -192,10 +170,10 @@ def forward(self, src, tgt):
192170
tgt: [batch_size, tgt_seq_len, input_dim] (from the decoder)
193171
'''
194172
src_embedded = self.embedding(src)
195-
src_encoded = self.positional_encoding(src_embedded)
173+
src_encoded = self.src_encoded(src_embedded)
196174

197175
tgt_embedded = self.embedding(tgt)
198-
tgt_encoded = self.positional_encoding(tgt_embedded)
176+
tgt_encoded = self.tgt_encoded(tgt_embedded)
199177

200178
#Transformer encoder
201179
memory = self.encoder(src_encoded)
@@ -208,10 +186,6 @@ def forward(self, src, tgt):
208186

209187
return output
210188

211-
212-
213-
214-
215189
'''
216190
Testing the model so far:
217191

src/Models/TransformerTesting.py

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
from torch.utils.data import DataLoader
2+
import matplotlib.pyplot as plt
3+
from OrbitAITransformer import OrbitAI
4+
from TransformerTraining import OrbitDataset
5+
import torch
6+
7+
#-----------------------------------------------------------------------------------------------------------------------
8+
INPUT_DIM = 7 #The dimensions/neurons for the input layer: time, pos_x, pos_y, pos_z, vel_x, vel_y, vel_z
9+
EMBED_DIM = 128 #Embedding Dimension for input vectors.
10+
NUM_HEADS = 8 #Number of attention heads in multi-head attention block
11+
NUM_LAYERS = 6 #Number of encoder layers
12+
FEED_FORWARD_DIM = 256 #Size of feedforward layers within the Transformer's MLP
13+
OUTPUT_DIM = 6 #Predicting the 6 dimensional outputs (the next state vectors)
14+
LEARNING_RATE = 0.00001 #The learning rate for the optimizer function
15+
BATCH_SIZE = 32 #Number of sequences per batch
16+
EPOCHS = 50 #Number of training iterations
17+
DROPOUT = 0.1 #Overfitting prevention
18+
#Add another parameter, dropout, if experiencing overfitting
19+
SEQ_LENGTH = 270 #Length of the input sequences, 270 = 8100/30 = PropagationDuration/steps
20+
PRED_LEN = 2 #Number of sequences we want outputted
21+
#-----------------------------------------------------------------------------------------------------------------------
22+
23+
#Load Dataset
24+
dataset = OrbitDataset(csv_path = "training_data.csv", input_len = 270, pred_len = 1)
25+
dataloader = DataLoader(dataset, batch_size=BATCH_SIZE, shuffle=True)
26+
device= torch.device('cuda')
27+
28+
#Instantiate model
29+
MODEL = OrbitAI(
30+
input_dim = INPUT_DIM,
31+
embed_dim = EMBED_DIM,
32+
output_dim = OUTPUT_DIM,
33+
num_heads = NUM_HEADS,
34+
feedforward_dim = FEED_FORWARD_DIM,
35+
num_layers = NUM_LAYERS,
36+
dropout = DROPOUT,
37+
seq_len = SEQ_LENGTH,
38+
pred_len = PRED_LEN
39+
).to(device)
40+
41+
state_dict = torch.load('orbitai_checkpoint.pth',map_location=device)
42+
MODEL.load_state_dict(state_dict['model_state_dict'])
43+
44+
# Evaluation
45+
MODEL.eval()
46+
with torch.no_grad():
47+
sample = dataset[0]
48+
src = sample['src'].unsqueeze(0).to(device) # [1, input_len, 7] this is the input sequence we want the model to learn from, historical trajectory data. When src is encoded it becomes memory
49+
tgt = sample['tgt'].unsqueeze(0).to(device) # [1, pred_len, 7] this is the input to the decoder to start generating predictions, the previously known state
50+
tgt_y = sample['tgt_y'] # [pred_len, 6]
51+
52+
output = MODEL(src, tgt).squeeze(0).cpu().numpy() # [pred_len, 6]
53+
prediction_unscaled = dataset.inverse_transform(output)
54+
target_unscaled = dataset.inverse_transform(tgt_y.numpy())
55+
56+
print("Prediction:\n", prediction_unscaled[:5]) # Check if it's real values
57+
print("Target:\n", target_unscaled[:5])
58+
59+
time = list(range(target_unscaled.shape[0]))
60+
61+
print("Time length:", len(time))
62+
print("Target shape:", target_unscaled.shape)
63+
print("Prediction shape:", prediction_unscaled.shape)
64+
65+
66+
# target_unscaled and prediction_unscaled are shape [pred_len, 6]
67+
68+
for i, label in enumerate(["X", "Y", "Z"]):
69+
plt.figure()
70+
plt.plot(time, target_unscaled[:, i], label=f"True{label}")
71+
plt.plot(time, prediction_unscaled[:, i], label=f"Predicted{label}")
72+
plt.xlabel("Timestep")
73+
plt.ylabel(f"Position {label} (km)")
74+
plt.title(f"{label}-Axis Position: True vs. Predicted")
75+
plt.legend()
76+
plt.grid(True)
77+
plt.show()

src/Models/TransformerTraining.py

Lines changed: 17 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -9,19 +9,19 @@
99
from sklearn.preprocessing import StandardScaler
1010
import time
1111

12-
1312
INPUT_DIM = 7 #The dimensions/neurons for the input layer: time, pos_x, pos_y, pos_z, vel_x, vel_y, vel_z
1413
EMBED_DIM = 128 #Embedding Dimension for input vectors.
1514
NUM_HEADS = 8 #Number of attention heads in multi-head attention block
16-
NUM_LAYERS = 8 #Number of encoder layers
17-
FEED_FORWARD_DIM = 512 #Size of feedforward layers within the Transformer's MLP
15+
NUM_LAYERS = 6 #Number of encoder layers
16+
FEED_FORWARD_DIM = 256 #Size of feedforward layers within the Transformer's MLP
1817
OUTPUT_DIM = 6 #Predicting the 6 dimensional outputs (the next state vectors)
19-
SEQ_LENGTH = 10 #Length of the input sequences
20-
LEARNING_RATE = 0.0001 #The learning rate for the optimizer function
18+
LEARNING_RATE = 0.00001 #The learning rate for the optimizer function
2119
BATCH_SIZE = 32 #Number of sequences per batch
22-
EPOCHS = 100 #Number of training iterations
20+
EPOCHS = 50 #Number of training iterations
2321
DROPOUT = 0.1 #Overfitting prevention
2422
#Add another parameter, dropout, if experiencing overfitting
23+
SEQ_LENGTH = 270 #Length of the input sequences, 270 = 8100/30 = PropagationDuration/steps
24+
PRED_LEN = 2 #Number of sequences we want outputted
2525

2626

2727
def train(model, dataloader, optimizer, criterion, device):
@@ -33,6 +33,7 @@ def train(model, dataloader, optimizer, criterion, device):
3333
tgt = batch['tgt'].to(device)
3434
tgt_y = batch['tgt_y'].to(device)
3535

36+
3637
optimizer.zero_grad()
3738
output = model(src, tgt)
3839

@@ -45,7 +46,7 @@ def train(model, dataloader, optimizer, criterion, device):
4546
return total_loss / len(dataloader)
4647

4748
class OrbitDataset(Dataset):
48-
def __init__(self, csv_path, input_len = 20, pred_len = 10):
49+
def __init__(self, csv_path, input_len = 270, pred_len = 1):
4950
super().__init__()
5051
self.input_len = input_len
5152
self.pred_len = pred_len
@@ -54,7 +55,11 @@ def __init__(self, csv_path, input_len = 20, pred_len = 10):
5455

5556
#Keep columns in order
5657
raw_data = df[['time', 'position_x', 'position_y', 'position_z',
57-
'velocity_x', 'velocity_y', 'velocity_z']].values
58+
'velocity_x', 'velocity_y', 'velocity_z']]
59+
60+
raw_data = raw_data.dropna().reset_index(drop=True)
61+
raw_data = raw_data.values
62+
5863
#Split time and state data
5964
self.time = raw_data[:, 0] - raw_data[:, 0].min()/(raw_data[:, 0].max() - raw_data[:, 0].min())
6065
self.time = self.time.reshape(-1,1) #normalize time
@@ -73,7 +78,7 @@ def __len__(self):
7378
return len(self.data) - (self.input_len + self.pred_len) + 1
7479

7580
def __getitem__(self, idx):
76-
#Encoder input (10 steps)
81+
#Encoder input
7782
src = self.data[idx : idx + self.input_len]
7883

7984
#Decoder input (use the last state from src then roll forward)
@@ -98,8 +103,6 @@ def inverse_transform(self, prediction):
98103
#Reverse scaling of predicted state vectors, un-normalized them
99104
return self.scaler.inverse_transform(prediction)
100105

101-
102-
103106
'''
104107
#Sanity check
105108
batch = next(iter(dataloader))
@@ -114,7 +117,7 @@ def inverse_transform(self, prediction):
114117
device = torch.device('cuda')
115118

116119
#Load Dataset
117-
dataset = OrbitDataset(csv_path = "training_data.csv", input_len = 10, pred_len = 5)
120+
dataset = OrbitDataset(csv_path = "training_data.csv", input_len = 270, pred_len = 1)
118121
dataloader = DataLoader(dataset, batch_size=BATCH_SIZE, shuffle=True)
119122

120123
#Instantiate model
@@ -126,7 +129,8 @@ def inverse_transform(self, prediction):
126129
feedforward_dim = FEED_FORWARD_DIM,
127130
num_layers = NUM_LAYERS,
128131
dropout = DROPOUT,
129-
seq_len = SEQ_LENGTH
132+
seq_len = SEQ_LENGTH,
133+
pred_len = PRED_LEN
130134
).to(device)
131135

132136
#Select loss function
@@ -152,18 +156,4 @@ def inverse_transform(self, prediction):
152156

153157
print("Model and Optimizer saved")
154158

155-
# Evaluation
156-
MODEL.eval()
157-
with torch.no_grad():
158-
sample = dataset[0]
159-
src = sample['src'].unsqueeze(0).to(device) # [1, input_len, 7]
160-
tgt = sample['tgt'].unsqueeze(0).to(device) # [1, pred_len, 7]
161-
tgt_y = sample['tgt_y'] # [pred_len, 6]
162-
163-
output = MODEL(src, tgt).squeeze(0).cpu().numpy() # [pred_len, 6]
164-
prediction_unscaled = dataset.inverse_transform(output)
165-
target_unscaled = dataset.inverse_transform(tgt_y.numpy())
166-
167-
print("Prediction (unscaled):\n", prediction_unscaled)
168-
print("Target (unscaled):\n", target_unscaled)
169159

src/Models/orbitai_checkpoint.pth

-19.3 MB
Binary file not shown.

0 commit comments

Comments
 (0)