My first really real line following robot.

Overview

Crabby is my first really real robotic project. Anything else up to this point has been either following plans someone else wrote, or prototype robots with breadboards glued to zip ties.

The first step was to build a prototype robot, called Tridolph. With a successful proof of concept, Crabby was built to be more rugged, robust, speedier and all around more awesome.

Before I go any further, Crabby gets his name from his future costume. When my daughter first saw the unnamed robot wiggle across the floor with his wide wheel base and short stubby length, the name Crabby was born. “Someday soon” we’ll head off to the craft store, get some crab-ish materials and the name will be more self explanatory. For now, imagine a fun red cartoon crab scrambling along the floor.

The robot uses a flavor of Arduino UNO made by Adafruit called the Pro Trinket 5v for control, two DC gearmotors with differential drive for motion, and three CdS photocells for line sensing.

A rudimentary algorithm worthy of his novice builder/daddy allows him to follow a line slightly better than a simple analog voltage comparator would allow, as in Sandwich. Building him has been an amazing experience, putting a cap on a 10 year on again, off again relationship with hobby electronics and robotics.

Construction

side view of robot body

Body

The robot does not really have an actual “body”. The closest thing to a body is a single rectangular piece of plywood I got at a local hobby shop. The motor mounts, rear castor wheel, motherboard, and line sensor board all connect to the plywood body with 3mm screws. Standoffs create enough height between the motherboard and plywood to fit a LiPo battery, which is then held in position with a velcro strap.

Motherboard

motherboard installed on robot

This is the masterpiece, and the part that took the most time for me to create. I first tried to create the PCB layout in Robot Room Copper Connection (sadly does not exist anymore), got frustrated. Then tried to create in Fritzing, got frustrated.

Started over from scratch in Copper Connection, with 0.5 inch larger size in both dimensions, giving me more room to squeeze traces and space for drilling DYI vias for top-layer jumpers. Carefully went over every single trace a dozen times, ensuring no short circuits, and everything was mapped exactly as in the breadboard prototype.

My first project to be exposed using Bronzer, my brand new UV exposure unit using presensitized PCB boards. After lots of coffee and patience, the board came out great.

motherboard before populating with components

Some features of the board:

  1. Single side board with only a few manually created jumpers on the top side.

  2. Top Silk Screen applied with a laminator and parchment paper. Some smudging but generally a high quality transfer.

  3. Mounting holes laid out to match Arduino UNO. In case my PCB failed, I wanted to be able to just throw a prototype controller on there and still be happy about my robot. This contingency was not needed, but I like the concept and plan to include this feature in future boards where possible.

  4. One of the mounting holes is right next to an electrolytic can capacitor, not enough space to get a nut in there. Of course, that is the ONE mounting hole that actually gets upward force applied to it, specifically when I remove the FTDI programming cable, which connects right next to the hole. The flex in the board isn’t too bad, and I take care to manually hold the board down when removing FTDI, but would be better to have a nut there securing the board. Lesson learned.

  5. Power switch to disconnect battery. When plugged into USB/FTDI, the logic circuits get power independent of the switch, but motors require a battery to be connected and the switch to be on.

  6. Molex KK connectors for the motors. Keep them far away from the IR Receiver.

  7. Non-polarized 2-pin header for battery connection. I did not have any JST connectors compatible with my LiPo battery, so I’ll just have to pay attention when I connect the battery. The Pro Trinket has reverse polarity protection, so the only other part of the circuit that would get reversed is the motor driver raw input, a risk I’ll just have to accept.

  8. Editor’s Note about tempting fate: I love, love, love that I wrote #7 above in the original text. After sitting on the shelf for a few months, I wanted to make a demo for the kid and her friend. Plugged in a fresh battery and POP. I plugged the battery in backwards and the robot doesn’t work right anymore. As a cautious person, I included a solderless socket for the motor controller, so it was a simple exercise to remove the possibly fried chip and try another, which worked like a charm, confirming my original hypothesis that the motor driver was the likely point of failure. I share this chuckle with you to remind myself to include reverse battery protection in future robots.

  9. 8-pin header to connect to the line sensor board

  10. A single SMD resistor on the bottom copper (the current limiter for the Low Battery Indicator). Mostly just for fun, but I suppose you could say I did it to save drilling 2 holes :-)

  11. Test Points for:

    • TP1 - battery voltage
    • TP2 - total current
    • TP3 - motor current
    • TP4 - current drawn off the 5v regulator

Line Sensor Board

sensor board installed on robot

