Microcontroller
Why a Microcontroller?
A microcontroller (MCU) is a small computer on a single chip, with its own processor, memory and input and output pins. It runs one program, usually without an operating system, so it can react to a signal within microseconds.
The robot's single board computer, a Raspberry Pi, runs Linux and ROS: mapping, navigation and the connection to your PC. But the standard Linux setup used here doesn't guarantee when an ordinary program gets its turn, because many processes share the processor. Linux can be configured for real-time work, but this setup isn't. Driving the motors needs exactly that guarantee:
- Counting encoder pulses: every pulse from the wheel encoders has to be counted, also while Linux is busy with other work. A missed pulse means a wrong distance.
- Speed control: the PID controllers that keep the wheels at the commanded speed have to run at a steady rate (see the low-level PID approach).
So the microcontroller does this time-critical work: it counts the encoder pulses, runs the speed control and drives the motors through the motor driver. Over USB, it receives the commanded wheel speeds from the Raspberry Pi and sends back the wheels' measured positions and speeds, computed from the encoder counts.
Which Microcontroller?
The firmware in diffbot_base/scripts/base_controller runs on a Teensy, a small board by PJRC with a 32-bit ARM processor. It's programmed like an Arduino, with the Teensyduino add-on or PlatformIO, so most Arduino code works on it. Two things make it a good fit here:
- Interrupts on every digital pin: the firmware counts the encoders with PJRC's Encoder library, which works best when both signals of an encoder are on interrupt pins. On the Teensy, all digital pins are; an Arduino Uno has only two, pins 2 and 3.
- Speed and memory to spare: a faster processor and much more RAM than the classic Arduinos, which leaves room for rosserial (see below).
The firmware has two PlatformIO environments, and CI builds both:
| Board | Robot | PlatformIO environment |
|---|---|---|
| Teensy 4.0 | Remo | teensy40 (default) |
| Teensy 3.2 | DiffBot | teensy31 |
Other boards, such as an Arduino
Only the two Teensy boards above are tested. Using another board, such as an Arduino Uno or Mega, means porting the firmware (pins, libraries, the PlatformIO environment) and then checking its memory use and timing on that board.
The firmware talks to ROS with rosserial: the board runs its own ROS node, which keeps message buffers and its publisher and subscriber tables in RAM. On the Teensy and the Mega 2560, rosserial uses 512-byte buffers in each direction and up to 25 publishers and subscribers; on the Uno (ATmega328P) it uses smaller 280-byte buffers. rosserial itself supports these boards, but the classic Arduinos leave much less room for the rest of the firmware:
| Board | Processor | Clock | RAM |
|---|---|---|---|
| Arduino Uno | ATmega328P | 16 MHz | 2 KB |
| Arduino Mega 2560 | ATmega2560 | 16 MHz | 8 KB |
| Teensy 3.2 | MK20DX256 | 72 MHz | 64 KB |
| Teensy 4.0 | i.MX RT1062 | 600 MHz | 1024 KB |
One attempt with an Arduino Mega and STM32 boards
In discussion #94, a port to an Arduino Mega 2560 kept failing with "Unable to sync with device", "Mismatched protocol version" and checksum errors. Tight memory was one suspected cause, alongside message rates, baud rate and the ported code, but the cause wasn't confirmed. Ports to STM32 boards with 64 KB and 128 KB RAM then connected, but still had sync and checksum errors, and the builder ordered a Teensy.
Without rosserial: a simple serial protocol
A different design avoids running ROS on the board at all. In ros_arduino_bridge, a ROS 1 project, the Arduino firmware only understands a few short text commands over the serial port (commands.h, handled in ROSArduinoBridge.ino), and a Python node on the computer translates between them and ROS topics; it also converts speeds from metres per second to ticks per PID cycle. The main commands:
| Command | Meaning |
|---|---|
e |
Read both encoder counts |
r |
Reset the encoder counts |
m <left> <right> |
Set the wheel speeds, in encoder ticks per PID cycle; the firmware's PID controllers keep them. The PID runs 30 times a second by default, so m 20 20 means about 600 ticks per second |
u <kp>:<kd>:<ki>:<ko> |
Set the PID gains; ko divides the PID output to scale it |
For safety, the motors stop by default when no new speed command arrives for two seconds. Because the board only parses short commands and keeps no ROS message buffers, a small Arduino is enough.
The same protocol also works with ROS 2: diffdrive_arduino is a ros2_control hardware interface for this firmware, and its author's fork of ros_arduino_bridge adds a command for raw PWM values (o <left> <right>). It's an option for DiffBot's ROS 2 firmware, which is on the roadmap.
Teensy Setup
The Teensy 3.2 microcontroller (MCU) is used to get the ticks from the encoders attached to the motors and send this information (counts) as a message over the /diffbot/ticks_left
and /diffbot/ticks_right topics. For this rosserial is running on the Teensy MCU which allows it to create a node on the Teensy that can communicate with
the ROS Master running on the Raspberry Pi.
To setup rosserial on the work PC and the Raspberry Pi the following package has to be installed:
1 | |
To program the Teensy board with the work PC the Arduino IDE with the Teensyduino add-on can be used. Other options are to use PlatformIO plugin for VSCode. How to install the Arduino IDE and Teensyduino is listed in the instructions on the Teensy website. Here the instructions to setup Teensyduino in Linux are listed:
- Download the Linux udev rules (link at the top of this page) and copy the file to /etc/udev/rules.d.
sudo cp 49-teensy.rules /etc/udev/rules.d/- Download and extract one of Arduino's Linux packages. Note: Arduino from Linux distro packages is not supported.
- Download the corresponding Teensyduino installer.
- Run the installer by adding execute permission and then execute it.
chmod 755 TeensyduinoInstall.linux64./TeensyduinoInstall.linux64
The first step can be used on the work PC and the Raspberry Pi to enable the USB communication with the Teensy board. Step two of these instructions are only necessary on the work PC to actually program the Teensy board.
Note
Make sure to download the Arduino IDE from the website and don't install it from the Ubuntu repositories.
The following video shows installation process, more instructions to setup the Arduino IDE can be found in the ROS wiki.
To check if the connection to the Teensy board works use these commands on the Raspberry Pi:
1 2 3 4 5 | |
And to see on which serial port it is connected use:
1 2 | |
If the output is empty it might be the case that the board is connected to another port like ttyUSB0.
Encoder Program
When installing Teensyduino new example programs are provided. One of them is to test Encoders. The code for the motor encoders uses it as basis together with a pubsub example from rosserial:
The program is encoders.ino in the diffbot repository.
After the program is flashed to the Teensy board it can be tested with the following procedure:
- Start a ROS master by executing
roscorein a new terminal. - Create a rosserial node using
rosserial_pythonpackage:
1 2 3 4 5 6 7 8 9 | |
In case of the following error, probably the wrong program is flashed to the Teensy board:
1 | |
Note
Note that the rosserial node needs to be stopped to flash new sketches to the Teensy board.
Each DG01D-E motor has two signal pins for its built-in encoder. For these, the Teensy pins 5, 6 are used for the left encoder and 7, 8 are used for the right one, see also the Teensy pinout.
The bread board view of Fritzing shows the connection schematic and is shown for both models in the following:
With one motor encoder connected to pins 5, 6, echo the /encoder_ticks topic:
1 | |
Rotating a wheel attached to the motor shaft 360 degree (one full turn) will increase the first value of the encoders array:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | |
The found value 540 for a full turn of the wheel is important for the hardware interface.
Base Controller
If you are working with Remo the recommende way is to use base_controller from diffbot_base/scripts instead of the encoders.ino.
Build instructions using Visual Studio Code including the PlatformIO plugin are shown in the following video:
When using base_controller you should use the low level PID controllers running on the MCU.
Comments
Comments are GitHub Discussions, shown with giscus. Loading them connects to giscus.app (hosted by Vercel, USA) and GitHub (USA); you need a GitHub account to comment.