Interfacing TraCI from Python

The TraCI commands are split into the 13 domains gui, lane, poi, simulation, trafficlight, vehicletype, edge, inductionloop, junction, multientryexit, polygon, route, person and vehicle, which correspond to individual modules. For a detailed list of available functions see the pydoc generated documentation. The source code can be found at [1]

importing traci in a script#

To use the library, the <SUMO_HOME>/tools directory must be on the python load path. This is typically done with a stanza like this:

 import os, sys
 if 'SUMO_HOME' in os.environ:
     tools = os.path.join(os.environ['SUMO_HOME'], 'tools')
     sys.exit("please declare environment variable 'SUMO_HOME'")

This assumes that the environment variable SUMO_HOME is set before running the script. Alternatively, you can declare the path to sumo/tools directly as in the line

sys.path.append(os.path.join('c:', os.sep, 'whatever', 'path', 'to', 'sumo', 'tools'))

First Steps#

In general it is very easy to interface with SUMO from Python (the following example is a modification of tutorial/traci_tls):

First you compose the command line to start either sumo or sumo-gui (leaving out the option which was needed before 0.28.0):

sumoBinary = "/path/to/sumo-gui"
sumoCmd = [sumoBinary, "-c", "yourConfiguration.sumocfg"]

Then you start the simulation and connect to it with your script:

import traci
step = 0
while step < 1000:
   if traci.inductionloop.getLastStepVehicleNumber("0") > 0:
   step += 1


After connecting to the simulation, you can emit various commands and execute simulation steps until you want to finish by closing the connection. By default, the close command will wait until the sumo process really finishes. You can disable this by calling:



Subscriptions can be thought of as a batch mode for retrieving variables. Instead of asking for the same variables over and over again, you can retrieve the values of interest automatically after each time step. TraCI subscriptions are handled on a per module basis. That is you can ask the module for the result of all current subscriptions after each time step. In order to subscribe for variables you need to know their variable ids which can be looked up in the traci/ file.

import traci
import traci.constants as tc

traci.start(["sumo", "-c", "my.sumocfg"])
traci.vehicle.subscribe(vehID, (tc.VAR_ROAD_ID, tc.VAR_LANEPOSITION))
for step in range(3):
   print("step", step)

The values retrieved are always the ones from the last time step, it is not possible to retrieve older values.

Context Subscriptions#

Context subscriptions work like subscriptions in that they retrieve a list of variables automatically for every simulation stop. However, the do so by setting a reference object and a range and then retrieving variables for all objects of a given type within range of the reference object.

TraCI context subscriptions are handled on a per module basis. That is you can ask the module for the result of all current subscriptions after each time step. In order to subscribe for variables you need to the domain id of the objects that shall be retrieved and the variable ids which can be looked up in the traci/ file. The domain id always has the form CMD_GET_<DOMAIN>_VARIABLE. The following code retrieves all vehicle speeds and waiting times within range (42m) of a junction (the vehicle ids are retrieved implicitly).

import traci
import traci.constants as tc

traci.start(["sumo", "-c", "my.sumocfg"])
traci.junction.subscribeContext(junctionID, tc.CMD_GET_VEHICLE_VARIABLE, 42, [tc.VAR_SPEED, tc.VAR_WAITING_TIME])
for step in range(3):
   print("step", step)

The values retrieved are always the ones from the last time step, it is not possible to retrieve older values.

Context Subscription Filters#

For vehicle-to-vehicle context subscriptions (i.e., context subscriptions, whose reference object is a vehicle and whose requested context objects are vehicles as well) it is possible to request additional filters to be applied already on the server side. The general procedure is to equip a requested context subscription with the filter directly after the call to subscribeContext() by a successive call to addSubscriptionFilter<FILTER_ID>() as for instance in the following snippet:

traci.vehicle.subscribeContext("ego", tc.CMD_GET_VEHICLE_VARIABLE, 0.0, [tc.VAR_SPEED])
traci.vehicle.addSubscriptionFilterLanes(lanes, noOpposite=True, downstreamDist=100, upstreamDist=50)

The first line requests a context subscription for the speed of vehicles in the neighborhood of the reference vehicle with the ID "ego". The range of the context subscription (which refers to the radial context region of the usual subscription mechanism) can be set equal to 0.0, since it is be overridden by the selective values of downstreamDist and upstreamDist, respectively, given to the call of addSubscriptionFilterLanes() in the second line. The call to addSubscriptionFilter<FILTER_NAME>() automatically takes effect on the last issued context subscription, which has to be of the vehicle-to-vehicle form for a successful application.

