Hardware required beyond the basic list:
Motor with an encoder. I am going to use one of the GM series of
motors from Solarbotics with a Wheel Watcher encoder. You will need to
make a header so the encoder outputs can be plugged in a breadboard.
1 sn754410 or l293 h-bridge
In robotics it is frequently overlooked how important it is to make a wheel/track/whatever turn exactly as fast as you want and to keep it turning at that speed no matter what happens.
For example, if a certain PWM duty cycle makes a wheel turn at a certain speed while driving your robot across linoleum it might result in a radicaly different RPM on carpet, sand or grass or going up/down a slope. This could result in bad odometry if you are doing based purely on time. It could also result in a change of course if the wheels are simultaneously on different surfaces. Finally, as the robot's batteries discharge and that can mean radically different speeds at the start and end of the run.
Of course, you could switch to godzilla torque motors that don't give a crap what surface your bot is on but
You could also switch to lock-antiphase PWM control which does a fairly good job but that technique is hard on battery power.
Of course, neither of these solve the problem they merely mitigate it. You bot could still wander off course it just might take a little longer.
To REALLY get the RPM you want you need to use feedback: you measure the RPM and adjust the power iteratively to bring the RPM back up or down to what you want.
There is an extremely common method for doing this called PID control. That's an acronym and it stands for Proportional, Integral, Derivative. I would NOT be exaggerating to say that there are hundreds of billions of PID controllers out there. It can control anything such as temperature of a room, gas pressure in a chemical plant, fuel flow in a car, navigation, anything where a computer/microcontroller/whatever can measure the value being monitored and adjust some parameter to move the value closer to what you want.
When I was trying to wrap my head around PID I was finding it quite difficult because all of the web pages out there seemed either too general which clouded the understanding or assumed the reader had a strong engineering or mathematical background. So for this page I am going to go into excrutiating detail and keep it very specific.
To keep it specific this example will be concerned entirely with driving a single Solarbotics GM3 motor with a wheelwatcher encoder attached. The encoder signal will be hooked up PB0 of a mega88 (pin 14 on DIP version). We'll be using the ICP1 function of that pin to make timestamps of state changes from the wheelwatcher.
Well, first, let's define some variables:
Before I discovered PID I was sort of on my way in this. I was reading the wheel's RPM, calculating the error and using that difference to apply to the PWM duty cycle.
Here's an example. Say I want 100 RPM, the current current duty cycle is 150 (out of 255; let's keep it to an 8 bit timer for now) and the sensor reads 80. The error is 20. If I add 20 to the current PWM duty cycle to make it 170 the wheel will speed up. Subsequent readings may indicate that the wheel is speeding up and (hopefully) the error for each change will get smaller and smaller until the error is 0 which means the wheel is going exactly the speed I want. The same goes if the wheel is turning too fast. The error will be negative value which means the PWM duty cycle will (hopefully) decrease until it goes away.
This is the P term in PID. It stands for Proportion. We will add a proportion of our error (in this case 100%) back into the system in order to make the currentrpm reading converge on our wantrpm.
To increase the flexibility of our algorithm let's make use of a constant that we will multiply the error by to find the value that we want to apply. Why would we do that? There are two important reasons.
Often, designers will just go with the P term and leave it at that. It might look like this in code form:
error = wantrpm - currentrpm; adjustment = Pconstant * error; dutycycle += adjustment;
Or in shorter form:
dutycycle = Pconstant * (wantrpm - currentrpm);
polling vs interrupts
keeping accurate time
measuring frequency vs pulse counting
adjusting the coefficients
We will use the motor and encoder plus software to measure a wheel's RPM and the software will do it's best to keep the motor running at the same speed even when we put a load on the wheel.