Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Core Concepts

Understanding the building blocks of miniROS-rs.

Nodes

Nodes are independent processes that communicate with each other.

#![allow(unused)]
fn main() {
// Create a node
let mut node = Node::new("robot_controller")?;
node.init().await?;

// Each node has a unique name and can create publishers/subscribers
}

Key Points:

  • Each node runs independently
  • Nodes communicate via topics and services
  • Multiple nodes can run in the same process

Messages

Messages are data structures sent between nodes.

Built-in Messages

#![allow(unused)]
fn main() {
use mini_ros::message::*;

// Simple types
let string_msg = StringMsg { data: "Hello".to_string() };
let number_msg = Float64Msg { data: 42.0 };
let int_msg = Int32Msg { data: 100 };
}

Custom Messages

#![allow(unused)]
fn main() {
use serde::{Deserialize, Serialize};

#[derive(Debug, Clone, Serialize, Deserialize)]
struct RobotPose {
    x: f64,
    y: f64,
    theta: f64,
}

// Automatically implements Message trait
}

Communication Patterns

Publisher/Subscriber

Asynchronous, many-to-many communication:

#![allow(unused)]
fn main() {
// Publisher side
let publisher = node.create_publisher::<StringMsg>("robot_status").await?;
publisher.publish(&StringMsg { data: "Moving".to_string() }).await?;

// Subscriber side  
let subscriber = node.create_subscriber::<StringMsg>("robot_status").await?;
subscriber.on_message(|msg| {
    println!("Robot status: {}", msg.data);
}).await?;
}

When to use: Sensor data, status updates, continuous streams

Services

Synchronous, one-to-one request/response:

#![allow(unused)]
fn main() {
// Server side
let _service = node.create_service("calculate_distance", |req: StringMsg| -> Result<Float64Msg> {
    let distance = req.data.len() as f64; // Simple calculation
    Ok(Float64Msg { data: distance })
}).await?;

// Client side
let client = node.create_service_client::<StringMsg, Float64Msg>("calculate_distance").await?;
let response = client.call(StringMsg { data: "test".to_string() }).await?;
}

When to use: Calculations, configuration, one-time requests

Topics

Topics are named channels for message passing:

#![allow(unused)]
fn main() {
// Topic names are strings
"/robot/pose"           // Robot position
"/sensors/lidar"        // Laser scan data  
"/camera/image"         // Camera images
"/mission/status"       // Mission updates
}

Naming Convention:

  • Use forward slashes for hierarchy
  • Lowercase with underscores
  • Descriptive and consistent

Error Handling

miniROS-rs uses Rust’s Result type:

use mini_ros::error::Result;

#[tokio::main]  
async fn main() -> Result<()> {
    let mut node = Node::new("my_node")?;  // Can fail
    node.init().await?;                    // Can fail
    
    // Handle specific errors
    match node.create_publisher::<StringMsg>("test").await {
        Ok(publisher) => { /* use publisher */ },
        Err(e) => println!("Failed to create publisher: {}", e),
    }
    
    Ok(())
}

Best Practices

Node Design

  • One node per logical component (sensor, controller, planner)
  • Keep nodes small and focused
  • Use descriptive names

Message Design

  • Keep messages simple and flat when possible
  • Use appropriate data types (f32 vs f64)
  • Include timestamps for time-sensitive data

Communication

  • Use pub/sub for streaming data
  • Use services for calculations and queries
  • Choose topic names carefully - they’re hard to change later

Next Steps