DPRG Events Shoptalk

Website design and hosting by NCC

Low Rider: My First Line-Following Robot

by John Wadley
1 July 1997


Robotics has become a very popular hobby with computer and electronics enthusiasts worldwide. Interest seems to be growing by leaps and bounds daily as more and more information is being shared via the internet. I've held a casual interest in robotics since I was in high school, but I didn't decide to take the plunge until recently when I ran across the Dallas Personal Robotics Group (DPRG) on the World Wide Web (WWW). This looked like a prime opportunity for a newbie like myself to learn something about robotics. My suspicions were confirmed when I went to my first DPRG meeting in February of 1997. The DPRG provided a place where I could learn from the robotics expertise of other members, contribute my own electronics expertise, borrow group books and equipment, get great freebies and have fun all at the same time.

Shortly after I joined the group, the DPRG decided to have a contest. I was still straddling the fence on how much I time and money I wanted to invest in this hobby, but decided I would give it a try. The contest had a very simple goal and didn't require a lot of special equipment or expertise in order for someone to compete. The goal was to build a robot that could move autonomously from a start area to a turn-around area and then back to the start area. The boundary was defined by masking tape and had physical walls on three sides. A masking tape "path" was included to allow more sophisticated, line-following robots to navigate the course. Judging was to be by audience response and/or time required to complete the course. Extra points would be given for line-following, visual or auditory effects, voice or sound activation and entertainment value. Despite my lack of robotics experience and lack of development tools, it seemed as if the gauntlet had been thrown down.

Contest Course:

 |          |                                   |          |
 |    ----------------------------------------------.    ^ |
W|          |                                   |    \   | |W
A|          |                                   |     |  | |A
L|          |                                   |     |4'| |L
L|          |                                   |     |  | |L
 |          |                                   |     |  | |
 |  START   |                                   |    /   | |
 |    ----------------------------------------------`    v |
 |          |                                   |          |

I enjoy a good challenge, but the odds seemed to be against me. I only had three months to get from ground zero to a working robot for the contest. I needed a good, simple strategy if was to have any chance of winning. I reasoned that my best chance was to go for the audience approval, to implement only what was necessary, and to use whatever tools or parts were immediately available. For audience approval, I felt that a line-follower would provide the sophistication to make the audience take notice. I could add lights, music or sound activation later, if I had time. To keep from re-inventing too many wheels, I decided to make the most of what was already available. I started with a Radio Shack RC monster truck that I had collecting dust on my shelf. Despite the fact that I have considerable knowledge in testing and designing uP's and DSP's, I did not own any development equipment for implementing a complex uP-based controller. I didn't have an EPROM programmer, either. I would have to resort to a simple behavioral controller made from standard logic gates that I could wire wrap myself.

The Low Rider Design.

The Low Rider My RC truck gave me the biggest jump start in building my robot. It provided the wheels, the motors and some control logic all implemented in a custom plastic chassis. If I could figure out how and where to insert my own control signals, all I would have to do is come up with my own sensors and controller logic. I started by pain-stakingly reverse engineering the electronics in the RC truck chassis. This was a very tedious job with only a multi-meter on hand, but that was made easier by the simplicity of the Radio Shack design. All the electronic traces were on the top and bottom of the board and the components were all easily identifiable. My EE experience really came in handy here in identifying what the circuits were designed to do. Hopefully, this description will help those with less electronics experience hack into their own RC vehicles for personal robot building.

I determined that the receiver consists of an oscillator that is excited by the RF signal from the antenna. The oscillator signal is filtered, amplified and used to generate the clock and reset signals for a ripple counter IC (MC14024BCP) and the clock and reset signals for a data flip-flop (DFF) IC (MC14174BCP). The counter counts up to some 6-bit number. The 2 LSB are not used. I assume they are not used to allow for variations in the start-up of the receiver oscillator. The next 4-bits (LSB to MSB) are used for forward, back, left and right control signals. After the counter has counted up, the control signals are latched into the DFF and are held there for a while before both the counter and DFF are reset. The forward/back control signals on the DFF outputs drive an H-bridge circuit that controls the drive motor, and the left/right control signals on the DFF outputs drive another H-bridge circuit that controls the steering servo motor. The block diagram of the RC electronics is shown below:

     \ / <-- Antenna
      |   .-------.    .--------.    .-------.
      `-->| OSC.  |--->| Filter |--->| Gain  |---.
          | Stage |    | Stage  |    | Stage |   | RF Clock
          `-------`    `--------`    `-------`   | Burst
      |    .---------------------.             left .----------.
      `--->| Clock/Reset         |     .----------->| H-bridge |
           | Timing Generation   |     |            | to Servo |
           `---------------------`     | .--------->| Motor    |
               |  | Reset   |  |       | |    right `----------`
               |  |         |  |       | |
         Clock |  |   Clock |  | Reset | |
               v  v         v  v       | |
           .---------.    .---------.  | |
           | Ripple  |-/->| Data    |--` |  forward .----------.
           | Counter | 4  | Flip    |----`   .----->| H-bridge |
           `---------`    | Flops   |--------`      | to Drive |
                          | (DFF)   |-------------->| Motor    |
                          `---------`          back `----------`

