mcpField
M
matlab-cosimulate-sumo-simulink

matlab-cosimulate-sumo-simulink

github.com/matlab测试与质量1.2k51 次安装1 天前更新
代码仓库

关于 matlab-cosimulate-sumo-simulink

面向 Simulink 用户的 SUMO 联合仿真技能,指导使用 Automated Driving Toolbox Interface for Eclipse SUMO 搭建同步交通仿真。内容包括车辆状态读取、车辆控制及动态生成或移除车辆。

主要功能

  1. 01配置 SUMO Server 与 Client
  2. 02同步 SUMO 和 Simulink 仿真
  3. 03读取车辆状态和仿真信息
  4. 04控制车辆速度与位置
  5. 05动态添加或移除车辆

适用场景

  1. 01测试 ADAS 或 AD 控制器
  2. 02在 Simulink 中读取车辆位置和速度
  3. 03从控制器操纵 SUMO 中的车辆
  4. 04无需路线文件生成背景交通

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/matlab/matlab-agentic-toolkit --skill matlab-cosimulate-sumo-simulink

安装后在 Claude Code 中输入/matlab-cosimulate-sumo-simulink确认已加载。

附带文件manifest.yaml

详细介绍

构建 SUMO-Simulink 联合仿真

使用 Automated Driving Toolbox Interface for Eclipse SUMO Traffic Simulator 中的 SumoInterfaceLibrary 模块,创建与 Eclipse SUMO 同步运行的 Simulink 模型。

适用场景

  • 构建连接到 Eclipse SUMO 的 Simulink 模型
  • 设置用于 ADAS/AD 控制器测试的交通联合仿真
  • 将车辆状态(位置、速度)从 SUMO 读入 Simulink
  • 使用 Simulink 控制器控制 SUMO 中的自车
  • 在仿真期间动态生成或移除车辆
  • 无需编写路线文件即可生成随机背景交通

不适用场景

  • 纯 SUMO 仿真,不使用 Simulink(直接使用 SUMO 的 CLI 或 Python TraCI)
  • 车辆动力学建模(使用 Vehicle Dynamics Blockset)
  • 使用 Driving Scenario Designer 设计场景(工作流不同)

前提条件

  1. 已安装 Eclipse SUMO,并设置了 SUMO_HOME 环境变量
  2. 已安装支持包:“Automated Driving Toolbox Interface for Eclipse SUMO Traffic Simulator”
  3. Windows:为 SUMO_HOME 添加尾随分隔符 — Server 模块的启动器会将 SUMO_HOME + "bin\..." 拼接起来,中间不添加分隔符。如果 SUMO_HOME 末尾没有 \ 或 /,即使 SUMO 已正确安装,每次 sim() 也都会失败并显示“Failed to launch Eclipse SUMO simulator”。在构建或仿真前务必运行:
if ~endsWith(getenv('SUMO_HOME'), filesep)
    setenv('SUMO_HOME', [getenv('SUMO_HOME') filesep]);
end
  1. 验证:getenv('SUMO_HOME') 必须返回以分隔符结尾的有效路径。

首选构建方式

如果可以使用 mcp__matlab__model_edit(SATK),优先使用它,而不是直接使用 add_block/add_line。它会在每次编辑后自动布局,生成整洁且无重叠的连线,并允许在同一次调用中通过 ref 引用新添加的模块。以下模式仍然适用——在单个 JSON 负载中将其表示为 add_block/connect/configure 操作。仅当 SATK 不可用,或操作无法用架构表示时(例如通过 sfroot 设置 MATLAB Function 的 Script),才使用 add_block/add_line。

**注意:**为新创建的 Enabled Subsystem 的 EnablePort 接线时,如果其默认的 In1/Out1 已被删除,model_edit 无法解析 Enable 端口。解决方法:add_line(model, 'Source/1', 'SubsystemName/Enable', 'autorouting','smart')。

工作流

