Skip to main content

Try it with the Flatland simulator

OpenRobOps/sim-flatland is a Docker environment with a complete simulated AMR — 2D physics, Nav2 navigation, a battery with charging zones, a camera, diagnostics — that can connect to OpenRobOps in three modes: standalone, through the InOrbit ROS2 agent, or as an ISO 21423 robot through a small Node.js sidecar built on @openrobops/iso21423. This page walks through the ISO mode end to end. The simulator's README (Connectivity modes, ISO 21423 Agent) is the authoritative reference; this page mirrors it for convenience.

What you get: the robot appears in ORO as an ISO robot with live pose, speed and battery, Battery/Dock/Message actions, statuses and incidents, and you can send it navigation goals from the map — all over the ISO 21423 wire format. Watch the raw traffic at any time with mosquitto_sub -t '/ISO_21423/v1/#' -v.

Prerequisites

  • OpenRobOps running locally (Quick Start), including Mosquitto and ingest.
  • The inorbit CLI configured with a user API key (INORBIT_CLI_API_KEY, INORBIT_CLI_URL=http://localhost:3000/api).
  • Docker, and the simulator repo cloned next to the SDK repo (the ISO agent image builds the SDK from a sibling checkout):
git clone https://github.com/OpenRobOps/iso21423.git
git clone https://github.com/OpenRobOps/sim-flatland.git
cd sim-flatland && git submodule update --init

1. Configure OpenRobOps ingest for ISO mode

Add to ingest/settings.json and restart ingest. Flatland's map origin is [0,0,0], so the facility coordinate system is the map frame itself and the calibration is the identity. The two docks are the simulated charging zones.

"iso21423": {
"ccs": {
"id": "0b1c2d3e-4f50-4a6b-8c7d-9e0f1a2b3c4d",
"name": "flatland",
"referencePoints": [
{ "map": { "x": 0, "y": 0 }, "ccs": { "x": 0, "y": 0 } },
{ "map": { "x": 10, "y": 0 }, "ccs": { "x": 10, "y": 0 } },
{ "map": { "x": 0, "y": 10 }, "ccs": { "x": 0, "y": 10 } }
]
},
"robots": {
"enabled": true,
"imrfmId": "9a1b2c3d-4e5f-4a6b-8c7d-0e1f2a3b4c5d",
"mqtt": { "username": "<settings.mqtt.brokers.local.username>", "password": "<its password>" },
"docks": { "A": { "x": 9.0, "y": 18.5 }, "D": { "x": 11.5, "y": 1.5 } }
},
"upstream": { "enabled": false }
}

Ingest's log should say Ingest is in ISO 21423 mode and ISO 21423 robots is ON.

2. Apply the ISO configuration profile and admit the robot

The simulator ships two OpenRobOps configuration profiles with the same object ids — oro-config/ros2/ for the ROS2 agent and oro-config/iso/ for ISO mode. Apply the ISO one; iso-robot.yaml is the IsoRobot admission object (Gate 2).

cd sim-flatland/oro-config
inorbit apply -f iso/config.yaml # data sources, dashboards, actions, statuses, incidents
inorbit apply -f iso/iso-robot.yaml # admits robot 7b1a9c3e-1111-4222-8333-444455556666

3. Configure and start the ISO agent

cd sim-flatland
cp local/iso-agent.env.sh.example local/iso-agent.env.sh

Edit local/iso-agent.env.sh:

VariableValue
ISO_ENTITY_UUID7b1a9c3e-1111-4222-8333-444455556666 — must equal metadata.id in iso/iso-robot.yaml
ISO_CCS_ID0b1c2d3e-4f50-4a6b-8c7d-9e0f1a2b3c4d — must equal iso21423.ccs.id above
ORO_API_KEYone of robotApiKeys from app/settings.json
ORO_URLhttp://localhost:3000/

Then start the simulation with the ISO agent profile instead of the default ROS2 agent:

xhost +local:docker # for rviz; skip with --no-rviz
COMPOSE_PROFILES=iso-agent docker compose up --build

The agent performs Gate 1 itself — POST /iso_mqtt_config with the API key — obtains its broker credential, registers as an IMR and starts publishing. Its log shows registered IMR … and rosbridge connected. (A 403 with "not admitted" means step 2 was skipped; the agent retries every 10 s.)

4. What to look at

  • Fleet dashboard: the robot flatland-iso online, Battery and Message statuses.
  • Robot dashboard: battery gauge and chart, linear/angular speed, charging state, Key-Value pairs (battery_*, estimated_time_remaining, echo) and the Actions widget:
    • Dock A / Dock D / Dock (nearest) → native ISO dock requests; the robot drives to the charging zone and starts charging.
    • Reset / Charging / DischargingcustomCommand requests, republished on the sim's command topic (the battery is simulated, these are test hooks).
    • Hello / Warning / Error / MessagecustomCommand; the robot echoes back through customData, which drives the message data source, status and incident.
  • Navigation dashboard: click a goal on the map → ISO move; Cancel navigationcancelRequest.
  • Raw ISO traffic: mosquitto_sub -h localhost -u <user> -P <pass> -t '/ISO_21423/v1/#' -v.

Under the hood

The sidecar (sim-flatland/iso-agent/, ~250 lines of JavaScript) reads ROS 2 through rosbridge and maps it onto ISO 21423 with the SDK:

ROS 2ISO 21423
/amcl_pose + /odom twistodometry at 2 Hz
/battery_statebatteryStatus
nav2 goal status + diagnostics + batterystatus.states (MODE_AUTO, READY/NOT_READY, IDLE/FORWARD/…, DOCKING, CHARGING, LOW_BATTERY)
/inorbit/custom_data key=valuecustomData (batched, 500 ms)
move / dock requestsnav2 NavigateToPose action goal
customCommand requests/inorbit/custom_command (std_msgs/String)

Switching back to the ROS2 agent is docker compose up (default profile) plus inorbit apply -f oro-config/ros2/config.yaml, and iso21423.robots.enabled: false in ingest.