How to make a Balboa robot balance, part 3: encoders

Posted by Paul on 30 March 2017

This is the third post in a series about how to make a Balboa 32U4 robot balance. Last week I talked about inertial sensors, especially the gyro. In this post I will talk about the Balboa’s built-in encoders, which allow accurate measurements of motor speed and distance.

To get your Balboa to balance, you will soon need to create a balancing algorithm, a program that takes sensor input and computes the appropriate motor speed settings to keep the robot upright. So far our only inputs, both from the gyro, are the rate of rotation and current angle of the robot. These are not quite enough to make a good balancer. To see why, suppose that your program tries to balance by holding the angle at a constant 90°. If your definition of 90° is even slightly off-balance, the robot will need to keep accelerating, driving faster and faster to maintain it, until it reaches top speed or hits an obstacle. You might be able to account for this by using the motor output settings themselves as an input to your algorithm, but this is difficult, especially at the low speeds used for balancing. Also, even if you can avoid accelerating, your robot will gradually drift in one direction or the other. The Balboa’s encoders are valuable additional sensor inputs that allow you to measure how fast the wheels are actually turning, so you can directly control acceleration and drift. As a bonus, encoders are great for driving straight, precision turns, and navigation.

The encoders consist of a magnetic disc attached to the shaft of each motor, and two Hall effect sensors on the PCB directly under each disc:

Each disc is (invisibly) magnetized with three north poles and three south poles; during a rotation of the motor shaft, each of the six poles rotates across the two sensors, giving 12 “counts” per revolution. Each rotation of the wheels then gives a number of counts that is 12 multiplied by the gear ratio. These counts are the basic units of your encoder inputs, so it’s a good idea to learn what they correspond to in terms of actual distance traveled. For a gear ratio G and wheels with diameter D, the distance per count is  pi D // ( 12 * G ). Using the 80mm wheels and the gear ratio table from my post about selecting mechanical parts, you can compute the distance per encoder count (in mm):

Gearmotor 30:1 50:1 0.24 0.14 0.10 0.28 0.16 0.11 0.33 0.19 0.13 0.38 0.22 0.15 0.43 0.25 0.17

The values are all fractions of a millimeter, so for speeds of 1 m/s or more, you need to be able to read thousands of counts per second. Microcontrollers like the Balboa’s ATmega32U4 are great at this kind of task because of their support for interrupts, which are special functions in your code that execute immediately when a specified event occurs, suspending the rest of your code while they run. The Balboa 32U4 Arduino library includes interrupts that handle encoder counts automatically; to see the details, take a look at Balboa32U4Encoders.cpp and also read the section on encoders in the Balboa User’s Guide.

Since the library takes care of the counting, using encoders in a sketch is pretty easy. You just need to declare a Balboa32U4Encoders object and call its methods getCountsLeft() and getCountsRight() whenever you want to know the number of counts that have accumulated. However, there is a small complication: for efficiency, these methods return 16-bit unsigned integers, so with a maximum value of 65,535, they will overflow after your Balboa drives forward a few tens of meters. You should hope to eventually make it farther than that, so let’s look at how to save your encoder counts in nearly overflow-proof 32-bit integers. Luckily, there is another pair of functions we can use: getCountsAndResetLeft() and getCountsAndResetRight() set the internal number of accumulated counts to zero each time you check them, and they return the count as a signed integer that you can add to your own 32-bit variables distanceLeft and distanceRight:

Balboa32U4Encoders encoders;
int32_t distanceLeft;
int32_t speedLeft;
int32_t distanceRight;
int32_t speedRight;

void integrateEncoders()
{
speedLeft = encoders.getCountsAndResetLeft();
distanceLeft += speedLeft;

speedRight = encoders.getCountsAndResetRight();
distanceRight += speedRight;
}

As a side benefit, the change in encoder counts can be used as a measure of speed, so we store that as well, in the variables speedLeft and speedRight. Note these will only be a good measure of speed if integrateEncoders() is called on a regular period, with enough time between calls to accumulate at least a few counts. For example, if we call it every 10 ms, on a Balboa with 80 mm wheels, the 45:21 plastic gears, and 50:1 gearmotors, at 1 m/s we will measure a speed of 1 text( m/s) * 0.01 text( s) // (0.19 text( mm/count)) ~~ 53.

We now have four new inputs available for our balancing algorithm, which I will talk about in my next post.

Continue reading with Part 4: a balancing algorithm.