A simple PCB featuring:

  • 2 white LEDs that are constant on, there to illuminate the floor surface
  • 3 CdS Photoresistors, each in series with a fixed resistor, there to measure the brightness of the light reflected off the floor
  • 3 red LEDs, each in series with a current limiting resistor, operated by the microcontroller, there to let the audience know what the robot thinks each eye sees
  • 1 capacitor, there to smooth the power supply coming in from an oversized connector wire. Very likely unnecessary but it is not hurting anything
  • 8-pin header, there to connect up to the motherboard
  • 4 mounting holes (3mm), 2 of which are in use

The idea was to keep the sensors a fixed distance from the floor. In the final construction, the front of the bot is higher than the rear, so the sensors are a bit higher off the ground than I’d like, and they angle forward. I can put a block between the rear castor wheel and the plywood body to lift the rear and correct the sensor angle, but it works well enough I’d rather not fuss with it now.

Sensors and User Interface

Input

infrared remote

Line Sensors

Crabby has 3 CdS Photoresistors on the line sensor board to see the position of the line. These worked just fine on the prototype Tridolph version of the robot. Once I hooked Crabby up with his more powerful motors, I quickly realized that photoresistors react too slow for fast line following, and had to reduce the top motor speed to not much more than half speed. Some of that speed issue is due to inefficient algorithm, but there is a big upper limit on speed using these sensors.

Low Battery Monitor

A simple voltage divider consisting of two 47k resistors in series from battery positive to ground, allowing the microcontroller to compare 50% of battery voltage against a fixed 5v reference (produced by the on-board regulator). I built this in to avoid killing my LiPo batteries by discharging them below 3v per cell.

Infrared Remote

Crabby listens for user input via IR remote control using a PNA4602M. It is a discontinued product, but I have a bag of them from 10 years ago, and it works fine. The following commands are in use:

Remote ButtonRobot Action
PowerStop motors (or keep them stopped if already stopped)
Play/PauseBegin motors if stopped, stop motors if running
ModeIf stopped, run calibration routine
Squiggle (no idea what this button is, maybe “rewind”?)If stopped, rotate for a fixed duration, which is hopefully suitable duration to rotate 180 degrees. The idea is to allow the robot to run a closed loop in the other direction, but running the motors for a fixed time interval results in a robot facing wherever he is when the interval is up, which varies greatly based on battery level and surface. As long as it does not leave him facing exactly the same as when he started, you can just hit this button a few times and he eventually ends up facing where you want :-)

This was one of the “gambles” when going from prototype to production. In the prototype robot, a Vishay TL1838 proves to be very susceptible to motor noise and frequently fails while the motors are running. Adding bypass capacitors helped some, but did not eliminate the problem. In the production robot, I made sure the motors and IR receiver were distant from one another, both physically and electronically. Works like a charm, no failed remote issues in the production model.

Output

SMD resistor to limit LED current

LED Line Indicators

Three LED indicators that are controlled by digital output pins of the Arduino. They are positioned on the sensor board directly above corresponding line sensors, giving the operator and audience information about what the robot thinks each sensor sees. Useful for debugging and entertainment.

Low Battery Indicator

A very small LED controlled by a digital output pin of the Arduino. When the battery level (as measured by an ADC analog pin reading the Low Battery Monitor described above) reaches a WARN threshold (6.7v), the indicator is illuminated solid red. When the battery level hits an ERROR threshold (6.4v), the LED flashes on for 0.25 sec, off for .75 sec. The robot also shuts down the motors and LED Line Indicators in the ERROR mode.

For fun, this guy’s current gets limited by the one and only SMD component that I used (not counting the stuff on the pre-assembled Adafruit pro trinket).

Drive Train

Power

The whole robot is powered with a single LiPo 2S 7.4v battery. This powers the motors directly (through the motor driver, described below) and the logic circuitry via the 5v regulator built into the Adafruit Pro Trinket.

During a test of my low battery logic, I needed to run the battery down to verify my calculated ADC readings for some specific voltages. Starting with a fully charged 8.4v, Crabby ran for about 5 hours of continuous line following action before shutting down operations at 6.4v battery level, plenty of margin for error before reaching the “6.0v dead lipo” state.

Motors

bottom view showing motors

The motors are a pair of gear motors from Ebay/China. They’re rated for 120rpm at 6v. I was happy with the price, you just have to wait 3 weeks for shipping, which feels like an eternity in this age of Amazon Prime. Plan ahead, have other projects to play with while shipping, and we’re in business.

In the photo you get a good view of my novice precision drilling abilities. The two motors are clearly mounted with angular misalignment, but reasonably parallel. For a first time robot that will not be pushing the performance edge, works well enough. I think the slow response of CdS photocells will cause more of an upper limit on speed than this alignment error, but gives me something to remember for next time.

Motor Mount

custom motor mount with motor attached

