Tutorial on Traffic Signal¶
This tutorial explains how to define and configure traffic signals in UXsim.
[1]:
%matplotlib inline
%load_ext autoreload
%autoreload 2
Basic definition¶
In UXsim, a node can have its traffic signal. A traffic signal determine which link can send vehicles to the node. To define a signal of a node, you need to set signal
parameter of Node
and signal_group
parameter of Link
s that are connected to the node.
The following code shows a simple example of traffic signal definition.
[2]:
from uxsim import *
# simulation world
W = World(
name="",
deltan=1,
tmax=1200,
print_mode=1, save_mode=1, show_mode=1,
random_seed=0
)
# scenario
#merge network with signal
W.addNode("orig1", 0, 0)
W.addNode("orig2", 0, 2)
W.addNode("merge", 1, 1, signal=[30, 60]) #`signal` is a list of [duration of phase 0, duration of phase 1, ...]
W.addNode("dest", 2, 1)
W.addLink("link1", "orig1", "merge", length=1000, signal_group=0) #if `signal_group` is 0, the exit of this link will be green at phase 0
W.addLink("link2", "orig2", "merge", length=1000, signal_group=1)
W.addLink("link3", "merge", "dest", length=1000)
W.show_network(network_font_size=20, show_id=False)

signal
parameter of Node
(defined via W.addNode()
) is a list of duration of signal phases. For example, signal=[30, 60]
of “merge” node means that the signal of this node has 2 phases. And the duration of phase 0 is 30 seconds, and that of phase 1 is 60 seconds.
signal_group
parameter of Link
(defined via W.addLink()
) specifies the group to which this link belongs. For example, signal_group=0
of “link1” means that this link will have green light when the signal phase is 0.
Thus, the above code means that the “merge” node has a traffic signal. The signal pattern is, “link1” will green light for 30 seconds at first. And then “link2” will have green light for 60 seconds. And this pattern is repeated forever.
Now we set a demand and execute the simulation as follows.
[3]:
W.adddemand("orig1", "dest", 0, 1000, 0.2)
W.adddemand("orig2", "dest", 500, 1000, 0.6)
# execute simulation
W.exec_simulation()
# visualize
W.analyzer.print_simple_stats()
W.analyzer.time_space_diagram_traj_links([["link1", "link3"], ["link2", "link3"]])
simulation setting:
scenario name:
simulation duration: 1200 s
number of vehicles: 499 veh
total road length: 3000 m
time discret. width: 1 s
platoon size: 1 veh
number of timesteps: 1200
number of platoons: 499
number of links: 3
number of nodes: 4
setup time: 0.33 s
simulating...
time| # of vehicles| ave speed| computation time
0 s| 0 vehs| 0.0 m/s| 0.00 s
600 s| 90 vehs| 15.6 m/s| 0.42 s
1200 s| 0 vehs| 0.0 m/s| 0.73 s
simulation finished
results:
average speed: 14.5 m/s
number of completed trips: 499 / 499
average travel time of trips: 138.5 s
average delay of trips: 38.5 s
delay ratio: 0.278
total distance traveled: 998000.0 m
drawing trajectories...


In the vehicle trajectory diagram, red dots (looks like a bold horizontal line) at “merge” node position mean that the link has red light at the moment. You can see that vehicles followed the traffic signal and waiting queues were generated accordingly.
4-legged intersections¶
Here is a more realistic traffic signal example with standard, 4-legged intersections. We consider the following network with 4 intersections.
N1 N2
| |
W1--I1--I2--E1
| |
W2--I3--I4--E2
| |
S1 S2
[4]:
from uxsim import *
import random
import itertools
# world definition
seed = 0
W = World(
name="",
deltan=5,
tmax=3600,
print_mode=1, save_mode=0, show_mode=1,
random_seed=seed,
duo_update_time=99999
)
# network definition
I1 = W.addNode("I1", 0, 0, signal=[60,60])
I2 = W.addNode("I2", 1, 0, signal=[60,60])
I3 = W.addNode("I3", 0, -1, signal=[60,60])
I4 = W.addNode("I4", 1, -1, signal=[60,60])
W1 = W.addNode("W1", -1, 0)
W2 = W.addNode("W2", -1, -1)
E1 = W.addNode("E1", 2, 0)
E2 = W.addNode("E2", 2, -1)
N1 = W.addNode("N1", 0, 1)
N2 = W.addNode("N2", 1, 1)
S1 = W.addNode("S1", 0, -2)
S2 = W.addNode("S2", 1, -2)
#E <-> W direction: signal group 0
for n1,n2 in [[W1, I1], [I1, I2], [I2, E1], [W2, I3], [I3, I4], [I4, E2]]:
W.addLink(n1.name+n2.name, n1, n2, length=500, signal_group=0)
W.addLink(n2.name+n1.name, n2, n1, length=500, signal_group=0)
#N <-> S direction: signal group 1
for n1,n2 in [[N1, I1], [I1, I3], [I3, S1], [N2, I2], [I2, I4], [I4, S2]]:
W.addLink(n1.name+n2.name, n1, n2, length=500, signal_group=1)
W.addLink(n2.name+n1.name, n2, n1, length=500, signal_group=1)
W.show_network(network_font_size=15, show_id=False)

