Self-Balancing Robot

Final project for ENGR2410: Signals and Systems

Trent Dye
Trent’s Blog

--

The finished product.

From this project, I wanted to learn how to visually tune PID loops. What do proportional, integral, and derivative control look like, and how do you know when you’ve gone too far? Is that all you need in a control loop?

Video

A lot of this article is summarized in the video I made for this project:

Background

Stabilizing an inverted pendulum is an age-old controls project. The concept is simple, and success and failure are well-defined. If it balances, the control system works. If it doesn’t? Back to the drawing board.

The inverted pendulum is an example of using a control loop for stabilization, which is a common application of control theory. The same techniques that can be used to stabilize a pendulum (in other words, basic PID control) can be adapted to systems with different sensors and actuators.

Because self-balancing robots are so popular, I was able to use a bunch of existing projects, from YouTube videos to complete Instructables, to scaffold my project. Although I wanted to design every aspect of my robot, I borrowed a lot of tricks from these projects to start myself off.

The System

This is a software-controlled PID system. The system revolves around the PID controller, a Teensy 3.1 running Arduino. It receives feedback from the IMU and acts by sending pulses to the stepper drivers. Each time a the stepper drivers receive a pulse, they advance a single half-step, with 400 half-steps constituting one revolution.

Block diagram for my robot.

Components

A Teensy 3.2 acts as the brain of the system. Nearly any microcontroller would suffice — this one was merely chosen before it was realized that a separate microcontroller altogether would be required to pull data quickly enough from the IMU.

Feedback is provided by the MPU-6050, a 6-degree-of-freedom Inertial Measurement Unit (IMU). This unit was chosen primarily for cost purposes, as it is the only sub-$10 IMU with onboard digital motion processing.

NEMA 17 Stepper motors were incorporated into the design of the robot because they were the most powerful motors available to me in such short notice.

DRV8825 Stepper Motor Driver. Source: Pololu

The stepper motors are each controlled by a DRV8825 Stepper Driver. These are commonly used in 3D printers and light duty CNC control. This component was also chosen because of availability, but the DRV8825 is also the optimal driver for these stepper motors because it is able to provide the 1.7A of current that each motor is capable of using.

Speaking of power, I used 18650-sized Li-Ion batteries to power the robot. This was a necessary step in my mind, because it prevented me from needing to tether power to the robot.

Control Loop

To further explain how the system works, the components used in the project have been juxtaposed on the following control loop:

Source for base image: Wikimedia Commons

The ‘Reference’ can be seen as the setpoint of the control loop, a manually selected value corresponding to the angle at which the robot is perfectly balanced. Every time the control loop is evaluated, this setpoint value is compared to the IMU’s angular measurement. Proportional, integral and derivative control is performed on the difference between these two numbers.

The output of the PID controller is a desired speed to run the stepper motors at (which is also the “system input”). The system output is what literally happens to the robot as a result of the system input, as well as anything else, such as nudges from the operator. This physical state of the robot is what the MPU-6050 is able to measure and record back on.

Programming a PID Loop

One of the reasons why PID is such a ubiquitous method of control is because it’s so easy to implement. It can be implemented in as few lines as this piece of sample code below:

float previousError = 0; //(used by derivative control)
float setPoint = 0; //radians, set by the user
float sensorValue = 0; //radians, from the IMU
float speed; //output of calculatePID()
float integral;
void calculatePID() { float proportional, derivative;
float error = sensorValue - setPoint;
proportional = error;
integral += error * dt;
derivative = (error - previousError)/dt;
previousError = error; speed = proportional*kp + integral*ki + derivative*kd;}

As can be seen, proportional control is literally just the instantaneous error. At each time step the instantaneous error is added to the integral term. In control theory, this integrated sum is known as the accumulator. It’s simple to see what happens if the robot stays on one side of the setpoint for too long: the accumulator builds up and contributes more and more effort to getting the robot back to its setpoint. Derivative control stems from the instantaneous change in error from one error reading to the next.

Results

Unfortunately, things did not go quite as I hoped. The problems mostly stem from the IMU not being as reliable as I thought it would be.

Theory vs. Reality

In the real world, sensor delay and sensor error can be a real pain. In a control loop, the error value is trusted to represent the exact physical state of the robot. If this is not the case, the robot tries to account for a state that it’s already been to. This makes tuning the control loop almost impossible.

This was the reality of working with the MPU-6050. Yaw, pitch and roll were calculated values from the accelerometer and gyroscope data (using something like a Kalman filter, as well as the direction of gravity, to sleuth out how much the board had tilted). Unfortunately, the board did not respond instantaneously when tipped or jolted, and I would have to wait, sometimes upwards of half a second, for the value to equilibrate when the sensor was brought back to rest.

Even then, the exact values that the robot converged to weren’t reliable. As I wasn’t able to exactly align the sensor with the axes of the robot, rotating the robot in space to balance in a different direction would change the balancing equilibrium roll value. This was an extremely difficult problem to face. All of a sudden, the robot would be trying to balance itself to a position that wouldn’t make it balance in real life.

Tuning the PID Loop

I referred to Finn Haugen’s PID Control, chapter 4, as a primary source for PID tuning techniques. While many of these techniques were more complicated than I needed them to be, the text offered a lot of advice on visually tuning control loops.

Yet tuning the PID loop didn’t go as swimmingly as expected. Some methods of control — namely proportional and integral — responded better to the sensor delay than others — namely derivative. Given that derivative control is essentially supposed to “predict the future” to account for where the robot is traveling, with the sensor delay it was effectively trying to respond to events in the past.

Proportional control worked fine, allowing me to tune the robot to a reasonably high level before it oscillated out of control. The consideration with integral control was tuning it so that the robot didn’t try to recover too quickly, or else it would spin its wheels too quickly and lose traction with the ground. As described before, the sensor delay fundamentally prevented derivative control from having any positive effect on the system.

Conclusion and Further Research

As I have witnessed in others’ projects, the balancing robot can be taken so much further than what I have tried — and failed — to accomplish. Obviously, a primary goal would be to get the robot to work as expected, or to be able to give it P, I, and D control and have it respond to bigger perturbations.

A first step would be a mechanism for the user to change the angular setpoint of the control loop, and be able to set the wheels’ setpoints independently of each other. This seems to be the simplest means of achieving motion and rotation with the self-balancing robot. The robot corrects itself to tip slightly forward, and thus constantly needs to move its motors in that direction to keep up with the setpoint.

Cascade Loop Control. Source: ControlDesign.com

A second step would be to implement a PID cascade to stabilize the position of the robot instead of just its rotation. One problem that robots with a single stabilization loop face is stabilizing themselves on tilted surfaces. In trying to stay upright, they don’t stay still — seeking to be upright in space sends them downhill.

To thwart this, a second loop would be implemented using the robot’s velocity to measure error, compared to a velocity setpoint of zero. The robot would then be able to dynamically change its angular setpoint in order to bring the speed of the robot to its velocity setpoint. On a tilted surface, the robot would be able to find the natural balance point of the slope and seek that. The added advantage is true speed control; to get the robot to drive, the user would be able to give it a velocity, instead of an angle “guess”. The biggest test for this implementation would be commanding a speed on a flat surface and seeing whether the robot is able to maintain this speed while the ground below it becomes tilted.

Exploring control theory with a platform such as this one, the possibilities are truly endless.

References

--

--