Tomasz Czajka

Robotik · Async Rust

Varför r2r slår den klassiska ROS 2-stacken

ROS 2 är ryggraden i modern robotik, men sättet de flesta av oss skriver noder på, med rclcpp eller rclpy byggda av colcon, har inte åldrats väl. r2r ger dig idiomatisk async Rust ovanpå samma ROS 2-kärna, och när du väl har testat det är det svårt att gå tillbaka.

Rust r2r ROS 2 Tokio DDS
Den humanoida roboten Atlas sedd framifrån
Den humanoida roboten Atlas. Foto: DARPA, allmän egendom.

Först: vad r2r faktiskt är

r2r ersätter inte ROS 2 i sig. Det är ett Rust-klientbibliotek som binder direkt mot rcl, samma C-lager som rclcpp och rclpy vilar på, så dina noder pratar fortfarande med alla andra ROS 2-noder över DDS. Det som ändras är allt ovanför det lagret: språket, samtidighetsmodellen och byggverktygen. Jämförelsen gäller alltså r2r mot standardsättet att skriva ROS 2-noder: C++ eller Python, byggt med colcon.

8 fördelar med r2r

1

Asynkront från grunden

Prenumerationer är Streams, tjänsteanrop och action-mål är Futures. Du kombinerar dem med tokio::select!, tidsgränser och kanaler i stället för att jonglera executors, callback-grupper och tillstånd delat mellan callbacks.

2

Bara cargo, ingen colcon

Meddelandebindningar genereras vid bygget från den inlästa ROS 2-miljön. Ingen package.xml, inga CMakeLists, ingen ament: cargo build, cargo test och rust-analyzer fungerar direkt.

3

Minnes- och trådsäkerhet

Kompilatorn utesluter datakapplöpningar, use-after-free och hängande pekare i callbacks, de klassiska orsakerna till att C++-noder kraschar. På en gående humanoid spelar det roll.

4

Samma ROS 2-kärna under huven

Eftersom r2r kapslar in rcl fungerar det med alla RMW-implementationer (Fast DDS, Cyclone DDS, Zenoh) och samverkar med befintliga C++- och Python-noder: topics, tjänster, actions, parametrar och klockor.

5

Nativ hastighet, ingen GIL

Kompilerad Rust med nollkostnadsabstraktioner och utan skräpsamlare. Till skillnad från rclpy finns inget Global Interpreter Lock som stryper högfrekventa topics, så latensen förblir förutsägbar.

6

Hela crates.io-ekosystemet

serde, nalgebra, tracing, axum, WebRTC: en ROS 2-nod, ett webb-API och en telemetriström i en enda binär, utan ett enda CMake find_package.

7

Otypade meddelanden för bryggor

subscribe_untyped och create_publisher_untyped arbetar med JSON via serde, så generiska bryggor, inspelare och felsökningsverktyg behöver inga meddelandetyper vid kompilering.

8

Moderna verktyg och driftsättning

clippy, rustfmt, cargo test och reproducerbara Cargo.lock-byggen i CI. Leverera en fristående binär i en slimmad container i stället för ett helt install/-träd.

Så här ser det ut

En komplett nod: prenumerera på ledtillstånd och publicera hastighetskommandon, allt i en Tokio-runtime. Jämför med motsvarande 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 --release
use 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(())
}

För att vara rättvis: avvägningarna

  • r2r underhålls av communityn och är inte ett av de officiella ROS 2-klientbiblioteken, så det finns färre guider och svar än för rclcpp eller rclpy.
  • Bygget kräver en inläst ROS 2-installation, eftersom bindningarna genereras från de meddelandepaket den hittar.
  • Första bygget är långsamt, eftersom bindgen och meddelandegenerering körs för varje paket. Inkrementella byggen går bra.
  • Launch-filer och colcon-arbetsytor behöver lite lim (till exempel colcon-ros-cargo) för att behandla en cargo-crate som ett ROS-paket.

Slutsats

För ny mellanprogramvara, bryggor, teleoperation och styrpipelines är r2r det bättre standardvalet: asynkronitet som verkligen passar robotikens I/O, säkerhet vid kompilering och ett bygge som bara är cargo. Behåll rclcpp för äldre kod och leverantörsdrivrutiner; välj r2r för allt nytt.