SuperAPI AI Directory

matlab-cosimulate-sumo-simulink

General & other

View repository

Updated 10-16 00:21

About this skill

Build SUMO-Simulink Co-simulation

Create Simulink models that run synchronized co-simulation with Eclipse SUMO using the SumoInterfaceLibrary blocks from the Automated Driving Toolbox Interface for Eclipse SUMO Traffic Simulator.

When to Use

  • Building a Simulink model that connects to Eclipse SUMO
  • Setting up traffic co-simulation for ADAS/AD controller testing
  • Reading vehicle states (position, speed) from SUMO into Simulink
  • Controlling an ego vehicle in SUMO from a Simulink controller
  • Spawning or removing vehicles dynamically during simulation
  • Generating random background traffic without writing route files

When NOT to Use

  • Pure SUMO simulation without Simulink (use SUMO's CLI or Python TraCI directly)
  • Vehicle dynamics modeling (use Vehicle Dynamics Blockset)
  • Scenario design with Driving Scenario Designer (different workflow)

Prerequisites

  1. Eclipse SUMO installed with SUMO_HOME environment variable set
  2. Support package installed: "Automated Driving Toolbox Interface for Eclipse SUMO Traffic Simulator"
  3. Windows: append trailing separator to SUMO_HOME — The Server block's launcher concatenates SUMO_HOME + "bin\..." without a separator. If SUMO_HOME lacks a trailing \ or /, every sim() fails with "Failed to launch Eclipse SUMO simulator" even though SUMO is installed correctly. Always run before building/simulating:
if ~endsWith(getenv('SUMO_HOME'), filesep)
    setenv('SUMO_HOME', [getenv('SUMO_HOME') filesep]);
end
  1. Verify: getenv('SUMO_HOME') must return a valid path ending with a separator.

Preferred Construction Path

When mcp__matlab__model_edit (SATK) is available, prefer it over raw add_block/add_line. It applies auto-layout after every edit, produces clean wiring with no overlap, and lets you reference newly-added blocks by ref within the same call. The patterns below still apply — express them as add_block/connect/configure operations in a single JSON payload. Fall back to add_block/add_line only when SATK is unavailable or when the operation is not expressible in the schema (e.g., setting MATLAB Function Script via sfroot).

Note: When wiring EnablePort on a freshly-created Enabled Subsystem whose default In1/Out1 have been deleted, model_edit cannot resolve the Enable port. Workaround: add_line(model, 'Source/1', 'SubsystemName/Enable', 'autorouting','smart').

Workflow

Idempotent Build Template

modelName = 'sumo_cosim';
workDir = fileparts(mfilename('fullpath'));
% SUMO_HOME workaround (Windows)
sh = getenv('SUMO_HOME');
if ~endsWith(sh, filesep), setenv('SUMO_HOME', [sh filesep]); end
% Idempotent rebuild
if bdIsLoaded(modelName), close_system(modelName, 0); end
slxPath = fullfile(workDir, [modelName '.slx']);
if isfile(slxPath), delete(slxPath); end
new_system(modelName);
open_system(modelName);
set_param(modelName, 'StopTime','100', 'SolverType','Fixed-step', 'FixedStep','0.1');
% ... build model ...
save_system(modelName, slxPath);

1. Prepare SUMO Scenario Files

Ask the user for their existing .sumocfg. Only generate new files if explicitly requested. For quick networks use netgenerate:

sumoHome = getenv('SUMO_HOME');
cmd = sprintf('"%s" --grid --grid.number 2 --grid.length 200 --output-file "%s"', ...
    fullfile(sumoHome,'bin','netgenerate'), fullfile(pwd,'network.net.xml'));
system(cmd);

Minimal .sumocfg (route-files optional when using EnableRandomTraffic):

<configuration>
    <input><net-file value="network.net.xml"/></input>
    <time><begin value="0"/><end value="100"/><step-length value="0.1"/></time>
</configuration>

Route file rules: All <vehicle>, <person>, and <personFlow> entries MUST be sorted by depart time (begin for flows). Out-of-order entries are silently ignored. Routes used by Actor blocks must be standalone <route> elements — not embedded in <flow>.

OpenDRIVE import: Use netconvert --opendrive-files file.xodr -o network.net.xml. Note: SUMO applies a netOffset (visible in .net.xml <location> element) — all SUMO coordinates = OpenDRIVE coordinates + netOffset.

2. Create Server and Client

add_block('SumoInterfaceLibrary/Server', [modelName '/SUMO Server']);
set_param([modelName '/SUMO Server'], 'ServerFile',fullfile(pwd,'cosim.sumocfg'), ...
    'ServerPort','8813', 'ServerNumClients','1', 'EnablePacing','on', ...
    'PacingRate','1', 'SampleTime','0.1');
add_block('SumoInterfaceLibrary/Client', [modelName '/SUMO Client']);
set_param([modelName '/SUMO Client'], 'ClientAddress','127.0.0.1', ...
    'ClientPort','8813', 'ClientOrder','1', 'SampleTime','0.1');

Critical: ClientPort must equal ServerPort. FixedStep must match SUMO step-length. Enable pacing (EnablePacing='on') so the GUI doesn't flash by in 2 seconds.

3–6. Add Readers, Writers, Actors, Save and Run

See Block Reference and Patterns sections below. Always save_system(modelName) before sim(modelName) when the model has been modified programmatically.

Block Reference

Server Block

Parameter Purpose Default
ServerFile Path to .sumocfg —
ServerPort TraCI port '8813'
ServerNumClients Number of clients '1'
EnableRandomTraffic Spawn vehicles without route file 'off'
RandomTraffic Vehicle count when random traffic enabled '0'
EnablePacing Real-time pacing (recommended for demos) 'off'
PacingRate Pacing multiplier '1'
SampleTime Step interval '-1'

Note: SUMO default time-to-teleport=300s — any vehicle stuck >300s gets teleported. This can surprise when deliberately stopping an ego at a bus stop.

Client Block

Parameter Purpose Default
ClientAddress Server IP '127.0.0.1'
ClientPort Must match ServerPort '8813'
ClientOrder Client priority '1'

Reader Block — Vehicle Topics

All Vehicle-topic Readers AND Writers crash if the target vehicle does not exist. Gate them in Enabled Subsystems when the vehicle spawns after t=0. See Safe Vehicle State Access.

Topic Input Output
Speed Vehicle Id Speed (scalar)
Position Vehicle Id Position [x, y]
Position3D Vehicle Id Position [x, y, z]
Acceleration Vehicle Id Acceleration
Angle Vehicle Id Heading (degrees, 0=north, CW)

Vehicle Id inputs must come from a String Constant block — never from another Reader's output.

Reader Block — Simulation Topics (no input port)

Topic Output
ID list Vehicle ID strings
ID count Scalar count
Arrived list IDs that reached destination
Departed list IDs that entered network

Writer Block — Vehicle Topics

Writers crash if the target vehicle does not exist — same failure mode as Readers. Gate in Enabled Subsystems for delayed-spawn vehicles.

Topic Input Ports Notes
Speed Vehicle Id, Speed Sets desired speed; SUMO car-following still brakes for leaders
Position (RouteFlag off) Vehicle Id, X, Y, Angle —
Position (RouteFlag on) Vehicle Id, Edge Id, Lane Index, X, Y, Angle, Keep Route See type table below
Min gap Vehicle Id, Gap —
Speed mode Vehicle Id, Mode —

Writer(Vehicle/Position, RouteFlag=on) port types — type mismatches silently fail:

Port Signal Type
1 Vehicle Id string
2 Edge Id string
3 Lane Index int32
4 X double
5 Y double
6 Angle double (degrees)
7 Keep Route uint8 (use uint8(1))

Warning: Writer(Vehicle/Position) does no collision-checking. Teleporting near other vehicles causes pile-ups. Place spawned vehicles in a different lane from nearby traffic.

Writer Block — Visualization Topics

Use TopicCategory='Visualization' with GUITopic parameter. Visualization commands are sticky — once SUMO receives Schema/Track/Zoom, it retains the setting. Always wrap in a one-shot Enabled Subsystem. Do NOT leave running every step.

GUITopic Input Ports Valid Values
Track Vehicle View Id, Vehicle Id —
Zoom factor View Id, Zoom —
Schema View Id, Schema "standard", "real world", "fast standard", "fast real world"

All Visualization topics require a View Id input on port 1. The default SUMO GUI view name is "View #0" — always use this exact string.

% Track Vehicle example (one-shot): camera follows ego
add_block('SumoInterfaceLibrary/Writer', [subsysPath '/Track Writer'], ...
    'TopicCategory','Visualization', 'GUITopic','Track Vehicle', 'SampleTime','0.1');
add_block('simulink/String/String Constant', [subsysPath '/View Id'], ...
    'String','"View #0"');  % Default SUMO GUI view — always "View #0"
add_block('simulink/String/String Constant', [subsysPath '/Vehicle Id'], ...
    'String','"ego_vehicle"');
add_line(subsysPath, 'View Id/1', 'Track Writer/1');
add_line(subsysPath, 'Vehicle Id/1', 'Track Writer/2');
% Wrap in one-shot Enabled Subsystem (Constant(0)->IC(1)->Enable)
% Schema example (one-shot): switch GUI to "real world"
add_block('SumoInterfaceLibrary/Writer', [modelName '/Set Schema'], ...
    'TopicCategory','Visualization', 'GUITopic','Schema', 'SampleTime','0.1');
add_block('simulink/String/String Constant', [modelName '/View Id'], ...
    'String','"View #0"');  % Default SUMO GUI view — always "View #0"
add_block('simulink/String/String Constant', [modelName '/Schema Name'], ...
    'String','"real world"');
% Wrap in one-shot Enabled Subsystem (Constant(0)->IC(1)->Enable)

Actor Block

Parameter Purpose
VehicleState 'Add' or 'Remove'
SpawnMethod 'Lane' (use this) or 'Position' (broken — see Known Issues)
departLane 'random', 'free', 'allowed', 'first', 'best'
departSpeed 'max' (recommended), 'random', 'desired', 'speedLimit'
enableRouteID 'on'/'off'
routeID Route ID from .rou.xml (requires enableRouteID='on')
typeID Vehicle type ID (default: 'DEFAULT_VEHTYPE')

Always place Actor blocks inside Enabled Subsystems. Set Priority='1' on Spawn subsystem so it executes before Readers (Priority='10').

Patterns

Single-Shot Triggers

Trigger Pattern
Spawn at t=0 only Constant(0) -> IC(Value=1) -> Enabled Subsystem
Spawn at simulation time T Clock -> MATLAB Function (persistent fired) -> Enabled Subsystem
Continuous from T onward (e.g., speed writer) Clock -> Compare To Constant (>=T) -> Enabled Subsystem
Spawn when condition true Clock/sensor -> MATLAB Function (persistent fired) checking condition

Time-based one-shot (spawn at t=1.2):

% MATLAB Function code:
% function pulse = fcn(t)
% %#codegen
% persistent fired
% if isempty(fired), fired = false; end
% if (t >= 1.2) && ~fired, pulse = 1.0; fired = true;
% else, pulse = 0.0; end
% end

Ego Vehicle Control (Route-File Approach — Recommended)

Define ego in .rou.xml with depart="0" so Readers work from t=0:

<routes>
    <vType id="ego_type" accel="2.6" decel="4.5" sigma="0" length="5" maxSpeed="25"/>
    <route id="ego_route" edges="B1B2 B2B3 B3B4 B4B1 B1B2 B2B3 B3B4 B4B1"/>
    <vehicle id="ego_veh" type="ego_type" route="ego_route" depart="0"
             departLane="free" departSpeed="max"/>
</routes>

If Simulink owns longitudinal control via Writer(Vehicle/Speed), do NOT put <stop> clauses in the ego's route — SUMO's stop directive overrides the Speed Writer.

Gated Writer(Vehicle/Speed) for Delayed-Spawn Vehicles

% Latched enable: HIGH from t >= spawnTime onwards
% Clock -> Compare To Constant (relop='>=', const=spawnTime) -> Enable
% Inside Enabled Subsystem:
%   String Constant("veh_1") -> Writer(Vehicle/Speed) port 1
%   Constant(desiredSpeed)   -> Writer(Vehicle/Speed) port 2

Use Compare To Constant >= spawnTime (stays HIGH) rather than one-shot — speed must be continuously asserted while the vehicle is alive.

Distinct Routes Per Vehicle (Dynamic Spawning)

<routes>
    <route id="ego_route"   edges="A0A1 A1A2 A2B2 B2C2 C2C1 C1C0"/>
    <route id="veh_1_route" edges="A1B1 B1B2 B2C2 C2C1 C1C0 C0B0"/>
    <route id="veh_2_route" edges="B0B1 B1A1 A1A2 A2B2 B2C2 C2C1"/>
</routes>
% Per Actor block: set_param(actorPath, 'routeID', sprintf('veh_%d_route', k));

Spawn-Then-Teleport (Position Workaround)

Since SpawnMethod='Position' is broken (see Known Issues), use this two-step pattern:

Step N:   Actor(Add, SpawnMethod='Lane', routeID=...) -> vehicle at edge start
Step N+1: Writer(Vehicle/Position, RouteFlag=on) with (VehId, EdgeId, int32(LaneIdx),
          X, Y, Angle, uint8(1)) -> vehicle teleports to (X,Y)

A one-step delay between Spawn and Move is mandatory — use a Unit Delay on the enable signal. The vehicle does not exist on the SUMO side until one step after the Actor fires.

Periodic Batch Spawn Near Moving Reference

Clock -> PulseGen (persistent, fires every 5s, increments batch id)
       -> SpawnBatch (Enabled SS, Priority=1): N IDGen -> N Actor(Add)
       -> [Unit Delay z^-1]
       -> MoveBatch (Enabled SS, Priority=10): N Writer(Vehicle/Position)
Reader(Vehicle/Position) on ego feeds PosGen so batch lands at egoPos + offset.

MATLAB Function with String Outputs

For dynamically generated vehicle IDs (e.g., "dyn_" + string(batch) + "_1"):

chart = sf.find('-isa','Stateflow.EMChart','Path',chartPath);
chart.ChartUpdate = 'DISCRETE';
chart.SampleTime = '0.1';
outData = chart.find('-isa','Stateflow.Data','Scope','Output');
outData.DataType = 'string';
outData.Props.Array.Size = '1';

Safe Vehicle State Access

All Vehicle-topic Readers AND Writers dereference the Vehicle Id at every step. They crash if the vehicle does not exist. This applies to Reader(Vehicle/...), Writer(Vehicle/...), AND Writer(Visualization/Track Vehicle).

For vehicles spawning after t=0, gate in an Enabled Subsystem:

% Clock -> Compare To Constant (>= spawnTime) -> Enabled Subsystem
% Inside: String Constant + Reader/Writer blocks

For ego defined in .rou.xml with depart="0" — no gating needed.

Random Traffic Quick-Start

set_param([modelName '/SUMO Server'], 'EnableRandomTraffic','on', 'RandomTraffic','10');

No <route-files> needed. Random vehicles are named SpawnedVehicle_1, SpawnedVehicle_2, etc.

Known Issues (Support Package Bugs)

Issue Impact Workaround
SUMO_HOME without trailing separator → launch failure (Windows) P0 blocker setenv('SUMO_HOME', [getenv('SUMO_HOME') filesep])
SpawnMethod='Position' passes wrong RouteFlag in internal moveToXY Vehicle lands at edge start, not requested position Use spawn-then-teleport pattern (Actor + Unit Delay + Writer(Vehicle/Position))

Common Mistakes

Mistake Why It Fails Fix
Reader/Writer on vehicle before it exists S-function crash in mdlOutputs Gate in Enabled Subsystem; use depart="0" for ego
Reader output -> Reader input ID List is variable-length; Vehicle readers need single string Use String Constant for Vehicle Id
ClientPort != ServerPort Client can't connect Match both (default '8813')
Variable-step solver SUMO requires lockstep Fixed-step, match step-length
Actor fires repeatedly "Vehicle already exists" Enabled Subsystem with one-shot logic
Writer(Vehicle/Speed) ungated for delayed-spawn vehicle Crash at t=0 Gate with Clock >= spawnTime
Visualization Writer running every step Wastes sim time; crashes if vehicle missing One-shot Enabled Subsystem
Route entries not sorted by depart time Silently dropped Sort all entries by depart/begin
Route embedded in <flow> Actor can't find it Define standalone <route> elements
<stop> in ego route + Speed Writer SUMO stop overrides Writer Remove <stop>; use Simulink speed profile
Multiple Clock blocks Clutter; all output same time Single Clock, fan out to all consumers
double for Lane Index or Keep Route in Position Writer Type mismatch rejection int32(laneIdx), uint8(keepRoute)
Teleport into occupied lane Pile-ups, stuck vehicles Teleport to different lane from nearby traffic
Missing save_system before sim "Unapplied changes" mask error Always save after programmatic changes
Ego depart="2" with Reader at t=0 Garbage/crash from undefined vehicle Set ego depart="0"
Spawning at fractional times with FixedStep=1.0 Solver never hits target time Match FixedStep, step-length, and all SampleTime

Conventions

  • Always use Fixed-step solver with step size matching SUMO step-length and all block SampleTime values
  • Always save_system(modelName) before sim(modelName) after programmatic changes
  • Always match ClientPort to ServerPort
  • Use a single Clock at model root — fan out to all comparators/pulse generators
  • Prefer defining ego in .rou.xml with depart="0" over Actor block
  • Give ego a long/circular route to prevent mid-simulation removal
  • Use departSpeed='max' as the practical default for vehicles that should land moving
  • Writer(Speed) sets desired speed — SUMO handles gap maintenance via car-following
  • Enable pacing (EnablePacing='on') for demos so the GUI doesn't flash by
  • Wrap Visualization writers (Track, Schema, Zoom) in one-shot Enabled Subsystems — they are sticky
  • Always place Actor blocks inside Enabled/Triggered Subsystems
  • Every dynamically spawned vehicle needs a unique ID and a valid standalone route
  • Validate .sumocfg with SUMO CLI before Simulink: system(sprintf('"%s" -c "%s" --end 0.1', sumoExe, cfgFile))
  • When using mcp__matlab__model_edit, fall back to add_line for Enable port wiring

Copyright 2026 The MathWorks, Inc.