The following filter types are available:

  • Lanes: Return surrounding vehicles on lanes specified relatively to the reference vehicle
  • CFManeuver: Return leader and follower on the reference vehicle's lane
  • LCManeuver: Return leader and follower on the reference vehicle's lane and neighboring lane(s)
  • Turn: Return conflicting vehicles on upcoming junctions along the vehicle's route
  • VType: Only return vehicles of the specified vTypes
  • VClass: Only return vehicles of the specified vClasses

See the pydoc documentation for detailed specifications.


The filter only takes effect in subsequent simulation steps. The vehicle values returned directly after issuing the subscription are not affected.

Adding a StepListener#

Often a function needs to be called each time when traci.simulationStep() is called, to let this happen automatically (always after each call to simulationStep()) it is possible to add a StepListener object 'listener' (more precisely an instance of a subclass of traci.StepListener) i.e.

 class ExampleListener(traci.StepListener):
    def step(selft):
        # do something after every call to simulationStep
        print("ExampleListener called with parameter %s." % t)
        # indicate that the step listener should stay active in the next step
        return True
 listener = ExampleListener()

Please note that the listener is not activated for every simulation step but for every call to simulationStep (which may perform multiple steps up to the given time t). Furthermore the parameter t is not the current simulation time but exactly the (optional) parameter passed to the simulationStep call (which is 0 by default).


A TraCI StepListener cannot be used in the case that one TraCI client controls several SUMO-instances.

Controlling parallel simulations from the same TraCI script#

The TraCI python library can be used to control multiple simulations at the same time with a single script. The function traci.start() has an optional label argument which allows you to call it multiple times with different simulation instances and labels. The function traci.switch() can then be used to switch to any of the initialized labels:

 traci.start(["sumo""-c""sim1.sumocfg"], label="sim1")
 traci.start(["sumo""-c""sim2.sumocfg"], label="sim2")
 traci.simulationStep() # run 1 step for sim1
 traci.simulationStep() # run 1 step for sim2 

If you prefer a more object oriented approach you can also use connection objects to communicate with the simulation. They have the same interface as the static traci. calls but you will still need to start the simulation manually for them:

 traci.start(["sumo""-c""sim1.sumocfg"], label="sim1")
 traci.start(["sumo""-c""sim2.sumocfg"], label="sim2")
 conn1 = traci.getConnection("sim1")
 conn2 = traci.getConnection("sim2")
 conn1.simulationStep() # run 1 step for sim1
 conn2.simulationStep() # run 1 step for sim2

Controlling the same simulation from multiple clients#

To connect with multiple clients, the number of clients must be known in advance and specified with sumo option --num-clients <INT>. Also, the connection port must be known to all clients. After deciding on a port it can be made available to the clients via arguments or configuration files. A free port can be obtained by

from sumolib.miscutils import getFreeSocketPort
port = sumolib.miscutils.getFreeSocketPort()

One client may use method traci.start() to start the simulation and connect to it at the same time while the other client only needs to connect. After establishing client order, each client must continuously call simulationStep to allow the simulation to advance:

#client1PORT = int(sys.argv[1]) # example
traci.start(["sumo""-c""sim.sumocfg""--num-clients""2"], port=PORT)
traci.setOrder(1) # number can be anything
while traci.simulation.getMinExpectedNumber() > 0:
   # more traci commands
client2PORT = int(sys.argv[1]) # example
traci.setOrder(2) # number can be anything as long as each client gets its own number
while traci.simulation.getMinExpectedNumber() > 0:
   # more traci commands

Embedded Python#

This feature has been removed Please use libsumo instead.

Additional Functions#

When using TraCI there are some common tasks which are not covered by the traci library such as

  • Analyzing the road network
  • Parsing simulation outputs

For this functionality it is recommended to use Tools/Sumolib

Pitfalls and Solutions#

  • Note that strings, if exchanged, have to be ASCII-encoded.
  • If you start sumo from within your python script using subprocess.Popen, be sure to call wait() on the resulting process object before quitting your script. You might loose output otherwise.

Determine the traci library being loaded#

When working with different sumo versions it may happen that the call import traci loads the wrong library. The easiest way to debug this is to add the following lines after the import

import traci
print("LOADPATH:", '\n'.join(sys.path))                                                                                                                                      
print("TRACIPATH:", traci.__file__) 