Custom motor mounts made from aluminium angle stock from the hardware store. To make the drill template, I cut a hole in a piece of transparency to fit the center bearing of the motor. Then marked the screw hole locations with a Sharpie. Poke holes through the transparency in those spots, lay over the aluminium and use a marker to draw the 3 drill holes (1 center and 2 screws).

In between making the couplers (above) and the motor mounts, I got a drill press (happy birthday to me!) and some high quality machine length drill bits. My drill hole locations were very tight to the 3mm screw size, so the slightest inaccuracy was unforgiving. Even using a spotting drill to pilot the holes, I’m a beginner with a drill press, so the locations were a bit off. Dremel with diamond grinding tip to the rescue, shaved a bit off the screw holes and everything aligns perfectly(-ish). Along the way I learned that the grinding wheel on aluminum just kind of melts the metal and ruins your grinder, but enough elbow grease and the job is done, for better or worse.

These work well enough for my first pair of custom mounts, and gave me a fun project to try out on my brand new drill press. In the future I’ll take greater care to:

  • Make the mounts the same length, which will ease aligning them on the robot body.
  • Make the center holes the exact same distance from the edge, resulting in better angular alignment of the motors and consequently, the robot’s body.
  • Make the screw holes larger (i.e. use 4mm drill for 3mm mounting screws) to allow for adjustment.

Wheels and Couplers

wheel coupler side view
wheel coupler rear view showing drill hole

The two drive wheels are Lego, and the couplers are straight out of Intermediate Robot Building. Start with some aluminium rod, drill a hole down the center. Epoxy a Lego axle in one side, put a set screw on the other to attach that side to the motor output shaft. There’s a lot more to it, and the book does an outstanding job explaining detailed steps. Following the procedure in the book can produce very precise and professional results…

…BUT…I did not have a drill press (yet) when I made these, so I hand drilled down the “center” of the rod. Atrociously off center, not straight, wrong size on the motor shaft side, just generally a poor showing of workmanship…

…AND YET…they get the job done, showing how robust the technique actually is. With practice, patience, and better tools, these can come out really professional.

Some masking tape to pad the output shaft up to the size of the hole, clamping really hard on the set screw, they don’t wobble all that much. This type of robot does so much correctional steering, and I’m not going for any world records here, so it is good enough for me.

Motor Driver

The motors driver is a SN754410NE that I had laying around from 10 years ago when I first started robotin’. Takes in 5v logic and raw battery for motor power. Following the Robot Room philosophy, extra bypass capacitors are used on both voltage inputs, hopefully reducing noise throughout the rest of the electronics as the motors’ spinning coils and magnets do the nasty.

Each motor is controlled by 3 lines. PWM on the enable pin for speed, and direction/coast/brake using the two direction pins. If I read the datasheet correctly (very possibly not) SN754410NE does not have any built-in protection diodes, but I chose to skip using external ones. Will I pay the price for that decision down the road?

Control and Logic

Microcontroller

Crabby uses an Adafruit Pro Trinket 5v for the microcontroller (and voltage regulator for logic circuitry) programmed with the Arduino IDE. Pro Trinket is an Arduino UNO almost-compatible board, in a very small 24-pin “double wide” DIP package. I say “almost compatible”, the limitations are minor. The voltage regulator can only supply 150mA, Arduino digital pins 2 and 7 are unavailable for general use, and Serial I/O needs to be done with an external chip (like the FTDI Friend from Adafruit).

I was a bit worried about the 150mA capacity for the 5 volt output. That needs to supply the following components:

ComponentCurrentCalculation Method
Pro Trinket board (ATmega328 microcontroller, built-in power LED, any other components)30mAMeasured current usage running a very simple Arduino sketch
Sensor board51mAMeasured current with all 3 Line Indicators turned on (worst case)
PNA4602M IR Remote sensor3mADatasheet max current listing
Low Battery LED6mAMeasured
SN754410NE Motor Driver (logic side)28mAScary one. Datasheet said up to 70mA which is too much. I measured a variety of motor conditions and maxed out at 28mA, which fits within the limit
Total118mAMath - simple addition

Luckily the motor driver measurements were lower than the datasheet max specifications because I really didn’t want to add a separate voltage regulator. It is possible that under operating conditions (such as heating up during continuous usage) it may draw more current, in which case I’ll have brownouts and wonder what is going on as my robot intermittently resets, but has not happened yet after following a line for continuous hours.

Line Following Algorithm

