Mechanical Odometry and Speed Control

In addition to steering the car, the autonomous control system also needs to control the speed of the electric drive motor.  Since the motor in my car is controlled by an electronic speed control (ESC), and since ESCs are designed to use as inputs the same signals that control servos, the Arduino servo library can control the ESC the same way it controls the steering.  However, the input to the ESC only controls how much electrical power is sent to the motor and this only indirectly controls the speed.  If the motor is lightly loaded, the motor will spin fast for a given amount of power but, if heavily loaded, the motor will soil more slowly or, in cases of very heavy load, or very light power, not at all.  This was undesirable, as the car's control system should be able to know, and control exactly how fast the wheels were spinning.

To accomplish this, the car needs some way to sense how fast the motor is turning.  Then, since the car has a fixed gear ratio and wheel circumference, it should be able to compute its speed.  Some of the competing cars in prior years of the AVC have used optical encoders mounted to the wheels to readout speed, but I rejected that approach as a bit  too complex for my tastes.  Instead, I decided to use a device called a Hall Effect sensor to count the rotations of a magnet mounted to some portion of the drive train.  Sparkfun sells an inexpensive Hall sensor, the Melixis US1881, for less than $1.  This particular Hall sensor is a "latching" type which switches the output signal each time the magnetic field reverses from North to South, or South to North.

I originally thought about directly measuring the motor rotations, but could not find a reliable and way to mount a magnet onto the drive shaft.  However, after opening and examining the car's main gear assembly, I realized that the spur gear would be a perfect place to mount two, small rare earth magnets, as is shown in the following photo:

This approach may not be possible on all RC cars, but the spur gear on the Torment contained two areas near the edge where I could drill out and then press fit in two cylindrical, axially magnetized, neodymium (NdFeB) magnets (to be sure they stayed put, I also secured them with a dollop of superglue.)  However, since the latching Hall sensor I chose requires N/S flux reversals to switch, I mounted one magnet's North pole facing out and the other one's facing in.  So, with this arrangement, the Hall sensor would output a square wave with a period equal to one rotation of the spur gear.  After applying this mod, the reassembled gear housing now looks like this:

The second problem was figuring out where to mount the Hall sensor.  I thought about placing it inside the gear housing, but found that there was no room for a mounting bracket to hold the sensor close enough to the spur gear to operate properly.  Then, after examining the whole gear assembly a bit more closely, I realized that the backside of the housing for the spur gear had indentations that were in close proximity to the spur gear with the separating distance consisting of only plastic that would not impede reading the magnetic field.  So, as step one, I soldered the US1881 to a small, 3 pin JST connector, like this:

Then, as shown below, I used some 15 minute epoxy, I glued the JST connector to the differential section of the gear housing in a way that held the Hall sensor in close contact with the back side of the spur gear housing.  Once the epoxy hardened, I wired up the cable to the male portion of the connector and plugged it in to the glued in female section.

The plug is wired so that the Red wire supplies 5 volts, the Black wire is Ground, and the Yellow wire is the output signal (with a 22 K pull-up resistor added in another portion of the circuit.)

Measuring Speed and Distance

The next step is to create code that can read the signal from the Hall sensor and compute the car's current speed while also keeping track of the distance it has traveled.  To measure distance, we simply need to count the pulses over time and then use a formula that converts counts into distance.  Since the Hall sensor is counting the revolutions of the spur gear, we need to know the gear ratio for the portion of the gear train that connects the spur gear to the wheels.  In the Torment, this ratio is 2.6 to 1, which means that 2.6 revolutions of the spur gear translates into one revolution of the car's rear tires (assuming no differential slip, as the Torment does have a differential gear train.)  Or, put another way, one revolution of the spur gear will rotate the tires 1/2.6th of a revolution.  I measured the circumference of the tires on the Torment at 13.25 inches so, by dividing 13.25 by 2.6, we can compute (after rounding up) that one revolution of the spur gear will move the car about 5.1 inches.

The most efficient way to process the input from the Hall sensor is to connect its output to one of the Ardunio's external interrupt pins (if you're new to interrupts, I recommend reading this tutorial.)  Since the car is now using an Arduino Mega, there are 6 external interrupt lines available for this task but, alas, 4 of these inputs share pins with the hardware serial connections, which I've already used.  And, the other two external interrupt pins, 2 and 3, are covered over by the LCD shield.  However, there is another type of interrupt called a "pin change" interrupt that is available.  Actually, quite a few of the I/O pins on an Arduino have this capability, but use of this feature on the Mega is not well supported, or documented so a bit of direct fiddling with some of the Mega internal control registers is required to use them.  I chose to use pin change interrupt PCINT23, which is connected to, and shares its function with the Mega's Analog input 15 pin.  The setup code required to use this interrupt is demonstrated by the following test program:

#define LED  13

volatile boolean state = true;

void setup() {

  pinMode(LED, OUTPUT);

  pinMode(A15, INPUT);

  PCICR |= (1 << PCIE2);          // Enable change interrupt for PCINT23:16

  PCMSK2 |= (1 << PCINT23);       // Enable interrupt on PCINT23 (Analog pin 15)

  sei();

}

void loop() {

  digitalWrite(LED, state);

}

ISR (PCINT2_vect) {

  state = !state;

}

If the output of the Hall sensor was connected to Analog input pin 15 and the spur gear was spinning, you would observe the LED on the Mega board (it's a built in feature on a standard Mega board, and on most Arduinos) blinking on and off.  What's happening is that each time the output from the Hall sensor changes from high to low, or low to high, the special interrupt service routine (ISR) is called.  This code, which is the block of code after the loop() section that looks a bit like a function, then toggles the "state" variable on and off with each interrupt.  Also, notice how state is defined as a volatile variable.  This extra declaration is needed to tell the compiler to generate special code in the loop() function that will always check the state of state each time through the loop (without this declaration, the compiler would assume state never changed because it's set to false in its declaration and good compilers like to "optimize" code in this way.)

While the above code simply demonstrates how to implement a pin change interrupt, it should be fairly obvious how to convert this code into an odometer.  For example, instead if toggling the state of state, the code could increment a volatile int variable named halfRevs.  The value of halfRevs would then increase by one for each 1/2 revolution of the spur gear because the passing of each of the two magnets past the Hall sensor would cause a change in its output and trigger an interrupt.  Then, for convenience, the following function could change halfRevs into inches traveled using a simple formula:

float toInches (int halfRevs) {

  return (5.096f / 2.0f) * (float) halfRevs;

}

Likewise, similar functions could be written to convert halfRevs into feet, meters, or whatever unit is convenient for the navigation task.

Controlling Motor Speed

<more soon>