This microstack demonstrates production-ready ROS2 patterns in Rust using the r2r bindings. The architecture prioritizes safety, observability, and educational clarity to help developers learn ROS2 + Rust integration.
┌─────────────────────────────────────────────────────────────┐
│ ROS2 DDS Network │
└─────────────────────────────────────────────────────────────┘
│ │ │
▼ ▼ ▼
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ Safety │ │ Teleop │ │ Sensor │
│ Watchdog │ │ Mux │ │ Filter │
└──────────────┘ └──────────────┘ └──────────────┘
│ │ │
Subs: /heartbeat Subs: /teleop/cmd_vel Subs: /scan
Pubs: /cmd_vel /nav/cmd_vel Pubs: /scan/filtered
Pubs: /cmd_vel
Flow: External sources → Sensor Filter → Perception
Telop/Nav → Teleop Mux → Safety Watchdog → Robot
Safety Watchdog (nodes/safety_watchdog)
- Purpose: Emergency stop on heartbeat timeout
- Pattern: Timeout-based safety interlock
- Key Learning: Timer-based logic, QoS profiles (best_effort vs reliable)
Teleop Mux (apps/teleop_mux)
- Purpose: Arbitrate between manual and autonomous control
- Pattern: Priority-based message selection
- Key Learning: Multi-subscription, clap configuration, tokio concurrency
Sensor Filter (apps/sensor_filter)
- Purpose: Clamp lidar outliers for stable perception
- Pattern: Stateless message transformation
- Key Learning: sensor_msgs handling, metadata preservation, benchmarking
| Feature | r2r | rclrs | rosrust |
|---|---|---|---|
| ROS Version | ROS2 | ROS2 | ROS1 only |
| Async Model | tokio-native | Manual | Callbacks |
| Type Safety | Strong | Strong | Moderate |
| Message Gen | Build-time (r2r_msg_gen) | Build-time | Runtime |
| Maturity | Active | Official but early | Mature (ROS1) |
| Learning Curve | Moderate | Steep | Low |
| Production Ready | Yes | Experimental | Yes (ROS1) |
- Async-first design: r2r embraces tokio, making concurrent node logic natural in Rust
- Ecosystem compatibility: Works seamlessly with existing Rust async libraries
- Build-time safety: Message types are generated at build time, catching errors early
- Active development: Regular updates and responsive maintainers
- Real-world usage: Deployed in production robotics systems
Advantages:
- Ergonomic async/await patterns for node logic
- Excellent interop with tokio ecosystem (tracing, anyhow, etc.)
- Minimal boilerplate compared to rclrs
- Strong community examples and documentation
Disadvantages:
- Not the official ROS2 Rust binding (rclrs is)
- Smaller community than rclcpp or rclpy
- Some advanced rcl features may lag behind C++ API
- Requires understanding of tokio runtime model
For this portfolio project, r2r's ergonomics and async-first design make it ideal for teaching modern Rust + ROS2 patterns.
nodes/ are libraries with logic exposed for:
- Unit testing without spawning processes
- Reuse across multiple binaries
- Example:
safety_watchdogcould be imported by integration tests
apps/ are binaries meant to:
- Be run directly via
cargo run - Provide user-facing CLI interfaces
- Example:
teleop_muxis an end-user tool
This mixed structure balances testability and clarity.
Use r2r's event loop (spin) when:
- Node logic is purely reactive (callbacks only)
- Simple pub/sub without complex async coordination
Use tokio runtime when:
- Timeout detection (safety_watchdog)
- Concurrent subscriptions with selection logic (teleop_mux)
- Integration with async Rust libraries (HTTP clients, databases)
All nodes in this project use tokio to demonstrate async patterns.
Best Effort (safety_watchdog heartbeat subscriber):
- Low latency, tolerates packet loss
- Suitable for high-rate health monitoring
- Missed heartbeat triggers safety action anyway
Reliable (all publishers, most subscribers):
- Guaranteed delivery (retries on packet loss)
- Depth 10: Handles temporary subscriber lag
- Standard for command and sensor topics
See ros2-primer.md for QoS deep dive.
workspace/
├── apps/
│ └── teleop_mux/ # Binary crate with main.rs + CLI
├── nodes/
│ ├── safety_watchdog/ # Library crate exposing WatchdogNode
│ └── sensor_filter/ # Library crate exposing FilterNode
├── examples/ # Standalone learning examples
└── tests/
└── integration/ # ROS2 message flow tests
Rationale: Libraries in nodes/ allow tests/integration/ to import and test node logic without subprocess management.
- r2r: ROS2 bindings
- tokio: Async runtime (matches r2r's async model)
- tracing: Structured logging (better than println!, integrates with ROS2 ecosystems)
- anyhow: Error handling (ergonomic error propagation)
- clap: CLI parsing (derive macros for maintainability)
- criterion: Micro-benchmarking (sensor_filter performance)
- tokio::time::pause: Deterministic async testing (watchdog timeout tests)
- tracing-subscriber: Configurable log output (RUST_LOG env var)
- Future: OpenTelemetry integration for distributed tracing
- Action servers: Demonstrate long-running tasks with feedback
- Parameter server: Runtime reconfiguration without restart
- Lifecycle nodes: Managed startup/shutdown for complex systems
- Component composition: Multiple nodes in single process for efficiency
- Cross-compilation: Deploy to ARM targets (e.g., Raspberry Pi)
- r2r GitHub
- ROS2 Humble Docs (concepts apply to Jazzy)
- Tokio Documentation
- Rust API Guidelines
Next: See ros2-primer.md for ROS2 concepts explained for Rust developers.