cerulion ros2 attach points Cerulion at a robot that is already running ROS 2. It discovers the live DDS topics, generates a bridge graph for the ones whose types resolve, and runs it — so those topics become ordinary Cerulion topics you can echo, record, visualize, and consume from your own nodes.
Run it on the robot, or on a machine on the robot’s DDS network.
Look before you write
--dry-run prints the discovery report and stops: it writes nothing and runs nothing.
The report tells you which topics resolved to a known type and which did not. A topic whose type Cerulion cannot resolve is left out of the generated graph — add the message definition to your workspace and re-run if you need it.
Read the migration section
Every attach report ends with aMIGRATION section — no flag, --dry-run included. It groups the discovered processes by whether they could run on Cerulion transport directly instead of being bridged, and prints the commands that would do it:
.msg files, and stays bridged when a type resolves nowhere.
Endpoints attach cannot attribute to a node are counted as topics rather than processes, and their line says so: no ros_discovery_info record was seen for them, which is either a vendor or raw-DDS process or a node table the discovery window did not observe. Where such a topic’s type does resolve, the line still tells you what a restart would buy. On a robot that publishes no node table at all, the section says plainly that no restartable process can be named, and prints no command block — there is no process to name in it.
Paste the ros2: block into a graph file and fill in each package and executable: DDS discovery sees endpoints, not launch metadata, so attach cannot know which package ships a node. See Or run the ROS 2 nodes on Cerulion transport below.
ROS plumbing endpoints — parameter services,
/rosout, /parameter_events, ros_discovery_info — are left out of the judgment. Every ROS 2 node carries them under any RMW, so counting them would mark every stock node unrestartable. A process that exposed nothing else is listed as having no message endpoints seen.Generate and run the bridge
graphs/go2.yaml plus graphs/go2.bridge.yaml, asks you to confirm, and then runs the bridge. Use --yes for scripts and non-interactive shells.
--robot-name is not the network identity. The name that shows up in cerulion topic list and over mDNS comes from the machine’s hostname, or from CERULION_ROBOT_IDENTITY if you set it.See the data on your desk
The generated graph bridges the robot’s topics and nothing else — no visualization node is staged on the robot, which keeps the robot’s job to shipping raw frames. To look at the data, work from your own machine:Consume a bridged topic from your own node
A bridged topic is a normal topic, so wire an input to it by absolute name:Record a bridged system
A bridge run records like any other run, and a recording of one plays back as a robot substitute when the robot is not available:Use plain
bag play for a bridge recording. A bridge node reads a live device rather than computing from its graph inputs, and --resim all re-executes every node in the bag — including the bridge, against DDS that is no longer there. See Record and replay a run.Or run the ROS 2 nodes on Cerulion transport
Bridging carries another robot’s DDS traffic across. When the ROS 2 nodes are yours to launch, run them on Cerulion transport instead of bridging to them — swap the command, not the stack:run or launch reaches ros2 untouched, and Cerulion stages the child so ROS 2 loads rmw_cerulion — the nodes’ topics land on the same shared-memory transport your native nodes use. Part of that staging is an LD_PRELOAD of libcerulion_heaphook.so when it ships beside librmw_cerulion.so — the cerulion binary’s own directory, or wherever CERULION_LIB_DIR points; one info line names the path it injected, and CERULION_ROS2_PRELOAD=off turns it off. This is Unix only. See cerulion ros2.
The topics keep their ROS names verbatim: a ROS 2 publisher on /camera/image_raw is the Cerulion topic /camera/image_raw, leading slash included, so you wire an #[input] to it and record it under the name ROS 2 already uses. Each of those publishers also registers its topic with the machine’s network plane, so a ROS 2 node’s output is announced and demandable from your desk exactly like a native node’s — see Reach remote robots.
A ROS 2 publisher has no graph YAML to size its payloads in, so each variable-length message type gets the same per-type shared-memory budget Cerulion gives one of its own outputs of that schema — a type Cerulion does not know takes 128 MiB. A message larger than its budget fails to publish, loudly; raise the ceiling for that type with CERULION_RMW_SLICE_CEILING:
Publish a large message without the copy
That preloaded hook does more than ride along: it lets a publisher hand your node the shared-memory slot it is about to publish from, on Linux with glibc and libstdc++. A message type qualifies for this in-slot fill when it has at least one unbounded numeric sequence —sensor_msgs/Image.data, sensor_msgs/PointCloud2.data, sensor_msgs/LaserScan.ranges — so a camera node that borrows a loaned sensor_msgs/Image and fills data writes the frame once, into the slot a subscriber reads, instead of filling a heap buffer that is copied at publish. Nodes that publish normally are unaffected.
The parts a publisher cannot place in the slot are still copied for you, so whether a field is filled in place or copied never changes whether the publish succeeds or what lands on the wire. A message too large for its slot ceiling still fails loudly, exactly as it does without the hook. Fields that always copy: bounded sequences, bool[], fields declared with a default value, and a sequence your node assigned from memory it allocated elsewhere. When a publish falls back to copying, the RMW says so once for that publisher.
Two things switch the behavior off, both loudly: CERULION_ROS2_PRELOAD=off (no hook in the child), and a CERULION_RMW_SLICE_CEILING too small to hold the message struct plus a slot header, which keeps that topic on the copy path for the life of that publisher.
To bring both halves of a mixed robot up from one file, declare the ROS 2 processes as ros2: entries beside your native nodes and run cerulion graph run:
graph run spawns and supervises each entry; a ros2: entry declares no ports, so its topics meet native nodes by absolute topic name. See ros2: entries.
Next steps
Message types
The 254 built-in ROS 2 messages that resolve out of the box.
Record and replay a run
Bag a bridged system and play it back without the robot.