├── project.nod.xml ├── project.sumocfg ├── project.netccfg ├── project.con.xml ├── project.edg.xml ├── project.add.xml ├── README.md ├── project.net.xml ├── AI_Traffic_Light.py └── project.rou.xml /project.nod.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /project.sumocfg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /project.netccfg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /project.con.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /project.edg.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /project.add.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # AI-Traffic-Control-System 2 | This project was completed in partial fulfilment of my engineering degree. 3 | 4 | ## **Outline:** 5 | 6 | The system utilises SUMO traffic simulator and Python 3 with TensorFlow. The system is developed for a minor arterial road intersection 7 | with traffic lights with turning signals. Utilising the reinforcement learning algorithm called Deep Q-learning, the system tries to choose the optimal traffic duration and phase to minimise traffic waiting 8 | times around the intersection. 9 | A 7 second yellow interval was employed, and green durations were adjusted between 10 s, 15 s and 20 s, depending on the vehicle demand. 10 | This system is a modified and adapted system developed by [1] as well as extracts from work done by [2, 3]. 11 | A more realistic approach was undertaken when developing this system with regards to real world sensors and data. 12 | Induction loop sensors were considered and thus the data from these sensors is what is used to drive the system. 13 | 14 | ## **The Agent:** 15 | 16 | **Framework:** Deep Q-Learning 17 | 18 | **Environment:** A 4-way intersection with 2 incoming lanes and 2 outgoing lanes per arm. The traffic signals include a turning signal. 19 | 500 cars were used for the simulation 20 | 21 | **Sensors:** 3 induction loop sensors were placed 21 m apart per approaching lane to the intersection. Totalling 12 sensors used for 22 | the intersection. These sensors are used to obtain the waiting times, vehicle occupancy, vehicle position and vehicle velocity 23 | used by the agent to obtain the reward and develop the state. 24 | 25 | **State:** The state is made up of the position and velocity data received from the induction loop sensors as well as the 26 | current traffic phase and current duration. 27 | 28 | **Action:** The actions are the traffic phases. Since a turning signal is included, there are 4 possible phases for the intersection. 29 | However, the phase durations were as consider. 10 s, 15 s and 20 s green phase durations were used in this system. 30 | Therefore, the total number of actions were 12. 31 | 32 | **Training:** Q-learning was used with an Adaptive Momemnt Estimation (ADAM) optimiser. Additionally, Experience Replay techniques were also used so that the system 33 | could learn over time based on its previous memory. 34 | 35 | **Reward:** The reward is based on the vehicle occupancy on the induction loop sensors. These sensors given the time the 36 | vehicle was on the sensor thus, this is used to determine the reward. The waiting time of the vehicles on each sensor is 37 | summed to obtain the total waiting time. The goal of the system was to decrease the total waiting time as much as possible. 38 | 39 | **Action Policy:** Since the system is developed to learn over time, an epsilon greedy action possible is applied. 40 | 41 | 42 | ## **Requirements to run the code:** 43 | • Python 44 | 45 | • TensorFlow 46 | 47 | • SUMO Traffic simulator 48 | 49 | • Traffic Control Interface (TraCI) – this is included with SUMO 50 | 51 | ## **Additional files for the traffic generation and intersection layout:** 52 | • Add.xml – This file for the induction loops and initialling the traffic light phases. 53 | 54 | • Rou.xml – This file is created when running the code. It is for the vehicle routes and the paths in the simulation. 55 | 56 | • Con.xml – This file is for the round connections in the simulations. 57 | 58 | • Edg.xml – This is for the lanes. 59 | 60 | • Nod.xml – This is for the state and end points for the roads. 61 | 62 | • Net.xml – This is a configuration file to combine all the above files and create the road network. 63 | 64 | • Netccfg – This is a sumo network configuration. 65 | 66 | • Sumocfg – This is GUI file for the simulation 67 | 68 | 69 | ## **References:** 70 | 1. Vidali A, Crociani L, Vizzari G, Bandini,S, (2019). Bandini. A Deep Reinforcement Learning Approach to Adaptive Traffic Lights Management [cited 23 August 2019]. Available from: http://ceur-ws.org/Vol-2404/paper07.pdf 71 | 2. Gao J, Shen Y, Liu J, Ito M and Shiratori N. Adaptive Traffic Signal Control: Deep Reinforcement Learning Algorithm with Experience Replay and Target Network. [Internet]. Arxiv.org. 2019 [cited 28 June 2019]. Available from: https://arxiv.org/pdf/1705.02755.pdf 72 | 3. Liang X, Du X, Wang G, Han Z. (2018). Deep Reinforcement Learning for Traffic Light Control in Vehicular Networks. [cited 10 July 2019]. Available from: https://www.researchgate.net/publication/324104685_Deep_Reinforcement_Learning_for_Traffic_Light_Control_in_Vehicular_Networks 73 | 4. DLR - Institute of Transportation Systems - Eclipse SUMO – Simulation of Urban MObility [Internet]. Dlr.de. 2019 [cited 10 July 2019]. Available from: https://www.dlr.de/ts/en/desktopdefault.aspx/tabid-9883/16931_read-41000/ 74 | 75 | 76 | 77 | 78 | 79 | -------------------------------------------------------------------------------- /project.net.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | -------------------------------------------------------------------------------- /AI_Traffic_Light.py: -------------------------------------------------------------------------------- 1 | #Sanil Lala 2 | 3 | from __future__ import absolute_import 4 | from __future__ import print_function 5 | import os 6 | import sys 7 | import matplotlib.pyplot as plt 8 | import datetime 9 | import tensorflow as tf 10 | import numpy as np 11 | import math 12 | import timeit 13 | import random 14 | 15 | 16 | #Set SUMO environment path and import SUMO library and Traci 17 | tools = os.path.join(os.environ['SUMO_HOME'], 'tools') 18 | sys.path.append(tools) 19 | import sumolib 20 | from sumolib import checkBinary 21 | import traci 22 | 23 | #Class for traffic generation 24 | class TrafficGenerator: 25 | def __init__(self, Max_Steps): 26 | self.Total_Number_Cars = 500 #Number of cars used in the simulation 27 | self._max_steps = Max_Steps 28 | def generate_routefile(self, seed): 29 | np.random.seed(seed) 30 | Timing = np.random.poisson(2, self.Total_Number_Cars) #Poisson distribution for the car approach rate to teh intersection 31 | Timing = np.sort(Timing) 32 | Car_Generation_Steps = [] 33 | min_old = math.floor(Timing[1]) 34 | max_old = math.ceil(Timing[-1]) 35 | min_new = 0 36 | max_new = self._max_steps 37 | 38 | #Create .xml file for SUMO simulation 39 | for value in Timing: 40 | Car_Generation_Steps = np.append(Car_Generation_Steps, ((max_new - min_new) / (max_old - min_old)) * (value - max_old) + max_new) 41 | 42 | Car_Generation_Steps = np.rint(Car_Generation_Steps) 43 | with open("project.rou.xml", "w") as routes: 44 | 45 | #Generate Routes 46 | print(""" 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | """, file=routes) 61 | 62 | #Generate cars to follow routes 63 | for car_counter, step in enumerate(Car_Generation_Steps): 64 | Straight_or_Turn = np.random.uniform() 65 | if Straight_or_Turn < 0.25: 66 | Straight = np.random.randint(1, 9) 67 | if Straight == 1: 68 | print(' ' % (car_counter, step), file=routes) 69 | elif Straight == 2: 70 | print(' ' % (car_counter, step), file=routes) 71 | elif Straight == 3: 72 | print(' ' % (car_counter, step), file=routes) 73 | elif Straight == 4: 74 | print(' ' % (car_counter, step), file=routes) 75 | elif Straight == 5: 76 | print(' ' % (car_counter, step), file=routes) 77 | elif Straight == 6: 78 | print(' ' % (car_counter, step), file=routes) 79 | elif Straight == 7: 80 | print(' ' % (car_counter, step), file=routes) 81 | else: 82 | print(' ' % (car_counter, step), file=routes) 83 | else: 84 | Turn = np.random.randint(1, 5) 85 | if Turn == 1: 86 | print(' ' % (car_counter, step), file=routes) 87 | 88 | elif Turn == 2: 89 | print(' ' % (car_counter, step), file=routes) 90 | 91 | elif Turn == 3: 92 | print(' ' % (car_counter, step), file=routes) 93 | else: 94 | print(' ' % (car_counter, step), file=routes) 95 | print("", file=routes) 96 | 97 | #Main class for running the simulation 98 | class RunSimulation: 99 | def __init__(self, SimSession, model, memory, traffic_gen, total_episodes, gamma, Max_Steps, Green_Duration, Yellow_Duration, SUMO_Command): 100 | self.Session = SimSession 101 | self._Model = model 102 | self._memory = memory 103 | self._traffic_gen = traffic_gen 104 | self._total_episodes = total_episodes 105 | self._gamma = gamma 106 | self._epsilongreedy = 0 107 | self._steps = 0 108 | self._waiting_times = {} 109 | self._SUMO_Command = SUMO_Command 110 | self._max_steps = Max_Steps 111 | self._green_duration = Green_Duration 112 | self._yellow_duration = Yellow_Duration 113 | self._sum_intersection_queue = 0 114 | self._StoreReward = [] 115 | self._cumulative_wait_store = [] 116 | self._avg_intersection_queue_store = [] 117 | 118 | 119 | #Defining the initial conditions and running the simulation 120 | def run(self, episode): 121 | self._traffic_gen.generate_routefile(episode) 122 | traci.start(self._SUMO_Command) 123 | self._epsilongreedy = 1.0 - (episode / self._total_episodes) #Epsilon Greedy action policy 124 | self._steps = 0 125 | tot_neg_reward = 0 126 | old_total_wait = 0 127 | intersection_queue = 0 128 | self._waiting_times = {} 129 | self._sum_intersection_queue = 0 130 | 131 | while self._steps < self._max_steps: 132 | current_state = self._get_state() 133 | current_total_wait = self._get_waiting_times() 134 | reward = old_total_wait - current_total_wait 135 | 136 | #Add previous state, action, reward and current state to memory 137 | if self._steps != 0: 138 | self._memory.Add_Sample((old_state, old_action, reward, current_state)) 139 | action = self._choose_action(current_state) 140 | 141 | 142 | #Set yellow phase if traffic signal is different from previous signal 143 | if self._steps != 0 and old_action != action: 144 | self._Set_YellowPhase(old_action) 145 | self._simulate(self._yellow_duration) 146 | self._Set_GreenPhaseandDuration(action) 147 | self._simulate(self._green_duration) 148 | old_state = current_state 149 | old_action = action 150 | old_total_wait = current_total_wait 151 | if reward < 0: 152 | tot_neg_reward += reward 153 | 154 | #Save stats and print current eposide results 155 | self._save_stats(tot_neg_reward) 156 | print("Total reward: {}, Eps: {}".format(tot_neg_reward, self._epsilongreedy)) 157 | traci.close() 158 | 159 | 160 | def _simulate(self, steps_todo): 161 | if (self._steps + steps_todo) >= self._max_steps: 162 | steps_todo = self._max_steps - self._steps 163 | self._steps = self._steps + steps_todo 164 | while steps_todo > 0: 165 | traci.simulationStep() 166 | self._replay() 167 | steps_todo -= 1 168 | intersection_queue = self._get_stats() 169 | self._sum_intersection_queue += intersection_queue 170 | 171 | 172 | #Obtain vehicle waiting times from simulation 173 | def _get_waiting_times(self): 174 | incoming_roads = ["1i", "4i", "2i", "3i"] 175 | halt_N = traci.inductionloop.getLastStepOccupancy("Loop4i_0_1") + traci.inductionloop.getLastStepOccupancy("Loop4i_0_2") + traci.inductionloop.getLastStepOccupancy( 176 | "Loop4i_0_3") + traci.inductionloop.getLastStepOccupancy("Loop4i_1_1") + traci.inductionloop.getLastStepOccupancy( 177 | "Loop4i_1_2")+ traci.inductionloop.getLastStepOccupancy("Loop4i_1_3") 178 | halt_S = traci.inductionloop.getLastStepOccupancy("Loop3i_0_1") + traci.inductionloop.getLastStepOccupancy("Loop3i_0_2") + traci.inductionloop.getLastStepOccupancy( 179 | "Loop3i_0_3") + traci.inductionloop.getLastStepOccupancy("Loop3i_1_1") + traci.inductionloop.getLastStepOccupancy( 180 | "Loop3i_1_2") + traci.inductionloop.getLastStepOccupancy("Loop3i_1_3") 181 | halt_E = traci.inductionloop.getLastStepOccupancy("Loop1i_0_1") + traci.inductionloop.getLastStepOccupancy("Loop1i_0_2") + traci.inductionloop.getLastStepOccupancy( 182 | "Loop1i_0_3") + traci.inductionloop.getLastStepOccupancy("Loop1i_1_1") + traci.inductionloop.getLastStepOccupancy( 183 | "Loop1i_1_2") + traci.inductionloop.getLastStepOccupancy("Loop1i_1_3") 184 | halt_W = traci.inductionloop.getLastStepOccupancy("Loop2i_0_1") + traci.inductionloop.getLastStepOccupancy("Loop2i_0_2") + traci.inductionloop.getLastStepOccupancy( 185 | "Loop2i_1_3") + traci.inductionloop.getLastStepOccupancy("Loop2i_1_1") + traci.inductionloop.getLastStepOccupancy( 186 | "Loop2i_1_2") + traci.inductionloop.getLastStepOccupancy("Loop2i_1_3") 187 | wait = halt_N + halt_S + halt_E + halt_W 188 | total_waiting_time =wait 189 | return total_waiting_time 190 | 191 | #choose action 192 | def _choose_action(self, state): 193 | if random.uniform(0.0,1.0) < self._epsilongreedy: 194 | return random.randint(0, self._Model.Number_Actions - 3) #Random action 195 | else: 196 | return np.argmax(self._Model.predict_one(state, self.Session) ) #Use memory 197 | 198 | #set yellow phase 199 | def _Set_YellowPhase(self, old_action): 200 | if old_action == 0 or old_action == 1 or old_action == 2: 201 | yellow_phase = 1 202 | elif old_action == 3 or old_action == 4 or old_action == 5: 203 | yellow_phase = 3 204 | elif old_action == 6 or old_action == 7 or old_action == 8: 205 | yellow_phase = 5 206 | elif old_action == 9 or old_action == 10 or old_action == 11: 207 | yellow_phase = 7 208 | traci.trafficlight.setPhase("0",yellow_phase) 209 | 210 | #set green phase duration 211 | def _Set_GreenPhaseandDuration(self, action): 212 | if action == 0: 213 | traci.trafficlight.setPhase("0", 0) 214 | traci.trafficlight.setPhaseDuration("0", 15) 215 | self._green_duration = 15 216 | elif action == 1: 217 | traci.trafficlight.setPhase("0", 0) 218 | traci.trafficlight.setPhaseDuration("0", 10) 219 | self._green_duration = 10 220 | elif action == 2: 221 | traci.trafficlight.setPhase("0", 0) 222 | traci.trafficlight.setPhaseDuration("0", 20) 223 | self._green_duration = 20 224 | elif action == 3: 225 | traci.trafficlight.setPhase("0", 2) 226 | traci.trafficlight.setPhaseDuration("0", 15) 227 | self._green_duration = 15 228 | elif action == 4: 229 | traci.trafficlight.setPhase("0", 2) 230 | traci.trafficlight.setPhaseDuration("0", 10) 231 | self._green_duration = 10 232 | elif action == 5: 233 | traci.trafficlight.setPhase("0", 2) 234 | traci.trafficlight.setPhaseDuration("0", 20) 235 | self._green_duration = 20 236 | elif action == 5: 237 | traci.trafficlight.setPhase("0", 4) 238 | traci.trafficlight.setPhaseDuration("0", 15) 239 | self._green_duration = 15 240 | elif action == 7: 241 | traci.trafficlight.setPhase("0", 4) 242 | traci.trafficlight.setPhaseDuration("0", 10) 243 | self._green_duration = 10 244 | elif action == 8: 245 | traci.trafficlight.setPhase("0", 4) 246 | traci.trafficlight.setPhaseDuration("0", 20) 247 | self._green_duration = 20 248 | elif action == 9: 249 | traci.trafficlight.setPhase("0", 6) 250 | traci.trafficlight.setPhaseDuration("0", 15) 251 | self._green_duration = 15 252 | elif action == 10: 253 | traci.trafficlight.setPhase("0", 6) 254 | traci.trafficlight.setPhaseDuration("0", 10) 255 | self._green_duration = 10 256 | elif action == 11: 257 | traci.trafficlight.setPhase("0", 6) 258 | traci.trafficlight.setPhaseDuration("0", 20) 259 | self._green_duration = 20 260 | 261 | #obtain queue stats from simulation 262 | def _get_stats(self): 263 | intersection_queue = 0 264 | halt_N = traci.inductionloop.getLastStepOccupancy("Loop4i_0_1") + traci.inductionloop.getLastStepOccupancy("Loop4i_0_2") + traci.inductionloop.getLastStepOccupancy( 265 | "Loop4i_0_3") + traci.inductionloop.getLastStepOccupancy("Loop4i_1_1") + traci.inductionloop.getLastStepOccupancy( 266 | "Loop4i_1_2")+ traci.inductionloop.getLastStepOccupancy("Loop4i_1_3") 267 | halt_S = traci.inductionloop.getLastStepOccupancy("Loop3i_0_1") + traci.inductionloop.getLastStepOccupancy("Loop3i_0_2") + traci.inductionloop.getLastStepOccupancy( 268 | "Loop3i_0_3") + traci.inductionloop.getLastStepOccupancy("Loop3i_1_1") + traci.inductionloop.getLastStepOccupancy( 269 | "Loop3i_1_2") + traci.inductionloop.getLastStepOccupancy("Loop3i_1_3") 270 | halt_E = traci.inductionloop.getLastStepOccupancy("Loop1i_0_1") + traci.inductionloop.getLastStepOccupancy("Loop1i_0_2") + traci.inductionloop.getLastStepOccupancy( 271 | "Loop1i_0_3") + traci.inductionloop.getLastStepOccupancy("Loop1i_1_1") + traci.inductionloop.getLastStepOccupancy( 272 | "Loop1i_1_2") + traci.inductionloop.getLastStepOccupancy("Loop1i_1_3") 273 | halt_W = traci.inductionloop.getLastStepOccupancy("Loop2i_0_1") + traci.inductionloop.getLastStepOccupancy("Loop2i_0_2") + traci.inductionloop.getLastStepOccupancy( 274 | "Loop2i_1_3") + traci.inductionloop.getLastStepOccupancy("Loop2i_1_1") + traci.inductionloop.getLastStepOccupancy( 275 | "Loop2i_1_2") + traci.inductionloop.getLastStepOccupancy("Loop2i_1_3") 276 | intersection_queue = halt_N + halt_S + halt_E + halt_W 277 | return intersection_queue 278 | 279 | #ibtain state after action 280 | def _get_state(self): 281 | #Create 12 x 3 Matrix for Vehicle Positions and Velocities 282 | Position_Matrix = [] 283 | Velocity_Matrix = [] 284 | for i in range(8): 285 | Position_Matrix.append([]) 286 | Velocity_Matrix.append([]) 287 | for j in range(3): 288 | Position_Matrix[i].append(0) 289 | Velocity_Matrix[i].append(0) 290 | Position_Matrix = np.array(Position_Matrix) 291 | Velocity_Matrix = np.array(Velocity_Matrix) 292 | 293 | Loop1i_0_1 = traci.inductionloop.getLastStepVehicleIDs("Loop1i_0_1" ) 294 | Loop1i_0_2 = traci.inductionloop.getLastStepVehicleIDs("Loop1i_0_2" ) 295 | Loop1i_0_3 = traci.inductionloop.getLastStepVehicleIDs("Loop1i_0_3" ) 296 | Loop1i_1_1 = traci.inductionloop.getLastStepVehicleIDs("Loop1i_1_1" ) 297 | Loop1i_1_2 = traci.inductionloop.getLastStepVehicleIDs("Loop1i_1_2" ) 298 | Loop1i_1_3 = traci.inductionloop.getLastStepVehicleIDs("Loop1i_1_3" ) 299 | 300 | 301 | if len(Loop1i_0_1) != 0: 302 | Velocity_Matrix[0,0] = traci.vehicle.getSpeed(Loop1i_0_1[0]) 303 | Loop1i_0_1 = 1 304 | else: 305 | Loop1i_0_1 = 0 306 | 307 | if len(Loop1i_0_2) != 0: 308 | Velocity_Matrix[0,1] = traci.vehicle.getSpeed(Loop1i_0_2[0]) 309 | Loop1i_0_2 = 1 310 | else: 311 | Loop1i_0_2 = 0 312 | 313 | if len(Loop1i_0_3) != 0: 314 | Velocity_Matrix[0,2] = traci.vehicle.getSpeed(Loop1i_0_3[0]) 315 | Loop1i_0_3 = 1 316 | else: 317 | Loop1i_0_3 = 0 318 | 319 | if len(Loop1i_1_1) != 0: 320 | Velocity_Matrix[1,0] = traci.vehicle.getSpeed(Loop1i_1_1[0]) 321 | Loop1i_1_1 = 1 322 | else: 323 | Loop1i_1_1 = 0 324 | 325 | if len(Loop1i_1_2) != 0: 326 | Velocity_Matrix[1,1] = traci.vehicle.getSpeed(Loop1i_1_2[0]) 327 | Loop1i_1_2 = 1 328 | else: 329 | Loop1i_1_2 = 0 330 | 331 | if len(Loop1i_1_3) != 0: 332 | Velocity_Matrix[1,2] = traci.vehicle.getSpeed(Loop1i_1_3[0]) 333 | Loop1i_1_3 = 1 334 | else: 335 | Loop1i_1_3 = 0 336 | 337 | Position_Matrix[0,0] = Loop1i_0_1 338 | Position_Matrix[0,1] = Loop1i_0_2 339 | Position_Matrix[0,2] = Loop1i_0_3 340 | Position_Matrix[1,0] = Loop1i_1_1 341 | Position_Matrix[1,1] = Loop1i_1_2 342 | Position_Matrix[1,2] = Loop1i_1_3 343 | 344 | Loop2i_0_1 = traci.inductionloop.getLastStepVehicleIDs("Loop2i_0_1" ) 345 | Loop2i_0_2 = traci.inductionloop.getLastStepVehicleIDs("Loop2i_0_2" ) 346 | Loop2i_0_3 = traci.inductionloop.getLastStepVehicleIDs("Loop2i_0_3" ) 347 | Loop2i_1_1 = traci.inductionloop.getLastStepVehicleIDs("Loop2i_1_1" ) 348 | Loop2i_1_2 = traci.inductionloop.getLastStepVehicleIDs("Loop2i_1_2" ) 349 | Loop2i_1_3 = traci.inductionloop.getLastStepVehicleIDs("Loop2i_1_3" ) 350 | 351 | 352 | if len(Loop2i_0_1) != 0: 353 | Velocity_Matrix[2,0] = traci.vehicle.getSpeed(Loop2i_0_1[0]) 354 | Loop2i_0_1 = 1 355 | else: 356 | Loop2i_0_1 = 0 357 | 358 | if len(Loop2i_0_2) != 0: 359 | Velocity_Matrix[2,1] = traci.vehicle.getSpeed(Loop2i_0_2[0]) 360 | Loop2i_0_2 = 1 361 | else: 362 | Loop2i_0_2 = 0 363 | 364 | if len(Loop2i_0_3) != 0: 365 | Velocity_Matrix[2,1] = traci.vehicle.getSpeed(Loop2i_0_3[0]) 366 | Loop2i_0_3 = 1 367 | else: 368 | Loop2i_0_3 = 0 369 | 370 | if len(Loop2i_1_1) != 0: 371 | Velocity_Matrix[3,0] = traci.vehicle.getSpeed(Loop2i_1_1[0]) 372 | Loop2i_1_1 = 1 373 | else: 374 | Loop2i_1_1 = 0 375 | 376 | if len(Loop2i_1_2) != 0: 377 | Velocity_Matrix[3,1] = traci.vehicle.getSpeed(Loop2i_1_2[0]) 378 | Loop2i_1_2 = 1 379 | else: 380 | Loop2i_1_2 = 0 381 | 382 | if len(Loop2i_1_3) != 0: 383 | Velocity_Matrix[3,2] = traci.vehicle.getSpeed(Loop2i_1_3[0]) 384 | Loop2i_1_3 = 1 385 | else: 386 | Loop2i_1_3 = 0 387 | 388 | Position_Matrix[2,0] = Loop2i_0_1 389 | Position_Matrix[2,1] = Loop2i_0_2 390 | Position_Matrix[2,2] = Loop2i_0_3 391 | Position_Matrix[3,0] = Loop2i_1_1 392 | Position_Matrix[3,1] = Loop2i_1_2 393 | Position_Matrix[3,2] = Loop2i_1_3 394 | 395 | Loop3i_0_1 = traci.inductionloop.getLastStepVehicleIDs("Loop3i_0_1" ) 396 | Loop3i_0_2 = traci.inductionloop.getLastStepVehicleIDs("Loop3i_0_2" ) 397 | Loop3i_0_3 = traci.inductionloop.getLastStepVehicleIDs("Loop3i_0_3" ) 398 | Loop3i_1_1 = traci.inductionloop.getLastStepVehicleIDs("Loop3i_1_1" ) 399 | Loop3i_1_2 = traci.inductionloop.getLastStepVehicleIDs("Loop3i_1_2" ) 400 | Loop3i_1_3 = traci.inductionloop.getLastStepVehicleIDs("Loop3i_1_3" ) 401 | 402 | 403 | if len(Loop3i_0_1) != 0: 404 | Velocity_Matrix[4,0] = traci.vehicle.getSpeed(Loop3i_0_1[0]) 405 | Loop3i_0_1 = 1 406 | else: 407 | Loop3i_0_1 = 0 408 | 409 | if len(Loop3i_0_2) != 0: 410 | Velocity_Matrix[4,1] = traci.vehicle.getSpeed(Loop3i_0_2[0]) 411 | Loop3i_0_2 = 1 412 | else: 413 | Loop3i_0_2 = 0 414 | 415 | if len(Loop3i_0_3) != 0: 416 | Velocity_Matrix[4,2] = traci.vehicle.getSpeed(Loop3i_0_3[0]) 417 | Loop3i_0_3 = 1 418 | else: 419 | Loop3i_0_3 = 0 420 | 421 | if len(Loop3i_1_1) != 0: 422 | Velocity_Matrix[5,0] = traci.vehicle.getSpeed(Loop3i_1_1[0]) 423 | Loop3i_1_1 = 1 424 | else: 425 | Loop3i_1_1 = 0 426 | 427 | if len(Loop3i_1_2) != 0: 428 | Velocity_Matrix[5,1] = traci.vehicle.getSpeed(Loop3i_1_2[0]) 429 | Loop3i_1_2 = 1 430 | else: 431 | Loop3i_1_2 = 0 432 | 433 | if len(Loop3i_1_3) != 0: 434 | Velocity_Matrix[5,2] = traci.vehicle.getSpeed(Loop3i_1_3[0]) 435 | Loop3i_1_3 = 1 436 | else: 437 | Loop3i_1_3 = 0 438 | 439 | Position_Matrix[4,0] = Loop3i_0_1 440 | Position_Matrix[4,1] = Loop3i_0_2 441 | Position_Matrix[4,2] = Loop3i_0_3 442 | Position_Matrix[5,0] = Loop3i_1_1 443 | Position_Matrix[5,1] = Loop3i_1_2 444 | Position_Matrix[5,2] = Loop3i_1_3 445 | 446 | Loop4i_0_1 = traci.inductionloop.getLastStepVehicleIDs("Loop4i_0_1" ) 447 | Loop4i_0_2 = traci.inductionloop.getLastStepVehicleIDs("Loop4i_0_2" ) 448 | Loop4i_0_3 = traci.inductionloop.getLastStepVehicleIDs("Loop4i_0_3" ) 449 | Loop4i_1_1 = traci.inductionloop.getLastStepVehicleIDs("Loop4i_1_1" ) 450 | Loop4i_1_2 = traci.inductionloop.getLastStepVehicleIDs("Loop4i_1_2" ) 451 | Loop4i_1_3 = traci.inductionloop.getLastStepVehicleIDs("Loop4i_1_3" ) 452 | 453 | if len(Loop4i_0_1) != 0: 454 | Velocity_Matrix[6,0] = traci.vehicle.getSpeed(Loop4i_0_1[0]) 455 | Loop4i_0_1 = 1 456 | else: 457 | Loop4i_0_1 = 0 458 | 459 | if len(Loop4i_0_2) != 0: 460 | Velocity_Matrix[6,1] = traci.vehicle.getSpeed(Loop4i_0_2[0]) 461 | Loop4i_0_2 = 1 462 | else: 463 | Loop4i_0_2 = 0 464 | 465 | if len(Loop4i_0_3) != 0: 466 | Velocity_Matrix[6,2] = traci.vehicle.getSpeed(Loop4i_0_3[0]) 467 | Loop4i_0_3 = 1 468 | else: 469 | Loop4i_0_3 = 0 470 | 471 | if len(Loop4i_1_1) != 0: 472 | Velocity_Matrix[7,0] = traci.vehicle.getSpeed(Loop4i_1_1[0]) 473 | Loop4i_1_1 = 1 474 | else: 475 | Loop4i_1_1 = 0 476 | 477 | if len(Loop4i_1_2) != 0: 478 | Velocity_Matrix[7,1] = traci.vehicle.getSpeed(Loop4i_1_2[0]) 479 | Loop4i_1_2 = 1 480 | else: 481 | Loop4i_1_2 = 0 482 | 483 | if len(Loop4i_1_3) != 0: 484 | Velocity_Matrix[7,2] = traci.vehicle.getSpeed(Loop4i_1_3[0]) 485 | Loop4i_1_3 = 1 486 | else: 487 | Loop4i_1_3 = 0 488 | 489 | Position_Matrix[6,0] = Loop4i_0_1 490 | Position_Matrix[6,1] = Loop4i_0_2 491 | Position_Matrix[6,2] = Loop4i_0_3 492 | Position_Matrix[7,0] = Loop4i_1_1 493 | Position_Matrix[7,1] = Loop4i_1_2 494 | Position_Matrix[7,2] = Loop4i_1_3 495 | 496 | 497 | 498 | #Create 4 x 1 matrix for phase state 499 | Phase = [] 500 | if traci.trafficlight.getPhase('0') == 0 or traci.trafficlight.getPhase('0') == 1 or traci.trafficlight.getPhase('0') == 2: 501 | Phase = [1, 0, 0, 0] 502 | elif traci.trafficlight.getPhase('0') == 3 or traci.trafficlight.getPhase('0') == 4 or traci.trafficlight.getPhase('0') == 5: 503 | Phase = [0, 1, 0, 0] 504 | elif traci.trafficlight.getPhase('0') == 6 or traci.trafficlight.getPhase('0') == 7 or traci.trafficlight.getPhase('0') == 8: 505 | Phase = [0, 0, 1, 0] 506 | elif traci.trafficlight.getPhase('0') == 9 or traci.trafficlight.getPhase('0') == 10 or traci.trafficlight.getPhase('0') == 11: 507 | Phase = [0, 0, 0, 1] 508 | 509 | Phase = np.array(Phase) 510 | Phase = Phase.flatten() 511 | 512 | state = np.concatenate((Position_Matrix,Velocity_Matrix), axis=0) 513 | state = state.flatten() 514 | state = np.concatenate((state,Phase), axis=0) 515 | 516 | #Create matrix for duration 517 | Duration_Matrix = [traci.trafficlight.getPhaseDuration('0')] 518 | 519 | Duration_Matrix = np.array(Duration_Matrix) 520 | Duration_Matrix = Duration_Matrix.flatten() 521 | state = np.concatenate((state,Duration_Matrix), axis=0) 522 | 523 | 524 | return state 525 | 526 | 527 | #Replay memory 528 | def _replay(self): 529 | Batch = self._memory.Get_Samples(self._Model.batch_size) 530 | if len(Batch) > 0: 531 | states = np.array([val[0] for val in Batch]) 532 | next_states = np.array([val[3] for val in Batch]) 533 | QSA = self._Model.predict_batch(states, self.Session) 534 | QSATarget = self._Model.predict_batch(next_states, self.Session) 535 | x = np.zeros((len(Batch), self._Model.Number_States)) 536 | y = np.zeros((len(Batch), self._Model.Number_Actions)) 537 | for i, b in enumerate(Batch): 538 | state, action, reward, next_state = b[0], b[1], b[2], b[3] 539 | Current_Q = QSA[i] 540 | Current_Q[action] = reward + self._gamma * np.amax(QSATarget[i]) 541 | x[i] = state 542 | y[i] = Current_Q 543 | self._Model.train_batch(self.Session, x, y) 544 | 545 | 546 | def _save_stats(self, tot_neg_reward): 547 | self._StoreReward.append(tot_neg_reward) 548 | self._cumulative_wait_store.append(self._sum_intersection_queue) 549 | 550 | @property 551 | def reward_store(self): 552 | return self._StoreReward 553 | 554 | @property 555 | def cumulative_wait_store(self): 556 | return self._cumulative_wait_store 557 | 558 | @property 559 | def avg_intersection_queue_store(self): 560 | return self._avg_intersection_queue_store 561 | 562 | #Reinforcement learning model 563 | class Model: 564 | def __init__(self, Number_States, Number_Actions, batch_size): 565 | self._Number_States = Number_States 566 | self._Number_Actions = Number_Actions 567 | self._batch_size = batch_size 568 | 569 | self._states = None 570 | self._actions = None 571 | 572 | self._logits = None 573 | self._optimizer = None 574 | self._var_init = None 575 | 576 | self._define_model() 577 | 578 | #Create neural network 579 | def _define_model(self): 580 | 581 | self._states = tf.placeholder(shape=[None, self._Number_States], dtype=tf.float32) 582 | self._q_s_a = tf.placeholder(shape=[None, self._Number_Actions], dtype=tf.float32) 583 | 584 | fc1 = tf.layers.dense(self._states, 33, activation=tf.nn.relu) 585 | fc2 = tf.layers.dense(fc1, 33, activation=tf.nn.relu) 586 | fc3 = tf.layers.dense(fc2, 33, activation=tf.nn.relu) 587 | self._logits = tf.layers.dense(fc3, self._Number_Actions) 588 | 589 | loss = tf.losses.mean_squared_error(self._q_s_a, self._logits) 590 | self._optimizer = tf.train.AdamOptimizer().minimize(loss) 591 | self._var_init = tf.global_variables_initializer() 592 | 593 | def predict_one(self, state, SimSession): 594 | return SimSession.run(self._logits, feed_dict={self._states: state.reshape(1, self.Number_States)}) 595 | 596 | def predict_batch(self, states, SimSession): 597 | return SimSession.run(self._logits, feed_dict={self._states: states}) 598 | 599 | def train_batch(self, SimSession, x_batch, y_batch): 600 | SimSession.run(self._optimizer, feed_dict={self._states: x_batch, self._q_s_a: y_batch}) 601 | 602 | @property 603 | def Number_States(self): 604 | return self._Number_States 605 | 606 | @property 607 | def Number_Actions(self): 608 | return self._Number_Actions 609 | 610 | @property 611 | def batch_size(self): 612 | return self._batch_size 613 | 614 | @property 615 | def var_init(self): 616 | return self._var_init 617 | 618 | #Class for storying and receiving memory 619 | class Memory: 620 | def __init__(self, Memory_Size): 621 | self._Memory_Size = Memory_Size 622 | self.Samples = [] 623 | 624 | def Get_Samples(self, Number_Samples): 625 | if Number_Samples > len(self.Samples): 626 | return random.sample(self.Samples, len(self.Samples)) 627 | else: 628 | return random.sample(self.Samples, Number_Samples) 629 | 630 | def Add_Sample(self, sample): 631 | self.Samples.append(sample) 632 | if len(self.Samples) > self._Memory_Size: 633 | self.Samples.pop(0) 634 | 635 | 636 | #Saving and ploting graphs 637 | def save_graphs(sim_runner, total_episodes, plot_path): 638 | 639 | plt.rcParams.update({'font.size': 24}) 640 | 641 | # reward 642 | data = sim_runner.reward_store 643 | plt.plot(data) 644 | plt.ylabel("Cumulative negative reward") 645 | plt.xlabel("Episode") 646 | plt.margins(0) 647 | min_val = min(data) 648 | max_val = max(data) 649 | plt.ylim(min_val + 0.05 * min_val, max_val - 0.05 * max_val) 650 | fig = plt.gcf() 651 | fig.set_size_inches(20, 11.25) 652 | fig.savefig(plot_path + 'reward.png', dpi=96) 653 | plt.close("all") 654 | with open(plot_path + 'reward_data.txt', "w") as file: 655 | for item in data: 656 | file.write("%s\n" % item) 657 | 658 | data = sim_runner.cumulative_wait_store 659 | plt.plot(data) 660 | plt.ylabel("Cumulative Qccupancy (s)") 661 | plt.xlabel("Episode") 662 | plt.margins(0) 663 | min_val = min(data) 664 | max_val = max(data) 665 | plt.ylim(min_val - 0.05 * min_val, max_val + 0.05 * max_val) 666 | fig = plt.gcf() 667 | fig.set_size_inches(20, 11.25) 668 | fig.savefig(plot_path + 'delay.png', dpi=96) 669 | plt.close("all") 670 | with open(plot_path + 'delay_data.txt', "w") as file: 671 | for item in data: 672 | file.write("%s\n" % item) 673 | 674 | 675 | if __name__ == "__main__": 676 | 677 | gui = True 678 | total_episodes = 100 679 | gamma = 0.75 680 | batch_size = 32 681 | Memory_Size = 3200 682 | path = "./model/model_1g/" 683 | # ---------------------- 684 | 685 | Number_States = 53 686 | Number_Actions = 12 687 | Max_Steps = 3600 688 | Green_Duration = 10 689 | Yellow_Duration = 7 690 | 691 | #Change to False if Simulation GUI must be shown 692 | if gui == True: 693 | sumoBinary = checkBinary('sumo') 694 | else: 695 | sumoBinary = checkBinary('sumo-gui') 696 | 697 | model = Model(Number_States, Number_Actions, batch_size) 698 | memory = Memory(Memory_Size) 699 | traffic_gen = TrafficGenerator(Max_Steps) 700 | SUMO_Command = [sumoBinary, "-c", "project.sumocfg", '--start', "--no-step-log", "true", "--waiting-time-memory", str(Max_Steps)] 701 | saver = tf.train.Saver() 702 | 703 | with tf.Session() as SimSession: 704 | print("PATH:", path) 705 | print("----- Start time:", datetime.datetime.now()) 706 | SimSession.run(model.var_init) 707 | sim_runner = RunSimulation(SimSession, model, memory, traffic_gen, total_episodes, gamma, Max_Steps, Green_Duration, Yellow_Duration, SUMO_Command) 708 | episode = 0 709 | 710 | while episode < total_episodes: 711 | print('----- Episode {} of {}'.format(episode+1, total_episodes)) 712 | start = timeit.default_timer() 713 | sim_runner.run(episode) # run the simulation 714 | stop = timeit.default_timer() 715 | print('Time: ', round(stop - start, 1)) 716 | episode += 1 717 | 718 | os.makedirs(os.path.dirname(path), exist_ok=True) 719 | saver.save(SimSession, path + "my_tlcs_model.ckpt") 720 | print("----- End time:", datetime.datetime.now()) 721 | print("PATH:", path) 722 | save_graphs(sim_runner, total_episodes, path) 723 | -------------------------------------------------------------------------------- /project.rou.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | 287 | 288 | 289 | 290 | 291 | 292 | 293 | 294 | 295 | 296 | 297 | 298 | 299 | 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | 309 | 310 | 311 | 312 | 313 | 314 | 315 | 316 | 317 | 318 | 319 | 320 | 321 | 322 | 323 | 324 | 325 | 326 | 327 | 328 | 329 | 330 | 331 | 332 | 333 | 334 | 335 | 336 | 337 | 338 | 339 | 340 | 341 | 342 | 343 | 344 | 345 | 346 | 347 | 348 | 349 | 350 | 351 | 352 | 353 | 354 | 355 | 356 | 357 | 358 | 359 | 360 | 361 | 362 | 363 | 364 | 365 | 366 | 367 | 368 | 369 | 370 | 371 | 372 | 373 | 374 | 375 | 376 | 377 | 378 | 379 | 380 | 381 | 382 | 383 | 384 | 385 | 386 | 387 | 388 | 389 | 390 | 391 | 392 | 393 | 394 | 395 | 396 | 397 | 398 | 399 | 400 | 401 | 402 | 403 | 404 | 405 | 406 | 407 | 408 | 409 | 410 | 411 | 412 | 413 | 414 | 415 | 416 | 417 | 418 | 419 | 420 | 421 | 422 | 423 | 424 | 425 | 426 | 427 | 428 | 429 | 430 | 431 | 432 | 433 | 434 | 435 | 436 | 437 | 438 | 439 | 440 | 441 | 442 | 443 | 444 | 445 | 446 | 447 | 448 | 449 | 450 | 451 | 452 | 453 | 454 | 455 | 456 | 457 | 458 | 459 | 460 | 461 | 462 | 463 | 464 | 465 | 466 | 467 | 468 | 469 | 470 | 471 | 472 | 473 | 474 | 475 | 476 | 477 | 478 | 479 | 480 | 481 | 482 | 483 | 484 | 485 | 486 | 487 | 488 | 489 | 490 | 491 | 492 | 493 | 494 | 495 | 496 | 497 | 498 | 499 | 500 | 501 | 502 | 503 | 504 | 505 | 506 | 507 | 508 | 509 | 510 | 511 | 512 | 513 | 514 | 515 | 516 | 517 | --------------------------------------------------------------------------------