#can #odrive #motor-control #rust-bindings

rustodrive

Rust bindings for controlling multiple ODrives with the CAN protocol

1 unstable release

0.1.0 Jul 29, 2022

#442 in Concurrency

37 downloads per month

MIT license

65KB
1K SLoC

rustodrive is a client library for communicating with ODrives using the CAN protocol.

It is more than a simple CAN sender/receiver and has many convenience structs/methods alongside support for multiple threads sending and receiving safely, loosely following the Command Query Responsibility Segregation (CQRS) paradigm. Only one thread is given permission to send commands that can modify the state of the ODrive while other threads have permission to request any data that doesn't change state.

Why allow multiple threads?

One might be wondering why one would want multiple threads, and for most cases, you can treat this as a single threaded application and simply take advantage of the methods available. But for our case, we are needed to develop a GUI which should silently listen to the commands being send back and forth. This might also be useful for debugging purposes and could allow for playback of past data for non-physical testing.

State of the library

This library is currently in early stages and doesn't support all the CAN commands. However, most of the grunt work has already been done and it should be a matter of passing parameters into our objects (see axis.rs and odrivegroup.rs for what remains to be done).

The library will be under active development for the next few months as this is being used for an overarching project found here AMBER @ UConn

Current functionality

  • Safe multithreading
  • Setting axis states, reading encoder values, setting the control mode, setting input velocity or position

We intend to implement the remaining supported CAN messages in the future.

Examples

// main.rs
use rustodrive::{
    canproxy::CANProxy,
    state::{ODriveAxisState::*, ControlMode, InputMode},
    odrivegroup::ODriveGroup,
    threads::ReadWriteCANThread,
};
use signal_hook::{consts::SIGINT, iterator::Signals};
use std::{error::Error};

fn odrive_main(can_read_write: ReadWriteCANThread) {
    // Specify the CAN ids of all odrives connected
    let odrives = ODriveGroup::new(can_read_write, &[0, 1, 2, 3, 4, 5]);
    odrv.all_axes(|ax| ax.set_state(EncoderIndexSearch));

    odrives.all_axes(|ax| ax.set_state(ClosedLoop));
    odrives.all_axes(|ax| ax.motor.set_control_mode(ControlMode::PositionControl, InputMode::PosFilter));
    odrives.all_axes(|ax| ax.motor.set_input_pos(180.0 / 360.0));

    //odrives.all_axes(|ax| ax.motor.set_input_vel(10.0)); // if we had velocity control enabled
}


// This is useful code to stop threads and exit peacefully
fn main() -> Result<(), Box<dyn Error>> {
    let mut can_proxy = CANProxy::new("can0");

    // We register a thread that is capable of reading state, but also modifying it
    // We can also register a thread that can send "read only" commands.
    can_proxy.register_rw("thread 1", odrive_main);
    can_proxy.register_ro("read only thread", |read_only| {})

    // Turn on the thread to process CAN commands from various threads
    let stop_all = can_proxy.begin();

    // Handle ctrl-c to exit
    let mut signals = Signals::new(&[SIGINT])?;
    for sig in signals.forever() {
        println!("\nQuitting the program {:?}", sig);
        break;
    }

    // Use the hook from `can_proxy.begin()` to clean up the registered threads
    stop_all().unwrap();
    println!("all done!");
    Ok(())
}

Documentation

You can build our public documentation with cargo doc or to include private method documentation, with cargo doc --document-private-items. Currently, most documentation with examples is found in CANProxy and ODriveGroup

Contributing & License

Pull requests are greatly appreciated and all work is available under the MIT license.

Dependencies

~2.5MB
~53K SLoC