Introduction

This article will show you how to dynamically select vehicles to detour around specified roads within a SUMO traffic simulation.

Detours are a common event in urban driving and are caused by many reasons, including construction and vehicular accidents, to name a few. Whatever the reason, not all vehicles will choose to avoid these roads; some may drive straight through without a care, while others might take to the side streets. We will simulate both behaviours.

Prerequisites

I assume you have already installed SUMO on your machine and configured your PATH and SUMO_HOME environment variables. Read the documentation to learn how to get set up. To test that your machine is ready, you should be able to execute the following commands in a terminal:

  • sumo --version
  • sumo-gui
  • python --version
  • netedit

Getting started

If you’re familiar with SUMO traffic simulations or have read my other tutorials, then you might already know that the foundational components to run a simulation in SUMO include:

  • a network
  • traffic demand

In SUMO, simulations are deterministic by default, but there are various ways to introduce randomness. For our purposes, we’ll leverage a tool called Traffic Control Interface (TraCI for short) provided by SUMO to allow modifying the simulation in real-time.

TraCI enables us to introduce custom logic (in the form of Python code) to track and manipulate many aspects of the simulation, including the routes of individual vehicles. This article provides various source code examples to help you follow along. Three configuration files are grouped in a config folder, and one Python script — called main.py — contains all the logic to run and manipulate our simulation. To get started, mimic this folder structure on your machine.

Screenshot of the project folder structure.
Screenshot of the project folder structure.

Create the network

This section focuses on the network. The shape and complexity of the network are irrelevant. What matters is that you identify the name (id) of an edge you wish for the vehicles to avoid in the simulation. For the convenience of clarity, I’ll edit the network to rename three edges:

  • the origin edge (as “origin”) where all vehicles will depart;
  • the destination edge (as “destination”) where all vehicles are destined;
  • and the edge some vehicles will avoid (as “closed”).

If you’re unfamiliar with creating networks, check out my tutorial on using netedit to design your own networks.

Screenshot of the example network with custom-named edges highlighted for illustration purposes.
Screenshot of the example network with custom-named edges highlighted for illustration purposes.

For those following along and wanting more practice in using netedit, I’ve provided two tables below for the lists of junctions and edges you can use as reference when recreating the example network.

List of junctions

Table of junction metadata.

List of edges

Table of edge metadata.

For those who feel too lazy to draw their own networks, here is the source code for the example network shown above. Simply paste it into a file called network.net.xml in the config folder.

network.net.xml

Create traffic demand

This section is about traffic demand, another foundational component for traffic simulations. For our purposes, we can keep things simple using a handful of identical vehicles initially following the same route. We’ll later introduce custom logic to modify these routes dynamically.

  • Route ⇒ an expanded trip including the origin and destination edges and all edges in between.
  • Trip ⇒ a vehicular movement from one place to another.

There are many ways to generate routes in SUMO, with the choice depending on your available inputs. For our needs, we know the origin and destination edges. Still, we can save time by leaving out the intermediate edges and allowing SUMO to figure out the best path. To learn more about demand modelling, read the documentation.

With the example network, there are only two possible routes a vehicle can travel from origin to destination. In the figure below, these routes are illustrated with coloured arrows. By default, SUMO estimates travel time based on edge speed limits and distance, and it’ll choose the optimal path (aka. route) for individual vehicles to follow.

Possible vehicular route configurations on the example network.
Possible vehicular route configurations on the example network.

Let’s start by describing our trips. Although it’s possible to configure traffic demand using tools like netedit, it’s much easier to write the source code directly in this instance. Start by creating a file in the config folder, and let’s name it trips.trips.xml.

The basic description of a trip requires only an identifier (id), the origin (from) and destination (to) edges, as well as the time when the vehicle enters the network (depart). As previously mentioned, we’re leveraging automatic routing to generate our routes during runtime. To learn more about automatic routing, check out the documentation.

trips.trips.xml

Optional visualization

By default, SUMO GUI uses the “standard” view setting, representing vehicles as triangles and colouring the network grey. I prefer to use the “real world” view setting, which, as the name suggests, adds a more realistic visual representation of the elements in the simulation.

With SUMO GUI open, you can switch between the available view settings, but to change the default when the application loads, the config file viewSettings.xml is required.

viewSettings.xml

Running the simulation

By this point, you should have a project structure that resembles the one shown in the introduction, with two (optionally three) configuration files in the config folder. What’s missing is the runner script that orchestrates the simulation — and later, the handling of our detouring logic. This section focuses on the runner script, so let’s get started!

At the very least, the runner script will initiate the simulation, move the clock forward, and close the simulation once all the vehicles have exited.

Start the simulation

To simply get the simulation to run, we need TraCI to pipe our network and trip files into SUMO GUI (the graphical interface of SUMO). We’ll define a function, startSim(), to handle this operation. The following two procedures include moving the simulation time forward and also stopping the simulation.

Code snippet of the startSim function.

To use TraCI, we must provide it with a binary of an application and execution options. This binary will be SUMO-GUI, and we’ll obtain its binary using the checkBinary function in the sumolib library. The execution options depend on the application. For our purposes, we’re required to use the following options:

  • --net-file [FILE] → Reads the network file.
  • --route-files [FILES] → Reads the routes/trips file.

