RADU: Motor Controller Software for Arduino and Raspberry Pico
Building a robot is a unique project with several design decisions. Starting from the general chassis, sensors, actuators and visuals down to the concrete microcontroller, libraries and programming languages. Specifically, the later encompasses Arduino, Teensy, ESP32, Raspberry Pico and C, C++, Python, MicroPython and Lua.
This article presents a compact overview about all options you have when working with Arduino/C or a Raspberry Pico with MicroPython. It lists and explains the available libraries, and discusses for which purposes you can use them. Furthermore, it also distinguishes between ROS1 and ROS2 compatible libraries.
This article originally appeared at my blog admantium.com.
The Robot Operating Software, Version 1, is a widely used robotics middleware standard used in hobbyists and industrial robots alike. Several projects from the Community, featuring Arduino-based robots and ones based on the Raspberry Pi, can be found. The options for choosing a serial communication library are plenty.
A core package of ROS1 is the Rosserial. It supports several concrete microcontrollers, including the Arduino, STM32, Teensy. Effectively, this package is a set of C/C++ headers that you incorporate in your microcontroller code. Then, you can directly receive and send ROS messages using the imported ROS message format. The receiver, e.g. your single board computer, needs to implement a ROS server. Here, you can use either C++ or Python. There are several examples in the Github repository. I also recommend a great blog article about connecting ROS to Arduino with L298D to directly convert TWIST messages to motor controller PWMs.
When you want to combine a simulation of your Robot to a real-world robot that is visualized in Gazebo and RViz, then check the ROS Control framework. This framework provides an abstraction of your robot. On the top level, you have Controllers for aspects like the position of joints, or for concrete velocity and effort effects. These controllers read and write to a concrete robot via a hardware interface. The hardware interface is a C++ library that needs to run on the robot’s hardware. Using this to drive your robot requires you to write custom code that is compatible to this library.
Finally, the ROS Motor Controller Drivers documentation page details how to use this very specific set of motors:
- PhidgetMotorControl HC (1064)
- Roboteq AX2550 Motor Controller
- Robotis Dynamixel Servos
- Esmacat Motor Driver
- Chip Robotics
You can also use the default client languages C/C++ and Python to manually subscribe to topics and interface with your connected microcontroller.
Quite contrary to its name, Simple Drive is a powerful and complete solution with full ROS compatibility. It allows you to use a joystick for moving your robot in real time. This is achieved by starting a series of ROS nodes that will translate ROS topic for joystick controls down to Twist messages in the
/cmd_vel topic, and then transform these commands via a custom USB protocol to generate PWM on a connected Arduino device. The ROS side of this project is implemented in Python. The code running is a custom C firmware, in which you can embed other Arduino C code.
ROS2 is the successor of ROS1 with a focus on implementing industrial robots. The most profound change is the overall network architecture: Instead of a central master node, server independent nodes can be started and communicate with each other. The network stack is based on the Data Distribution Standard, an industry standard for real-time systems using a publish-subscriber pattern.
Although its available since 2017, there are not many projects from robot hobbyists that use ROS2, and also the options for establishing a serial connection to microcontrollers are limited. Instead, nodes should implement a DDS compliant network stack.
ROS2 does not provide a package similar to rosserial. A technical study how serial communication could be implemented is the ROS2 serial bridge. The basic idea: Incoming data from the ROS2 network is serialized and send to the microcontroller, and received messages are translated to ROS2 messages. However, this not a concrete library.
Alternatively, you can use the default client libraries like rclpy or rclcpp for writing custom code that reads and writes ROS2 messages.
When you want to use an Arduino compliant board, you can either use a serial connection from a host computer to Arduino.
MicroROS is a project to implement a complete ROS2 node in your microcontrollers. The experimental MicroROS Arduino branch extends Arduino programs with additional C/C++ headers for full ROS2 compatible message subscription and publication. Specifically, its provides as precompiled binaries that support specific Microcontrollers such as the Arduino Nano RP2040 Connect or the Teensy 4.1. The list of natively supported ROS messages is impressive.
The ROS2Arduino library support more powerful Arduino Boards with at lest 32KB RAM and a TCP/IP hardware. Internally, it is build on top of the Micro XRCE-DDS framework, which provides Serial, UDP or TCP connections. According to the documentation, it only supports ROS2 up to the Dashing Diademata distribution.
When Running ROS on your Raspberry Pi, you will need to write a custom wrapper for your ROS messages as there is not direct library.
The most commonly used library for programming the Raspberry Pico is RPI.GPIO. This allows easy access to the GPIO pins, including software-bases PWM that can be used to control external sensors. You can also interface any connected microcontroller directly to the Tx/Rx pins.
The second option is to use Pyserial. With only a few lines of code, you can create a serial connection over USB to any connected microcontroller.
Still experimental is U2IF, a very specific library to run MicroPython code on a host computer, like an Raspberry Pi, to which a Raspberry Pico is connected via USB. The MicroPython environment gives full access to the GPIO Pins and additional bus systems like I2C or SPI.
All of these options however do not have an ROS-compatible abstraction included, you will need to write your own wrapper. This means to run ROS nodes on the Raspberry Pi which subscribe to topics or publish to topics, and then connecting these nodes via serial to your microcontroller. Messages exchanged via serial could mirror the original ROS message, or you invent your own language.
Comparing the Libraries
The choice for connecting ROS/ROS2 to your microcontroller code are plenty. However, we can distil them into three categories:
- Embedded ROS: This category means to run ROS and the code to control sensor and actuator on the same device. In ROS1, you can use rosserial to obtain ROS message interfaces and access to publish/subscribe function. The actual message passing is done via the serial connection to a connected host computer, on which you need to setup a ROS node. This library works with many boards. In ROS2, you can use the experimental MicroROS library. You have similar options to work with ROS2 message formats, but sending and receiving messages is done best via UDP and TCP. Therefore, you need more powerful boards which are equipped with TCP/IP hardware.
- ROS Wrapper: In this category, you have a host computer to which the client microcontroller is connected. The host computer starts a ROS node that is responsible for reading, processing and writing ROS messages. The node code communicates with the connected microcontroller node via a custom message format that you invent. On the host computer, you would run rosc or rospy, and then add Firamata, RPI.Gpio and similar libraries that will implement your custom message format and send it to the connected microcontroller.
- Full Integration: The third category are complete frameworks that handle the ROS abstractions and the microcontroller interface. In this category, we can only place Simple Drive for ROS1.
So, which option should you use? If you want to be most flexible, you will use the ROS wrapper approach. This allows you to work with ROS1 or ROS2, and with any combination of host computer and microcontroller. However, you would need to write your own message handling code. If you want to work with an Arduino board and ROS1, use the embedded ROS approach with rosserial, a full compatible and mature version. If you use ROS2, try MicroROS.
Connecting your host computer with microcontroller that runs C/C++ or MicroPython, and make it ROS-compatible, provides you with plenty options. There are three categories. Embedded ROS means to work with microcontroller code that uses ROS abstractions directly: The message types, and publication/subscription to topics. The ROS Wrapper approach means to separate microcontroller code from the code running on the host computer. The host subscribes/publishes ROS messages, and communicates with the microcontroller via a custom message format. This approach gives you the most options to work with any microcontroller and framework, as well as to use either ROS1 or ROS2. The last option, full integration, is only available with one ROS1 compatible library.