Timing of the clock and reset signals are controlled using the diodes and RC time-delay circuits shown below:

                             |                     |\
                             > 390Kohm         .---| >O---> To Ripple
                      22nF   >                 |   |/          Counter
                             >   MC14069UBCP   |               Clock
                        ||   |   |\     |\     |               (Pin 1)
   From RF Clocking >---||---*---| >O---| >O---*---.
        Signal          ||   |   |/     |/     |   |
                             |                 |   |
                             `--------VVVV-----`   |
                                      470Kohm      |
             |        Vdd
             |         ^
             |         |
             |         > 1Mohm
             |         >
             |         >   MC14069UBCP              1nF
             |         |   |\     |\              ||
             `---|<|---*---| >O-*-| >O---*---*----||---*---> To DFF Clock
                1N914  |   |/   | |/     |   |    ||   |     (Pin 9)
                       |        |        |   |         >
           Injection   |        |  ||    |   |         > 4700ohm
           Point A --> `--------+--||----`   |         >
                                |  ||        |         |
                                |    2.2nF   |        ---
             .------------------`            |       ///
             |                               |
             |                               |        Vdd
             |                               |         ^
             |                               |         |
             |                               |         > 56Kohm
             |                               |         >
             |                               |         >
             |                               |         |
             `---|<|---*---*---> To DFF      `---|<|---*---> To Ripple
                1N914  |   |     Reset          1N914  |        Counter
                       |   |     (Pin 1)               |        Reset
                       |   |                           |        (Pin 2)
                100nF ---  > 2.2Mohm                  --- 1 nF
                      ---  >                          ---
                       |   >                           |
                       |   |                           |
                       |   |                           |
                      --- ---                         ---
                     /// ///                         /// 

Based on my hand calculated time-delays, I figured out the relative timing for the clock and reset signals. Shown below is my estimate of the clock and reset timing. Please note that this is speculation. I make no guarantees of its accuracy since I did not verify them with an oscilloscope:

Transmitted RF Clock:
    ---------------. .-. .-. .-    . .-. .-. .--------------------
                   | | | | | | ....| | | | | |
                   `-` `-` `-`     `-` `-` `-`
                       Frequency:  <= 2Mhz and >=629hz
                       Period:     >= 500ns and <=1.59ms
                       Forward:    8 to 11 cycles
                       Backward:   16 to 19 cycles
                       Left:       32 to 35 cycles
                       Right:      64 to 67 cycles 

The RF Clock is a burst of pulses designed to clock the ripple counter from a reset state to some number determined by the command being sent. This signal starts high and should end high based on the logic. The third bit of the ripple counter is used for the forward signal; the fourth bit, for backward; etc. This allows the two least significant bits as slop in the transmission of the burst. For combinations like forward AND left, it should send a number of pulses equal to the their bit sums plus or minus the 2 bit slop which would be 40 to 43, in this case. I don't know the exact frequency of the RF clock since I couldn't measure it. I stated the frequency and period ranges based on the maximum ripple counter clock speed and the minimum delay for the other RC time-delay signals.

