RNE is not trying to clone one simulator's GUI or file format. “Equivalent to existing OSS” means that a contributor can describe a world, attach a robot and sensors, run a fixed-step experiment, connect a controller, inspect the result, and replay it without a commercial engine or a renderer.
The comparison baseline is deliberately workflow-oriented:
| Reference | Capabilities used as the baseline |
|---|---|
| Choreonoid | World/body models, replaceable physics simulator items, vision sub-simulators, controller input/output, replay, and plugins |
| Gazebo Sim | Entity/system simulation loop, physics separation, sensor systems with noise, GUI/transport plugins, and reusable worlds |
| AWSIM and Scenario Simulator v2 | Vehicle/sensor/environment/ROS 2 integration, scenario-driven execution, traffic co-simulation, and simulator-independent sensor APIs |
The detailed, executable acceptance criteria live in the OSS workflow parity matrix. This page remains the narrative status report; the matrix is the gate for deciding whether a workflow is truly complete.
| Capability | RNE today | Remaining parity gap |
|---|---|---|
| World and robot assets | .rne.scene.toml, .rne.robot.toml, URDF, OBJ, static glTF/GLB, PLATEAU import, minimal OpenSCENARIO 1.0 import, and minimal SDF (rne_sdf) and MJCF (rne_mjcf) model import → URDF |
None for the current workflow slice |
| Fixed-step execution | rne-asset simulate and rne-asset run run a scene or OpenSCENARIO manifest headlessly with an explicit rate and step count. rne-asset run --control-stdin accepts runner commands on stdin (pause, resume, step N, reset, quit) through a rne_core transport-neutral control state machine; legacy --control-port PORT remains supported. Production --frontend-port PORT uses explicit binary negotiation, finite socket deadlines, bounded queues, and reconnect-without-quit semantics. reset rebuilds the world from the episode's initial conditions and step N advances exactly N frames before pausing again |
None for the current fixed-step and runner-control slice |
| Controller I/O | Typed ActuatorCommand, named joint velocity/effort/wheel paths, interpolated multi-joint position trajectories in run manifests, controller plugins invoked through a rne_plugin trait boundary ([controller] kind = "plugin"), dynamically loaded controller libraries through the versioned C ABI, episode APIs, and an isolated ROS 2 adapter |
None for the current controller slice; sensor and agent plugin ABIs remain future scope |
| Physics | Backend-neutral traits with Rapier, a deterministic collision-free analytic backend, and a default-off MuJoCo 3.9 backend for rigid bodies, hinge/slide articulation, position/velocity/effort actuation, canonical contact force, and sensor overlaps; selectable per run manifest with public capability negotiation plus an executable canonical conformance report | MuJoCo raycasts and broader constraint/geometry coverage remain future scope; every currently advertised capability has unit-bearing evidence |
| Sensors | LiDAR, IMU, RGB-D/camera, wheel encoders, noise, latency, bounded DataBus retention, per-step replay stream summaries, full typed payload export, and framed lossless RGB-D/LiDAR frontend payloads preserving stream sequence/capture/availability timestamps | None for the current workflow slice |
| Rendering | Native wgpu, browser replay viewer, PBR materials, glTF maps, HDR/IBL, TAA, legacy interactive_viewer --connect, and production interactive_viewer --frontend-connect |
Remote diff-drive, scenario traffic, and articulated joints project locally; production binary RGB/depth frames drive PiP and full LiDAR frames drive a bounded display overlay without stepping a second physics world |
| Scenario and traffic | Typed behavior contracts, deterministic traffic routing/signals, PLATEAU assets, multi-seed reports, multi-actor OpenSCENARIO 1.0 execution with per-kind routes and canonical action evidence, mixed runtime/external ownership metrics, versioned scenario replay, offline SUMO import, and recoverable live TraCI co-simulation | None for the M5 scenario-scale and TraCI recovery slice; broader OpenSCENARIO action coverage remains future scope |
| Replay and evaluation | Episode logs, stable hashes, vectorized checkpoints, behavior CI, JUnit/JSON reports, tagged wheel/joint .rne-replay actions, rne-scenario-replay XOSC/network/clock/result records, joint-state/sensor summaries, per-step contact statistics, fall/failure annotations in the final report, and browser interval inspection |
Full sensor payload streams for every sensor are opt-in via subscriptions |
| Extension and compatibility model | Dependency-free controller author ABI (rne_plugin_sdk), host-side manifests/interfaces (rne_plugin), dynamic loading through a versioned C ABI, versioned offline controller and accelerator scaffolding, standalone plugin conformance (rne-asset plugin check), standalone accelerator process conformance (rne-accelerator-conformance), publishable external physics-backend conformance (rne_physics_conformance), content-addressed external hardware-process conformance (rne-hardware-conformance), a fixed 31-crate Rust API baseline, and an installed thirty-six-fixture corpus including both scaffold contracts and the plugin-conformance reader, distinct historical/current TaskSpec identities, TaskSpec-bound hardware-session evidence, all ten frontend protocol-v1 message families, renderer-capture evidence, provenance-bound state migration, historical checkpoint/replay decisions, and exact dataset/Failure Capsule ancestor retention |
Independent third-party plugin and backend/adapter certification, future schema transitions, and the six-month stability window remain external v0.9/1.0 gates |
The first parity slice is the headless fixed-step runner and its versioned manifest:
cargo run --release -p rne_asset_cli -- run \
assets/runs/mesh_diff_drive.rne.run.tomlThe manifest pins the scene reference, optional seed, fixed clock, controller, determinism check, and optional replay output path. The controller can target every matching URDF/ECS joint by name:
cargo run --release -p rne_asset_cli -- run \
assets/runs/mm_minimal_joint_velocity.rne.run.tomlThe runner applies typed wheel, named joint-velocity, or named joint-effort
commands through the actuator buffer, advances Rapier at a fixed simulation
rate, and records named joint state plus DataBus sensor stream count/sequence/
payload digests after each step. determinism_check = true repeats the complete
run and requires the final report, observations, and per-step physics hashes to
match. The command is headless; no GPU or ROS 2 installation is needed.
The direct form remains useful for one-off overrides:
cargo run --release -p rne_asset_cli -- simulate \
assets/scenes/mesh_diff_drive.rne.scene.toml \
--steps 600 --hz 60 --wheel-velocity-rad-s 6 \
--determinism-check \
--replay-out target/runs/mesh_diff_drive.rne-replay
# Re-run the recorded action schedule and verify every frame hash/observation
cargo run --release -p rne_asset_cli -- replay \
target/runs/mesh_diff_drive.rne-replayThe .rne-replay file is versioned JSON. Version 1 now records tagged
wheel/joint actions while remaining able to read the original flat wheel-action
form. Each frame can also contain the selected base translation, deterministic
named joint state, sensor stream summaries, the physics hash, and the final
report. Replay comparison uses exact step/time/hash checks and a documented
1e-12 relative-scale tolerance for floating-point observations.
The web viewer can inspect the same artifact without rerunning the recorded policy or physics. Select the file in its Replay inspector to scrub or play the interval and inspect the selected observation and exact 64-bit physics and sensor payload hashes.
Run manifests can also request full typed sensor payload capture. A
[[sensors]] subscription selects sensors by entity name or kind, and the
recording then stores the complete IMU, LiDAR, camera (RGB+D), or wheel-encoder
payload for each frame in addition to the stream summaries:
[[sensors]]
kind = "lidar"
[[sensors]]
name = "wrist_camera"A joint_trajectory controller drives named joints through time-indexed
position waypoints; the runner interpolates them per fixed step and records the
interpolated targets as the frame action:
[controller]
kind = "joint_trajectory"
[[controller.joint_trajectories]]
joint = "shoulder_joint"
waypoints = [
{ t_s = 0.0, position_rad = 0.0 },
{ t_s = 0.5, position_rad = 1.0 },
{ t_s = 1.0, position_rad = 0.0 },
]cargo run --release -p rne_asset_cli -- run \
assets/runs/mesh_diff_drive_lidar_payload.rne.run.toml
# Re-run and verify the recorded actions, observations, and every frame hash
cargo run --release -p rne_asset_cli -- replay \
target/runs/mesh_diff_drive_lidar_payload.rne-replayThe typed payloads are JSON-encoded in the artifact, so the browser inspector and other tools can read them without rerunning the sensor models.
Every frame also records contact statistics (active pair count and summed/max
normal impulse per step) and the final report annotates the run outcome: the
maximum concurrent contact pairs, the largest per-step contact impulse, the
minimum base height of the first differential-drive robot, and a fell failure
when that height drops below half of its initial value.
- Add a minimal OpenSCENARIO/traffic adapter after the native run/replay
contract is stable. Delivered: the
rne_openscenarioimporter parses a strict OpenSCENARIO 1.0 subset into a versioned.rne.scenario.jsondocument, its executor drives the document over the traffic runtime, and run manifests can reference the scenario through[scenario] xosc = ...sorne-asset runexecutes it headlessly. Lane changes are supported as aRelativeTargetLaneaction that switches the actor to a synthetic parallel route,AssignRouteActionfollows scripted waypoints,${parameter}references resolve fromParameterDeclarations,CatalogReferenceentities resolve fromVehicleCatalogdirectories, and the network's fixed-time signal programs drive stop lines during the run.
This order closes the common simulator workflow first. Photoreal rendering, large asset libraries, and GPU-scale parallelism remain separate capabilities, not prerequisites for headless behavior testing.
OpenSCENARIO manifests can write a deterministic scenario replay artifact just
like robot manifests. assets/runs/scenario_speed.rne.run.toml enables the
output by default:
cargo run --release -p rne_asset_cli -- run \
assets/runs/scenario_speed.rne.run.toml
cargo run --release -p rne_asset_cli -- replay \
target/runs/scenario_speed.rne-replayThe JSON discriminator is rne-scenario-replay. Schema version 4 records the
XOSC path, resolved traffic-network path, fixed-step options, the consumed
pause/resume/step/reset/quit command transcript, completed step count,
UUID-ordered actor state, canonical action evidence and result digest, mixed
pose ownership, minimum gap, signal/collision metrics, average speed, and
stable hash. Interactive artifacts are replayable too: rne-asset replay restores the
initial paused state, feeds the recorded commands back through the same
transport-neutral runner, and compares the final result.
The committed flagship checks can be run as one machine-readable catalog:
cargo run -p xtask -- parityThe default report is artifacts/oss-parity/report.json (ignored generated
output). CI uploads the same report so a failed workflow identifies the exact
robot, controller ABI/multi-robot ordering, physics, sensor, scenario, traffic,
or runner-control slice that regressed. Physics evidence is also written to
artifacts/physics-conformance/report.json by:
cargo run -p xtask -- physics-conformanceThe catalog currently contains eight cases. Analytic covers both advertised capabilities (2/2), and Rapier covers all five (5/5). Same-backend repeat runs use canonical snapshot hashes; analytic-vs-Rapier and contact-rich checks use the versioned SI-unit tolerance registry. The report also exposes Rapier's current effective-mass convention: configured body mass is additional to the default-density collider mass.
M5 scenario/traffic evidence is emitted by the release-mode scale gate:
cargo run -p xtask -- scenario-scaleThe committed 2.5 km fixture expands one multi-actor event to 100 canonical
actions and runs 600 fixed steps. After an untimed warm-up, three repetitions
must reproduce the actor/action digest exactly; the slowest must sustain at
least 60 steps/s on the GitHub-hosted Windows parity runner. The JSON report at
artifacts/scenario-scale/report.json includes ownership counts, minimum gap,
all six violation-registry rows, timing samples, and zero unexplained
violations.
rne-sumo-powered rne-asset sumo-net converts a SUMO .net.xml road network
into a .rne.traffic.json traffic asset. The importer maps edge/lane
geometry (2D x,y SUMO coordinates) into the RNE Y-up frame
([x, z, -y]), converts allow/disallow road-user classes, and then lets
rne_traffic::build_traffic_topology deterministically derive junctions and
lane connections from the lane endpoints:
cargo run --release -p rne_asset_cli -- sumo-net \
assets/networks/minimal_cross.net.xml \
--out target/runs/minimal_cross.rne.traffic.jsonInternal/connector edges are skipped, malformed input is rejected, and the
fixture network derives a four-way junction with seven movements. A run
manifest can reference the .net.xml directly as its OpenSCENARIO LogicFile:
rne-asset run imports the network (deriving topology) and routes the scenario
actors on it. assets/runs/sumo_cross.rne.run.toml spawns a vehicle on the
eastbound approach and drives it toward the intersection (206 m route, 10 m/s,
no collisions, deterministic).
connection and tlLogic elements are imported too: the signalized_cross
fixture assigns a fixed-time program (20 s green northbound, then 15 s green
eastbound), and the importer matches each linkIndex to the derived
connection with the same (incoming, outgoing) lane pair, building one RNE
TrafficSignal group per link and one phase per parsed phase. The signal
drives RNE stop-line control: a scenario actor on the eastbound approach is
held at the red stop line without violating the signal.
rne_traci is a minimal TraCI (Traffic Control Interface) client for driving
and observing a running SUMO process: it negotiates the API version, advances
simulation steps, lists vehicles, and reads their positions using SUMO's
big-endian TCP framing. Positions map into the RNE Y-up frame ([x, 0, -y]),
matching rne_sumo's import frame, so SUMO vehicles land on the same network
geometry RNE imports. rne_traci::CoSimulation mirrors every live SUMO
vehicle into the RNE ECS as a TrafficActor with a TrafficPose and
TrafficPoseSource::External, so RNE sensors, logging, and rendering can
observe live SUMO traffic without the backend-neutral runtime overwriting its
poses. RNE-owned traffic actors can still run through the same runtime and
World.
rne-asset co-sim <net.xml> --routes <rou.xml> --steps N runs a headless SUMO
co-simulation (spawning SUMO, connecting over TraCI, stepping, and mirroring
vehicles) and reports a deterministic stable hash; --determinism-check runs
it twice and requires identical outcomes. CI installs eclipse-sumo so these
co-simulation tests run against a real SUMO process with a moving vehicle
(sumo_cross_flow.rou.xml). CoSimulation::set_vehicle_speed_m_s is the first
explicit control-return path: it sends a SUMO vehicle.setSpeed command only
when the caller asks for it, while SUMO remains the motion and routing
authority. The default step path is still observe-and-synchronize and never
derives commands from an RNE mirror pose; route assignment and richer control
ownership remain future work.
CoSimulation exposes connected, disconnected, and closed states. I/O or
protocol failure preserves the last complete mirror; bounded recover opens a
fresh endpoint connection and reads a complete sorted snapshot before commit,
without deliberately repeating the ambiguous simulationStep. Existing SUMO
IDs retain entity identity, additions/removals reconcile transactionally, and
session metrics expose failed steps, reconnect attempts, and successful
recoveries.
rne-asset run --control-stdin drives a manifest interactively (or from a
script) without a GUI. The runner consults a transport-neutral control state
machine (rne_core::control) at every fixed-step boundary:
printf 'pause\nstep 5\nreset\nstep 3\nquit\n' | \
cargo run --release -p rne_asset_cli -- run \
assets/runs/mesh_diff_drive.rne.run.toml --control-stdinpausesuspends the loop at the next step boundary; the runner blocks until the next command.resumecontinues advancing freely.step Nadvances exactlyNframes, then pauses again. A step budget is never interrupted by queued commands, so scripts stay ordered.resetrebuilds the world from the episode's initial conditions (same seed) and restarts from step 0. The final report and replay artifact describe the current episode.quitends the run gracefully; the current episode is still reported and written to the replay artifact.- stdin EOF while paused is treated as
quit, so piped scripts terminate deterministically.
The runner starts paused and waits for the first command, so a piped script
like step N\nquit advances exactly N frames regardless of timing.
Scenario manifests use the same control flags. Their status snapshot contains
positions_m, signal_violations, collisions, stable_hash, and
average_speed_m_s; scenario reset and quit return the current partial episode
without running the non-interactive determinism re-check. A scenario
--replay-out record includes every command consumed by the session and can be
verified with the same rne-asset replay command.
For a GUI or renderer frontend, --control-port PORT serves the same commands
over a local TCP connection (port 0 picks an ephemeral port). On connect the
runner sends ready paused protocol=1, acknowledges each accepted command with
ok <state>, and
streams status step=<n> t=<t> state=<state> snapshot=<json> after every
completed step. The snapshot is a compact single-line JSON observation
(base, base_yaw_rad, joints, sensors, or scenario positions_m and
traffic metrics), so a frontend can render the live state without re-running
physics. The native wgpu frontend connects with:
Command acknowledgement is a queue-acceptance boundary; the following status
line is the applied-state boundary. TCP writes use a finite timeout. RGB-D
payloads are always bounded (1920x1080 per image even in opt-in source mode),
and snapshots above 32 MiB are replaced with a compact
snapshot_limit_exceeded status instead of an unbounded write.
cargo run -p interactive_viewer --example 14_interactive_viewer -- \
--connect 127.0.0.1:9000 \
assets/scenes/mesh_diff_drive.rne.scene.tomlStart the runner first with rne-asset run ... --control-port 9000; the viewer
starts attached and initially paused. Space toggles pause/resume, N advances
one frame, T advances ten frames, R resets, and Escape sends quit. The
viewer renders the local scene geometry while applying the runner's reported
pose, so the runner remains the only physics authority. For generic URDF
profiles, joint snapshots update only render-space link transforms from the
authored joint origins; the viewer never steps its local Rapier world while
connected. The test suite drives the binary over TCP and verifies the reply
protocol, the snapshot, and the resulting replay record.
--replay-out PATH overrides the manifest's replay path, and determinism
re-checks are skipped in interactive mode. The native viewer also accepts
scenario traffic snapshots and generic URDF joint snapshots:
# Scenario status: the first positions_m actor is projected into the local
# diff-drive scene for a lightweight traffic view.
cargo run -p interactive_viewer --example 14_interactive_viewer -- \
--connect 127.0.0.1:9000 \
assets/scenes/mesh_diff_drive.rne.scene.toml
# Articulated runner status: joint names/positions update render-only URDF
# link transforms without stepping a second physics simulation.
cargo run -p interactive_viewer --example 14_interactive_viewer -- \
--connect 127.0.0.1:9000 --so101The production frontend path is a separate framed binary protocol, so legacy line clients keep working unchanged. Start the runner and native viewer with:
cargo run -p rne_asset_cli -- run \
assets/runs/mm_minimal_joint_velocity.rne.run.toml \
--frontend-port 9001
cargo run -p interactive_viewer --example 14_interactive_viewer -- \
--frontend-connect 127.0.0.1:9001 --so101The client sends its supported version range, capabilities, maximum payload, and frame/byte queue budgets before any live data is sent. The server responds with the effective contract or an explicit bounded rejection. Status remains a compact JSON metadata payload inside a binary frame; RGB8, depth-f32 metres, and LiDAR arrays use typed binary frames with stream sequence and simulation capture/availability ticks.
The simulation thread only performs non-blocking latest-only queue publication.
Dedicated threads own finite-deadline socket I/O. Reliable acknowledgements
never evict one another; an over-budget client is disconnected instead. Sensor
and status frames replace stale frames by stable key, emit gap accounting, and
are discarded on disconnect. A later client reconnects to the same session and
resumes at current state without an offline backlog or an implicit quit.
[controller] kind = "plugin" loads a controller from a shared library when the
manifest sets library:
[controller]
kind = "plugin"
plugin = "velocity_servo"
library = "../../target/debug/rne_plugin_example_velocity_servo.so"
joint = "shoulder_joint"
target_rad = 1.0
gain = 2.0
max_velocity_rad_s = 5.0The host and the plugin communicate through a versioned C ABI
(rne_plugin::cabi). The current ABI is v3 and the supported range is v2-v3.
Every plugin exports the v2 base symbols (rne_plugin_abi_version,
rne_plugin_name, rne_controller_create, rne_controller_destroy, and
rne_controller_step). ABI v3 additionally exports capability negotiation,
configure/reset/shutdown lifecycle hooks, and a robot-scoped fixed-step call.
All data crosses the boundary as #[repr(C)] values, integer masks/timestamps,
and NUL-terminated UTF-8 strings. Versions outside the supported range and
missing version-required symbols are rejected at load time.
rne_plugin_example_velocity_servo is the minimal reference implementation and
produces identical velocity commands to the built-in VelocityServoController;
the frozen rne_plugin_legacy_v2_fixture proves the oldest ABI still loads in
the newest runtime. Runner tests also require byte-identical replay frames for
loaded versus built-in controllers and spawn-order-independent robot-scoped
actions for a two-robot scene.
Plugins can also be discovered by name instead of an explicit path. A manifest
lists directories to search, and the runner loads the first shared library
whose file name contains the requested plugin name and whose rne_plugin_name
matches, falling back to the built-in registry when none does:
[controller]
kind = "plugin"
plugin = "velocity_servo"
plugin_paths = ["../../target/debug"]
joint = "shoulder_joint"
target_rad = 1.0
gain = 2.0
max_velocity_rad_s = 5.0rne-asset plugin new scaffolds a complete, compilable controller-plugin crate
implementing the C ABI, with a versioned rne-plugin.json manifest:
cargo run --release -p rne_asset_cli -- plugin new my_controller --dir plugins --schema 1
cd plugins/my_controller && cargo buildThe scaffolded crate starts as an ABI-v3 velocity-servo policy (so it compiles
and loads immediately); replace rne_controller_step_v3 with your controller.
rne-asset plugin list --path <dir> enumerates the built-in velocity_servo
and any discoverable plugin libraries in the given directories. An
end-to-end test scaffolds, builds, and loads a plugin from the generated
source.
The generated rne-scaffold.json content-addresses the exact schema-v1 author
files. The reader rejects extra, missing, modified, or symlinked entries, and
the compatibility corpus regenerates the fixed fixture to prevent generator
drift.
Before distribution, run the standalone conformance kit against the built library and its manifest:
rne-asset plugin check \
--library target/debug/libmy_controller.so \
--manifest plugins/my_controller/rne-plugin.json \
--output my_controller.conformance.jsonUse the platform's .dll or .dylib name where appropriate. Report schema v1
content-addresses both inputs and checks manifest/binary identity, supported ABI
symbols, capability negotiation, fixed-step observation/action validation,
exact action replay after an identical seeded reset, and lifecycle shutdown.
Semantic failures still produce a machine-readable failed report and a nonzero
CLI exit.
The author-facing ABI definitions are isolated in the dependency-free
rne_plugin_sdk crate. rne-asset plugin new vendors that exact module into
the generated crate, so the first build works with cargo build --offline and
does not depend on RNE host crates or crates.io. The reference ABI-v3 plugin
uses the SDK directly; the frozen ABI-v2 fixture remains independent. See
PLUGIN_SDK.md for ownership and upgrade rules.