关于 roadrunner-scenario-simulating
此 MATLAB 技能指导 RoadRunner 场景仿真的运行、逐步控制、协同仿真与结果读取,适合需要从 MATLAB 或 Simulink 操作场景的用户。内容涵盖演员状态、观察器、传感器和场景变量等工作流。
主要功能
- 01运行并逐步控制仿真
- 02通过 MATLAB 或 Simulink 控制演员
- 03监视仿真状态与演员属性
- 04附加传感器并读取目标数据
- 05访问仿真日志和场景变量
适用场景
- 01逐帧推进场景并读取仿真日志
- 02在协同仿真中控制车辆演员
- 03只读监视仿真状态
- 04获取传感器目标位姿或车道边界
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ matlab/ matlab-agentic-toolkit --skill roadrunner-scenario-simulating安装后在 Claude Code 中输入/roadrunner-scenario-simulating确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
manifest.yamlreferences/simulation-api-reference.mdsimulink-cosim-details.md详细介绍
RoadRunner 场景仿真
通过 MATLAB 和 Simulink 仿真 RoadRunner 场景、逐步执行仿真、在协同仿真中控制参与者、添加观察器、附加传感器并检索结果。
适用场景
- 用户想要运行 RoadRunner 场景仿真
- 用户想要逐帧执行仿真
- 用户想要从外部控制参与者(协同仿真)
- 用户想要观察仿真状态(只读监控)
- 用户想要添加传感器并读取目标位姿或车道边界
- 用户想要以编程方式检索仿真日志
- 用户想要读取或写入场景变量
- 用户询问与 Simulink 的协同仿真
- 用户询问如何发布参与者行为
不适用场景
- 启动或连接 RoadRunner
- 编写场景(在编辑器中添加参与者、路径、行为)
- 构建或编辑场景(道路、地形、资产)
- 将参与者轨迹导出为 CSV — 请直接使用
exportActorTrajectoryToCSV - 使用
drivingScenario(这是另一个工具箱 — Automated Driving Toolbox)
重要:不要与 drivingScenario 混淆
drivingScenario(来自 Automated Driving Toolbox)是 MATLAB 原生的场景工具。
RoadRunner 场景仿真使用 roadrunner 对象上完全不同的 API。
切勿混用这两者 — 它们互不相关。
决策树
User wants to simulate →
├── Just run to completion? → Workflow A (simulateScenario)
├── Need step control OR programmatic log? → Workflow B (createSimulation)
├── Need to read actor state during sim? → Workflow C (getAttribute)
├── Need external actor control? →
│ ├── From MATLAB? → Workflow D (System object co-sim)
│ └── From Simulink? → Workflow E (Simulink blocks)
├── Need read-only monitoring? → Workflow F (Observers)
└── Need sensor data during sim? → Workflow G (SensorSimulation)
工作流 A:简单仿真(运行至完成)
当用户只想仿真一个已打开的场景时使用:
openScenario(rrApp, "MyScenario");
simulateScenario(rrApp, EnableLogging=true);
选项:Pacing、IsBlocking、IsSteppingStart、EnableLogging。
重要: simulateScenario 不会返回日志对象。仅在只需运行至完成时使用它。如果需要在 MATLAB 中以编程方式访问仿真日志,请改用 createSimulation(工作流 B)。
工作流 B:逐步仿真
当用户需要逐帧控制或以编程方式访问日志时使用。
**关键调用顺序:**必须先调用 createSimulation,然后才能调用 simulateScenario。仿真正在运行或暂停时,Scenario Server 会拒绝新连接。此外,不要使用 set(rrSim, SimulationCommand="Start") 然后再使用 "Step" — "Start" 会让仿真自由运行直至完成。
% 1. Get the simulation handle FIRST (before anything is running)
rrSim = createSimulation(rrApp);
stepSize = 0.01;
set(rrSim, StepSize=stepSize);
set(rrSim, MaxSimulationTime=30);
% 2. THEN start simulation in stepping mode
simulateScenario(rrApp, IsSteppingStart=true, IsBlocking=false, EnableLogging=true);
pause(0.5); % Allow sim to initialize before stepping
% 3. Step through the simulation
for i = 1:numSteps
set(rrSim, SimulationCommand="Step");
pause(stepSize); % REQUIRED — Step is async, must wait for frame to complete
end
set(rrSim, SimulationCommand="Stop");
simLog = get(rrSim, "SimulationLog");
关键:"Step" 是异步的 — 每次 Step 之后都必须添加 pause(stepSize)。否则,命令会堆积并被静默丢弃。不要使用点方法语法(rrSim.step())— 始终使用 set(rrSim, SimulationCommand=...)。逐步执行时不要设置 Logging="On" — 请在 simulateScenario 调用中使用 EnableLogging=true。
SimulationCommand 的取值
"Start"、"Step"、"Pause"、"Continue"、"Stop"、"Replay"
Replay 使用位置语法:set(rrSim, "SimulationCommand", "Replay", fileName)
轮询 SimulationStatus
get(rrSim, "SimulationStatus") 返回:"Inactive"、"Running"、"Paused"、"Done"
运行非阻塞仿真时,在等待循环中使用。如果轮询未能及时捕获状态,短场景可能会在轮询检查到 "Done" 之前就已转为其他状态,因此请同时检查 "Done" 和 "Inactive":
status = get(rrSim, "SimulationStatus");
while ~ismember(status, ["Done", "Inactive"])
pause(0.1);
status = get(rrSim, "SimulationStatus");
end
工作流 C:读取参与者状态
在逐步仿真过程中获取参与者信息:
注意:get(rrSim, "ActorSimulation") 始终会在索引 1 处包含世界参与者(ID 0)。这是一个不可移动的根参与者,不是车辆。遍历时请跳过它,或按 ID 进行筛选。
**注意:**只有仿真处于活动状态(Running 或 Paused)时,才能查询参与者。"Stop" 之后,get(rrSim, "ActorSimulation") 会返回空值。
% Get all actors — returns a CELL ARRAY, use {idx} not (idx)
actors = get(rrSim, "ActorSimulation");
actorSim = actors{2}; % cell indexing required; index 1 is world actor (ID 0)
% Or find a specific actor by ID (returns a single object)
% NOTE: ActorID must be uint64 — double will fail silently or error
actorSim = Simulink.ScenarioSimulation.find("ActorSimulation", ActorID=uint64(1));
% Read runtime attributes — use getAttribute, NOT property access
pose = getAttribute(actorSim, "Pose"); % 4x4 matrix
velocity = getAttribute(actorSim, "Velocity"); % 1x3 vector
angVel = getAttribute(actorSim, "AngularVelocity"); % 1x3 vector
**关键:**不要使用 actorSim.Pose 或 actorSim.Velocity — 它们不是公共属性。始终使用 getAttribute(actorSim, "AttrName")。
运行时属性:"ID"、"Pose"、"Velocity"、"AngularVelocity"、"WheelPoses"、"LaneLocation"、"Children"、"Parent"、"PhaseStatus"、"ActorType"、"TrafficSignalRuntime"、"TrafficSignalControllerRuntime"
静态属性(名称、边界框等)
要获取 Actor 的名称或其他静态属性,请使用 ActorModel,而不是 getAttribute(actorSim, "Name")(该函数不存在):
actorModel = get(actorSim, "ActorModel");
actorName = getAttribute(actorModel, "Name");
boundingBox = getAttribute(actorModel, "BoundingBox");
ActorModel 上的静态属性:"ID"、"Name"、"PaintColor"、"BoundingBox"、"WheelSpec"、"TrafficSignalSpec"、"TrafficSignalControllerSpec"
工作流 D:使用 MATLAB System 对象进行协同仿真
架构
- 为 Actor 分配行为(见下文)
- 在 MATLAB 中:编写一个在运行时控制 Actor 的 System 对象
- System 对象通过
Simulink.ScenarioSimulation.find查找自身
分配行为
R2024a: 只能在 RoadRunner Scenario Editor UI 中分配行为(没有 MATLAB API)。
R2025a+: 可通过 roadrunnerAPI 创作接口以编程方式分配行为:
rrApi = roadrunnerAPI(rrApp);
prj = rrApi.Project;
scnro = rrApi.Scenario;
% Get or create a behavior asset
behaviorAsset = getAsset(prj, "Behaviors/MyBehavior.rrbehavior", "BehaviorAsset");
% Assign to an existing actor
car.BehaviorAsset = behaviorAsset;
要点:
roadrunnerAPI(rrApp)可访问Project和Scenario对象- 使用
getAsset(prj, path, "BehaviorAsset")获取现有的.rrbehavior资源 - 使用
createAsset(prj, path, "BehaviorAsset")创建新的行为资源 - 创建行为资源后,设置其平台:
setPlatform(behaviorAsset, "SimulinkPlatform")——这对 MATLAB System 对象和 Simulink 模型都适用。不要使用"MATLAB"、"External"或"MATLABSystem"(这些平台未注册) - 通过
Vehicle/Character/MovableObject上的BehaviorAsset属性进行分配 - 不存在
setBehavior()函数——请改用属性赋值
System 对象模式
classdef MyActorController < matlab.System
properties (Access = private)
ActorSim % ActorSimulation handle
end
methods (Access = protected)
function setupImpl(obj)
obj.ActorSim = Simulink.ScenarioSimulation.find( ...
"ActorSimulation", SystemObject=obj);
end
function stepImpl(obj)
action = getAction(obj.ActorSim, "PathAction");
currentPose = getAttribute(obj.ActorSim, "Pose");
% Modify pose
currentPose(1,4) = currentPose(1,4) + 0.5;
setAttribute(obj.ActorSim, Pose=currentPose);
% Signal action completion (guard: ActionID may not exist)
if ~isempty(action) && isfield(action, "ActionID")
sendEvent(obj.ActorSim, "ActionComplete", action.ActionID);
end
end
end
end
Actor 控制方法
setAttribute(actorSim, Pose=poseMatrix); % 4x4
setAttribute(actorSim, Velocity=[vx vy vz]); % 1x3
setAttribute(actorSim, AngularVelocity=[wx wy wz]); % 1x3
action = getAction(actorSim, "PathAction");
action = getAction(actorSim, "SpeedAction");
action = getAction(actorSim, "LaneChangeAction");
sendEvent(actorSim, "ActionComplete", actionID);
sendEvent(actorSim, "UserDefinedEvent", eventName, eventStruct);
eventData = receiveEvent(actorSim, "UserDefinedEvent", eventName);
工作流 E:使用 Simulink 进行协同仿真
架构
- 在根级别创建一个包含 RoadRunner Scenario 模块的 Simulink 模型
- 使用 RoadRunner Scenario Reader 模块接收来自 RoadRunner 的数据
- 使用 RoadRunner Scenario Writer 模块将数据发送回 RoadRunner
- 发布行为:
Simulink.publish.publishActorBehavior("myModel", OutputFile="output.slprotodata") - 将行为分配给 Actor(通过 RoadRunner UI,或在 R2025a+ 中以编程方式分配——见工作流 D)
- 通过
set_param("myModel", SimulationCommand="start")启动仿真——不要使用sim()
关键限制
- RoadRunner Scenario 模块必须位于模型根级别
- 使用
set_param(..., SimulationCommand="start")启动——绝不要使用sim("model") - RoadRunner Scenario 模块中的采样时间定义协同仿真的步长(默认为 0.02s)
- 一个 MATLAB 实例一次只能连接一个 RoadRunner 协同仿真
- 总线类型从
rrScenarioSimTypes.mat加载
读取器主题
Actor 位姿、规格、车道位置、车辆车轮位姿、交通信号、目标位姿、车道边界、动作(路径/速度/变道等)、用户定义事件
写入器主题
Actor 位姿、车辆位姿、动作完成、用户定义事件、诊断信息
模块配置和总线类型请参见 references/simulink-cosim-details.md。
工作流 F:观察器(只读监控)
观察器监控仿真状态,但不会对其进行修改。与协同仿真行为(在 UI 中分配)不同,观察器以编程方式添加。
rrSim = createSimulation(rrApp);
addObserver(rrSim, "VelocityMonitor", "MyVelocityObserver");
simulateScenario(rrApp, IsBlocking=false);
% Observer's stepImpl is called each step automatically
注意: 观察器 .m 文件必须位于 MATLAB 路径中。如有需要,请使用 addpath。必须在启动仿真之前调用 addObserver。
观察器 System 对象模式
classdef MyVelocityObserver < matlab.System
properties (Access = private)
ScenarioSim
end
methods (Access = protected)
function setupImpl(obj)
obj.ScenarioSim = Simulink.ScenarioSimulation.find( ...
"ScenarioSimulation");
end
function stepImpl(obj)
actors = get(obj.ScenarioSim, "ActorSimulation");
for idx = 1:numel(actors)
vel = getAttribute(actors{idx}, "Velocity");
% Read-only analysis — logging, visualization, etc.
end
end
end
end
重要: 观察器是只读的。它们可以调用 getAttribute,但不得调用 setAttribute。它们在 setupImpl 中查找 ScenarioSimulation(而非 ActorSimulation)。
管理:
addObserver(rrSim, name, fileName)— 添加观察器(返回逻辑成功状态)removeObserver(rrSim, name)— 移除观察器get(rrSim, "Observers")— 查询已注册的观察器
自 R2024b 起,观察器也可以是 Simulink 模型(.slx)。
工作流 G:传感器仿真
将驾驶传感器附加到参与者,并在仿真期间读取真实值数据。
rrSim = createSimulation(rrApp);
sensorSim = get(rrSim, "SensorSimulation");
% Create and attach sensors to ego vehicle (actor ID 1)
visionSensor = visionDetectionGenerator(SensorIndex=1, ...
SensorLocation=[2.4 0], MaxRange=50);
radarSensor = drivingRadarDataGenerator(SensorIndex=2, ...
MountingLocation=[1.8 0 0.2]);
addSensors(sensorSim, {visionSensor, radarSensor}, 1);
% During step loop
set(rrSim, SimulationCommand="Start");
for i = 1:numSteps
set(rrSim, SimulationCommand="Step");
targets = targetPoses(sensorSim, 1); % struct array in ego frame
lanes = laneBoundaries(sensorSim, 1); % lane boundary data
end
set(rrSim, SimulationCommand="Stop");
要点:
SensorSimulation通过get(rrSim, "SensorSimulation")获取——不能直接构造- 每个传感器都必须有唯一的
SensorIndex targetPoses(sensorSim, actorID)的第二个参数是自车(即通过addSensors附加传感器的车辆)的 actorID,不是 SensorIndextargetPoses返回相对于宿主车辆的位置/速度laneBoundaries支持OutputOption:"EgoLane"、"EgoAdjacentLanes"、"AllLanes"
支持的传感器:visionDetectionGenerator、drivingRadarDataGenerator、ultrasonicDetectionGenerator、lidarPointCloudGenerator、lidarSensor
场景变量
% Get a variable
value = getScenarioVariable(rrApp, "EgoSpeed");
% Set a variable (value MUST be a string)
setScenarioVariable(rrApp, "EgoSpeed", "30");
% Get all variables — returns struct ARRAY with Name/Value fields
allVars = getAllScenarioVariables(rrApp);
for i = 1:numel(allVars)
fprintf("%s = %s\n", allVars(i).Name, allVars(i).Value);
end
不要使用 fieldnames(allVars)——它返回的是结构体字段名(Name、Value),而不是变量名。
仿真日志
以编程方式访问日志需要处于步进模式或仿真已完成:
% After simulation completes (or during stepping)
simLog = get(rrSim, "SimulationLog");
% Query actor pose history — returns struct array with .Time and .Pose fields
poseLog = get(simLog, "Pose", "ActorID", 1);
% poseLog(i).Time — scalar timestamp
% poseLog(i).Pose — 4x4 transform matrix
% Extract positions for plotting
positions = arrayfun(@(s) s.Pose(1:3,4)', poseLog, UniformOutput=false);
positions = vertcat(positions{:}); % Nx3 matrix
% Save/load logs
save(rrSim, "SimulationLog", "myLog.mat");
log = load(rrSim, "SimulationLog", "myLog.mat");
日志返回格式:get(simLog, "Pose", "ActorID", id) 返回一个结构体数组(不是数值数组)。每个元素都有 .Time(标量)和 .Pose(4x4 矩阵)。使用 arrayfun 提取位置以进行绘图。
关键函数
| 函数 | 用途 | 起始版本 |
|---|---|---|
simulateScenario(rrApp) | 运行仿真直至完成 | R2024a |
createSimulation(rrApp) | 创建用于步进控制的 ScenarioSimulation | R2022a |
prepareSimulation(rrApp) | 在协同仿真客户端连接前提交仿真数据 | R2024a |
set(rrSim, SimulationCommand=cmd) | 控制仿真 | R2022a |
get(rrSim, param) | 查询仿真状态 | R2022a |
addObserver(rrSim, name, file) | 添加只读观察器 | R2022a |
getAttribute(actorSim, attr) | 读取参与者运行时状态 | R2022a |
setAttribute(actorSim, NV) | 设置参与者状态(仅限协同仿真) | R2022a |
getAction(actorSim, name) | 获取已分配的动作 | R2022a |
sendEvent(actorSim, event, ...) | 向场景发送事件 | R2022b |
receiveEvent(actorSim, event, name) | 接收来自场景的事件 | R2023a |
addSensors(sensorSim, sensors, actorID) | 附加传感器 | R2023a |
targetPoses(sensorSim, sensorID) | 获取自车坐标系中的目标姿态 | R2023a |
laneBoundaries(sensorSim, sensorID) | 获取车道边界数据 | R2023a |
Simulink.ScenarioSimulation.find(...) | 查找活动仿真/参与者 | R2022a |
Simulink.publish.publishActorBehavior(model) | 发布 Simulink 行为 | R2022a |
roadrunnerAPI(rrApp) | 获取创作 API(Project、Scenario) | R2025a |
getAsset(prj, path, type) | 获取资源(BehaviorAsset、VehicleAsset 等) | R2025a |
createAsset(prj, path, type) | 在项目中创建新资源 | R2025a |
addActor(scnro, asset, position) | 以编程方式向场景添加参与者 | R2025a |
完整的函数签名、名称-值对和返回类型,请参阅 references/simulation-api-reference.md。
架构:参与者行为类型
**R2024a:**只能在 RoadRunner Scenario Editor UI 中分配行为。
**R2025a+:**也可以通过 car.BehaviorAsset = behaviorAsset 以编程方式分配行为(参见工作流 D)。
| 行为类型 | 含义 | MATLAB 中的职责 |
|---|---|---|
| Ready-to-Run | 参与者遵循内置路径/逻辑 | 无需编写代码——只需观察 |
| MATLAB System | 参与者由 System 对象控制 | 编写 System 对象(工作流 D) |
| Simulink Model | 参与者由 Simulink 模型控制 | 使用 RR 模块构建模型(工作流 E) |
常见错误
| 错误 | 错误原因 | 正确做法 |
|---|---|---|
rrSim.start() / .step() / .stop() | 不存在点方法语法 | set(rrSim, SimulationCommand="Start"|"Step"|"Stop") |
actorSim.Pose 或 actorSim.Velocity | 不是公共属性 | getAttribute(actorSim, "Pose") |
get(rrSim, "Actors") | 参数名称错误 | get(rrSim, "ActorSimulation") |
set(rrSim, Logging=true) | 值必须是字符串 | set(rrSim, Logging="On") |
将 drivingScenario 用于 RoadRunner | 它属于完全不同的工具箱 | 使用 simulateScenario/createSimulation |
roadrunnerScenario(name) | 此类不存在 | 先调用 openScenario(rrApp, name),再进行仿真 |
使用 sim("model") 进行协同仿真 | 协同仿真的命令错误 | set_param("model", SimulationCommand="start") |
setBehavior(actor, ...) | 此函数不存在 | 使用 car.BehaviorAsset = behaviorAsset(R2025a+),或在 RoadRunner UI 中分配 |
fieldnames(getAllScenarioVariables(rrApp)) | 获取的是结构体字段,而不是变量名称 | allVars(i).Name |
log = simulateScenario(rrApp, ...) | 没有返回值 | 使用 createSimulation + get(rrSim, "SimulationLog") |
在观察器中使用 setAttribute | 观察器为只读 | 只能使用 getAttribute |
addSensors(rrSim, ...) | 对象错误 | 在 SensorSimulation 上使用 addSensors(sensorSim, ...) |
getAttribute(actorSim, "Name") | Name 是静态属性,不是运行时属性 | 使用 get(actorSim, "ActorModel"),然后使用 getAttribute(actorModel, "Name") |
在调用 get(rrSim, "ActorSimulation") 后使用 actors(idx) | 返回的是元胞数组,而不是对象数组 | 使用 actors{idx}(元胞索引) |
先调用 set(rrSim, SimulationCommand="Start"),再调用 "Step" | "Start" 会自由运行——仿真会在你能执行步进之前完成 | 先使用 simulateScenario(rrApp, IsSteppingStart=true, IsBlocking=false) |
不带 pause(stepSize) 的步进循环 | "Step" 是异步的——命令会堆积并被静默丢弃 | 在每次 set(rrSim, SimulationCommand="Step") 后添加 pause(stepSize) |
步进期间调用 set(rrSim, Logging="On") | 会报错 "Failed to enable or disable logging while simulation is running" | 在调用 createSimulation 之前,使用 simulateScenario(rrApp, ..., EnableLogging=true) |
将 actors{1} 视为自车 | 索引 1 始终是世界 Actor(ID 0),而不是车辆 | 使用 actors{2},或通过 ID 查找:Simulink.ScenarioSimulation.find("ActorSimulation", ActorID=uint64(id)) |
Simulink.ScenarioSimulation.find(..., ActorID=1) | ActorID 必须是 uint64,而不是 double | 使用 ActorID=uint64(1) |
在步进模式下,调用 simulateScenario 后再调用 createSimulation | 仿真暂停时,服务端会拒绝连接 | 在调用 simulateScenario(rrApp, IsSteppingStart=true, ...) 之前调用 createSimulation(rrApp) |
targetPoses(sensorSim, sensorIndex) | 第二个参数是 actorID,而不是 sensorIndex | 使用 targetPoses(sensorSim, actorID),其中 actorID 是自车的 ID |
setPlatform(behaviorAsset, "MATLAB") | 不是已注册的类型字符串 | MATLAB 和 Simulink 行为都使用 setPlatform(behaviorAsset, "SimulinkPlatform") |
约定
- 使用 ScenarioSimulation 时,始终使用
set/get,切勿使用点方法 - 对于 Actor 运行时状态,始终使用
getAttribute/setAttribute - 对于简单的运行至完成场景,使用
simulateScenario;不要用createSimulation过度设计 - 需要控制步进、检查 Actor 或访问日志时,使用
createSimulation - 行为分配: 在 R2024a 中,使用 RoadRunner 界面。在 R2025a+ 中,使用
car.BehaviorAsset = getAsset(prj, path, "BehaviorAsset") - 协同仿真系统对象必须在
setupImpl中使用Simulink.ScenarioSimulation.find("ActorSimulation", SystemObject=obj) - 观察器查找
"ScenarioSimulation"(而非"ActorSimulation"),且为只读 - 生命周期: 打开新场景(
openScenario)会使任何现有的rrSim失效;请再次调用createSimulation