幂等构建模板
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. 准备 SUMO 场景文件

请用户提供现有的 .sumocfg。只有在用户明确要求时才生成新文件。对于快速构建的网络,使用 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);

最简 .sumocfg(使用 EnableRandomTraffic 时可省略路由文件):

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

**路由文件规则:**所有 <vehicle>、<person> 和 <personFlow> 条目都必须按 depart 时间排序(流使用 begin)。顺序错误的条目会被静默忽略。Actor 模块使用的路线必须是独立的 <route> 元素,不能嵌入 <flow> 中。

**导入 OpenDRIVE:**使用 netconvert --opendrive-files file.xodr -o network.net.xml。注意:SUMO 会应用 netOffset(可在 .net.xml 的 <location> 元素中查看)——所有 SUMO 坐标 = OpenDRIVE 坐标 + netOffset。

2. 创建 Server 和 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');

重要:ClientPort 必须等于 ServerPort。FixedStep 必须与 SUMO 的 step-length 匹配。启用 pacing(EnablePacing='on'),避免 GUI 在 2 秒内快速闪过。

3–6. 添加读取器、写入器、Actor,保存并运行

请参阅下文的模块参考和模式部分。如果模型已通过编程方式修改,务必先执行 save_system(modelName),再执行 sim(modelName)。

模块参考

服务端模块
参数用途默认值
ServerFile.sumocfg 的路径—
ServerPortTraCI 端口'8813'
ServerNumClients客户端数量'1'
EnableRandomTraffic不使用路线文件生成车辆'off'
RandomTraffic启用随机交通时的车辆数量'0'
EnablePacing实时步进(演示时推荐)'off'
PacingRate步进倍率'1'
SampleTime步长间隔'-1'

注意: SUMO 默认的 time-to-teleport=300s——任何停滞超过 300s 的车辆都会被传送。这可能会导致一个意外情况:有意让自车停在公交站时,车辆会被传送。

客户端模块
参数用途默认值
ClientAddress服务端 IP'127.0.0.1'
ClientPort必须与 ServerPort 匹配'8813'
ClientOrder客户端优先级'1'
Reader 模块——车辆话题

如果目标车辆不存在,所有车辆话题的 Reader 和 Writer 都会崩溃。 如果车辆在 t=0 之后才生成,请在 Enabled Subsystems 中控制它们的启用时机。请参阅 Safe Vehicle State Access。

话题输入输出
Speed车辆 ID速度(标量)
Position车辆 ID位置 [x, y]
Position3D车辆 ID位置 [x, y, z]
Acceleration车辆 ID加速度
Angle车辆 ID朝向(度,0=北,顺时针)

车辆 ID 输入必须来自 String Constant 模块——绝不能使用另一个 Reader 的输出。

Reader 模块——仿真话题(无输入端口)
话题输出
ID list车辆 ID 字符串
ID count数量(标量)
Arrived list已到达目的地的 ID
Departed list已进入路网的 ID
Writer 模块——车辆话题

如果目标车辆不存在,Writer 就会崩溃——与 Reader 的故障模式相同。对于延迟生成的车辆,请在 Enabled Subsystems 中控制其启用时机。

话题输入端口备注
Speed车辆 ID、速度设置期望速度;SUMO 的跟驰模型仍会因前车而制动
Position(RouteFlag off)车辆 ID、X、Y、Angle—
Position(RouteFlag on)车辆 ID、Edge ID、Lane Index、X、Y、Angle、Keep Route请参阅下方的类型表
Min gap车辆 ID、间距—
Speed mode车辆 ID、模式—

Writer(Vehicle/Position, RouteFlag=on) 端口类型——类型不匹配会静默失败:

端口信号类型
1车辆 IDstring
2Edge IDstring
3Lane Indexint32
4Xdouble
5Ydouble
6Angledouble(度)
7Keep Routeuint8(使用 uint8(1))

