stepper_lib 0.11.1

A simple library to control motors and components
Documentation
stepper_lib-0.11.1 has been yanked.

stepper_lib

Crates.io version stepper_lib: rustc 1.68+

A library for all types of components used in robots, including controlls for stepper motors, servo motors and more complex assemblies using said motors. Currently all implementations are made for the raspberry pi, though new implementations for more controllers are currently being made.

Basis library for the sybot_lib

In Action

Let us assume we want to control a simple stepper motor (in this example a 17HE15_1504_S) with a PWM controller connected to the BCM pins 27 and 19.

# ...

[dependencies]
# Include the library configured for the raspberry pi
stepper_lib = { version = "0.11.0", features = [ "rasp" ] } 

# ...
use core::f32::consts::PI;

// Include components and data
use stepper_lib::{StepperCtrl, StepperConst, SyncComp};
use stepper_lib::data::LinkedData;
// Include the unit system
use stepper_lib::units::*;

// Pin declerations (BCM on raspberry pi)
const PIN_DIR : u8 = 27;
const PIN_STEP : u8 = 19;

// Define distance and max speed
const DELTA : Delta = Delta(2.0 * PI);
const OMEGA : Omega = Omega(10.0);

fn main() -> Result<(), stepper_lib::Error> {
    // Create the controls for a stepper motor
    let mut ctrl = StepperCtrl::new(StepperConst::MOT_17HE15_1504S, PIN_DIR, PIN_STEP);
    // Link the component to a system
    ctrl.write_link(LinkedData { 
        u: 12.0,    // System voltage in volts
        s_f: 1.5    // System safety factor, should be at least 1.0
    }); 

    // Apply some loads
    ctrl.apply_inertia(Inertia(0.2));
    ctrl.apply_force(Force(0.10));

    println!("Staring to move");
    ctrl.drive_rel(DELTA, OMEGA)?;      // Move the motor
    println!("Distance {}rad with max speed {:?}rad/s done", DELTA, OMEGA);

    Ok(())
}

(Source: "examples/stepper_motor.rs")

Overview

Features

  • Motors
    • Stepper motors
    • Servo motors
    • DC motors
  • Components
    • Cylinder
    • Gear joint
    • Cylinder-triangle
    • Conveyor
  • Tools
    • Tongs
    • Axial joint
  • Calculation
    • Complex acceleration curves
    • Overloads
    • Forces
    • Inertias
  • Measurements
    • Simple switch
    • Rotary resolver
  • Extendable
    • Custom components
    • Custom tools
  • Minimal
    • Fully supports no_std environment
    • Available for basic embedded systems
  • Platforms
    • Raspberry Pi and similar

Components

A component (trait SyncComp in the library) represents a synchronous motor, optionally connected to a mechanical structure that transforms the rotatory movements created by the motor. If a struct implements the SyncComp trait, it can be linked into a group with SyncCompGroup, later required in the sybot_lib.

The library does include some standard components commonly used

  • Cylinder, a simple cylinder translating the rotatory movements of a motor to a linear extension
  • GearJoint, a motor connected to a gear that translates the movement with a certain ratio
  • Cylinder-triangle, a cylinder being the hypotenuse in a triangular shape, creating a high torque/slow movement joint

In this example we drive a cylinder by a certain amount of millimeters.

# ...

[dependencies]
# Include the library configured for the raspberry pi
stepper_lib = { version = "0.11.0", features = [ "rasp" ] } 

# ...
// Include components and data
use stepper_lib::{StepperCtrl, StepperConst, SyncComp};
use stepper_lib::comp::Cylinder;
use stepper_lib::data::LinkedData;
// Include the unit system
use stepper_lib::units::*;

// Pin declerations (BCM on raspberry pi)
const PIN_DIR : u8 = 27;
const PIN_STEP : u8 = 19;

// Define distance and max speed
const DELTA : Delta = Delta(10.0);      // 10 millimeters
const OMEGA : Omega = Omega(20.0);      // 20 millimeters per second

fn main() -> Result<(), stepper_lib::Error> {
    // Create the controls for a stepper motor
    let mut cylinder = Cylinder::new(
        StepperCtrl::new(StepperConst::MOT_17HE15_1504S, PIN_DIR, PIN_STEP),
        1.273       // Spindle pitch of the cylinder, per radian the cylinder extends for 1.273 millimeters,
                    // this factor calculates out of the pitch per revolve (8mm) divided by 2*PI (for radians) 
    );
    // Link the component to a system
    cylinder.write_link(LinkedData { 
        u: 12.0,    // System voltage in volts
        s_f: 1.5    // System safety factor, should be at least 1.0
    }); 

    // Apply some loads
    cylinder.apply_inertia(Inertia(0.2));
    cylinder.apply_force(Force(0.10));

    println!("Staring to move ... ");
    let delta_real = cylinder.drive_rel(DELTA, OMEGA)?;         // Move the cylinder
    println!("Distance {}mm with max speed {:?}mm/s done", delta_real, OMEGA);

    Ok(())
}

(Source: "examples/cylinder.rs")

Custom components

If a component is desired that is not included in the standard components, then a custom component can be created. Simply implement the SyncComp trait for the component.

There are two ways of defining a new component

  • Defining a super component, which would be the motor to a gear or the cylinder in the CylinderTriangle, the only functions that have to be overwritten then are the functions required to communicate with the super component. Though in many cases some kind of ratio is added.
  • Completely implementing the trait, therefore defining a completely new type of motor.

The following example shows a custom component with a stepper motor as super component. Additionally it prints out a message every time write drive a relative distance.

# ...

[dependencies]
# Include the library configured for the raspberry pi
stepper_lib = { version = "0.11.0", features = [ "rasp" ] } 

