Robotik · Async Rust
Warum r2r den klassischen ROS-2-Stack schlägt
ROS 2 ist das Rückgrat moderner Robotik, aber die Art, wie die meisten von uns Nodes schreiben (rclcpp oder rclpy, gebaut mit colcon), ist nicht gut gealtert. r2r bietet idiomatisches Async Rust auf demselben ROS-2-Kern, und wer es einmal ausprobiert hat, will kaum zurück.

Zuerst: Was r2r eigentlich ist
r2r ersetzt nicht ROS 2 selbst. Es ist eine Rust-Client-Bibliothek, die direkt an rcl bindet, dieselbe C-Schicht, auf der rclcpp und rclpy aufsetzen. Deine Nodes sprechen also weiterhin über DDS mit jedem anderen ROS-2-Node. Was sich ändert, ist alles darüber: die Sprache, das Nebenläufigkeitsmodell und die Build-Werkzeuge. Verglichen wird also r2r mit dem Standardweg, ROS-2-Nodes zu schreiben: C++ oder Python, gebaut mit colcon.
8 Vorteile von r2r
Von Grund auf asynchron
Subscriptions sind Streams, Service-Aufrufe und Action-Goals sind Futures. Du kombinierst sie mit tokio::select!, Timeouts und Channels, statt Executors, Callback-Gruppen und zwischen Callbacks geteilten Zustand zu jonglieren.
Einfach cargo, kein colcon
Message-Bindings werden beim Build aus der geladenen ROS-2-Umgebung erzeugt. Kein package.xml, keine CMakeLists, kein ament: cargo build, cargo test und rust-analyzer funktionieren einfach.
Speicher- und Thread-Sicherheit
Der Compiler schließt Data Races, Use-after-free und hängende Zeiger in Callbacks aus, die klassischen Absturzursachen von C++-Nodes. Bei einem laufenden Humanoiden zählt das.
Derselbe ROS-2-Kern darunter
Da r2r rcl kapselt, funktioniert es mit jeder RMW-Implementierung (Fast DDS, Cyclone DDS, Zenoh) und arbeitet mit bestehenden C++- und Python-Nodes zusammen: Topics, Services, Actions, Parameter und Clocks.
Native Geschwindigkeit, kein GIL
Kompiliertes Rust mit Zero-Cost-Abstraktionen und ohne Garbage Collector. Anders als bei rclpy bremst kein Global Interpreter Lock hochfrequente Topics, die Latenz bleibt vorhersagbar.
Das gesamte crates.io-Ökosystem
serde, nalgebra, tracing, axum, WebRTC: ROS-2-Node, Web-API und Telemetrie-Stream in einer einzigen Binary, ohne ein einziges CMake-find_package.
Untypisierte Nachrichten für Bridges
subscribe_untyped und create_publisher_untyped arbeiten über serde mit JSON, sodass generische Bridges, Recorder und Debugging-Tools keine Nachrichtentypen zur Compile-Zeit brauchen.
Moderne Werkzeuge und Deployment
clippy, rustfmt, cargo test und reproduzierbare Cargo.lock-Builds in der CI. Liefere eine eigenständige Binary in einem schlanken Container statt eines ganzen install/-Verzeichnisbaums.
So sieht es aus
Ein vollständiger Node: Gelenkzustände abonnieren und Geschwindigkeitsbefehle publizieren, alles in einer Tokio-Runtime. Vergleiche das mit dem entsprechenden rclcpp-Boilerplate.
# Cargo.toml
[dependencies]
r2r = "0.9"
tokio = { version = "1", features = ["full"] }
futures = "0.3"
# build & run: no colcon, no package.xml
$ source /opt/ros/jazzy/setup.bash
$ cargo run --releaseuse futures::StreamExt;
use r2r::{geometry_msgs::msg::Twist, sensor_msgs::msg::JointState, QosProfile};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let ctx = r2r::Context::create()?;
let mut node = r2r::Node::create(ctx, "teleop_bridge", "")?;
let mut joints = node.subscribe::<JointState>("/joint_states", QosProfile::default())?;
let cmd = node.create_publisher::<Twist>("/cmd_vel", QosProfile::default())?;
// rcl spins on its own thread, everything else is plain async Rust
tokio::task::spawn_blocking(move || loop {
node.spin_once(std::time::Duration::from_millis(5));
});
while let Some(state) = joints.next().await {
let mut twist = Twist::default();
twist.linear.x = state.velocity.first().copied().unwrap_or(0.0) * 0.5;
cmd.publish(&twist)?;
}
Ok(())
}Fairerweise: die Kompromisse
- r2r wird von der Community gepflegt und gehört nicht zu den offiziellen ROS-2-Client-Bibliotheken, daher gibt es weniger Tutorials und Antworten als für rclcpp oder rclpy.
- Zum Bauen ist eine geladene ROS-2-Installation nötig, da die Bindings aus den gefundenen Message-Paketen erzeugt werden.
- Der erste Build ist langsam, weil bindgen und die Message-Generierung für jedes Paket laufen. Inkrementelle Builds sind unproblematisch.
- Launch-Dateien und colcon-Workspaces brauchen etwas Klebstoff (z. B. colcon-ros-cargo), um ein cargo-Crate als ROS-Paket zu behandeln.
Fazit
Für neue Middleware, Bridges, Teleoperation und Steuerungspipelines ist r2r die bessere Standardwahl: Asynchronität, die wirklich zu Robotik-I/O passt, Sicherheit zur Compile-Zeit und ein Build, der einfach cargo ist. rclcpp bleibt für Legacy-Code und Herstellertreiber; für alles Neue nimm r2r.