The intersection nodes I1, I2, I3, and I4 have 2 phases. During phase 0, links in east-west direction will have green light. Note that the links are added programatically by using for-loop, as manually adding this number of links is a little bit tedious.
The simulation results with random demand is shown below.
[5]:
# random demand definition
dt = 30
demand = 0.19
for n1, n2 in itertools.permutations([W1, W2, E1, E2, N1, N2, S1, S2], 2):
for t in range(0, 3600, dt):
W.adddemand(n1, n2, t, t+dt, random.uniform(0, demand))
# simulation
W.exec_simulation()
# resutls
W.analyzer.print_simple_stats()
W.analyzer.time_space_diagram_traj_links([["W1I1", "I1I2", "I2E1"], ["N1I1", "I1I3", "I3S1"]])
simulation setting:
scenario name:
simulation duration: 3600 s
number of vehicles: 3870 veh
total road length: 12000 m
time discret. width: 5 s
platoon size: 5 veh
number of timesteps: 720
number of platoons: 774
number of links: 24
number of nodes: 12
setup time: 0.51 s
simulating...
time| # of vehicles| ave speed| computation time
0 s| 0 vehs| 0.0 m/s| 0.00 s
600 s| 145 vehs| 14.5 m/s| 0.21 s
1200 s| 180 vehs| 9.4 m/s| 0.36 s
1800 s| 95 vehs| 7.4 m/s| 0.52 s
2400 s| 155 vehs| 6.0 m/s| 0.64 s
3000 s| 165 vehs| 12.7 m/s| 0.73 s
3600 s| 145 vehs| 13.8 m/s| 0.79 s
simulation finished
results:
average speed: 11.6 m/s
number of completed trips: 3700 / 3870
average travel time of trips: 132.3 s
average delay of trips: 52.7 s
delay ratio: 0.399
total distance traveled: 6101750.0 m
drawing trajectories...