Very basic algorithm. Works well for staying on a line. Not particularly fast or direct, lots of wobble, but it works.

  1. Full Speed Ahead - If all 3 eyes see the (wide) line OR only the middle eye sees the (narrow) line, drive forward full speed, both motors.

  2. Gentle Nudge - If only 2 eyes see the line, one motor full speed, other one half speed. Minor correction back towards the line.

  3. Shove - If only one side eye sees the line, one motor full speed, other one stop. Major correction back towards the line.

  4. Evasive Maneuvers - If none of the eyes sees the line, begin evasive maneuver. Stop forward motion, rotate towards the side that last saw the line. If you recover the line, go to one of the other modes, otherwise continue this action until you find the line or a set period of time (5 seconds) is up.

  5. Stop - If none of the eyes sees a line and the evasive maneuver duration is exceeded, shut down the motors.

This routine worked very well on the slow moving prototype robot. With the faster more powerful motors on the production model, the “Gentle Nudge” mode is ineffective. By the time the slow reaction of the CdS photocells notices a change in the line that would warrant a gentle nudge, the robot is already moved far enough that either Shove or more commonly Evasive mode is required. He routinely enters evasive mode, and engages it for a few milliseconds, after which he’s back on the line. You barely notice it visually unless he really overshoots a sharp corner.

I may fiddle with the algorithm, using either PID (well, PD) or simply treating the sensors in a more analog way (the current algorithm makes a binary decision about each sensor, with no “kinda” on the line values, only entirely on or entirely off). Most likely, I’ll play with it until my next shipment of Chinese motors arrives, at which point I’ll be using more fast reacting phototransistors.

Calibration

When commanded via IR Remote, Crabby runs a calibration routine. For a fixed interval of time, one motor powers forward and one powers backward, thus pivoting the robot around its center. Up to 100 samples are taken for each of the 3 line sensors. I was worried that I’d use up the RAM available on the microcontroller, so it stops sampling after 100, although it never reaches there because I built the timing of the loop to complete the rotation interval before 100 samples is reached.

The following calculations are done for each sensor independently:

  • Let “mean overall” equal the average of all samples
  • Let “mean above” equal the average of all values above the mean overall
  • Let “mean below” equal the average of all values below the mean overall
  • Let “line threshold” equal the average of mean above and mean below

Once this is done for each of the 3, choose one (doesn’t matter, but my program uses the right sensor) and see if the mean overall is closer to the mean above or the mean below. The way he’s wired, high numbers are bright numbers, so if the mean overall is closer to the mean above, then calibration saw more bright samples than dark ones, implying the background is bright and the line is dark. Otherwise, there were more dark samples than bright ones, so the bg is dark and the line is bright.

Using this technique, the only slightly-matching photoresistors are calibrated to the current surface/line/ambient light and the program can independently analyse each sensor and decide whether it is over a line or not. Saves me from using trim potentiometers, which seem to be all the rage but I’m lazy and cheap and personally am more able to comprehend complex software rather than complex electronics and hardware.

For the record, the “fixed interval of time” technique is garbage. I calculated the duration by testing my prototype robot on one specific surface (my smooth formica desk surface). Using that interval, the prototype bot does a nice 360 degree turn with a full 9v battery. Fast forward to using the exact same program and interval on the production bot with different motors, wheels, battery, and variable surfaces. Sometimes he rotates 5 times in the interval, sometimes 3 times, and also in between, requiring a manual shove to point back in the desired direction of movement.

Sure, the number of millis in the interval is a simple constant, nicely organized with the other constants in the #define section at the top of the sketch, but what fun is changing that when I can just complain about it here? A better way would be to see the line at the start, keep rotating until 180 and you see the line again, then continue another 180 until you are back home over the line again.

A robot is never done, is it?

Other Notable Behaviors

laser baffle blocking light interference

There was a fun incident with a laser beam. Shortly after finishing Crabby, I wanted to time how long a course took, allowing some sense of scientific measurement to algorithm tweaks. Enter the laser break-beam sensor.

Crabby drives along, passing the laser a few times, letting me rejoice in my fully functional timing system.

Then he passes the laser, and makes a direct bee line for the laser itself. With no optical shielding in place, the laser happened to be at the perfect height to shine directly on his right eye, causing him to think a really bright line was off to the right. As he turned into the “line”, it ended up aligning perfectly to have the laser right on his middle eye, causing him to think that middle was on the line, and the two sides were off the line. Quick check to my algorithm up above, and you see that condition indicates “full speed forward, both motors”, so he drove right at the laser, snagging the wire and dragging my whole apparatus across the room.

The kid and I had a laugh and then taped some construction paper over the sides to optically shield Crabby from the laser timing module.

The picture shows a slightly different orientation than described above, with the laser on the left, shining brightly on Crabby’s left-side red construction paper optical baffle, allowing the robot to function just fine, while the laser can properly know when the robot passed the starting line.