# Mecha10 Controllers
Hardware controller abstractions for the Mecha10 framework.
## Overview
The controllers package provides a composable layer between hardware drivers and nodes. Controllers wrap device SDKs/libraries and provide standardized interfaces for nodes to manage hardware.
### Architecture
```text
┌─────────────────┐
│ Node │ (Business logic)
└────────┬────────┘
│
▼
┌─────────────────┐
│ Controller │ (Device management + SDK abstraction)
└────────┬────────┘
│
▼
┌──────────────┐
│ Hardware SDK │ (realsense-rust, bno055, etc.)
└──────────────┘
```
## Benefits
1. **Separation of Concerns**: Nodes focus on business logic, controllers handle hardware
2. **Testability**: Easy to mock and test hardware interactions
3. **Composability**: Mix and match controllers, wrap them with middleware
4. **Hot-swappable**: Switch between real hardware and simulation
5. **Consistent Interfaces**: Standard error handling and capabilities discovery
## Core Traits
### `Controller` - Base trait
All controllers implement this trait providing:
- Initialization and configuration
- Start/stop lifecycle
- Health checks
- Capability discovery
```rust
use mecha10_controllers::{Controller, ControllerHealth};
#[tokio::main]
async fn main() -> anyhow::Result<()> {
let config = MyControllerConfig::default();
let mut controller = MyController::init(config).await?;
controller.start().await?;
let health = controller.health_check().await;
assert_eq!(health, ControllerHealth::Healthy);
controller.stop().await?;
Ok(())
}
```
### Domain-Specific Traits
- **`CameraController`**: For RGB, depth, and RGBD cameras
- **`ImuController`**: For IMUs and orientation sensors
- **`LidarController`**: For 2D and 3D LiDARs
- **`MotorController`**: For motors and actuators
## Examples
### Using a Camera Controller
```rust
use mecha10_controllers::{Controller, CameraController};
use mecha10_controllers::mock::MockCameraController;
#[tokio::main]
async fn main() -> anyhow::Result<()> {
// Initialize camera
let config = MockCameraConfig::default();
let mut camera = MockCameraController::init(config).await?;
// Start streaming
camera.start().await?;
// Capture frames
loop {
let frame = camera.capture_frame().await?;
match frame {
CameraFrame::Rgb(rgb) => {
println!("RGB frame: {}x{}", rgb.width, rgb.height);
}
CameraFrame::Rgbd { color, depth, .. } => {
println!("RGBD frame: {}x{}", color.width, color.height);
}
_ => {}
}
tokio::time::sleep(tokio::time::Duration::from_millis(33)).await;
}
Ok(())
}
```
### Using an IMU Controller
```rust
use mecha10_controllers::{Controller, ImuController};
use mecha10_controllers::mock::MockImuController;
#[tokio::main]
async fn main() -> anyhow::Result<()> {
let config = MockImuConfig::default();
let mut imu = MockImuController::init(config).await?;
imu.start().await?;
// Check calibration
let status = imu.calibration_status();
if !status.is_fully_calibrated() {
println!("Calibrating IMU...");
imu.calibrate().await?;
}
// Read IMU data
loop {
let data = imu.read_imu().await?;
println!("Orientation: roll={}, pitch={}, yaw={}",
data.roll, data.pitch, data.yaw);
tokio::time::sleep(tokio::time::Duration::from_millis(10)).await;
}
Ok(())
}
```
### Using a Motor Controller
```rust
use mecha10_controllers::{Controller, MotorController};
use mecha10_controllers::mock::MockMotorController;
use mecha10_core::actuator::Twist;
#[tokio::main]
async fn main() -> anyhow::Result<()> {
let config = MockMotorConfig::default();
let mut motors = MockMotorController::init(config).await?;
motors.start().await?;
// Send velocity commands
let twist = Twist {
linear: 0.5, // 0.5 m/s forward
angular: 0.1, // 0.1 rad/s rotation
};
motors.set_twist(twist).await?;
// Get encoder readings
let (left, right) = motors.get_encoders().await?;
println!("Encoders: left={}, right={}", left, right);
// Emergency stop
motors.emergency_stop().await?;
Ok(())
}
```
## Mock Implementations
All controller traits have mock implementations for testing:
- `MockCameraController`: Generates synthetic RGB/depth images with gradients
- `MockImuController`: Generates synthetic IMU data with simulated motion
- `MockLidarController`: Generates synthetic laser scans
- `MockMotorController`: Simulates differential drive motors with encoders
Use these in your tests:
```rust
#[cfg(test)]
mod tests {
use mecha10_controllers::mock::*;
use mecha10_controllers::{Controller, CameraController};
#[tokio::test]
async fn test_camera_capture() {
let config = MockCameraConfig::default();
let mut camera = MockCameraController::init(config).await.unwrap();
camera.start().await.unwrap();
let frame = camera.capture_frame().await.unwrap();
// Test your code with the mock frame
camera.stop().await.unwrap();
}
}
```
## Creating Custom Controllers
To create a controller for new hardware:
1. Implement the base `Controller` trait
2. Implement the appropriate domain trait (`CameraController`, `ImuController`, etc.)
3. Define your configuration struct
4. Handle initialization, start/stop, and error recovery
Example skeleton:
```rust
use mecha10_controllers::{Controller, CameraController, ControllerCapabilities, ControllerHealth};
use async_trait::async_trait;
pub struct MyCustomCameraConfig {
pub device_path: String,
pub resolution: (u32, u32),
}
pub struct MyCustomCameraController {
config: MyCustomCameraConfig,
// Your SDK handle here
}
#[async_trait]
impl Controller for MyCustomCameraController {
type Config = MyCustomCameraConfig;
type Error = anyhow::Error;
async fn init(config: Self::Config) -> Result<Self, Self::Error> {
// Initialize your hardware SDK
Ok(Self { config })
}
async fn start(&mut self) -> Result<(), Self::Error> {
// Start data acquisition
Ok(())
}
async fn stop(&mut self) -> Result<(), Self::Error> {
// Stop gracefully
Ok(())
}
async fn health_check(&self) -> ControllerHealth {
// Check device health
ControllerHealth::Healthy
}
fn capabilities(&self) -> ControllerCapabilities {
ControllerCapabilities::new("camera", "my_custom_camera")
.with_vendor("MyCompany")
.with_feature("depth", false)
}
}
#[async_trait]
impl CameraController for MyCustomCameraController {
type Frame = CameraFrame;
async fn capture_frame(&mut self) -> Result<Self::Frame, Self::Error> {
// Capture frame from SDK
todo!()
}
// Implement other required methods...
}
```
## Usage in Nodes
Nodes should accept controllers via dependency injection:
```rust
use mecha10_controllers::{Controller, CameraController};
use mecha10_core::prelude::*;
pub struct VisionNode {
camera: Box<dyn CameraController<Frame = CameraFrame>>,
}
impl VisionNode {
pub async fn new(camera: Box<dyn CameraController<Frame = CameraFrame>>) -> Self {
Self { camera }
}
pub async fn run(&mut self, ctx: &Context) -> Result<()> {
self.camera.start().await?;
loop {
let frame = self.camera.capture_frame().await?;
// Process frame...
// Publish to topics...
}
}
}
```
## Design Principles
1. **Trait-based**: Use traits for abstraction and polymorphism
2. **Type-safe**: Strong typing with generics where appropriate
3. **Async-first**: All I/O operations are async
4. **Error-transparent**: Controllers expose hardware errors clearly
5. **Zero-copy where possible**: Avoid unnecessary data copying
6. **Composable**: Controllers can wrap other controllers
## Related Documentation
- [Controller Layer Design](../../CONTROLLER_LAYER_DESIGN.md) - Complete design document
- [Driver Documentation](../drivers/) - Hardware driver implementations
- [Core Framework](../core/) - Core types and traits