
cerulion ros2 attach puts a Cerulion graph on the same topics as your existing
ROS 2 nodes. See Bridge to ROS 2.
This page explains the design choices behind those properties. For the mental
model of how Cerulion works, start with Core concepts.
Where Cerulion pulls ahead
ROS 2’s architecture carries decades of accumulated complexity: a DDS networking layer, performance features that are opt-in rather than automatic, and execution order that depends on the executor and thread pool. Cerulion is engineered to beat that head-on, turning the frustrations teams feel most on a single machine into deliberate design advantages.Zero-copy is the default, not an opt-in
In ROS 2, zero-copy means choosing a compatible RMW, using loaned messages, and tuning configuration, so most teams run the default copy-on-receive path and never get it. In Cerulion, zero-copy shared memory is the hot path. A message is written once into a shared-memory slot and read in place, so latency stays flat as payloads grow, with no tuning.Deterministic, replayable execution
In ROS 2, execution order depends on the executor, thread pool, and DDS scheduling, which makes reproducing a timing bug notoriously hard. In Cerulion, execution order is derived from the graph file, so a recorded run re-executes the way it ran live:cerulion bag play --resim all --verify runs your nodes
against the bag and byte-compares the result against what the robot produced.
Debug once, reproduce exactly. See
Execution order for the user-facing controls, and
Record and replay a run for the workflow.
Predictable, low-jitter tails
Cerulion’s internal benchmarks show that the tail does not grow with payload: p99 stays under about 10 µs multi-process and about 9 µs single-process at every size, with one disclosed outlier. That makes p99 about 2× the median multi-process and about 3.6–3.8× the median single-process. See the benchmark caveat below before comparing individual cells.
Timing violations are loud, not silent
ROS 2’s DDSDEADLINE QoS exists but is easy to misconfigure or have silently
ignored by the RMW. In Cerulion a deadline is an attribute on the node that owns
it — expect_within_ms on an input, promise_within_ms on an output,
tick_within_ms on the tick itself — and each one carries a miss counter and
emits a structured warning the moment it slips. The count is observable state,
not log text you have to grep for. See
Backpressure and deadlines.
One source of truth, less config sprawl
In ROS 2, behavior is spread across code, launch files, parameter YAMLs, and QoS profiles that can silently conflict. In Cerulion, node behavior lives on the macro — there is no policy block in graph YAML to contradict it — and the graph file carries the wiring plus how the graph is deployed. No silent overrides, no config drift.A shallow learning curve
ROS 2 has a steep ramp: DDS QoS matrices, launch files, parameter plumbing. Cerulion asks you to write a Rust struct with#[cerulion_node] and a tick()
under #[cerulion_node_impl]; the CLI scaffolds the workspace, nodes, and graph
for you.
A memory-safe Rust foundation
ROS 2’s core client library (rclcpp) is C++, exposed to whole classes of memory
bugs. Cerulion is built in Rust: memory safety without a garbage collector, and
predictable performance.
The performance story
Cerulion’s strongest performance claim is payload flatness. Cerulion multi-process holds 4.94–4.99 µs from 64 B to 16 MiB (1.00× growth); single-process holds 2.39–2.43 µs. ROS 2 defaults grow 248× over the same range. The product default is the multi-process column, not the single-process result. Round-trip latency at a fixed 100 Hz publish rate, single machine, one publisher/subscriber pair. Both ROS 2 lanes are ROS 2 Jazzy.
All values are p50. The
vs. ROS 2 defaults column is the ratio of the ROS 2
defaults p50 to the Cerulion multi-process p50 — the default-vs-default
pairing, computed from unrounded p50s; dividing the rounded µs/ms values shown
may not reproduce the ratios exactly.
These are internal benchmarks, single machine, one
publisher/subscriber pair. They are not guarantees and have no error bars.
Every row sustained the 100 Hz target on all four lanes. The
ROS 2 (defaults) lane is the zero-config ros2 run experience on Jazzy:
default RMW (rmw_fastrtps_cpp), no profiles XML, no transport environment,
plain publish() plus typed-callback receive, and three separate processes.
It claims no transport-engagement label — its claim is “the defaults,
whatever they do.”
The composed lane is a single process with the nodes on one executor and
intra-process communications enabled, which is why its latency is payload-flat.
Its shape-matched Cerulion counterpart is the single-process column, not the
multi-process default.
The process-count asymmetry is explicit: Cerulion multi-process uses 2 worker
processes with 1 process crossing inside the measured window; ROS 2 defaults
use 3 processes with 2 crossings. Executor models also differ
(barrier-lockstep level executor versus 3× rclcpp spin).Reading the table
- Payload flatness. Cerulion multi-process holds 4.94–4.99 µs from 64 B to 16 MiB (1.00× growth); single-process holds 2.39–2.43 µs. ROS 2 defaults grow 248× over the same range. This is the shape claim, and it is the strongest one.
- Default-vs-default lead. Cerulion is 71–105× faster than ROS 2 defaults at control-loop payloads (64 B – 256 KiB); 2,227× at a 1 MiB depth frame; 20,114× at a 16 MiB dense scan. These figures describe ROS 2 defaults, never ROS 2 in every possible configuration.
- Against ROS 2 configured for speed. ROS 2 composed with intra-process communications is 14.6–29.4 µs — still 2.9–5.9× behind Cerulion’s multi-process default and 6.0–12.2× behind single-process — and unlike the defaults it is payload-flat too (1.03× growth).
- Tail. The tail does not grow with payload: p99 stays under about 10 µs multi-process and about 9 µs single-process at every size, apart from the occasional multi-process excursion below. p99 is about 2× the median multi-process and about 3.6–3.8× single-process.
- Rate. Every published row sustained the 100 Hz target on all four lanes.
RT-tuned posture disclosure. On an RT-tuned machine (RT priority plus
DMA lock), ROS 2 composed plus intra-process reaches 5.0–6.2 µs, comparable
to Cerulion’s multi-process leg at 5.9–6.5 µs, while Cerulion
single-process is 2.6–3.3 µs. These tuned rows are not rate-matched and are
therefore not published as a table: Cerulion’s tuned legs did not sustain
100 Hz (single-process fell to the 20 Hz ladder step, and multi-process
reported a mixed achieved rate), while both ROS 2 lanes held 100 Hz. The
collapsed-shape gap narrows under RT tuning; do not treat the tuned values as
a like-for-like comparison.
Beyond one machine. The figures above are the single-machine graph, which is
where the zero-copy path lives. Across machines, Cerulion runs a network layer
on the same core:
cerulion topic list finds robots on the LAN with no
configuration, and cerulion connect pulls a robot’s topics onto your desk as
if they were local. See
Networking and remote robots.What you get
With a graph file as your source of truth, you get flat latency at sensor payload sizes without tuning, byte-reproducible runs, and loud deadline misses. Put those behaviors into practice with execution order, run a graph across processes, and record and replay a run.Next steps
Core concepts
Workspaces, nodes, graphs, topics, and schemas: the mental model.
Execution order
See how your graph determines levels, triggers, clocks, and process layout.
Quickstart
Go from zero to a running graph you can observe with
topic echo.Define a node
Create a node type, choose a trigger policy, and write
tick().CLI reference
Every command and flag, in one place.