mecha10-drivers-motor

Crates.iomecha10-drivers-motor
lib.rsmecha10-drivers-motor
version0.1.47
created_at2026-01-15 07:27:38.26863+00
updated_at2026-01-25 07:05:50.967991+00
descriptionMotor driver for the Mecha10 robotics framework
homepage
repositoryhttps://github.com/mecha10/mecha10
max_upload_size
id2044794
size172,608
Peter C (PeterChauYEG)

documentation

README

Mecha10 Motor Drivers

Production-ready motor drivers for the Mecha10 robotics framework, supporting the most popular motor types used in robotics.

Supported Motors

1. Dynamixel Smart Servos

  • Series: XL, XM, XH, XC series
  • Protocol: Dynamixel Protocol 2.0
  • Interface: TTL/RS-485 via USB2Dynamixel or U2D2
  • Features: Position/velocity/torque control, hardware PID, built-in encoder
  • Use Cases: Robot arms, humanoid robots, grippers, pan-tilt mechanisms

2. ODrive BLDC Controllers

  • Models: ODrive v3.x
  • Interface: ASCII protocol over UART/USB
  • Features: High-power BLDC/PMSM control, field-oriented control, encoder feedback
  • Use Cases: Electric vehicles, robotic actuators, gimbal control, high-torque applications

3. Stepper Motors

  • Drivers: DRV8825, A4988, TMC2209
  • Interface: Step/Dir or GRBL
  • Features: Microstepping, acceleration profiles, precise positioning
  • Use Cases: 3D printers, CNC machines, camera sliders, linear actuators

Features

  • ✅ Unified MotorDriver trait for all motor types
  • ✅ Position, velocity, and torque control modes
  • ✅ Multi-motor synchronized control
  • ✅ Calibration data persistence
  • ✅ Hardware error detection and reporting
  • ✅ Configurable PID gains
  • ✅ Temperature and current monitoring
  • ✅ Acceleration profiles for smooth motion

Installation

Add to your Cargo.toml:

[dependencies]
mecha10-drivers-motor = { path = "path/to/mecha10-monorepo/packages/drivers/motor" }

Quick Start

Dynamixel Example

use mecha10_drivers_motor::dynamixel::{DynamixelDriver, DynamixelConfig};
use mecha10_drivers_motor::common::{MotorDriver, MotorCommand, ControlMode};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Configure driver
    let config = DynamixelConfig {
        port: "/dev/ttyUSB0".to_string(),
        baud_rate: 1_000_000,  // 1 Mbps
        motor_ids: vec![1, 2, 3],
        control_mode: ControlMode::Position,
        update_rate_hz: 100.0,
        calibration_file: Some("motor_calibration.json".to_string()),
        position_pid: None,
        velocity_pid: None,
    };

    // Initialize driver
    let mut driver = DynamixelDriver::new(config);
    driver.init().await?;

    // Enable motors
    driver.enable(1).await?;

    // Send position command (90 degrees)
    let cmd = MotorCommand {
        motor_id: 1,
        mode: ControlMode::Position,
        target: std::f64::consts::PI / 2.0,  // 1.57 rad
        velocity_limit: Some(2.0),
        torque_limit: Some(1.0),
        profile_velocity: None,
        profile_acceleration: None,
    };

    driver.send_command(&cmd).await?;

    // Read motor state
    let state = driver.read_state(1).await?;
    println!("Position: {} rad, Velocity: {} rad/s", state.position, state.velocity);
    println!("Temperature: {}°C, Voltage: {}V", state.temperature, state.voltage);

    Ok(())
}

ODrive Example

use mecha10_drivers_motor::odrive::{ODriveDriver, ODriveConfig};
use mecha10_drivers_motor::common::{MotorDriver, MotorCommand, ControlMode};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let config = ODriveConfig {
        port: "/dev/ttyACM0".to_string(),
        baud_rate: 115200,
        axes: vec![0, 1],  // Dual-axis control
        control_mode: ControlMode::Velocity,
        update_rate_hz: 100.0,
        calibration_file: None,
        current_limit: 10.0,  // 10A
        velocity_limit: 10000.0,  // counts/s
    };

    let mut driver = ODriveDriver::new(config);
    driver.init().await?;

    // Enable axis
    driver.enable(0).await?;

    // Send velocity command
    let cmd = MotorCommand {
        motor_id: 0,
        mode: ControlMode::Velocity,
        target: 5000.0,  // counts/s
        velocity_limit: None,
        torque_limit: Some(5.0),
        profile_velocity: None,
        profile_acceleration: None,
    };

    driver.send_command(&cmd).await?;

    Ok(())
}

Stepper Example