Ripple Counter Clock:
                   .-. .-. .-.     .-. .-. .-.
                   | | | | | | ....| | | | | |
    ---------------` `-` `-` `-`   ` `-` `-` `--------------------

This signal just follows the RF clock burst, but is inverted. This is the signal that goes to the ripple counter IC pin. It should start low and end low by the logic, but if the RF clock somehow ends low, it will decay back low after 12.4ms.

Data Flip Flop Clock:
                                         | |
    -------------------------------------` `------------------ 

This signal is used to latch the ripple counter bits into the DFF after the count burst is sent. It pulses high at the end of an ripple counter clock burst and then decays low after 6.78us. It stays low until the end of the next ripple counter clock burst.

Ripple Counter Reset:
    -----------.                             .----------------
               |                             |

This signal resets the ripple counter after its has been latched into the DFF. It goes low when ripple counter clock burst starts and stays low as long as clock burst continues. It decays high 80.8us after end of ripple counter clock burst and stays high as long as another clock burst is not sent.

Data Flip Flop Reset:
               |                                         |
    -----------`                                         `----

This signal resets the DFF and is the dominant factor in determining how fast the commands can be sent to the steering servo motor and the drive motor. Based on my estimates of the RF clock burst frequency range, the DFF could be updating the control signals every 333ms to 440ms. It goes high when ripple counter clock burst starts and stays high as long as clock burst continues. It decays low 317ms after end of ripple counter clock burst and stays low as long as clock burst is not sent.

To inject my own control signals, I de-soldered the ripple counter IC from my board and replaced it with an empty socket so I could go back to using it in RC mode for my son. With the counter removed, I injected my control signals to the inputs of the DFF and used a 555 Timer IC, configured as an a-stable oscillator, to generate the DFF clock and reset. The 555 Timer circuit signal was injected just after the first diode (labeled Injection Point A) to make use of the existing clock and reset time-delay circuits already there. Here is the timer circuit I obtained from the "Radio Shack Engineer's Mini-Notebook of 555 Timer IC Circuits" by Forrest M. Mims, III:

            Vdd    Vdd
             ^      ^
             |      *--.
             <      |  | 
     68Kohm  <    .-------.
             <    | 8  4  |
             |    |       |
             *---<|7 555 3|---< To Injection Point A
             |    |       |
             <    |       |     Pulse Width High = 7.1ms
    150Kohm  <    |       |     Pulse Width Low  = 4.9ms
             < .-<|6      |     Frequency        = 83.3hz
             | |  |       |
             *-*-<|2      |
             |    |   1   |
       47nF ---   `-------`
            ---       |
             |        |   
            ---      ---
           ///      ///   

The tricky part was setting the duty cycle and frequency without the aid of an oscilloscope. I thought I could continually clock the DFF to update the control signals going to the H-bridges. I found that my hand-calculated, slower frequencies did not seem to work. Then, I remembered something I had read on the WWW about servos needing a 10-20 millisecond command refresh rate so I tried it for my oscillator and that seemed to work. I'll have to find an oscilloscope to figure out for sure why that worked. I suspect the DFF is not being reset; it is only being clocked by my injected signal at a rate the steering servo motor controller likes. This would mean that the original RF burst clock must be at least 7.6Khz or higher to count up to 63 before the counter is reset.

For the controller, I needed to generate four signals: Left, Right, Forward and Backward. The neutral steering condition was when Left and Right were both zero, and the neutral drive condition was when Forward and Backward were both zero. I made having both Left and Right set to one and both Forward and Backward set to one an impossible state to prevent conflicting signals from being sent to the motors which might cause damage. The controller implementation was a simple ROM-like, hard-wired mux logic design. I felt like I only needed four sensor signals as inputs, and I was able to use a technique called Variable-Entered Mapping (VEM) to map my 16 sensor states into four 8-to-1 muxes. (I apologize to those whom I mistakenly said this was called "mux folding".) Here is a simple example of VEM:

  A B C | Y     
  ------+--     In a direct mux implementation of this truth table,
  0 0 0 | 0     you would use the A, B and C inputs as the selectors
  0 0 1 | 1     to a 8-to-1 mux and wire the data inputs as 0's or 1's.
  0 1 0 | 0     In mux folding, an eight state truth table can be 
  0 1 1 | 1     mapped to a 4-to-1 mux by using B and C as the selector 
  ------+--     inputs and using 0's, 1's, A and Abar as data inputs.
  1 0 0 | 0
  1 0 1 | 1           |\ .---A,B,C
  1 1 0 | 1           | \v
  1 1 1 | 0     0 --->|D0\
                1 --->|D1 |---> Y
                A --->|D2 |
             Abar --->|D3/
                      | /

  For the B=0, C=0, we want the Y output to always be 0 so the D0 mux 
    input can be wired to 0 or GND.
  For the B=0, C=1, we want the Y output to always be 1 so the D1 mux 
    input can be wired to 1 or VDD.
  For the B=1, C=0, we want the Y output to follow the A signal so the 
    D2 mux input can be wired to a A signal source.
  For the B=1, C=1, we want the Y output to follow the Abar signal so 
    the D3 mux input can be wired to an Abar signal source.

In general, I wanted my robot to forge ahead even though the sensors might be giving it conflicting information. I didn't want it to just stop and sit like a lump in front of a lot of people if it became confused. I'd rather see it go flying into a wall (for entertainment value). The masking tape normally should be detected by one or both of the right or left side sensor pairs. If three sensors came on, then the two sensors closest together (left pair or right pair) would dominate the behavior. If two opposite sensors came on (a left and a right), I would assume the outside sensor(s) was(were) false and the robot was still on course.

Here is how I mapped sensor inputs to the controller signals:

  FL = Far Left Sensor Input     L = Left Controller Output
  NL = Near Left Sensor Input    R = Right Controller Output
  NR = Near Right Sensor Input   F = Forward Controller Output
  FR = Far Right Sensor Input    B = Backward Controller Output

  FL NL NR FR | L R | F B | Comments
   0  0  0  0 | 0 0 | 1 0 | No tape detected - Move forward
   0  0  0  1 | 0 0 | 0 1 | Tape is far right! - Back up and try again
   0  0  1  0 | 0 1 | 1 0 | Tape is near right - Turn right, move forward
   0  0  1  1 | 0 1 | 1 0 | Tape is near, far right - Turn right, move forward
   0  1  0  0 | 1 0 | 1 0 | Tape is near left - Turn left, move forward
   0  1  0  1 | 0 0 | 1 0 | Two tapes? - Ignore right tape, move forward
   0  1  1  0 | 0 0 | 1 0 | Two tapes? - Ignore either tape, move forward
   0  1  1  1 | 0 1 | 1 0 | Three tapes?? - Ignore left tape, turn right
   1  0  0  0 | 0 0 | 0 1 | Tape is far left! - Back up and try again
   1  0  0  1 | 0 0 | 0 1 | Two tapes? - Ignore both tapes, move forward
   1  0  1  0 | 0 0 | 1 0 | Two tapes? - Ignore left tape, move forward
   1  0  1  1 | 0 1 | 1 0 | Three tapes?? - Ignore left tape, turn right
   1  1  0  0 | 1 0 | 1 0 | Tape is near, far left - Turn left, move forward
   1  1  0  1 | 1 0 | 1 0 | Three tapes?? - Ignore right tape, turn left
   1  1  1  0 | 1 0 | 1 0 | Three tapes?? - Ignore right tape, turn left
   1  1  1  1 | 0 0 | 0 0 | Tape barrier or finish line - Stop

The sensors were the most difficult to design. I kept wondering, "How do you detect masking tape?" It has few distinct (or redeeming) qualities. Luckily, the contest was to be held on a dark carpet, so I could detect it by the contrast of light tape against a dark background. I tinkered with IR emitters and detectors, but could not come up with a usable IR design. IR was hard to debug since you could not see the light. For all I knew the carpet might have the same reflective signature to IR as the tape did which could be a real disappointment on contest day. I decided to use Cadmium Sulfide photoresistors which were slower, but were easier to work with.

For each tape detection sensor, I created two voltage dividers. One was made of a 10K pot which I used as a programmable voltage reference. The other was made of a CdS photoresistor and a 100K resistor whose output voltage was proportional to the light received. I bought two packages of CdS components from Radio Shack, measured them and chose CdS devices with roughly the same resistance. The output voltages of these dividers were fed into a comparitor that had an open collector output. For the comparitor output pull-up, I used an LED and resistor so I could have visual feedback of each comparitor output signal going to my controller logic. When light to the CdS increased (saw light from tape) the resistance dropped and the output voltage of the divider increased above the voltage reference and switched the comparitor output high turning off the LED. When light to the CdS decreased (saw dark from carpet) the resistance increased and the output voltage of the divider decreased below the voltage reference and switched the comparitor output low turning on the LED. The two inverters, although redundant, were used as buffers to the controller. I could have inverted the CdS/100K divider and only used one inverter, but I had them available and didn't want to redo the wire wrap.

        Vdd   Vdd                     ^
         ^      ^                     >  2.7Kohm
         |      |                     >  
         >      >            |\      ---
  CdS    >      > 10K pot.   | \      V  Green LED
 ~68Kohm >      >            |  \    ---
         >      ><-----------| - \    |       |\    |\
         |      >            |    >---*-------| >O--| >O---> Sensor Output
         *------>------------| + /            |/    |/       to Controller
         |      >            |  /             74HCT04's
         >      >            | /
100Kohm  >      >            |/
         >      >            LM339N
         |      |            Quad
        ---    ---           Comparitor
       ///    ///

I learned while building my robot that there are three things that are important in positioning the sensors. They are location, location and location. In my case, since my robot moved pretty fast and could turn fairly quickly, I had to put the sensors as close the front wheels as possible to prevent the sensors from swinging past the tape when the controller tried to correct its path. I arranged two sensors near each front wheel with the goal of keeping the masking tape between the two sensor pairs. Each pair of sensors was close enough together to detect the tape under at least one of the pair. I found later that having the sensors straddle the tape led to a "wagging" motion as the robot bounced back a forth trying to keep the tape between the sensors pairs, but it still worked. I just had to aim the pairs of sensors toward the middle to minimize the wagging. I mounted the sensors to the grill of my truck by lashing pieces of a wire hanger into a frame that could be hooked and unhooked to the grill of the truck. The sensors were attached by rubber bands to allow me some flexibility in repositioning and aiming the sensors for best performance.

Sensor Placement:

      S S       S S <- CdS Sensors
      WWW       WWW
      WWW-------WWW <- Front Wheels
      WWW       WWW

      WWW       WWW
      WWW-------WWW <- Rear Wheels
      WWW       WWW

I had to construct my sensors from scratch after some fiddling with different breadboard designs. I mounted my CdS sensors 1" deep inside paper tubes of about 1/4" diameter and placed them 1" above the floor to give the CdS sensors a 1" view of the floor. (I say 1" because the edge of the CdS can see straight ahead on the side next to the paper tube, but it can see 1/2" of the tape to the other side of the tube. Taking opposite edges of the CdS into account gives a 1" field of view out the end of the paper tube.) A wad of Kleenex was used to help block light from above. I ran the wires from the CdS leads via a handmade harness back to a connector at the edge of my controller board.

Sensor Construction:

              | <-Tape   Paper tube
            ^ |   on     |        CdS
            | |   floor  |        |
            | |          v        v
  Field     | |          --------------------
  of     1" | |       ^           H------------ <-Vdd
  View      | |  1/4" |           H @@@ <-- Wad of Kleenex
            | |       v           H------------ <-33K and Comparitor
            | |          --------------------
            | |
            v |<--------><--------><-------->
              |   1"        1"         1"

I got power for my controller board from the traces on my RC truck board and added a power-up LED so I could tell when it was on. To help minimize noise on my board I generously added .01uF caps for each chip.

For testing the modules of my robot, I used a set of dip switches and pull-up resistors to generate test input signals and I used LED's with pull-ups to display my module output signals. I was able to debug the CdS sensor array, the controller board and the inputs to the RC truck separately with this set-up. To modularize the connections of the modules, I split a wire-wrap socket in half, length-wise, and trimmed the pins so they could be plugged into another wire-wrap socket on my controller board. This facilitated modular connection and disassembly of my robot for transport or debug.

Debug Circuits:

           Vdd                                  Vdd
            ^                                    ^
            |                                    |
            >                                    >
            > ~10Kohm                            > ~2.7Kohm       
            >                                    >
            |                                    |
            *---> Test Input Signal              _ 
            |                                    V LED
            O                                    -
             / Dip                               |     
            o  Switch      Test Output Signal >--`        

