Simplicity is the ultimate sophistication. In the previous version of the self-balancing robot I used in total 3 microprocessors but have now designed and programmed on a faster ARM microprocessor. Greater processor performance provides a host of benefits:
Simple circuit diagram
Robust mechanical design because of fewer cables
No communication between processors is necessary which gives a very simplified code
A small RTOS takes care of scheduling (which did not work on AVR)
In short I am using cascaded PID controllers. The outer loop is regulating the speed of the robot which is measured with encoders on the motors. The internal loop controls the robot's angle, which is measured with an IMU. The outer loop has slow dynamics and the internal loop is much faster. Theoretically it should be possible to program a self balancing robot using only the inner loop but you get very easily a behaviour of the robot inching forward or backward across the floor because of the missing feedback from the wheel speeds.
To gain additional performance gain scheduling is used on the inner loop, which means that the regulator parameters will change depending on the robot's angle. A small angle near the equilibrium means conservative regulator parameters where only small adjustments are made to keep the robot standing. If the angle of the robot is close to falling, the aggressive paramter settings are activated in order to do everything to regain stability.
Cascade PID implementation theory
Likely all parameters will not work without being adjusted slightly. In the code there is a struct that can be put in setConfiguration(). Smarter, however, is to use a serial terminal to change parameters while the code is running. In this way there is no need to reprogram the processor every time you want to change a parameter. Open the Serial Monitor in Arduino. Test writing "print configuration" to the Arduino. If everything works as it should, it will respond by printing all parameters. All variables can be set in the struct Configuration from the Terminal. Just type "set" followed by the variable name and value. For example "set speedPIDKp 1.5". To activate debug type "set debugLevel 2". If you want to debug the IMU then type "set angleRawDebug 1". To turn off a debug variable write, for example, "set angleRawDebug 0".
Finding the right regulator parameters
Use a realtime plotter when you configure your controller parameters to make stuff less difficult.
In the video you can see that the robot stays at roughly the same spot on the floor and handles a lighter push without any problem. To achieve this I have two cascade PID controllers and low pass filter on both wheel speed and the robot's angle.
As an Arduino does not have a lot of processing power, I chose to use a total of 3 microprocessors:
A standalone Arduino Uno takes pulses from the tachometer on each wheel and converts it to a speed. The speed is sent to the main micro processor.
One more stand alone Arduino Uno to view and adjust parameters. Also drives the display. The parameters are sent to the main micro processor.
Arduino Leonardo is the brain. It receives information from the console and the engine speed sensor via I2C.
Basic control theory
Two cascaded PID controllers. The first has speed as setpoint (always 0 in this case) and angle as output. If the robot is moving forward, the controller will have a positive input value and send a negative output value. This means that the robot will want to lean back to slow down.
PID number 2 takes care of the robot's angle relative to the floor. The setpoint is the output of the previous PID controller. Normally the set point will range from -3 to 3. Zero corresponds to the robot standing completely straight up. The output signal is connected to the engine management system and range from -100 to 100. -100 corresponds to full voltage to the motors in reverse direction.
To get cleaner inputs I use FIR filters for both angle and wheel speed. Low-pass filter blocks rapid change (for example, sensor noise) from passing through.
For the angle relative to the floor, a IMU with accelerometer and gyro is used. A complementary filter combines the signals from these and provides an output signal that can be used.
I had the objective that all code for this project should be open sourced and available for download from github. The readme contains links to libraries I have used.
Improvements that could be made
Better parameters to the FIR filter. Much could probably be done on the wheel speed.
On-the-fly programming over the air with Xbee. This means that you can send new code to the robot without USB cable. Will make programming more flexible.
Radio control would be nice.
Building the arduino for sending speed from encoders via I2C
I now have enough parts to start to assemble the robot.
I had to drill out the holes slightly to get them to fit M3.
The accelerometer is as close as possible to the motor shaft.
To tighten all the nuts use a screwdriver, a piece of tape and then rotate the threaded rod. Hold the nut with your fingers and let the tool do the work. Otherwise, it takes a while to get it into all 24 nuts.