ROS Simulation Library for C++ (RSLCPP)
- ROS Simulation Library for C++ (RSLCPP) is a deterministic simulation framework that converts asynchronous ROS 2 processes into sequential, discrete-event simulations with bit-exact reproducibility.
- It encapsulates all ROS 2 nodes within a single process using a FIFO event queue and simulated time stepping to eliminate OS-level timing jitter and non-determinism.
- The library offers an intuitive API and configurable delay model that support reproducible benchmarking and migration of robotic workloads across diverse hardware platforms.
The ROS Simulation Library for C++ (RSLCPP) is a deterministic simulation framework built atop the Robot Operating System 2 (ROS 2), designed to enable reproducible execution of arbitrary ROS 2 node graphs across heterogeneous hardware. RSLCPP achieves this by transforming the inherently asynchronous, multiprocess ROS 2 execution model into a single-process, single-threaded discrete-event simulation, offering bit-exact trace reproducibility for applications ranging from synthetic benchmarks to complex robotics workloads such as LiDAR odometry (Sagmeister et al., 11 Jan 2026).
1. Architecture and Component Structure
RSLCPP’s architecture centers on its encapsulation of all ROS 2 nodes within a single process and event loop, fundamentally differing from the native ROS 2 multiprocess paradigm. The major architectural components include:
- rslcpp::Simulation: Encapsulates the simulation job, containing start time, stop condition, a list of component nodes to load, a delay model, and the simulation outcome (via
is_complete()and exit code). - rslcpp::EventLoop: A bespoke wrapper around the standard rclcpp events executor, enforcing strict FIFO callback sequencing at each simulation time step.
- rslcpp::DelayModel: Introduces optional, configurable publication latencies. Message statements are published at , where represents simulated communication delay.
- rslcpp Component Loader: Dynamically imports arbitrary rclcpp-based nodes via ROS 2 node composition, enabling zero-copy intra-process message transport to exclude indeterministic IPC or memory copies.
- Simulated rclcpp Clock: All ROS 2 time APIs point to a unified simulated clock. Wall-clock time is never seen by simulation nodes, fully decoupling computation from wall time.
The orchestrated workflow executes as follows: (1) spin callbacks with executor until idle, (2) check stop condition, (3) advance simulated time to the next scheduled timer, (4) repeat until completion.
2. Deterministic Discrete-Event Scheduling
At the core of RSLCPP is a discrete-event scheduler engineered for deterministic, bit-exact callback execution. Key properties include:
- Aggregate Execution: All node graphs are combined into one executable, eliminating inter-process and OS-level races.
- Single-Threaded Processing: All callbacks execute sequentially with no preemption, nullifying non-determinism arising from kernel thread scheduling.
- FIFO Event Queue: The wrapped executor queues callbacks in the order received (arrival order), preventing priority inversion.
- Simulated Time Stepping: After draining all ready callbacks at simulation time , the clock increments to , where are timers registered from components.
- Latency Modeling: Published messages are delayed by a simulated , so actual publish occurs at .
An enforced invariant guarantees upper-bounded communication lag, even under pathological computation scenarios. Time does not advance unless the event queue is idle, prohibiting uncontrolled feedback loops without timers.
3. API Usage and Migration Practices
The RSLCPP API exposes high-level abstractions for configuring and executing reproducible simulation jobs without mandating business logic changes in existing ROS 2 nodes—conditional on exclusive use of rclcpp time APIs. The key steps are as follows:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
#include <rclcpp/rclcpp.hpp> #include <rslcpp/simulation.hpp> int main(int argc, char **argv) { rclcpp::init(argc, argv); auto sim = std::make_shared<rslcpp::Simulation>(); sim->set_start_time(rclcpp::Time(0, RCL_ROS_TIME)); sim->set_delay_model(rslcpp::DelayModel::Constant(std::chrono::milliseconds(5))); sim->add_component("my_lidar", "my_driver::MyLidarNode"); sim->add_component("estimator", "my_algo::MyStateEstimator"); sim->set_stop_condition( [](const rslcpp::Simulation &job) { return (job.get_clock()->now().seconds() >= 60.0); }); int exit_code = sim->run(); rclcpp::shutdown(); return exit_code; } |
Nodes using non-simulated wall clocks (e.g., std::chrono::steady_clock) must be patched to use the rslcpp-provided rclcpp. Published messages must flow through rslcpp’s queue to ensure deterministic delay injection.
4. Cross-Platform Validation and Reproducibility
Validation experiments demonstrate RSLCPP’s reproducibility on synthetic and real robotic workloads. The synthetic benchmark, involving a racing cluster of nodes with timers, subscriptions, and internal hash state perturbed by a splittable PRNG, produced bit-identical end-of-run hashes across 100 runs on seven distinct CPUs. These CPUs included Intel and AMD x86_64 servers, laptops, and an ARM Cortex-A72 platform.
For the LiDAR odometry experiment (KISS-ICP on KITTI Seq 00), native ROS 2 exhibited substantial RMSE jitter and in some cases pipeline divergence. Under RSLCPP, repeated executions—despite multi-threaded backends—produced a single RMSE (≈0.079 m) with zero variability on all platforms tested. This result illustrates elimination of OS-level timing jitter and platform-induced nondeterminism.
| CPU Architecture | Example CPUs Tested | Reproducibility Outcome (RSLCPP) |
|---|---|---|
| x86_64 | i7-11850H, Ryzen 7 Pro 6850U, etc. | Bit-exact, identical output |
| aarch64 | ARM Cortex-A72 | Bit-exact, identical output |
5. Practical Trade-offs and Migration Guidance
RSLCPP’s determinism is secured by a single-threaded, single-process design. The following practical considerations arise:
- Throughput: Multicore scalability is forfeited; complex scenarios may run more slowly compared to native ROS 2. For throughput, independent simulations may be run in parallel.
- Blocking Semantics: Any long-blocking call in a callback stalls simulation; users must refactor or avoid such patterns.
- Feedback Loops: Loops not relying on timers prevent simulated time from advancing; periodic timers must encapsulate all event-triggered loops.
- ROS 2 Dependency: Tight integration with ROS 2’s events executor internals may require updates for future ROS 2 releases.
Migration recommendations include auditing all node time sources, minimizing subscriber queue sizes in zero-copy transport use, externalizing stop criteria via “job monitor” callbacks, and preferring RSLCPP’s delay mechanisms over thread sleep calls inside callbacks.
6. Open-Source Software and Community Practices
RSLCPP is released under the Apache 2.0 license and is available at https://github.com/TUMFTM/rslcpp. To use, users create a ROS 2 workspace, clone the RSLCPP repository, install dependencies (ROS 2 Humble or later), build with colcon, and source the resulting setup files. Example simulations can be launched via standard ros2 run commands.
The RSLCPP project welcomes contributions through issue reports and pull requests, conforming to the ROS 2 Contribution Guide. All new features must be accompanied by corresponding tests or usage examples.
With its ability to deliver platform-independent, bit-exact simulation traces and outputs, RSLCPP is positioned for applications in regression testing, continuous integration, and scientific benchmarking, where reproducibility is a non-negotiable requirement (Sagmeister et al., 11 Jan 2026).