Finally, the night before the contest, the moment of truth arrived. I hooked the modules together for the first system test. I had to tweak the clock frequency to get the servo motor for the steering to work. Then, I had to replace the LED pull-ups because they were drawing too much power from the other electronics. Then, things started coming together. I got the sensors calibrated and positioned to where they were sensing and following a straight tape, but to my dismay, I discovered that the truck moved too fast for the CdS sensors respond in the turn. It was 1 a.m., and I was in a deep blue funk after spending so much time on the design. I tried to put a resistor in series with the drive motor wire from the H-bridge but that did not work. I finally took the rubber tires off the wheels which made it look like a low-rider, but the truck was still moving too fast. Finally, in desperation, I build another 555 timer oscillator so I could pulse-width modulate the forward control signal that I was injecting into the RC truck board. I was hoping I could control the truck speed by varying the pulse width with a potentiometer. That was enough to do the job even though now it jerked along like a dancing low-rider. It seems that the difference in frequency between my first 555 timer circuit which was supplying my clock and my second 555 timer circuit for my forward speed control was causing beat modulation which made the jerking movement speed up and slow down. I did not want to use the 555 timer that was generating my clock to pulse-width modulate my forward control signal because the servo motor for the steering was so particular about changes in frequency. I could not get my robot the make the turn in my trial runs that night, but I was hoping for some luck in the contest the next day.