警告: Writer(Vehicle/Position) 不会进行碰撞检查。将车辆传送到其他车辆附近会造成车辆堆积。生成车辆时,请将其放在与附近交通不同的车道上。

Writer 模块——可视化话题

使用 TopicCategory='Visualization' 和 GUITopic 参数。可视化命令会持续生效——SUMO 收到 Schema/Track/Zoom 后,会保留该设置。务必将其封装在一次性 Enabled Subsystem 中。不要让它每个步长都运行。

GUITopic输入端口有效值
Track VehicleView Id、Vehicle Id—
Zoom factorView Id、Zoom—
SchemaView Id、Schema"standard"、"real world"、"fast standard"、"fast real world"

所有 Visualization 话题都要求在端口 1 输入 View Id。SUMO GUI 的默认视图名称是 "View #0"——务必使用此确切字符串。

% 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 模块
参数用途
VehicleState'Add' 或 'Remove'
SpawnMethod'Lane'(使用此项)或 'Position'(存在缺陷——参见已知问题)
departLane'random'、'free'、'allowed'、'first'、'best'
departSpeed'max'(推荐)、'random'、'desired'、'speedLimit'
enableRouteID'on'/'off'
routeID.rou.xml 中的路线 ID(需要 enableRouteID='on')
typeID车辆类型 ID(默认:'DEFAULT_VEHTYPE')

始终将 Actor 模块放在已启用子系统内。将 Spawn 子系统的 Priority='1',使其先于 Readers(Priority='10')执行。

模式

单次触发器
触发条件模式
仅在 t=0 生成Constant(0) -> IC(Value=1) -> Enabled Subsystem
在仿真时间 T 生成Clock -> MATLAB Function (persistent fired) -> Enabled Subsystem
从 T 开始持续执行(例如速度写入器)Clock -> Compare To Constant (>=T) -> Enabled Subsystem
条件为真时生成Clock/传感器 -> MATLAB Function (persistent fired) 检查条件

基于时间的单次触发(在 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
自车控制(路线文件方案——推荐)

在 .rou.xml 中使用 depart="0" 定义自车,以便 Readers 从 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>

如果 Simulink 通过 Writer(Vehicle/Speed) 控制纵向运动,请勿在自车的路线中加入 <stop> 子句——SUMO 的停止指令会覆盖 Speed Writer。

延迟生成车辆的门控 Writer(Vehicle/Speed)
% 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

请使用 Compare To Constant >= spawnTime(保持为 HIGH),而不是单次触发——车辆存在期间必须持续设置速度。

每辆车使用独立路线(动态生成)
<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));
先生成后瞬移(Position 变通方案)

由于 SpawnMethod='Position' 存在缺陷(参见已知问题),请使用以下两步模式:

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)

Spawn 与 Move 之间必须延迟一个时间步——在 enable 信号上使用 Unit Delay。Actor 触发后,车辆要到下一个时间步才会在 SUMO 端创建。

在移动参考点附近定期批量生成
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

