Professional Documents
Culture Documents
An Introduction To Encoders
An Introduction To Encoders
My bot won't drive straight! Such is a common problem in robotics. Unfortunately, two
motors will never be identical, two surfaces will never have the same coefficient of friction,
and two wheels will never be exactly the same diameter. So what is a roboticist to do? We
need to incorporate a form of feedback into our motor control. Encoders are one possible
solution to the problem of a bot that won't drive straight, as well as these other problems:
Encoders send out pulses related to rotation of the motor shaft. Unfortunately, you often
want to control the speed, which is the distance/time, or the distance, which is the
summation of pulses. We need to be able to count the pulses now. We have to realize that
our pulse rate may be very high. If our wheel is going even 300RPM, our very simple
encoder will give us 500 counts per second. We don't want to eat up all the processing time
on our delicate micro controller - so we will use an interrupt! (see my interrupt tutorial)
Code:
// sample arduino code for getting relative speed from an encoder
// this code will print the # of encoder ticks each 1/2 second
volatile int counter = 0;
void setup(){
attachInterrupt(0, count, RISING);
serial.begin(19200);
}
void loop(){
counter=0;
delay(500);
serial.print(counter);
serial.print("\n");
}
void count(){
counter++;
}
Theory of Operation - Quadrature Encoders
While a simple single-channel encoder can give us speed, we need more data to extract
direction of rotation. While you may not always need to know the direction from the
encoder, it can come in handy (for instance, if you are parked on a hill, it might be nice to
know you rolled backwards rather than forwards).
A quadrature encoder has two channels of output, commonly referred to as A and B. The
two channels are 90 degrees out of phase with each other, by decoding the pulses coming
out of the encoder, we can determine both speed and direction of rotation from our encoder.
For instance, if you have an interrupt that triggers on a positive change in A, you can
choose how your count changes based on B: if B = 0, rotation is CCW, if B = 1, rotation is
CW, as seen in Figure 1 (your particular encoder may be reversed as far as CCW or CW).
You can do a lot more to increase the resolution of the encoder. The Nubotics sensors I'll be
using below give out only 32 pulses on the A channel for each rotation. However, if we
then add the B channel, and pay attention to our truth table below, we can see that by
keeping track of what B was last time, we can get much better resolution. Specifically, if
we are interrupting on the rising edge of A:
and B is logic high, and B was logic high last time, we've gone clock-wise, but
we've actually gone through a full wave cycle of the A channel. This is actually
twice as much travel as if we had reversed direction.
and B is logic high, but B was logic low last time, we've still gone clock-wise, but
we've reversed direction, and thus gone only 1 click in distance.
and B is logic low, we've gone counter-clockwise. Based on the previous B value,
we can decide if we've gone 1 or 2 steps.
Using this, we've doubled our resolution. As they say on TV though, but wait, there's more.
If we interrupt on both the rising and falling edges of A, and do a similar decoding, we can
double our resolution again.
Of course, since I'm using these Nubotics sensors, it's somewhat academic to decode the
A/B signals, since these awesome encoders provide an alternative Clock + Direction output
that is already decoded as described above. But, we should at least discuss this, since not all
encoders are quite as nice.
Figure 1 - Quadrature Encoder output and truth table. A quadrature encoder has two channels, A and B,
which are some distance out of sync with each other. If we have an interrupt routine that runs on the rising
edge of A, we can determine the direction of rotation by reading B. When our encoder turns in one direction,
the interrupt will be triggered on the red dots, times at which the B channel is low. If our encoder turns in the
opposite direction, it will trigger our interrupt on the green dots, when B is high.
Figure 2 - A typical diagram of a feedback loop. Our set point and feedback go into a summation node, the
output of which is our error, and is sent to our controller, which produces an output.
When you drive down the road, the speed limit sign is your set point. You are a controller
that adjusts the car's speed, based on the actual value you read on your speedometer. If
your car has cruise control -- you've already used a closed-loop feedback device to regulate
the speed of a motor!
There are of course caveats with this. We have several things that may arise that can be
detrimental or downright destructive to our system. First and foremost, a poorly configured
feedback loop can easily go into oscillation, if it is really bad the oscillation could build to
infinity. It will be seen that this type of situation often happens if we try to adjust too
quickly. On the other hand, if we adjust too slowly, we will probably never get our actual
value to equal our set point value. There are also a multitude of physical issues that will
plague feedback loops. Motors don't typically have linear speed control, especially on their
low-end speeds. Friction on wheels, inclines, and other properties of the environment cause
spurious changes in the actual value -- these spurious changes can also set off oscillation in
the controller. For all of these reasons, creating the function or controller can be difficult,
time-consuming, and involve quite a bit of tuning.
Figure 3 - Diagrams of: (A) overshoot and oscillation, (B) undershoot and residual offset, (C) near-perfect
convergence.
Proportional term: this term adds a proportion of our error to the output, it is
typically what a novice would use for their entire feedback function. Typically just
Kp * error.
Integral term: this is used to adjust out any residual offset we might have. If our
controller typically approaches the value we want, but typically never actually
makes it all the way to the value, an integral term may be used. Many controllers are
actually just P-D controllers, they omit an Integral term. Typically just Ki *
sum(errors).
Derivative term: effectively fights back against overshooting, by adding a value
relative to how fast we are approaching our set point. Typically just Kd *
(error_this_cycle-error_last_cycle).
We will examine the control of a motor's speed, however the same process can be applied
to anything really: position control, temperature of an oven, etc. When using an encoder,
we have to count very delicate and often high-speed pulses, and thus we really need a
dedicated controller that is running in real-time. This is best achieved using a
microcontroller with interrupts to count pulses, and some hardware timer to set your time
intervals if doing speed control. Our example will use an Arduino, due to it's low cost, and
easy availability.
The first two parts of this are almost trivial. If using a quadrature encoder, the code for #2
may be more complex. #3 is where we really earn our pay though. For now, we'll stick to a
non-quadrature encoder, to keep the example code simple.
For this example, I am using an Arduino-based robot, the controller is actually an AVRRA
Mini board configured as an Arduino. I'm using GM8 motors and the associated wheels,
powered by an SN754410 motor driver. My encoders are the Nubotics ones designed
specifically for these motors. The encoders need just a simple 5V supply, and have both an
A/B style output or a clock+direction style output. They give 128 counts per rotation when
using the clock+direction output.
Figure 4 - The miniBot
There is an important question we need to answer: how often should the loop run? The
more often it runs, the more accurate your speed will be (in theory), however, the speed at
which ticks are generated by the encoder will govern the top speed of the loop. I'm going to
cheat, and use the Clock+Direction output on the encoders to get high resolution without a
lot of processing in my Interrupt routine. My encoders will therefore give 128 clicks per
rotation of the wheel doing a full rotation, RPM should be around 70-80, so we can expect
about 160 clicks per second. For now, we'll use a 5hz update rate, it should give us enough
ability to regulate speed, while still having fast enough reaction time to actually regulate the
speed.
It's tough to regulate speed when you're need your top end speed, since you don't have any
ability to go faster. Thus, we'll try to go about 100 clicks per second (which turned out to be
about a PWM value of 200 out of 255 or 80% speed on this robot). The demo code below
implements just a proportional control. It manages not to oscillate too badly (although
differing flooring materials may cause issues). The code below will run 3 trials. The first
trial has a low kP, and it doesn't ever make it to the desired speed. The second trial is too
high of a kP, it overshoots somewhat (although does manage to level out well). The third
trial is just about the right kP.
Code:
// Encoder Demo - M. Ferguson
// This sketch controls the speed of our motors. Only the code
// for the left channel is shown here.
// motor speeds
int lSpeed;
int rSpeed;
// main loop
void loop(){
while(millis() < last + FRAME_LEN);
// find error, and then reset counter...
int error = lSet - lCount;
lCount = 0;
last = millis();
// do our update
int nextSpeed = (kP * error)/100 + lSpeed;
drive.set(nextSpeed,0);
lSpeed = nextSpeed;
// output some data
Serial.print(nextSpeed);
Serial.print(",");
Serial.println(error);
For slightly larger robots, Lynxmotion sells thier 37MM gear motors with shafts on both
ends. The back end can be used with a very high-end US Digital encoder. These encoders
are actually so fine of a resolution that they typically give several thousand clicks per inch,
even with large wheels. Often, this is just too much data, and your poor micro controller
can't do anything else but count the rotations of the wheel. An encoder divider is a circuit
board that can be used to reduce the number of counts your encoder gives off, Banebots
sells a fairly inexpensive one that will also translate your A/B signals into clock+direction.
You can also often find surplus motors with built in encoders. Unfortunately, many times
the pin-outs are not that well known.