# ...
// Include components and data
use stepper_lib::{StepperCtrl, StepperConst, SyncComp};
use stepper_lib::data::LinkedData;
use stepper_lib::meas::SimpleMeas;
// Include the unit system
use stepper_lib::units::*;

// Pin declerations (BCM on raspberry pi)
const PIN_DIR : u8 = 27;
const PIN_STEP : u8 = 19;

// Define distance and max speed
const DELTA : Delta = Delta(10.0);      
const OMEGA : Omega = Omega(20.0);      

// Defining component structure
#[derive(Debug)]
struct MyComp {
    ctrl : StepperCtrl,
    ratio : f32
}

impl MyComp {
    pub fn new(ctrl : StepperCtrl, ratio : f32) -> Self {
        Self { ctrl, ratio }
    }
}

impl SimpleMeas for MyComp {
    fn init_meas(&mut self, _ : u8) {
        todo!()     // Not required in this example
    }
}

impl SyncComp for MyComp {
    // Required memebers
        fn vars<'a>(&'a self) -> &'a stepper_lib::data::CompVars {
            todo!()     // Not required in this example
        }

        fn link<'a>(&'a self) -> &'a LinkedData {
            todo!()     // Not required in this example
        }

        fn to_json(&self) -> Result<serde_json::Value, serde_json::Error> {
            todo!()     // Not required in this example
        }
    //
    
    // Super component (motor)
        // The following two overrides give the library access to the stepper motor controller stored in our component
        
        fn super_comp(&self) -> Option<&dyn SyncComp> {
            Some(&self.ctrl)
        }

        fn super_comp_mut(&mut self) -> Option<&mut dyn SyncComp> {
            Some(&mut self.ctrl)
        }
    // 

    // Ratio
        // The following two overrides cause the library to translate the distance by the ratio we defined for our component

        fn gamma_for_super(&self, this_gamma : Gamma) -> Gamma {
            this_gamma * self.ratio     // Distance is translated by the ratio
        }

        fn gamma_for_this(&self, super_gamma : Gamma) -> Gamma {
            super_gamma / self.ratio
        }
    // 

    fn drive_rel(&mut self, delta : Delta, omega : Omega) -> Result<Delta, stepper_lib::Error> {
        println!("Now driving!"); // Our custom message

        let delta_real = self.ctrl.drive_rel(
            self.delta_for_super(delta, self.gamma()), 
            self.omega_for_super(omega, self.gamma())
        )?;

        Ok(self.delta_for_this(delta_real, self.gamma_for_super(self.gamma())))
    }
}

fn main() -> Result<(), stepper_lib::Error> {
    // Create the controls for a stepper motor
    let mut comp = MyComp::new(
        StepperCtrl::new(StepperConst::MOT_17HE15_1504S, PIN_DIR, PIN_STEP),
        2.0 // Example ratio
    );
    // Link the component to a system
    comp.write_link(LinkedData { 
        u: 12.0,    // System voltage in volts
        s_f: 1.5    // System safety factor, should be at least 1.0
    }); 

    // Apply some loads
    comp.apply_inertia(Inertia(0.2));
    comp.apply_force(Force(0.10));

    println!("Staring to move ... ");
    let delta_real = comp.drive_rel(DELTA, OMEGA)?;         // Move the comp
    println!("Distance {}rad with max speed {:?}rad/s done", delta_real, OMEGA);

    Ok(())
}

// Console output: 
// "
// Starting to move ... 
// Now driving!
// Distance 10rad with max speed 20rad/s done
// "

(Source: "examples/custom_component.rs")

Tools

The library also includes various simple tools like tongs, axial joints and so on. These tools are controlled by a servo controller, though other types of motors can be used in custom tools by implementing the Tool trait, just like SyncComp.

This example controls a simple pair of tongs using a servo motor.

# ...

[dependencies]
# Include the library configured for the raspberry pi
stepper_lib = { version = "0.11.0", features = [ "rasp" ] } 

# ...
use core::time::Duration;

use std::thread::sleep;

use stepper_lib::{SimpleTool, Tool};
use stepper_lib::comp::tool::Tongs;
use stepper_lib::ctrl::servo::ServoDriver;
use stepper_lib::data::servo::ServoConst;

// Include the unit system
use stepper_lib::units::*;

// Pin declerations (BCM on raspberry pi)
const PIN_PWM : u8 = 27;

fn main() -> Result<(), stepper_lib::Error> {
    // Create the tongs controller
    let mut tongs = Tongs::new(
        ServoDriver::new(ServoConst::MG996R, PIN_PWM),    // The tongs use a MG996R servo connected to the BCM pin 27   
        0.2,        // when the pwm signal is at 20%, the tongs are open
        0.8,        // when the pwm signal is at 80%, the tongs are closed
        100.0,      // the tongs are 100 millimeters long
        Inertia(0.2)        // the tongs have a weight of 0.2 kg
    );

    tongs.mount();

    tongs.activate();
    sleep(Duration::from_secs(1));
    tongs.deactivate();

    Ok(())
}

Platforms and simulation

The final goal of the library is to work on as many platforms as possible, even on embedded systems. To configure the library for a specific platform, the right features have to be enabled. Note that some of the features automatically enable std usage.

The current platforms and features enabled are

  • "rasp": Raspberry Pi and similar controller
# Platform features
rasp = [ "std", "dep:rppal" ]

If no platform is selected the library automatically goes into simulation mode. Meaning no movements will be actually executed, no pins will be written to or checked, which can lead to problems. As the library does for example not care if the same two pins are used in simulation mode.

Issues and requests

If you encounter any issues or if you have any request for new features, feel free to create an issue at the GitHub repo.