用于动态生成车辆 ID(例如 "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';
安全访问车辆状态

所有 Vehicle 主题的 Readers 和 Writers 都会在每个时间步解引用 Vehicle Id。车辆不存在时,它们会崩溃。此规则适用于 Reader(Vehicle/...)、Writer(Vehicle/...) 以及 Writer(Visualization/Track Vehicle)。

对于在 t=0 后生成的车辆,请在已启用子系统中进行门控:

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

对于在 .rou.xml 中使用 depart="0" 定义的自车——无需门控。

随机交通快速入门
set_param([modelName '/SUMO Server'], 'EnableRandomTraffic','on', 'RandomTraffic','10');

无需 <route-files>。随机车辆的名称为 SpawnedVehicle_1、SpawnedVehicle_2 等。

已知问题(支持包缺陷)

问题影响变通方案
SUMO_HOME 末尾未带分隔符 → 启动失败(Windows)P0 阻塞问题setenv('SUMO_HOME', [getenv('SUMO_HOME') filesep])
SpawnMethod='Position' 在内部 moveToXY 中传入错误的 RouteFlag车辆会出现在边缘起点,而非请求的位置使用先生成后瞬移模式(Actor + Unit Delay + Writer(Vehicle/Position))

常见错误

错误失败原因修复方法
车辆尚不存在时就使用 Reader/WritermdlOutputs 中的 S-function 崩溃放入 Enabled Subsystem 中进行门控;对于自车,使用 depart="0"
Reader 输出 -> Reader 输入ID List 是可变长度的;Vehicle reader 需要单个字符串使用 String Constant 设置 Vehicle Id
ClientPort != ServerPort客户端无法连接将两者设为相同值(默认值为 '8813')
使用变步长求解器SUMO 要求锁步运行使用固定步长,并与 step-length 保持一致
Actor 重复触发“Vehicle already exists”使用带单次触发逻辑的 Enabled Subsystem
延迟生成车辆的 Writer(Vehicle/Speed) 未进行门控在 t=0 时崩溃使用 Clock >= spawnTime 进行门控
Visualization Writer 每个步长都运行浪费仿真时间;车辆不存在时会崩溃使用单次触发的 Enabled Subsystem
Route 条目未按 depart 时间排序条目会被静默丢弃按 depart/begin 对所有条目排序
Route 嵌入在 <flow> 中Actor 无法找到它定义独立的 <route> 元素
自车路线中包含 <stop>,且使用了 Speed WriterSUMO 的 stop 会覆盖 Writer移除 <stop>;使用 Simulink 速度曲线
存在多个 Clock 块结构杂乱;所有块的输出时间都相同使用单个 Clock,并将其分配给所有使用方
Lane Index 或 Keep Route 在 Position Writer 中使用 double类型不匹配,导致拒绝使用 int32(laneIdx)、uint8(keepRoute)
传送到被占用的车道车辆堆积、卡住传送到与附近车辆不同的车道
在 sim 前缺少 save_system出现“未应用更改”掩码错误以编程方式更改后,始终保存
自车设置 depart="2",但 Reader 在 t=0 读取车辆未定义,导致数据无效或崩溃将自车的 depart 设为 "0"
FixedStep=1.0 时在非整数时间生成车辆求解器不会到达目标时间使 FixedStep、step-length 和所有 SampleTime 保持一致

约定

  • 始终使用固定步长求解器,步长与 SUMO 的 step-length 及所有块的 SampleTime 值保持一致
  • 以编程方式更改模型后,在运行 sim(modelName) 前始终调用 save_system(modelName)
  • 始终确保 ClientPort 与 ServerPort 匹配
  • 在模型根级别使用单个 Clock,并将其分配给所有比较器/脉冲发生器
  • 优先在 .rou.xml 中使用 depart="0" 定义自车,而不是使用 Actor 块
  • 为自车设置较长或环形路线,避免其在仿真过程中途被移除
  • 对于应以行驶状态生成的车辆,使用 departSpeed='max' 作为实用默认值
  • Writer(Speed) 设置期望速度;SUMO 通过跟车行为处理间距维持
  • 对演示启用 pacing(EnablePacing='on'),避免 GUI 一闪而过
  • 将 Visualization writers(Track、Schema、Zoom)放入单次触发的 Enabled Subsystems 中——它们具有粘滞性
  • 始终将 Actor 块放在 Enabled/Triggered Subsystems 内
  • 每辆动态生成的车辆都需要唯一 ID 和有效的独立路线
  • 在 Simulink 中使用前,先通过 SUMO CLI 验证 .sumocfg:system(sprintf('"%s" -c "%s" --end 0.1', sumoExe, cfgFile))
  • 使用 mcp__matlab__model_edit 时,若 Enable 端口接线失败,则改用 add_line

版权所有 2026 The MathWorks, Inc.


评论

支持 Markdown