Está en la página 1de 7

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:

The need to turn a specific angle

The need to go forward/backward a specific distance

Complex path planning (not covered here)

Types of feedback for speed control of small robots


Most people are familiar with hobby servos, the most cheap and widely-available of all
motors that incorporate a feedback loop. However, servos only have a small range, about
180 degrees, due to the use of a potentiometer as their form of feedback. If we want to have
our robot go more than a few inches, we need motors that can turn a full rotation, but still
have feedback. What we need is an encoder that measures how much the motor or wheel
turn. Encoders often use either small magnets or light.

Do I really need encoders?


This will depend on your environment. If your robot can reference a nearby object, such as
a wall, then maybe not. You'd be surprised how much can be accomplished by just
following a wall with an IR sensor.

Theory of operation - Encoders


We will first look at the single-channel encoder, for ease of understanding, we will say it is
a magnetic one. The encoder would be constructed of a small magnet on the motor shaft,
and a hall-effect sensor that puts out a pulse every time the magnet passes it. Our simple
encoder gives us 1 pulse for each rotation of the motor shaft. If our motor then has a 100:1
gearbox which the wheel is connected to, we will get 100 counts per rotation of the wheel.
(this is a somewhat theoretical sensor, nearly all encoders today will be Quadrature
encoders and have 2 channels, but we can get this type of a sensor by ignoring the second
channel)
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.

Theory of operation - Feedback loops


The general idea of a feedback loop is fairly simple. We have some set point value (for
instance, the desired speed), and we have an actual value read from some sensor (the
current speed). From this, we can compute an error. We then need to create a function that
will adjust our outputs in order to make our error smaller. In technical feedback control
terms, the function is known as a controller, and the device (in our case a motor) that acts
on the output value is known as a plant.

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.

Typically, to make sure feedback is as responsive as needed, without overshooting,


designers use a PID loop. PID stands for Proportional, Integral, and Derivative. Each of
these are a smaller controller inside our control function. Each of our portions as an
associated K factor, the amount it adds to the output, for instance, our proportional term
might have a Kp of 0.5, and our derivative term might have a Kd of 0.2. If we have
computed the error, we now have to compute how much we change our output value:

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 Speed Control Problem


Controlling the speed of a motor is probably the most common application of wheel
encoders. A quick run-down of what our software will have to do:
1. We will store a set point value in a variable
2. An interrupt will count pulses from the encoder, creating our actual value
3. A second time-based interrupt will occur at some frequency (say, maybe 5 or 10hz),
this will trigger our function to run and update our outputs
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.
// My motors library is available at svn.blunderingbotics.com
#include <Motors.h>
Motors drive = Motors();
// ms for each frame, this gives us 5Hz update rate
#define FRAME_LEN
200
unsigned long last; // last time our loop ran, to do 5hz update
// left/right actual values, used by our ISR to count ticks
volatile int lCount = 0;
volatile int rCount = 0;
// left/right desired values
int lSet;
int rSet;

// motor speeds
int lSpeed;
int rSpeed;
// PID tuning parameters
int kP;
int trial = 0;
// enable our interrupts
void setup(){
Serial.begin(38400);
// Encoder clock output is tied to Digital2, call ISR function named
left.
attachInterrupt(0, left, RISING);
lSet = 20;
// we'll try to go about 50rpm
last = millis();
lSpeed = 0;
kP = 20;
// trial 0 - kP too low, residual offset
}
// 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);

// our demo code: change our kP over time


if(millis() > 30000){
// we are done
drive.set(0,0);
while(1);
}else if(millis() > 20000 && trial == 1){
// trial 3 - just right!
drive.set(0,0);
kP = 100;
lSpeed = 0;
trial = 2;
}else if(millis() > 10000 && trial == 0){
// trial 2 - kP is too high, oscillates
drive.set(0,0);
kP = 1500;
lSpeed = 0;
trial = 1;
}

// only regulating left speed right now...


void left(){
// we want this to be fast, so we'll avoid digitalRead()
// digital 8 is the direction channel, but that is actually AVR PB0,
so:
if(PINB&0x01)
lCount++;
else
lCount--;
}

Some commonly available (and awesome) encoders


While you could go the route of printing your own encoder disks, and building a circuit that
works, there are quite a few nice packages already out there. Possibly my favorite unit is
the Nubotics encoders for the Solarbotics motors (they also make a version for continuous
rotation modified servos). These encoders fit right onto the motor case and are about just
the right resolution for many common tasks people want to do with small robots.
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.

También podría gustarte