Make sure that the TRACIPATH corresponds to the sumo version that you wish to use. If it does not, then the order of directories in LOADPATH (sys.path) must be changed or the SUMO installation must be removed from any directories that come before the wanted directory.

Debugging a TraCI session on Linux#

Sometimes SUMO may crash while running a simulation with TraCI. The below steps make it simple to run sumo with traci in a debugger:

1) Add the option --save-configuration to your traci script:

traci.start([sumoBinary, '-c', 'run.sumocfg', '--save-configuration', 'debug.sumocfg'])

2) Run your traci script. Instead of starting sumo it will just write the configuration with the chosen port but it will still try to connect repeatedly.

3) Run

gdb --args sumoD -c debug.sumocfg

(where sumoD is sumo compiled in debug mode)

Generating a log of all traci commands#

To share a traci scenario (i.e. in a bug report) it may be useful to seperate the logic of the traci script from the actual commands. For this, the function traci.start accepts the optional arguments traceFile and traceGetters. When calling traci.start([<commands>], traceFile=<LOG_FILE_PATH>) all traci commands that were sent to sumo will be written to the given LOG_FILE_PATH. This allows re-running the scenario without the original runner script. When option traceGetters=False is set, only functions that change the simulation state are included in the log file. Functions that retrieve simulation data are technically not needed to reproduce a scenario but it may be useful to include them if the data retrieval functions are themselves the cause of a bug.


Avoid running the simulation with option --random since this will most likely prevent your traceFile from being repeated

Determine why the TraCI client cannot connect#

Possibly, the arguments given to traci.start generated an error when launching SUMO. This will manifest as

traci.exceptions.FatalTraCIError: Could not connect.

To diagnose the problem, add options for writing a log file traci.start (i.e. traci.start(['sumo', '-c', 'example.sumocfg', '--log', 'logfile.txt']) After the script fails to start, look into the written logfile and fix the error reported therein.

Usage Examples#

Run a simulation until all vehicles have arrived#

  while traci.simulation.getMinExpectedNumber() > 0:

Add trips (incomplete routes) dynamically#

Define a route that consists of the start and destination edge:

   traci.route.add("trip", ["startEdge", "endEdge"])

Then add the vehicle with that route

   traci.vehicle.add("newVeh", "trip", typeID="reroutingType")

This will cause the vehicle to compute a new route from startEdge to endEdge according to the estimated travel times in the network at the time of departure. For details of this mechanism see Demand/Automatic_Routing.

coordinate transformations#

 x, y = traci.vehicle.getPosition(vehID)
 lon, lat = traci.simulation.convertGeo(x, y)
 x2, y2 = traci.simulation.convertGeo(lon, lat, fromGeo=True)
 edgeID, lanePosition, laneIndex = traci.simulation.convertRoad(x3, y3)
 edgeID, lanePosition, laneIndex = traci.simulation.convertRoad(lon2, lat2, True)

Retrieve the timeLoss for all vehicles currently in the network#

import traci
import traci.constants as tc
traci.start(["sumo", "-c", "my.sumocfg"])
# pick an arbitrary junction
junctionID = traci.junction.getIDList()[0]
# subscribe around that junction with a sufficiently large
# radius to retrieve the speeds of all vehicles in every step
    junctionID, tc.CMD_GET_VEHICLE_VARIABLE, 1000000, 
stepLength = traci.simulation.getDeltaT()
while traci.simulation.getMinExpectedNumber() > 0:
   scResults = traci.junction.getContextSubscriptionResults(junctionID)
   halting = 0
   if scResults:
       relSpeeds = [d[tc.VAR_SPEED] / d[tc.VAR_ALLOWED_SPEED] for d in scResults.values()]
       # compute values corresponding to summary-output
       running = len(relSpeeds)
       halting = len([1 for d in scResults.values() if d[tc.VAR_SPEED] < 0.1])
       meanSpeedRelative = sum(relSpeeds) / running
       timeLoss = (1 - meanSpeedRelative) * running * stepLength
   print(traci.simulation.getTime(), timeLoss, halting)

Handling Exceptions#

Sometimes commands raise an (recoverable) exception to indicate an error (unknown id, route not found etc.). These exceptions can be handled by your code as follows:

    pos = traci.vehicle.getPosition(vehID)
except traci.TraCIException:
    pass # or do something smarter

Further Resources#

  • The module Simpla provides a library for platooning functions that can be integrated with user client scripts.