├── .gitattributes ├── .gitignore ├── LSTM_model.py ├── MLP_model.py ├── NGSIM_Testing.ipynb ├── README.md ├── average graphs of features.png ├── i-80_id_list ├── labeled_data ├── lane_and_xpos_vs_FrameID.png ├── lankershim_id_list ├── peachtree_id_list ├── reservoir_RNN.py ├── test └── us-101_id_list /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | share/python-wheels/ 24 | *.egg-info/ 25 | .installed.cfg 26 | *.egg 27 | MANIFEST 28 | 29 | # PyInstaller 30 | # Usually these files are written by a python script from a template 31 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 32 | *.manifest 33 | *.spec 34 | 35 | # Installer logs 36 | pip-log.txt 37 | pip-delete-this-directory.txt 38 | 39 | # Unit test / coverage reports 40 | htmlcov/ 41 | .tox/ 42 | .nox/ 43 | .coverage 44 | .coverage.* 45 | .cache 46 | nosetests.xml 47 | coverage.xml 48 | *.cover 49 | *.py,cover 50 | .hypothesis/ 51 | .pytest_cache/ 52 | cover/ 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | local_settings.py 61 | db.sqlite3 62 | db.sqlite3-journal 63 | 64 | # Flask stuff: 65 | instance/ 66 | .webassets-cache 67 | 68 | # Scrapy stuff: 69 | .scrapy 70 | 71 | # Sphinx documentation 72 | docs/_build/ 73 | 74 | # PyBuilder 75 | .pybuilder/ 76 | target/ 77 | 78 | # Jupyter Notebook 79 | .ipynb_checkpoints 80 | 81 | # IPython 82 | profile_default/ 83 | ipython_config.py 84 | 85 | # pyenv 86 | # For a library or package, you might want to ignore these files since the code is 87 | # intended to run in multiple environments; otherwise, check them in: 88 | # .python-version 89 | 90 | # pipenv 91 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 92 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 93 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 94 | # install all needed dependencies. 95 | #Pipfile.lock 96 | 97 | # poetry 98 | # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. 99 | # This is especially recommended for binary packages to ensure reproducibility, and is more 100 | # commonly ignored for libraries. 101 | # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control 102 | #poetry.lock 103 | 104 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 105 | __pypackages__/ 106 | 107 | # Celery stuff 108 | celerybeat-schedule 109 | celerybeat.pid 110 | 111 | # SageMath parsed files 112 | *.sage.py 113 | 114 | # Environments 115 | .env 116 | .venv 117 | env/ 118 | venv/ 119 | ENV/ 120 | env.bak/ 121 | venv.bak/ 122 | 123 | # Spyder project settings 124 | .spyderproject 125 | .spyproject 126 | 127 | # Rope project settings 128 | .ropeproject 129 | 130 | # mkdocs documentation 131 | /site 132 | 133 | # mypy 134 | .mypy_cache/ 135 | .dmypy.json 136 | dmypy.json 137 | 138 | # Pyre type checker 139 | .pyre/ 140 | 141 | # pytype static type analyzer 142 | .pytype/ 143 | 144 | # Cython debug symbols 145 | cython_debug/ 146 | 147 | # PyCharm 148 | # JetBrains specific template is maintainted in a separate JetBrains.gitignore that can 149 | # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore 150 | # and can be added to the global gitignore or merged into this file. For a more nuclear 151 | # option (not recommended) you can uncomment the following to ignore the entire idea folder. 152 | .idea/ 153 | Next_Generation_Simulation__NGSIM__Vehicle_Trajectories_and_Supporting_Data.csv 154 | -------------------------------------------------------------------------------- /LSTM_model.py: -------------------------------------------------------------------------------- 1 | import torch 2 | import torch.nn as nn 3 | import numpy as np 4 | import matplotlib.pyplot as plt 5 | from tqdm import tqdm 6 | device = torch.device('cuda' if torch.cuda.is_available() else 'cpu') 7 | 8 | """ 9 | Multi-layer perceptron model. Modified from code Lawson submitted for MLP homework in ECE 228 10 | """ 11 | 12 | def build_mlp( 13 | input_size=4, 14 | output_size=3, 15 | n_layers=2, 16 | size=50): 17 | """ 18 | Args: 19 | input_size: int, dim(inputs) 20 | output_size: int, dim(outputs) 21 | n_layers: int, number of hidden layers 22 | size: int, number of nodes in each hidden layer 23 | Returns: 24 | An instance of (a subclass of) nn.Module representing the network. 25 | """ 26 | class NN(nn.Module): 27 | def __init__(self, input_size, output_size, n_layers, n_classes): 28 | super(NN, self).__init__() 29 | self.ourNN = nn.Sequential() # Make sequential module container 30 | # Layer 1 31 | self.ourNN.add_module("Hidden_0", nn.LSTM(input_size,256, batch_first=True, bidirectional=False)) 32 | self.ourNN.add_module("Sigmoid_0", nn.Sigmoid()) 33 | # Middle Layers 34 | self.ourNN.add_module("Middle", nn.LSTM(256,256,n_layers-3, batch_first=False, bidirectional=False)) 35 | self.ourNN.add_module("Sigmoid_mid", nn.Sigmoid()) 36 | # Last but not least layer 37 | self.ourNN.add_module("Output_layer",nn.LSTM(256, 128, batch_first=False, bidirectional=False)) 38 | self.ourNN.add_module("Sigmoid_nwg2", nn.Sigmoid()) 39 | # last layer 40 | self.ourNN.add_module("Output_layer",nn.Linear(128, output_size, bias=True)) 41 | self.ourNN.add_module("Output_Softmax_activation",nn.Softmax()) 42 | self.ourNN.to(device) 43 | def forward(self, x): 44 | x=self.ourNN(x) 45 | return x 46 | return NN(input_size, output_size, n_layers, size) 47 | 48 | def np2torch(x, cast_double_to_float=True): 49 | """ 50 | Utility function that accepts a numpy array and does the following: 51 | 1. Convert to torch tensor 52 | 2. Move it to the GPU (if CUDA is available) 53 | 3. Optionally casts float64 to float32 (torch is picky about types) 54 | """ 55 | x = torch.from_numpy(x).to(device) 56 | if cast_double_to_float and x.dtype is torch.float64: 57 | x = x.float() 58 | 59 | return x 60 | 61 | def send_all_to_device(list,device): 62 | for thing in list: 63 | thing.to(device) 64 | 65 | 66 | def train_on_data(x, y, num_iterations, n_layers, size, device): 67 | """ 68 | :param x: inputs to MLP 69 | :param y: desired outputs of MLP 70 | :param n_layers: number of hidden layers + ReLU 71 | :param size: width of MLP layers 72 | :param num_channels: number of channels in input data 73 | :param num_iterations: number of training iterations 74 | :return: tuple (MLP network object, array of training losses) 75 | """ 76 | # input_size = 13 77 | # output_size = input_size 78 | # n_layers = 13 79 | # size = 30 80 | # num_channels = 1 81 | 82 | input_data = x 83 | target_data = y 84 | 85 | input_data = np2torch(input_data) 86 | target_data = np2torch(target_data) 87 | 88 | input_size = input_data.size() 89 | output_size = target_data.size() 90 | 91 | model = build_mlp(input_size, output_size, n_layers, size) # model 92 | criterion = nn.MSELoss(reduction="none") # loss function 93 | optimizer = torch.optim.SGD(model.parameters(), lr=0.01, momentum=0.9) # optimizer 94 | model.train() #sets model to train mode (dropout enabled) 95 | torch.set_grad_enabled(True) # L5kit does this, and I don't know why 96 | 97 | progress_bar = tqdm(range(num_iterations))#cfg["train_params"]["max_num_steps"])) 98 | losses_train = [] 99 | 100 | for _ in progress_bar: 101 | model.eval() 102 | outputs = torch.reshape(model.forward(input_data.to(device)),target_data.size()) 103 | loss = (criterion(outputs, target_data)).mean() 104 | 105 | # Backward pass 106 | optimizer.zero_grad() 107 | loss.backward() 108 | optimizer.step() 109 | 110 | losses_train.append(loss.item()) 111 | progress_bar.set_description(f"loss: {loss.item()} loss(avg ): {np.mean(losses_train)}") 112 | 113 | return (model, losses_train) 114 | -------------------------------------------------------------------------------- /MLP_model.py: -------------------------------------------------------------------------------- 1 | import torch 2 | import torch.nn as nn 3 | import numpy as np 4 | import matplotlib.pyplot as plt 5 | from tqdm import tqdm 6 | device = torch.device('cuda' if torch.cuda.is_available() else 'cpu') 7 | 8 | """ 9 | Multi-layer perceptron model. Modified from code Lawson submitted for MLP homework in ECE 228 10 | """ 11 | 12 | def build_mlp( 13 | input_size=4, 14 | output_size=3, 15 | n_layers=2, 16 | size=50, 17 | batch_size=10): 18 | """ 19 | Args: 20 | input_size: int, dim(inputs) 21 | output_size: int, dim(outputs) 22 | n_layers: int, number of hidden layers 23 | size: int, number of nodes in each hidden layer 24 | Returns: 25 | An instance of (a subclass of) nn.Module representing the network. 26 | """ 27 | class NN(nn.Module): 28 | def __init__(self, input_size, output_size, n_layers, size, batch_size): 29 | super(NN, self).__init__() 30 | self.ourNN = nn.Sequential() # Make sequential module container 31 | self.ourNN.add_module("Hidden_0", nn.Linear(input_size,size,bias=True)) # Layer 1 32 | self.ourNN.add_module("Sigmoid_0", nn.Sigmoid()) 33 | for n in range(1,n_layers): 34 | self.ourNN.add_module("Hidden_"+str(n), nn.Linear(size, size, bias=True)) 35 | self.ourNN.add_module("Sigmoid_"+str(n), nn.Sigmoid()) 36 | self.ourNN.add_module("Output_layer",nn.Linear(size, output_size, bias=True))# Last layer 37 | self.ourNN.add_module("Output_Softmax_activation",nn.Softmax()) 38 | self.ourNN.to(device) 39 | 40 | self.batch_size = batch_size 41 | def forward(self, x): 42 | x = x.reshape((self.batch_size, 100, 4)) 43 | x=self.ourNN(x) 44 | return x 45 | return NN(input_size, output_size, n_layers, size, batch_size) 46 | 47 | def np2torch(x, cast_double_to_float=True): 48 | """ 49 | Utility function that accepts a numpy array and does the following: 50 | 1. Convert to torch tensor 51 | 2. Move it to the GPU (if CUDA is available) 52 | 3. Optionally casts float64 to float32 (torch is picky about types) 53 | """ 54 | x = torch.from_numpy(x).to(device) 55 | if cast_double_to_float and x.dtype is torch.float64: 56 | x = x.float() 57 | 58 | return x 59 | 60 | def send_all_to_device(list,device): 61 | for thing in list: 62 | thing.to(device) 63 | 64 | 65 | def train_on_data(x_all_batches, y_all_batches, num_iterations, n_layers, size, batch_size, num_epochs, device): 66 | """ 67 | :param x: inputs to MLP 68 | :param y: desired outputs of MLP 69 | :param n_layers: number of hidden layers + ReLU 70 | :param size: width of MLP layers 71 | :param num_channels: number of channels in input data 72 | :param num_iterations: number of training iterations 73 | :return: tuple (MLP network object, array of training losses) 74 | """ 75 | # input_size = 13 76 | # output_size = input_size 77 | # n_layers = 13 78 | # size = 30 79 | # num_channels = 1 80 | 81 | progress_bar = tqdm(range(num_epochs))#cfg["train_params"]["max_num_steps"])) 82 | input_data = x_all_batches 83 | target_data = y_all_batches 84 | losses_train = [] 85 | 86 | for _ in tqdm(range(num_epochs)): 87 | np.random.shuffle(input_data) 88 | np.random.shuffle(target_data) 89 | 90 | input_size = input_data.shape[-1] 91 | output_size = target_data.shape[-1] 92 | 93 | num_batches = int(input_data.shape[0] / batch_size) 94 | 95 | model = build_mlp(input_size, output_size, n_layers, size, batch_size) # model 96 | print(model) 97 | criterion = nn.CrossEntropyLoss(reduction="none") # loss function 98 | optimizer = torch.optim.SGD(model.parameters(), lr=0.01, momentum=0.9) # optimizer 99 | model.train() #sets model to train mode (dropout enabled) 100 | torch.set_grad_enabled(True) # L5kit does this, and I don't know why 101 | 102 | # progress_bar = tqdm(range(num_batches))#cfg["train_params"]["max_num_steps"])) 103 | 104 | for i in range(num_batches): 105 | input_data_batch = input_data[i*batch_size:(i+1)*batch_size] 106 | target_data_batch = target_data[i*batch_size:(i+1)*batch_size] 107 | model.eval() 108 | # print("Shape of model.forward(input_data.to(device)):"+str((model.forward(input_data.to(device))).size())) 109 | # print("Shape of target data: "+str(target_data.size())) 110 | outputs = torch.reshape(torch.mean(model.forward(np2torch(input_data_batch).to(device)),dim=1),target_data_batch.shape) 111 | loss = (criterion(outputs, np2torch(target_data_batch))).mean() 112 | 113 | # Backward pass 114 | optimizer.zero_grad() 115 | loss.backward() 116 | optimizer.step() 117 | 118 | losses_train.append(loss.item()) 119 | progress_bar.set_description(f"loss: {loss.item()} loss(avg ): {np.mean(losses_train)}") 120 | 121 | return (model, losses_train) 122 | 123 | # plt.figure() 124 | # plt.plot(np.log10(np.array(losses_train))) 125 | # plt.xlabel("Training iteration") 126 | # plt.ylabel("Log_10(Loss)") 127 | # plt.title("Loss per Training") 128 | # plt.show() 129 | 130 | 131 | # input_size = 13 132 | # output_size = input_size 133 | # n_layers = 13 134 | # size = 30 135 | # num_channels = 1 136 | # 137 | # 138 | # input_data = np.random.uniform(low=-1,high=1,size=input_size) 139 | # target_data = np.random.uniform(low=-1,high=1,size=output_size) 140 | # 141 | # input_data = np2torch(input_data) 142 | # target_data = np2torch(target_data) 143 | # 144 | # model = build_mlp(input_size, output_size, n_layers, size) # model 145 | # criterion = nn.MSELoss(reduction="none") # loss function 146 | # optimizer = torch.optim.SGD(model.parameters(), lr=0.01, momentum=0.9) # optimizer 147 | # model.train() #sets model to train mode (dropout enabled) 148 | # torch.set_grad_enabled(True) # L5kit does this, and I don't know why 149 | # 150 | # progress_bar = tqdm(range(1000))#cfg["train_params"]["max_num_steps"])) 151 | # losses_train = [] 152 | # 153 | # for _ in progress_bar: 154 | # model.eval() 155 | # outputs = torch.reshape(model.forward(input_data.to(device)),target_data.size()) 156 | # print(outputs.size()) 157 | # loss = (criterion(outputs, target_data)).mean() 158 | # 159 | # # Backward pass 160 | # optimizer.zero_grad() 161 | # loss.backward() 162 | # optimizer.step() 163 | # 164 | # losses_train.append(loss.item()) 165 | # progress_bar.set_description(f"loss: {loss.item()} loss(avg ): {np.mean(losses_train)}") 166 | # 167 | # plt.figure() 168 | # plt.plot(np.log10(np.array(losses_train))) 169 | # plt.xlabel("Training iteration") 170 | # plt.ylabel("Log_10(Loss)") 171 | # plt.title("Loss per Training") 172 | # plt.show() 173 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # NGSIM_predict 2 | trajectory prediction using NGSIM. ECE 228 Sp22 3 | 4 | First, download the datasets. The NGSIM dataset is downloadable here: https://datahub.transportation.gov/Automobiles/Next-Generation-Simulation-NGSIM-Vehicle-Trajector/8ect-6jqj/data 5 | One can click "export" on the right hand side of the page and export as .csv format. Then, clone the github repository for our NGSIM code and put the .csv data in the same directory as NGSIM_testing.ipynb. 6 | 7 | Afterward, run the NGSIM_testing.ipynb script. 8 | -------------------------------------------------------------------------------- /average graphs of features.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ECE228SharedDesktop/NGSIM_predict/3aa563bc8fb5b1eb00d52cf8439d8cd55e589e2b/average graphs of features.png -------------------------------------------------------------------------------- /i-80_id_list: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ECE228SharedDesktop/NGSIM_predict/3aa563bc8fb5b1eb00d52cf8439d8cd55e589e2b/i-80_id_list -------------------------------------------------------------------------------- /labeled_data: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ECE228SharedDesktop/NGSIM_predict/3aa563bc8fb5b1eb00d52cf8439d8cd55e589e2b/labeled_data -------------------------------------------------------------------------------- /lane_and_xpos_vs_FrameID.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ECE228SharedDesktop/NGSIM_predict/3aa563bc8fb5b1eb00d52cf8439d8cd55e589e2b/lane_and_xpos_vs_FrameID.png -------------------------------------------------------------------------------- /lankershim_id_list: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ECE228SharedDesktop/NGSIM_predict/3aa563bc8fb5b1eb00d52cf8439d8cd55e589e2b/lankershim_id_list -------------------------------------------------------------------------------- /peachtree_id_list: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ECE228SharedDesktop/NGSIM_predict/3aa563bc8fb5b1eb00d52cf8439d8cd55e589e2b/peachtree_id_list -------------------------------------------------------------------------------- /reservoir_RNN.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import random 3 | import externally_provided_currents as epc 4 | import scipy as sp 5 | import matplotlib.pyplot as plt 6 | random.seed(2022) 7 | np.random.seed(2022) 8 | 9 | gamma = 0.5 # time constant. Larger gamma means RNN responds more strongly/quickly to incoming stimuli u(t) 10 | beta = 0.3 # Tikhonov regularization hyperparameter. Feel free to vary this to find whatever works best. 11 | 12 | u_spatial_dim = 3 13 | r_spatial_dim = 100 14 | dt = 0.01 15 | 16 | # Generic time stuff 17 | t_start=-1 18 | t_end = 105 19 | times = np.arange(start=t_start,stop=t_end,step=dt) 20 | 21 | # Training Time Stuff 22 | t_start_train = 0 23 | t_end_train = 50 24 | times_training = np.arange(start=t_start_train,stop=t_end_train,step=dt) 25 | 26 | # Testing Time Stuff 27 | t_start_test = 50 28 | t_end_test = 100 29 | times_testing = np.arange(start=t_start_test,stop=t_end_test,step=dt) 30 | 31 | W_in = np.random.uniform(low=-1,high=1,size=(r_spatial_dim,u_spatial_dim)) 32 | W_out = np.random.uniform(low=-1,high=1,size=(u_spatial_dim,r_spatial_dim)) 33 | A = np.random.uniform(low=-1,high=1,size=(r_spatial_dim,r_spatial_dim)) 34 | # divide A by spectral radius to help create generalized synchronization 35 | spectral_radius_A = np.max(np.abs(np.linalg.eigvals(A))) 36 | A = A/spectral_radius_A 37 | 38 | L63_obj = epc.L63_object() 39 | L63_obj.prepare_f(times) 40 | u = L63_obj 41 | r_initial = np.random.uniform(low=-1,high=1,size=r_spatial_dim) 42 | 43 | 44 | def calculate_W_out(r, u, beta): 45 | """ 46 | Calculate linear readout matrix for mapping r(t) back to u(t) 47 | :param r: (ndarray) dim(space,time) 48 | :param u: (ndarray) dim(time, space) 49 | :param beta: (float) Tikhonov regularization parameter 50 | :return: W_out matrix that maps from r to u. 51 | """ 52 | # see Lukosevicius Practical ESN eqtn 11 53 | # https://en.wikipedia.org/wiki/Linear_regression 54 | # Using ridge regression 55 | train_start_timestep = 0 56 | train_end_timestep = r.shape[1] # length along time axis 57 | # spatial dimensions 58 | N_r = sp.shape(r)[0] 59 | N_u = sp.shape(u)[1] 60 | 61 | # Ridge Regression 62 | # print("Shape of scipy.matmul(r, r.transpose()): " + str(sp.matmul(r, r.transpose()).shape)) 63 | # print("Shape of scipy.matmul(r, r.transpose()): " + str(sp.identity(N_r).shape)) 64 | W_out = sp.matmul(sp.array(u[train_start_timestep:train_end_timestep, :].transpose()), 65 | sp.matmul(r.transpose(), 66 | sp.linalg.inv( 67 | sp.matmul(r, r.transpose()) + beta * sp.identity(N_r)))) 68 | return W_out 69 | 70 | def integrate_RNN_driven(r,t,u,gamma,A,W_in): 71 | drdt = gamma*(-r + np.tanh(A@r + W_in@u.function(N=None,t=t))) 72 | return drdt 73 | 74 | def integrate_RNN_autonomous(r,t,W_out,gamma,A,W_in): 75 | predicted_u = r@(W_out.T) 76 | drdt = gamma*(-r + np.tanh(A@r + W_in@predicted_u)) 77 | return drdt 78 | 79 | # Driving 80 | RNN_driven_solution_1 = sp.integrate.odeint(integrate_RNN_driven, r_initial, times_training, args=(u,gamma,A,W_in)) 81 | # Training 82 | print(RNN_driven_solution_1.shape) 83 | print(u.function(N=None,t=times_training).shape) 84 | W_out = calculate_W_out(r=RNN_driven_solution_1.transpose(), 85 | u=u.function(N=None,t=times_training).transpose(), 86 | beta=beta) 87 | 88 | # Prediction 89 | r_initial_for_testing = RNN_driven_solution_1[-1]#np.random.uniform(low=-1,high=1,size=r_spatial_dim) 90 | RNN_driven_solution_2 = sp.integrate.odeint(integrate_RNN_autonomous, r_initial_for_testing, times_testing, args=(W_out,gamma,A,W_in)) 91 | 92 | print(W_out.shape) 93 | print(RNN_driven_solution_2.shape) 94 | predicted_u = RNN_driven_solution_2@(W_out.T) 95 | 96 | plt.figure() 97 | plt.plot(predicted_u) 98 | plt.title("Gamma="+str(gamma)) 99 | plt.show() -------------------------------------------------------------------------------- /test: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ECE228SharedDesktop/NGSIM_predict/3aa563bc8fb5b1eb00d52cf8439d8cd55e589e2b/test -------------------------------------------------------------------------------- /us-101_id_list: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ECE228SharedDesktop/NGSIM_predict/3aa563bc8fb5b1eb00d52cf8439d8cd55e589e2b/us-101_id_list --------------------------------------------------------------------------------