/RoboFirmwareToolkit

Simple C++ Robot Firmware Toolkit for makers

Primary LanguageC++MIT LicenseMIT

Intro

RoboFirmwareToolkit (RFT) is a simple, platform-independent, header-only C++ toolkit for building robot firmware to run on Arduino and other microcontrollers. Because of its platform-independence it can also be used as the control code in a C++-based robot simulator. As with other software platforms I've developed, the focus is on simplicity and ease-of-use rather than lots of features.

RFT is geared toward people like me who want to tinker with firmware and use it to teach students about topics like open-loop / closed loop control, sensor fusion, and related important ideas in robotics. People will typically learn about such important ideas from a textbook, and then implement them in a “bottom-up” (ad-hoc) way on Arduino, Teensy, STM32, ESP32, and other microcontrollers. This approach is useful for rapid prototyping but makes it difficult to extend the project beyond the prototyping stage. RFT aims to fill this gap by taking a top-down approach in which controllers, sensors, and actuators can be plugged into a pre-existing framework in a way that is easily extended for more advanced work. (If you're familiar with ROS, this approach will already make sense, but whereas ROS is a massive package providing an entire operating system supporting multiple robots and programming languages, RFT is focused entirely on simple C++ firmware for a single robot.)

C++ Classes

RFT grew out my experience developing a simple toolkit for flight-control firmware of multi-rotor vehicles. At some point I realized that most of this code would work just as well with other kinds of robotic vehicles. The object-oriented approach of C++ made it straightforward to support other robot types through abstract classes:

  • The Board class specifies an abstract (pure virtual) getTime() method that you must implement for a particular microcontroller or simulator.

  • The OpenLoopController class performs basic functions associated with open-loop control, and specifies a set of abstract methods that you implement for a particular controller like an R/C receiver.

  • The Actuator class is an abstract class that can be subclassed for various kinds of actuators; for example, a multirotor mixer controlling a number of motors. In principle, you need only one Actuator object for each project, since it can be subclassed to control any number of motors, arms, grippers, etc.

With these three classes (Board, OpenLoopController, Actuator) you can implement a traditional vehicle like an R/C car, that involves no closed-loop control. For most projects, of course, you'll want to add closed-loop control via sensors. Hence RFT also provides the following abstract classes:

  • The ClosedLoopController class specifies an abstract method modifyDemands() that inputs the robot's current state and outputs an array of floating-point values representing how that controller affects the demands. (For example, an altitude-hold controller for a quadcopter would use the 'copter's altitude and vertical velocity to adjust the throttle demand.) If you're mathematically-minded, you can think of a closed-loop controller as a function from a (State, Demands) pair to Demands: ClosedLoopController: State × DemandsDemands

  • The Sensor class specifies abstract methods ready() for checking whether the sensor has new data avaiable, and modifyState() for modifying the vehicle's state based on that data. If you're mathematically-minded, you can think of a sensor as a function from states to states: Sensor: StateState

Together, these classes interact as shown in the following diagram:

PID controllers

Because PID control is the most popular kind of closed-loop control, RFT sub-classes ClosedLoopController with a PidController class.

Other useful classes

  • A TimerTask class that allows you to run different processes (closed-loop control, serial communication) at different rates.

  • A Motor class that allows you to specify the hardware pin(s) for your motor(s), with individual motor types (brushed, brushless) supported through sub-classes. To spin a motor, you specify the index (id) of the motor you want, along with a speed normalized between 0 (off) and 1 (maximum possible speed), allowing you to avoid worrying about the low-level signal details.

  • A Debugger class providing a C-like printf method that works with the Board class to direct debugging output to the Arduino Serial Monitor (for Arduino-compatible boards) or an on-screen display (for simulators).

  • A Filters class providing static methods for simple filters (complementary), a class for Low-Pass-Filters, and two classes for the Quaternion-filtering algorithms Madgwick and Mahony. (Because I have not had much need for Kalman filtering in my robotics work, I did not include a Kalman filter class here; but I do have an implementation of this filter in another repository).

Serial communication

For serial communication, RFT relies on the lightweight Multiwii Serial Protocol (MSP). The SerialTask class contains code for parsing arbitrary MSP messages. By writing a subclass implementing the dispatchMessage() method of this class, you can support the messages you want for your robot. To help facilitate creating such messages, RFT provides a parser generator program that emits MSP-handling code in C++, Java, and Python based on simple JSON message specifications.