John Wadley

The Contest.
For some reason I thought the contest started at 1 p.m., and I end up arriving late. It was still going on so I swapped my batteries and did some quick sensor calibrations in the hallway while the audience was cheering the other robots. My turn arrived, and I stepped forward. I recalibrated my sensors on the tape, which turn out to be more reflective than the tape I had been using. Maybe this was the luck I needed to make the turn. The robot took of, dancing down the tape line. It made the first length and approached the corner. I kept thinking, "Please don't stall. Slow down for the turn." It almost got stuck at one point in the turn, but finally made it to the final stretch. I knew it could make the rest of the course as long as it didn't over-jerk itself off track. As it crossed the finish line, I was almost in shock that it had actually worked. I ended up placing 5th because it was decided, before I arrived, to judge by course time, but I didn't care. My robot (Low Rider) and I had beaten the odds.


Line following I think I'm hooked now. My next step is to get a microcontroller kit so I can implement more sophisticated speed and sensor controls. I knew CdS photoresistors were slow when I started, but I hadn't expected them to be so slow as to severely limit my truck speed. I'm hoping to upgrade to IR or Bright LED sensors for future designs. After watching the contest video tape, I think Clay Timmons had the better sensor design using high brightness LED's in his One Week Wonder. Had his robot not been caught on the masking tape and veered off course, I think his would have beat mine in course time. Instead of trying to keep the tape between two sensors, I think he tried to keep the tape under a central sensor with two or three steering sensors immediately to each side to reduce the wagging effect. I was impressed at how smooth One Week Wonder followed the tape with this design.

About the Author

John Wadley earned a BSEE in Computer Engineering from the University of Kansas in 1989. He has worked at Texas Instruments since 1990 as a Product/Test Engineer and as an Electrical Design Engineer supporting uP-based Speech products and DSP-based designs for the Digital Telephone Answering Devices (DTAD) market. He has been a Registered Professional Engineer in Texas in 1996 and an IEEE member since 1993. He is currently pursuing an MSEE degree in Digital Microelectronic Systems at the University of Texas in Dallas.

Copyright © 1984 - 2016 Dallas Personal Robotics Group. All rights reserved.