Chapter 18.1: TraCI Protocol

18.1.1 TraCI: Traffic Control Interface

TraCI (Traffic Control Interface) is the protocol through which external programs interact with a running SUMO simulation. Unlike file-based coupling (read outputs, modify inputs, restart), TraCI provides real-time bidirectional communication: you can query vehicle states and modify simulation parameters within the same timestep.

TraCI uses a custom TCP binary protocol. The server (SUMO) listens on a specified port, and clients connect to send commands and receive responses. This architecture enables:

  • Per-step queries: vehicle speed, position, emissions, waiting time
  • Per-step control: reroute vehicles, change traffic light phases, modify speed limits
  • Multi-client support: multiple controllers connected simultaneously
  • Language independence: any language that can open a TCP socket can speak TraCI

18.1.2 TCP Binary Protocol Structure

Every TraCI message follows a strict binary format. Understanding this format is essential for implementing clients in languages without official TraCI libraries (e.g., Java, Rust, C++).

Message Envelope

┌─────────────────────────────────────────────────────────┐
│  TraCI Message Format                                    │
├──────────────┬──────────────────────────────────────────┤
│  Bytes 0-3   │  Total message length (4 bytes, big-endian) │
├──────────────┼──────────────────────────────────────────┤
│  Byte 4      │  Command length (1 byte, or 0 → 4-byte)  │
├──────────────┼──────────────────────────────────────────┤
│  Byte 5      │  Command ID (1 byte)                     │
├──────────────┼──────────────────────────────────────────┤
│  Bytes 6+    │  Command content (variable)               │
└──────────────┴──────────────────────────────────────────┘

If command length byte = 0, next 4 bytes give extended length.
Multiple commands can be packed into one message.

Key Command IDs

CommandID (hex)DirectionPurpose
CMD_SIMSTEP0x02Client → ServerAdvance simulation by one step
GET_VEHICLE_VARIABLE0xa4Client → ServerQuery vehicle state
RESPONSE_GET_VEHICLE0xb4Server → ClientVehicle state response
SET_VEHICLE_VARIABLE0xc4Client → ServerModify vehicle state
GET_TL_VARIABLE0xa2Client → ServerQuery traffic light state
SET_TL_VARIABLE0xc2Client → ServerChange traffic light phase
CMD_CLOSE0x7fClient → ServerClose connection gracefully

18.1.3 Variable IDs and Data Types

When querying a vehicle, the variable ID specifies which property to retrieve. The response includes a type byte indicating the data format:

Vehicle Variable IDs

VariableID (hex)Return TypeUnit
Speed0x40double (0x0b)m/s
Position 2D0x42position2D (0x01)m, m
NOₓ emission0x64double (0x0b)mg/s
PMₓ emission0x65double (0x0b)mg/s
CO₂ emission0x60double (0x0b)mg/s
Acceleration0x72double (0x0b)m/s²
Route ID0x53string (0x0c)

Response Parsing

A response to a GET command follows this structure:

Response structure:
  [cmd_length: 1B] [response_id: 1B] [variable_id: 1B]
  [object_id: string] [type_byte: 1B] [value: variable]

Type bytes:
  0x08 = integer (4 bytes)
  0x09 = byte
  0x0b = double (8 bytes, IEEE 754)
  0x0c = string (4-byte length + UTF-8 bytes)
  0x01 = position2D (two doubles: x, y)
  0x0e = string list (4-byte count + strings)

18.1.4 Java SUMOController Outline

For production deployments, a Java controller provides type safety and performance. Here is the architectural outline:

public class SUMOController {
    private Socket socket;
    private DataOutputStream out;
    private DataInputStream in;

    public void connect(String host, int port) throws IOException {
        socket = new Socket(host, port);
        out = new DataOutputStream(socket.getOutputStream());
        in  = new DataInputStream(socket.getInputStream());
    }

    public void simulationStep() throws IOException {
        // CMD_SIMSTEP = 0x02
        byte[] cmd = buildCommand(0x02, new byte[0]);
        sendMessage(cmd);
        readResponse();
    }

    public double getVehicleSpeed(String vehId) throws IOException {
        // GET_VEHICLE_VARIABLE=0xa4, VAR_SPEED=0x40
        byte[] content = encodeString(vehId);
        byte[] cmd = buildCommand(0xa4, (byte)0x40, content);
        sendMessage(cmd);
        return parseDoubleResponse();
    }

    public void setTrafficLightPhase(String tlsId, int phase)
            throws IOException {
        // SET_TL_VARIABLE=0xc2, TL_PHASE_INDEX=0x22
        byte[] content = concat(encodeString(tlsId),
                               new byte[]{0x08},  // TYPE_INT
                               encodeInt(phase));
        byte[] cmd = buildCommand(0xc2, (byte)0x22, content);
        sendMessage(cmd);
        readResponse();
    }

    private byte[] buildCommand(int cmdId, byte varId, byte[] content) {
        int cmdLen = 1 + 1 + 1 + content.length;  // len + cmdId + varId + content
        ByteBuffer buf = ByteBuffer.allocate(cmdLen);
        buf.put((byte) cmdLen);
        buf.put((byte) cmdId);
        buf.put(varId);
        buf.put(content);
        return buf.array();
    }

    private void sendMessage(byte[] cmd) throws IOException {
        int totalLen = 4 + cmd.length;
        out.writeInt(totalLen);
        out.write(cmd);
        out.flush();
    }
}

18.1.5 Python: TraCI Message Builder/Parser

This implementation builds and parses TraCI binary messages from scratch, demonstrating the protocol at the byte level.

TraCI Binary Protocol Builder & Parser

Python
script.py211 lines

Click Run to execute the Python code

Code will be executed with Python 3 on the server

18.1.6 Key Takeaways

  • TraCI is a TCP binary protocol with 4-byte big-endian length prefix, 1-byte command ID, and variable-length content.
  • Command IDs distinguish GET (0xa*) vs SET (0xc*) operations across domains (vehicles, traffic lights, edges, lanes).
  • Variable IDs (speed=0x40, NOx=0x64, position=0x42) specify which property to query or modify.
  • Response parsing requires reading the type byte to determine how to decode the value (double, int, string, position2D).
  • The protocol is language-agnostic: the Python and Java implementations shown here demonstrate that any language with socket and binary packing support can control SUMO.