Project Structure¶
Overview¶
The design of the project is aimed at enabling the comparison of various intersection control algorithms across various environments.
Examples of algorithms include Query-based centralised intersection control, decentralised reinforcement learning and decentralised negotiation-based approaches. Examples of environments include SUMO, CARLA or a scale-model road environment like Duckietown.
In order to achieve this, the project separates an intersection control experiment into two components: the algorithm, and the environment: the algorithm determines the behaviour of the vehicles at each point in time, and interacts with the environment to get the information (speed, position etc) necessary to determine that behaviour, before using the environment to execute it.
The algorithm interacts with the environment through a uniform interface defined in
intersection_control.core.environment
. This means that in theory, the same algorithm could be tested in a number
of different environments - this could, for example, provide insights into the algorithm’s resilience and stability.
Similarly, the algorithms implement a uniform interface (all of their behaviour is determined by implementing a
step()
function), and so a number of algorithms could be run on the same environment in the same traffic conditions, and their
performance compared.
Directory/Package Structure¶
The intersection_control
package is split into 4 sub-packages:
intersection_control.core
provides the interfaces described here. Specifically, it defines the APIs for
environment
, algorithm
, communication
and
performance_indication
.
The 3 other packages contain concrete implementations of these interfaces:
intersection_control.algorithms
provides concrete implementations of the Algorithm API - namely Query-based intersection controlintersection_control.environments
provides concrete implementations of the Environment API - namely aSumoEnvironment
intersection_control.communication
provides concrete implementations of the Communication API - namely aDistanceBasedUnit
IntersectionControl
├── docs # Documentation images and files
├── intersection_control # The main source code package
│ ├── core # Defines all interfaces and defines the component structure
│ │ ├── environment # Provides an interface for any environment to implement
│ │ │ ├── environment.py # Defines the base Environment class
│ │ │ ├── intersectiont_handler.py # Defines the base IntersectionHandler class
│ │ │ └── vehicle_handler.py # Defines the base VehicleHandler class
│ │ ├── algorithm
│ │ │ ├── vehicle.py # Defines the base Vehicle class
│ │ │ └── intersection_manager.py # Defines the base IntersectionManager class
│ │ ├── communication.py # Provides an interface for communication - V2V or V2I is possible. Specifically, defines the base MessagingUnit class
│ │ └── performance_indication.py # Defines the base PerformanceIndicator class (Not yet implemented)
│ ├── algorithms # A collection of intersection control algorithm implementations (for now only QBIM). These are implementations of core.Vehicle and core.IntersectionManager
│ ├── environments # A collection of environment implementations (for now only SUMO). These are implementations of core.Environment
│ └── communication # A collection of communication implementations (for now only DistanceBasedUnit). These are implementations of core.MessagingUnit
├── test # unit tests for various components
└── misc # Miscellaneous stand-alone scripts and experiments
Environment¶
The Environment API is defined in intersection_control.core.environment
and consists of
three components:
The main Environment
class is what any intersection control
algorithm will interact with, and will provide methods for retrieving general information about the environment, such as
the current time, any new vehicles which have arrived in the environment etc. It also provides a step()
method, which should advance the state of the environment by a time step. In a simulated environment, this will consist
of advancing the simulation state, and in a physical environment this could consist of time.sleep()
-ing for a
few milliseconds.
Additionally, the Environment
class provides access to both
a VehicleHandler
and IntersectionHandler
object
(through its intersections
and
vehicles
properties) which provide more specialised behaviour for retrieving and modifying the state of vehicles and
intersections respectively.
Implementing your own environment¶
Creating a new environment to test intersection control algorithms in would involve subclassing these three components
and providing their implementations. One such implementation which is provided by the library is
intersection_control.environments.SumoEnvironment
. Some more information about implementing your own
environment can be found in the Implementing an intersection environment section
Algorithm¶
The Algorithm API is defined in intersection_control.core.algorithm
and consists of the
Vehicle
and
IntersectionManager
components:
As shown in the diagram, both the Vehicle
and
IntersectionManager
hold a reference to the
environment and are able to interact with it in order to retrieve or modify the state of the environment.
The base Vehicle
class already implements some trivial methods,
such as get_speed()
simply by calling the corresponding
method on the environment object with the corresponding
vehicle_id
:
def get_speed(self) -> float:
return self.environment.vehicles.get_speed(self.vehicle_id)
The base IntersectionManager
class also implements a
set of similarly trivial methods which call into the environment. These are simply convenience methods which make
algorithm implementations clearer:
# i.e.
self.get_speed()
# instead of:
self.environment.get_speed(self.vehicle_id)
Communication¶
As well as information from the environment, intersection control algorithms have at their disposal a means of both
V2V (vehicle-to-vehicle) and V2I (vehicle-to-infrastructure - in this case IntersectionManager) communication. This is
achieved through the Communication API which consists of
Messages
and
MessagingUnits
:
In order to keep it as general and as simple as possible, the
Message
class only has two attributes:
sender
and
contents
. Here,
contents
is a python dict, which is extremely general
and could be used to implement any sort of messaging protocol.
Messages
are exchanged by
MessagingUnits
using the four methods in the above
diagram.
This communication API was designed to be general enough to implement any type of communication mechanisms and
protocols. For example, the implementation of a
MessagingUnit
could be backed by a powerful network
simulator such as Omnet++.
A reasonably simple concrete implementation of a distance-based
MessagingUnit
(where the discoverability of other
communication units is based on distance) can be found in intersection_control.communication.DistanceBasedUnit
.
Vehicles
and
IntersectionManagers
can then make use of
MessagingUnits
to communicate:
# In Vehicle class
def __init__(self, vehicle_id: str, environment: Environment):
# ...
self.messaging_unit = DistanceBasedUnit(self.get_id(), 50, self.get_position)
# ...
def step(self):
# ...
# Broadcast current speed to all other units in range
self.messaging_unit.broadcast(Message(self.messaging_unit.address, {
"speed": self.get_speed(),
# ...
}))
Implementing your own algorithm¶
Implementing an intersection control algorithm then consists of providing an implementation for
Vehicle.step()
and
IntersectionManager.step()
, which you can assume
will be called repeatedly after a small delta. One such implementation which is provided by the library is
intersection_control.algorithms.qb_im
- which is a query-based intersection control approach inspired by the
well-known paper by Dresner and Stone:
A Multiagent Approach to Autonomous Intersection Management. For
more information on implementing your own intersection control algorithm, refer to the documentation section
Implementing an intersection control algorithm