We will be closed on November 25, 2021 and November 26, 2021 in observance of the Thanksgiving holiday. We will resume normal operations on November 29, 2021. Wishing you a safe and happy holiday from all your friends at SparkFun!
A classic control systems challenge is building an inverted pendulum robot. Today we'll discuss the theory behind the project.
A classic control systems challenge is building an inverted pendulum robot. Today we'll discuss the theory behind the project along with some practical tips on building one yourself!
The actual chassis of your robot must be carefully designed if you're going to have any hope of getting it to balance.
First, let's talk about the structure of a balancing robot. My experience with several attempts at building one has taught me one very important thing: rigidity matters. Your robot needs to be stiff so that it doesn't flex much as the wheels try to balance it. If it can flex too much, you'll get a whip-crack-like effect where a wave traveling up the structure causes the top to "crack" and throw the weight hard in the direction where you want it to go, causing a serious amount of overshoot.
The next point is counter-intuitive. You want the robot to be tall, with as much weight located as far from the axis of the wheels as possible. The taller the better, and the higher the center of gravity, the better. To understand why this is so, a simple experiment can be done. Grab a broom, or a mop, or something similar. Try balancing it vertically on your palm with the bristle end up. Pretty easy, right? Now try it bristle end down. Much harder, isn't it? The principle is no different.
You need fast motors to make this work. Our fastest Actobotics gear motor is just fast enough when run on a 7.4V two-cell LiPo battery to keep my robot upright, but they aren't fast enough to recover from a good shove.
You also want motors with minimal backlash. Backlash is the amount that a motor has to turn before the wheel starts to turn in the direction the motor is trying to drive it. Backlash is caused by slop in the gearbox, and it causes a real problem in a balancing robot. You can prevent the issue by using a motor with a planetary gearbox, which typically has lower backlash than a traditional gearbox.
In general, the bigger the better. There's a limit to this, of course, because a larger wheel requires more torque to turn, and you have a finite amount of torque to work with. Still, it makes good sense to use larger wheels rather than smaller ones. I used our 5" precision disc wheels, and they're terrific.
The electronics can be surprisingly simple.
Believe it or not, there are options here. The overall goal is to measure the angle of the robot and run the wheels in opposition to any motion such that the bottom of the robot is moved under the top of the robot as the top tips. There are at least two ways to do this: via an inertial measurement unit (IMU) or by measuring the distance between a fixed point on the robot's chassis and the ground.
By using a sensor such as an ultrasonic range finder, LIDAR or infrared time-of-flight sensor, one can determine the tip in the robot's chassis with some simple trigonometry, if one knows the ideal distance from the sensor to the ground. Of course, this has its drawbacks, chiefly that rough or angled surfaces will cause the sensor to have trouble getting an accurate measurement. However, if your only goal is to get some experience with the basic concept, this can be a very attractive method.
An IMU is a sensor containing two or more motion sensors. For our purposes, we need a gyroscope and an accelerometer. A gyroscope measures rotation around an axis, and an accelerometer measures acceleration (in this case, we're concerned with acceleration due to gravity). The reason we need both types of sensor is due to the shortcomings of each type. A gyroscope tends to drift over time, causing spurious readings of motion. An accelerometer is sensitive not only to acceleration caused by gravity but also acceleration caused by motion. Together, however, these sensors can have their flaws compensated out, thus providing an accurate measurement of angle with respect to the gravity vector.
I used an LSM9DS1 IMU board for my robot. I like the I2C communication, as it's more accurate than reading an analog sensor and requires fewer pins than SPI.
One further note on the topic: it doesn't really matter where your IMU is mounted. A lot of people go to lengths to mount the IMU as close to the wheel axis as possible, but that's not really necessary, especially if you heeded my advice and made your robot's chassis nice and rigid.
It turns out that the math required to keep a balancing robot upright isn't terribly awful, and an Arduino-class processor can handle it. I've had some success using our SAMD21 Mini Breakout and our FreeSoC2, but if you look around on the web you can find examples using Arduino Uno or similar boards.
You'll obviously need a motor controller. Some kind of H-bridge-type driver is necessary, so the motor can be driven forward or backward. I used our Serial Controlled Motor Driver because it can be controlled over I2C, minimizing the number of connections needed.
You want a big battery because it can then function as ballast to raise the center of gravity. I used our 7.4V two-cell LiPo battery mounted at the highest point of the chassis. An important caveat here: make sure you monitor the voltage of the battery! I ruined a couple of these batteries by over-discharging them before I added a voltage monitor circuit and warning LED to my robot.
The heart of the robot is, of course, the software that does the calculations to keep it upright. We're going to assume that you've decided on an IMU-based approach.
As mentioned above, we have two types of sensors on the robot (even though they are colocated in the same physical package): a MEMS gyroscope and a MEMS accelerometer. By clever math, these two sensors' outputs can be synthesized into a single value: the angle between vertical and the current rotational position of the robot. The means for this synthesis is an operation called Kalman filtering. In a nutshell, a Kalman filter uses a probabilistic model of where the robot was a moment ago combined with the input of the sensors to create a probabilistic output for where the robot is now. If you want to read more on how a Kalman filter works, I suggest this page for having a solid explanation clearly demonstrated in pictures. The actual math of the Kalman filter is beyond the scope of this article.
Thanks to the magic of the Kalman filter, we now have a good model of where our robot is in terms of angle from normal. We need to then calculate what motor response we need in order to return it to normal (i.e., upright). To do this, we use what is called a PID loop. PID stands for Proportional Integral Derivative, for the three components which make up the overall directive to our motor. The proportional portion is some constant times the error currently present in the system. To this, we add our integral portion, a constant times the sum of the error over the history of the system, and our derivative portion, which is a constant times the current rate of change of the system. By tuning these three constants, we can create a system that responds to perturbations in a controlled and stable manner. Tuning a PID loop is beyond the scope of this article.
Here's a video of my robot, teetering along.
I suspect that a great deal of the instability of the robot is due to motor backlash, as it allows a great deal of time (relatively) to elapse between the instigation of a corrective action and the actual corrective motion beginning. I'd like to try the robot with planetary gearbox motors to see if that removes some of the jittering motion, but that's outside of one of my initial constraints, which was that the robot should use only parts sold by SparkFun. I'm not sure why it does the thing where it suddenly takes off in one direction and then falls over. If you have any ideas, I'd be glad to hear them!
What do you think? Have you ever tried to build a robot like this? What was your experience? Would you like us to provide a more formal example, with sample code? Leave us a comment below!