While the following options are optional:

  • --delay [FLOAT] → Adds [FLOAT] delay between simulation steps, which we'll use to essentially slow it down so human eyes can watch.
  • --gui-settings-file [FILE] → Reads visualization settings from the file, which we'll use to apply the "real world" skin to the road and vehicles.
  • --start → Automatically starts the simulation once loaded, saving us the trouble of pressing the start button manually.

👉 To view the list of available execution options, it’s general practice to execute the application directly in a terminal, followed by the option --help.

Incrementing the simulation clock

Without instructing TraCI when or how to increment the simulation clock, the simulation will remain frozen in time. We can easily increment the clock by calling the traci.simulationStep() method. However, this method only moves the clock by one second, so we’ll need to wrap this in a loop to continuously increment time.

To prevent the simulation from running forever, we’ll create a new function, shouldContinueSim(), to check if all the vehicles from our trips have entered and exited the network.

Code snippet of the shouldContinueSim function.

Closing the simulation

And finally, to stop TraCI from running, we’ll use its traci.close() method.

Putting it all together

Let’s put everything together in the main.py file.

The main.py file (version 1)

Run the simulation

Let’s run the simulation. In the terminal, execute the runner script with the command python main.py. You’ll watch all our vehicles enter the network from the leftmost edge and drive their optimal routes towards the rightmost edge. Voila! Next, we’ll introduce the detour logic.

GIF — Simulation of all vehicles driving the optimal route.
GIF — Simulation of all vehicles driving the optimal route.

Introducing the detour

So we’ve got our simulation generating vehicles and their respective routes based on our trip descriptions. Now it’s time to introduce the detour logic. In this section, we’ll select a handful of vehicles to detour and change their colour to help visually distinguish them from the unaffected vehicles.

Detour logic

Our approach to deter our vehicle from driving through the specific edge is to tell the vehicle that its travel time through the edge would be infinitely large. This approach works if the routing algorithm used by SUMO considers travel time in its calculations (which it does by default). Each vehicle considers edge travel time individually, and to change its perspective of these estimated travel times, TraCI provides the traci.vehicle.setAdaptedTraveltime() method.

Changing a vehicle’s perspective of edge travel times does nothing on its own because the vehicle decided its route when it initially entered the network and will not automatically change it. We need to force it to recalculate its route using the traci.vehicle.rerouteTraveltime() method. Together, these two methods will convince the vehicle to choose an alternative route to avoid the targeted edges. We'll create a function, avoidEdge(), to handle this operation.

Code snippet of the avoidEdge function.

Applying logic to vehicles

Now that we have the logic, we need to select and apply it to vehicles and the edge to avoid.

There are many ways to select vehicles, but we’ll take the most straightforward approach and manually record the names from our trips file. Knowing the IDs of the vehicles and edge is required for this step. I’ll use a global variable array, VEHICLES, to contain the names of these vehicles and another variable, EDGE_ID, to contain the edge's name.

Unfortunately, SUMO may throw an error if you try to access specific vehicle properties while the vehicle is not on the network. So we need to ensure the vehicle exists before we try to change anything, but there is no direct way to do this. One technique is to check if its name is among the list of vehicles that entered the network during this time by using the traci.simulation.getDepartedIDList() method. We'll create a function, getOurDeparted(), to handle this operation.

Code snippet of the getOurDeparted function.

Change vehicle color

Next, but optional, is to change the colour of our vehicle to red using the traci.vehicle.setColor() method. This will help us visually identify it among the other vehicles. We'll create a function, setVehColor(), to handle this operation.

Code snippet of the setVehColor function.

Putting things together

With all the above components, we can start and stop the simulation, increment its clock, and change vehicle colour and routes. With these, your main.py file should look like the following:

The main.py file (version 2)

Final result

Suppose you run the simulation now (python main.py). In that case, you'll notice that the vehicles we previously selected appear red once they enter the network. Additionally, as they approach the first intersection, they'll slow down, signal and turn right down the side street. At the same time, all other yellow vehicles continue straight (or change lanes to avoid a collision).

You’ll recall that we did not specify any intermediate edges in the description of our trips; this is all handled by SUMO. In fact, this behaviour is dynamic and based on various aspects of the network and traffic. For instance, by adding more side streets or introducing traffic lights, the vehicles would likely choose separate routes to avoid causing traffic jams.

GIF — Simulation of some vehicles driving the detoured route.
GIF — Simulation of some vehicles driving the detoured route.

Congratulations! Through this article, you designed a network, described traffic demand, configured visualization settings, set up TraCI to manage the simulation, and introduced dynamic detouring logic.

If you have any questions/suggestions, let’s connect on Github or LinkedIn!

https://github.com/DFazekas

https://www.linkedin.com/in/devon-fazekas

--

--

Devon Fazekas
Devon Fazekas

Written by Devon Fazekas

Meet Devon, an entrepreneur who is passionate about helping others live better lives by creating environments that encourage creativity and innovation.