Adaptive traffic signal control¶
Adaptive traffic signal control, in which traffic light is changed in response to real-time traffic conditions to achieve efficient traffic flow, is an important task in traffic engineering. UXsim can handle this task, by changing traffic signal phase during simulation depending on traffic states.
Let’s use the same scenario to the 1st example.
[6]:
from uxsim import *
# Definition of the main simulation
W = World(
name="",
deltan=1,
tmax=1200,
print_mode=1, save_mode=1, show_mode=1,
random_seed=0
)
# Scenario definition
# Merging network: signal control
W.addNode("orig1", 0, 0)
W.addNode("orig2", 0, 2)
node_signal = W.addNode("merge", 1, 1, signal=[30,60]) # The variable 'signal' is a list of [duration of phase 0, duration of phase 1, ...]
W.addNode("dest", 2, 1)
W.addLink("link1", "orig1", "merge", length=1000, free_flow_speed=20, jam_density=0.2, merge_priority=1, signal_group=0) # The value of the variable 'signal_group' (say `n`) indicates that the exit of this link will turn green during phase 'n'
W.addLink("link2", "orig2", "merge", length=1000, free_flow_speed=20, jam_density=0.2, merge_priority=1, signal_group=1)
W.addLink("link3", "merge", "dest", length=1000, free_flow_speed=20, jam_density=0.2)
W.adddemand("orig1", "dest", 0, 1000, 0.2)
W.adddemand("orig2", "dest", 500, 1000, 0.6)
Now we code traffic signal control logic during the simulation as follows.
[7]:
# Run the simulation iteratively
while W.check_simulation_ongoing():
# Execute the simulation in increments of 10 seconds
W.exec_simulation(duration_t2=10)
# Investigate the number of vehicles per link
vehicles_per_links = {}
for l in node_signal.inlinks.values():
vehicles_per_links[tuple(l.signal_group)] = l.num_vehicles # l.num_vehicles: Number of vehicles on link 'l'
max_vehicles_group = max(vehicles_per_links, key=vehicles_per_links.get) # Returns the link with the maximum number of vehicles
if int(W.TIME)%100 == 0:
#print logs. Sorry for the messy code. This code itself is not important, so no need to understand it
print(f"t = {W.TIME} s;", "; ".join([f"phase {k[0]} - number of vehicle = {v:2d}" for k,v in vehicles_per_links.items()]), f"; green light - phase {max_vehicles_group[0]}")
# Set the signal to green for the link with the maximum number of vehicles
node_signal.signal_phase = max_vehicles_group[0]
# reset the time counter
node_signal.signal_t = 0
simulation setting:
scenario name:
simulation duration: 1200 s
number of vehicles: 499 veh
total road length: 3000 m
time discret. width: 1 s
platoon size: 1 veh
number of timesteps: 1200
number of platoons: 499
number of links: 3
number of nodes: 4
setup time: 0.12 s
simulating...
time| # of vehicles| ave speed| computation time
0 s| 0 vehs| 0.0 m/s| 0.00 s
t = 100 s; phase 0 - number of vehicle = 10; phase 1 - number of vehicle = 0 ; green light - phase 0
t = 200 s; phase 0 - number of vehicle = 10; phase 1 - number of vehicle = 0 ; green light - phase 0
t = 300 s; phase 0 - number of vehicle = 10; phase 1 - number of vehicle = 0 ; green light - phase 0
t = 400 s; phase 0 - number of vehicle = 10; phase 1 - number of vehicle = 0 ; green light - phase 0
t = 500 s; phase 0 - number of vehicle = 10; phase 1 - number of vehicle = 0 ; green light - phase 0
t = 600 s; phase 0 - number of vehicle = 26; phase 1 - number of vehicle = 30 ; green light - phase 1
600 s| 86 vehs| 16.0 m/s| 0.35 s
t = 700 s; phase 0 - number of vehicle = 30; phase 1 - number of vehicle = 30 ; green light - phase 0
t = 800 s; phase 0 - number of vehicle = 26; phase 1 - number of vehicle = 34 ; green light - phase 1
t = 900 s; phase 0 - number of vehicle = 30; phase 1 - number of vehicle = 30 ; green light - phase 0
t = 1000 s; phase 0 - number of vehicle = 26; phase 1 - number of vehicle = 34 ; green light - phase 1
t = 1100 s; phase 0 - number of vehicle = 0; phase 1 - number of vehicle = 0 ; green light - phase 0
1200 s| 0 vehs| 0.0 m/s| 0.70 s
simulation finished
t = 1200 s; phase 0 - number of vehicle = 0; phase 1 - number of vehicle = 0 ; green light - phase 0
The above code means that simulation is iteratively executed for 10 seconds. Thusm, users can write a code to interven the simulation. The while loop will automatically exit when the simulation is complete.
During the simulation, the signal phase is specified by Node.signal_phase
so that a link with the largest number of vehicles Link.num_vehicles
will have a green light.
You can see how this code works in the above logs. For some time points, it shows number of vehicles for each phase and determines the phase.
The results are visualized as follows. You can see that, before 500 seconds, “link1” always had green light, as no vehicles were comming from “link2”. After 500 seconds, traffic started coming from “link2”. Then, traffic signal changed efficiently to keep the waiting queue minimum. The efficiency can be confirmed by comparing to the results of the 1st example in this demo: The delay was smaller, the queues were shorter, the flow during the green time was always saturated, and so on.
[8]:
# visualize
W.analyzer.print_simple_stats()
W.analyzer.time_space_diagram_traj_links([["link1", "link3"], ["link2", "link3"]])
results:
average speed: 16.6 m/s
number of completed trips: 499 / 499
average travel time of trips: 120.5 s
average delay of trips: 20.5 s
delay ratio: 0.170
total distance traveled: 998000.0 m
drawing trajectories...


Furthremore, more advanced control method such as AI (more specifically, deep reinforcement learning) can be easily integrated with UXsim’s traffic signal control. Please see Jupyter notebook or document.