use mecha10_drivers_motor::stepper::{StepperDriver, StepperConfig, StepperInterface, StepperMotorConfig};
use mecha10_drivers_motor::common::{MotorDriver, MotorCommand, ControlMode};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let config = StepperConfig {
        interface: StepperInterface::GRBL {
            port: "/dev/ttyUSB0".to_string(),
            baud_rate: 115200,
        },
        motors: vec![
            StepperMotorConfig {
                motor_id: 0,
                steps_per_rev: 200,  // 1.8° stepper
                microsteps: 16,
                gear_ratio: 1.0,
                max_velocity: 1000.0,
                max_acceleration: 500.0,
                enable_active_high: false,
                invert_direction: false,
            },
        ],
        update_rate_hz: 100.0,
        calibration_file: None,
    };

    let mut driver = StepperDriver::new(config);
    driver.init().await?;

    // Send position command
    let cmd = MotorCommand {
        motor_id: 0,
        mode: ControlMode::Position,
        target: 3.14,  // π radians (180°)
        velocity_limit: Some(500.0),
        torque_limit: None,
        profile_velocity: None,
        profile_acceleration: Some(250.0),
    };

    driver.send_command(&cmd).await?;

    Ok(())
}

Hardware Setup

Dynamixel

Computer → USB2Dynamixel/U2D2 → Dynamixel Chain

Wiring:
- Data: TTL (XL series) or RS-485 (XM/XH series)
- Power: 12V (XL) or 12-24V (XM/XH)
- Daisy-chain supported (up to 253 devices)

Configuration:
- Set unique IDs for each servo
- Configure baud rate (57600, 115200, 1M, 3M, 4M)

ODrive

Computer → USB/UART → ODrive → BLDC Motor + Encoder

Wiring:
Axis 0/1:
- Motor: Phase A, B, C
- Encoder: A, B, Z (quadrature) or Hall U, V, W
- Power: 12-56V DC, up to 120A

Configuration:
- Calibrate motor (motor.config.resistance, motor.config.inductance)
- Calibrate encoder (encoder.config.cpr)
- Tune PID (controller.config.pos_gain, vel_gain)

Stepper Motors

Computer → GRBL Controller/GPIO → Stepper Driver → Stepper Motor

Wiring (GPIO):
- Step pin
- Direction pin
- Enable pin (optional)
- Motor: typically 4-wire bipolar

Configuration:
- Set microstepping (MS1, MS2, MS3 pins)
- Configure current limit (potentiometer on driver)

Control Modes

Position Control

MotorCommand {
    mode: ControlMode::Position,
    target: 1.57,  // radians
    velocity_limit: Some(2.0),  // max speed
    torque_limit: Some(1.0),    // max torque
    ..Default::default()
}

Velocity Control

MotorCommand {
    mode: ControlMode::Velocity,
    target: 5.0,  // rad/s
    torque_limit: Some(1.0),
    ..Default::default()
}

Torque Control

MotorCommand {
    mode: ControlMode::Torque,
    target: 0.5,  // N⋅m or Amps
    ..Default::default()
}

Multi-Motor Control

Synchronized control of multiple motors:

use mecha10_drivers_motor::common::MultiMotorCommand;

let cmd = MultiMotorCommand {
    motor_ids: vec![1, 2, 3],
    mode: ControlMode::Position,
    targets: vec![0.0, 1.57, 3.14],
    timestamp_us: now_micros(),
};

driver.send_multi_command(&cmd).await?;

Calibration

Save and load motor calibrations:

use mecha10_drivers_motor::common::MotorCalibration;

// Create calibrations
let calibrations = vec![
    MotorCalibration {
        motor_id: 1,
        position_offset: 0.0,
        direction: 1,
        gear_ratio: 1.0,
        encoder_resolution: 4096,
        max_velocity: 10.0,
        max_torque: 2.0,
    },
];

// Save to file
let path = std::path::PathBuf::from("motor_calibration.json");
MotorCalibration::save(&calibrations, &path)?;

// Load from file
let loaded = MotorCalibration::load(&path)?;

Error Handling

All drivers return detailed error information:

match driver.send_command(&cmd).await {
    Ok(_) => println!("Command sent successfully"),
    Err(e) => {
        match e {
            Mecha10Error::HardwareError(msg) => {
                eprintln!("Hardware error: {}", msg);
            }
            Mecha10Error::ConnectionError(msg) => {
                eprintln!("Connection error: {}", msg);
                // Attempt to reconnect
            }
            _ => eprintln!("Other error: {}", e),
        }
    }
}

// Read motor errors
let state = driver.read_state(1).await?;
if state.error_flags != 0 {
    let errors = MotorError::from_flags(state.error_flags);
    for error in errors {
        eprintln!("Motor error: {:?}", error);
    }
}

Performance

Motor Type Update Rate Latency Communication
Dynamixel 100-1000 Hz 1-10ms Serial (TTL/RS-485)
ODrive 100-500 Hz 2-20ms USB/UART
Stepper 1-100 Hz <1ms GPIO/GRBL

Common Issues

Dynamixel

  • Motor not found: Check ID, baud rate, and wiring
  • Overload error: Reduce torque limit or check mechanical binding
  • Temperature warning: Improve cooling or reduce duty cycle

ODrive

  • Motor calibration failed: Check encoder wiring and motor resistance
  • DC bus overvoltage: Add brake resistor or reduce deceleration
  • Encoder error: Verify CPR setting and encoder connections

Stepper

  • Missed steps: Reduce acceleration or increase current limit
  • Motor overheating: Add heat sink or reduce current
  • Vibration: Tune microstepping or use closed-loop stepper

See Also

References

Commit count: 0

cargo fmt