The number of times per second the BASIC Stamp checks the QTI sensors is an important consideration for line following. If a given PBASIC program doesn't take enough samples per second, it means it's waiting too long between samples, which can cause the Boe-Bot to miss a sudden change in the line's direction, or even lose the line altogether. On the other hand, if the program devotes itself entirely to checking the line, there might not be enough time for the BASIC Stamp to check other sensors. By studying the relationship between sampling rate and line following performance, you can determine the minimum sampling rate necessary to navigate a course with a certain set of features. You can then use whatever time is left to check other sensors.
The rate at which a microcontroller checks a sensor is commonly referred to as ‘sampling rate’ and it's typically expressed as a number of samples per second. The sample interval is the amount of time between each sample, which is expressed in terms of seconds. In cases where the sample interval doesn't change, it's called a constant sample interval. When the sample interval is constant, the sampling rate is just the reciprocal of the sample interval, and vice versa.
Frequency and Period Notation
Some documents refer to the sampling rate as a frequency (f) and quantify it in terms of Hertz (Hz). You might also see the sample interval expressed as a period (T) in terms of seconds (s). The sampling rate and sample interval equations for a constant sample interval would then be expressed as:
For example, you might see the sampling rate of ten thousand samples per second expressed as f = 10 kHz, and its corresponding sample interval would be T = 100 μs.
There are quite a number of adjustments that can be made to existing QTI example programs to make them more efficient and improve their sampling rates. One of the most important changes involves reducing the amount of time it takes to check the QTIs. This activity introduces a subroutine for reading multiple QTIs that takes very little time and is especially reliable for detecting black vinyl electrical tape on a white background (see Figure 1 below). This activity also features a line following example program that uses a subroutine to follow a line with four QTIs.
Figure 1 - Fully Assembled Boe-Bot and QTI Line Follower AppKit
This project contains advanced topics, not intended for Boe-Bot beginners. Before continuing, it is highly recommended you complete the following tasks in order to gain a better understanding on how this application works:
(1) Boe-Bot Robot, assembled and tested (#28132)
The parts below are included in the Boe-Bot Robot kit:
(1) Piezospeaker (#900-00001)
(1) QTI Line Follower AppKit (#28108)
The parts below are included in the QTI Line Follower AppKit:
(4) 3-pin Male-Male Headers (#451-00303)
(4) QTI Sensor Modules (#555-27401)
(4) 3/8” 4-40 pan-head screws (#700-00016)
(4) 7/8” 4-40 pan-head screws (#710-00007)
(4) 1” round standoffs (#700-00060)
(4) 1/2" round spacers (#713-00007)
(4) Nylon washers
(4) 10” servo cable extenders (#805-00011)
For this activity, you will also need to supply your own:
#2 Philips-head screwdriver
Black ¾-inch electrical tape
White poster board
Source Code for Line Following with Four QTI Sensors [1]
The QTI module is designed for close-proximity infrared (IR) detection. Take a look at the small square black box just above the QTI label, it’s nested below the capacitor and between the two resistors. That’s a QRD1114 reflective-object sensor containing an infrared diode behind its clear window and an infrared transistor behind its black window. When the infrared emitted by the diode reflects off a surface and returns to the black window, it strikes the infrared transistor’s base, causing it to conduct current. The more infrared incident on the transistor’s base, the more current it conducts.
When used as an analog sensor, the QTI can detect shades of gray on paper and distances over a short range if the light in the room remains constant. With this circuit, you can set P3 high and then test it with RCTIME to measure how long it takes the capacitor to discharge through the IR transistor. Since the IR transistor conducts more or less current depending on how much IR it receives, the RCTIME measurement can give you an indication of distance or shade of gray.
If all you want to know is whether a line is black or white, the QTI can also be converted to a digital sensor. This is how the array of four QTI sensors will be used later in our Boe-Bot line following application.
When W is connected to Vdd and B is connected to Vss, the R-terminal’s voltage will drop below 1.4 V when the IR transistor sees infrared reflected from the IR LED. When the IR LED’s signal is mostly absorbed by a black surface, the voltage at R goes above 1.4 V. Since the BASIC Stamp interprets any voltage above 1.4 V as 1 and any voltage below 1.4 V as 0, this circuit shown in Figure 2 gives us a quick and easy way to detect a black line on a white background.
Figure 2 - QTI Circuit
Figure 3 – QTI Mechanical Assembly
Figure 4 shows the schematic for four QTIs along with a partially completed wiring diagram. Although there are many ways this circuit can be built, the setup shown below is useful because it's thrifty with breadboard real-estate.
Figure 4 - QTI Circuit and Wiring Diagram Part 1
Figure 5 shows how to connect the servo extension cables to the male-male headers that were plugged into the breadboard in Figure 4. The other end of each cable is plugged into a QTI. Be careful when you make these connections, and pay close attention to the wire colors listed in the figure. Notice that the far left and mid left cables have white wires that plug onto header pins in the same breadboard row. The same applies to the mid right and far right QTI cables. Instead of common white wires, the mid left and mid right QTI cables share a pair of black wires.
Figure 5 – QTI Circuit and Wiring Diagram Part 2
It's a good idea to test all the sensors with the Debug Terminal before taking the Boe-Bot for a spin on the line following course. The next example program (CheckQtiSubroutine.bs2) has a Debug Terminal display you can use to make sure the QTIs are properly discerning the difference between black vinyl electrical tape and white paper. Figure 6 shows what you'd want to see in the Debug Terminal display when the middle-right and far-right QTIs are over black electrical tape, and the other two are over white paper.
Figure 6 - QTI Debug Terminal Test Display
Keep your Boe-Bot away from direct sunlight, and avoid direct light from incandescent desk lamps during these tests. Also, keep your line following course away from these sources of infrared interference.
' CheckQtiSubroutine.bs2 ' Displays QTI sensor states. 0 means white surface, 1 means black. ' {$STAMP BS2} ' {$PBASIC 2.5} qtis VAR Nib ' qti black/white states OUTB = %1111 ' Set OUTB bits to 1 DEBUG CRSRX, 8, "FMMF", CR, ' Display bit positions CRSRX, 8, "LLRR", CR DO ' Main DO...LOOP GOSUB Check_Qtis ' Get QTI states DEBUG BIN4 ? qtis, CRSRUP ' Display QTI states PAUSE 100 ' 1/10 s delay LOOP Check_Qtis: ' Result -> qtis variable. 0 means white surface, 1 means black ' surface. DIRB = %1111 ' P7..P4 -> output PAUSE 0 ' Delay = 230 us DIRB = %0000 ' P7..P4 -> input PAUSE 0 ' Delay = 230 us ' PULSOUT UnusedPin, 0 ' Delays = 208 + (Duration*2) us qtis = INB ' Store QTI outputs in INB RETURN
If you had problems with two QTIs sensing 1 when only one of them should have, try adjusting the standoffs so that the QTIs are further apart. Not so far that you can get 0000 when the line is between two QTIs though! On the other hand, if only one QTI sensed 1 when the stripe was between two of them, they may need to be positioned closer together. Otherwise, your Boe-Bot is ready for line following!
The BASIC Stamp has a variety of parallel I/O control features that make it possible to perform operations on groups of I/O pins. CheckQtiSubroutine.bs2 uses the BASIC Stamp's DIRB and OUTB variables to control the directions and output states of the I/O pins P7, P6, P5, and P4. When these I/O pins are set to input with DIRB = %0000, the program also takes a snapshot of the binary states these I/O pins sense as a result of the four QTIs' R-pin voltages with the command qtis = INB. Before going into detail about how these variables are used in the Check_Qtis subroutine to simultaneously check the four QTIs, let's take a closer look at how the BASIC Stamp's Parallel I/O control variables work.
In addition to the 13 words of variable RAM, the BASIC Stamp has three extra words of memory for controlling the directions of the I/O pins, setting their output states and reading their input states. These word variables don't have to be declared as variables because they already have the following PBASIC keyword names:
Since each word variable has 16 bits, the DIRS, OUTS, and INS variables conveniently have one bit apiece for each of the BASIC Stamp's 16 I/O pins. Each bit either stores a 1 or a 0, which in turn relates to some I/O pin characteristic, be it I/O pin direction, high/low output state, or sensed input state.
Figure 7 shows how each bit in the DIRS, OUTS, and INS variables maps to the BASIC Stamp's I/O pins. Bit 0 of each variable either controls or senses P0. Bit 1 of each variable controls/senses P1, and so on, all the way up to bit 15, which controls/senses P15.
Figure 7 - I/O pin bit mapping
Figure 8 shows some example commands can be used to control as well as read all 16 I/O pins with single commands that write values to and read values from DIRS, OUTS, and INS:
Figure 8 - Register example commands
Figure 9 shows how each variable's bits can be individually accessed with bit variables DIR0 through DIR15, OUT0 through OUT15 and IN0 through IN15. Each of these bit variable names are also PBASIC keywords.
Figure 9 - Variable bit access
PBASIC even has variables with keywords for parallel byte and nibble operations (Figure 10). Notice that the DIRB, OUTB, and INB nibble variables used in CheckQtiSubroutine.bs2 map to the BASIC Stamp I/O pins that are connected to the QTIs' R-pins.
Figure 10 - Keywords for byte and nibble operations
Notice how each I/O pin has one bit in the DIRS, OUTS, and INS variables. Collectively, the three bits define the I/O pin's direction, output state (when it's an output) and sense the signal the I/O pin is either sending or receiving. For example, bit-3 of the DIRS variable (DIRS.BIT3 or DIR3) sets the direction of I/O pin P3 to output if it is 1, or input if it is 0. If the direction of P3 is output, bit-3 in the OUTS variable (OUTS.BIT3 or OUT3) makes the I/O pin send a high signal if it's 1 or a low signal if it's 0. If P3 is set to output, bit-3 of the INS variable will store whatever is in bit-3 of the OUTS variable. If P3 is set to input, it will store a 0 if the voltage at P3 is below 1.4 V, or a 1 if the input signal is at or above 1.4 V.
I/O Variable Facts
A bit in the DIRS variable makes an I/O pin an output if it's 1 or an input if it's 0.
As an output an I/O pin will send a high signal if the corresponding bit in the OUTS variable is 1, or a low if that bit is 0.
Bit values in the OUTS variable have no effect on I/O pins that are set to input.
I/O pins that are set to input also have no effect on external circuits; they only sense voltage signals applied by external circuits.
Bit values in the INS variable will match the values in the OUTS variable if the I/O pin is an output. If the I/O pin is an input, the INS bit value depends on the voltage the I/O pin senses (1 if the voltage is at or above 1.4 V, or 0 if it's below that level).
These same rules apply to Byte, Nib, and Bit I/O variables:
DIRH / OUTH / INH and DIRL / OUTL / INL
DIRA / OUTA / INA through DIRD / OUTD / IND
DIR0 / OUT0 / IN0 through DIR15 / OUT15 / IN15
Here is an example of two commands that define a variety of settings, simultaneously, on all sixteen I/O pins with just two commands:
OUTS = %1010101010101010 DIRS = %0000000011111111
OUTS = %1010101010101010 sets and clears alternate bits in the OUTS variable. The leftmost bit is 1, which means P15 will send a high signal, so long as bit 15 in the DIRS variable is 1 (output). The second bit from the far left in the OUTS variable controls the P14 output state, and it's 0, which will make P14 send a low signal. This pattern repeats itself down through bit 0, the rightmost bit, which controls P0.
After setting and clearing alternate bits in the OUTS variable, the command DIRS = %0000000011111111 sets P15 through P8 to input, so they just listen for signals, and won't pay any attention to the OUTS variable. However, the command also sets the bits for P7 through P0 in the DIRS variable to 1’s, making them outputs. Since the OUTS variable has alternating 1’s and 0’s, P7 will send a high (5 V) signal, and P6 will send a low (0 V) signal. P5 also sends a high signal, and so on through P0, which sends a low.
Here, also is an example of using the INS variable to take a snapshot of all the BASIC Stamp I/O pins with just two commands:
DIRS = %0000000000000000 variable = INS
The command DIRS = %0000000000000000, sets all the I/O pins to input. Then, variable = INS takes all the input states sensed by the I/O pins at the instant the command is executed and copies them to variable. Let's say you didn't use the command DIRS = %0000000000000000 before variable = INS.
If DIRS is still %0000000011111111 from the parallel output example, the I/O pins will behave differently. Since bits 15 through 8 in the DIRS variable are all 0, bits 15 through 8 in the INS variable will still store whatever input states the P15 through P8 I/O pins sense. Again, those states depend on the voltages of the devices or circuits connected to those I/O pins. On the other hand, bits 7 through 0 in the DIRS variable are all 1s, so those I/O pins are outputs. As a result, bits 7 through 0 in the INS variable will instead store whatever they sense the I/0 pins are transmitting — a 1 if it's high (5 V), or 0 if it's low (0 V). Let's say again that the values in the OUTS variable are also from the previous example; that's OUTS = %1010101010101010. Because of this, bits 7 through 0 in the INS variable will store %10101010.
PBASIC Commands that Modify DIRS, OUTS, and INS
The DIRS, OUTS and INS variables are automatically updated whenever you use HIGH, LOW, INPUT, OUPUT, TOGGLE, or REVERSE. Let's say you use the command LOW 3. It automatically puts a 0 in bit-3 of the OUTS variable to set up the low signal and 1 in bit-3 of the DIRS variable to make it an output and transmit the signal. HIGH 3 would instead write a 1 to bit-3 in the OUTS variable, along with 1 to bit-3 in the DIRS variable. If you use INPUT 3, it will make the I/O pin an input, which stores a 0 in bit-3 of the DIRS variable.
You can also access the I/O direction, output and input variables as bytes, nibbles or even bits. The byte variable keyword names end in H for P15 through P8 and L for P7 through P0. So, there's DIRH, OUTH, and INH, and there's also DIRL, OUTL, and INL. There are also A, B, C and D I/O nibbles. A is P3 through P0, B is P7 through P4, C is P11 through P8, and D is P15 through P12. So, OUTB = %1101 sets up P7, P6, and P4 to send high signals and P5 to send a low signal. DIRB = %1111 changes all the I/O pins to outputs so that the high/low signals can be sent. Each I/O variable is also bit addressable. For example, DIR15, OUT15 control the direction and output state of P15 while IN15 reads its input. The same applies to for P14 and DIR14, OUT14, and IN14, and so on...
PBASIC Commands that Modify DIRS, OUTS, and INS
The BASIC Stamp Editor's Help documentation also has information about the BASIC Stamp's parallel I/O variables. After clicking Help and selecting Index, type OUTB into the Keyword field. The term OUTB will appear in the index list. When you click it and the Display button, the PBASIC Syntax Guide takes you to an excerpt from the BASIC Stamp Manual on Memory Organization and Variables.
Going back to the image above showing PBASIC variable keywords (above), the QTIs are connected to P7 through P4, so the parallel I/O operations are performed using DIRB, OUTB, and INB. In the example program, DIRB sets the direction of those four I/O pins, OUTB sets the output states, and INB reads inputs.
At the beginning of the program, the command OUTB = %1111 sets the bits for P7, P6, P5, and P4 so that the I/O pins will all send high signals. Since the BASIC Stamp initializes all I/O pins to input, this command will not have any effect until the corresponding bits in the DIRB variable are set to 1, which happens later in the program. The I/O pins remain inputs, but they are now all ready to send high signals when the DIRB variable bits are set to %1111.
In the Check_Qtis subroutine, P7 through P4 have to be set high to discharge all the QTIs' capacitors. Since the lower plate of each capacitor is connected to the R-pin as shown below, the voltage at the each R-pin rises as each QTI's capacitor discharges. The command that sets P7 through P4 to output is DIRB = %1111, and since all the OUTB bits were set to 1 at the beginning of the program, all the I/O pins send high signals. After all the I/O pins change to output-high, which causes the BASIC Stamp I/O pins to short the QTI's R-pin to the BASIC Stamp's 5 V supply, a brief delay is necessary to let the QTI capacitors discharge. PAUSE 0 inserts a 0.23 ms delay into the BASIC Stamp 2 program to give the QTIs' capacitors enough time to fully discharge.
Figure 11 - QTI Schematic
After DIRB = %1111 and PAUSE 0, the QTIs' capacitors are all just about fully discharged, and the voltage at each R-pin is very close to 5 V. The next step is to set all the I/O pins to input and wait for another 0.23 ms with the commands DIRB = %0000 and PAUSE 0. When all the I/O pins that were output high get changed to input, the QTIs' capacitors start to charge. How quickly they charge is governed by how much infrared the QRD1114's infrared transistor receives. The QTI should be between 1/16 and 3/16 of an inch from a surface. So, the IR light the QRD1114's IR transistor receives should come from the QRD1114's IR emitting diode as it reflects off the surface.
Since white surfaces reflect infrared, more of it gets back to the QRD1114's IR transistor's base, which causes the IR transistor to conduct more current. When the IR transistor conducts more current, the capacitor can charge quickly, which in turn causes the R-pin's voltage to drop below 1.4 V during the 0.23 ms delay caused by the Check_Qtis subroutine's second PAUSE 0 command. If the QTI is over a black surface, very little IR will get back to the transistor's base, which means it won't conduct much current. In that case, the capacitor charges slowly in comparison, and the voltage at the R-pin doesn't make it to 1.4 V during the 230 μs pause. With all this in mind, the program has to record what the P7 through P4 input bits store after the second PAUSE 0 with the command qtis = INB. After this command, each bit in the qtis variable will be 1 if the R-pin's voltage didn't make it to 1.4 V, or 0 if the voltage did drop below 1.4 V.
Use PAUSE 0 with caution.
While it's a convenient way to make the BASIC Stamp 2 pause for 0.23 ms, faster BASIC Stamp models will take less time to execute this command.
For this application, the first PAUSE 0 in the Check_QTIs subroutine should still give the capacitors enough time to discharge. However, the second PAUSE 0 will not work properly with a faster BASIC Stamp. The best approach for faster BASIC Stamps is to implement the self-calibration technique discussed near the end of this mini-project in a section titled "Self-Calibrating Code".
Let's say the qtis variable stores %0011 after the qtis = INB command. In this situation, the two QTIs on the Boe-Bot's right are over black electrical tape, and the two on the left are over white paper. The leftmost 0 indicates the R-pin connected to P7 decayed below the BASIC Stamp's 1.4 V logic threshold during the pause, as did the R-pin connected to P6. Those QTIs must be over white paper since their R pin voltages decayed quickly. The qtis variable's P5 and P4 bits are both 1s, indicating that those two QTIs' R-pins decayed so slowly that they didn't make it past the 1.4 V threshold before the qtis = INB snapshot. So, those QTIS must be over the black electrical tape.
What's the commented PULSOUT command in the Check_Qtis subroutine for?
You can use it instead of PAUSE 0 to tune QTI sensitivity by making fine adjustments to the amount of time the program waits before it takes a snapshot of the INB variable. After declaring UnusedPin in a PIN directive, the PULSOUT UnusedPin, 0 command takes about 208 μs to execute. You can add to the delay in 2 μs increments by increasing the Duration argument. For example, to make a 258 μs delay, PULSOUT UnusedPin, 25 will do the job.
You can also use a modified version of the Calibrate_Qtis subroutine in Applied Robotics with the SumoBot (#27403), Chapter 3, Activity #4. After calling this subroutine, the PULSOUT command would be PULSOUT UnusedPin, Threshold. This will make your QTIs self-calibrating. This approach is explained in more detail in that book’s “Self-Calibrating Code” section.
LineFollowWithCheckQtis.bs2 checks the QTIs and updates the servos every 9 ms. Remember that for a constant interval, the QTI sampling rate is the reciprocal of the sample interval. Since the sample interval is 9 ms, which is 0.009 or 9 x 10-3, the sampling rate is:
111 samples per second is actually more than enough for Boe-Bot line following. The servos could even go faster with higher battery voltage, and the Boe-Bot should continue following the line with ease. One thing to keep in mind is that each PBASIC command takes a fraction of a millisecond. So, to keep the sampling interval short, don't go overboard with long routines that make lots of decisions. Even more caution should be exercised when checking other sensors in addition to the QTIs each time through the main loop. If you're not careful, the sample interval can end up in the 40 ms neighborhood. The resulting sampling rate would be 25 Hz, which will probably be too low, which can lead to a variety of line tracking problems.
Reading other sensors doesn't necessarily have to delay the program that long. There are lots of techniques you can use to reduce sensor reading times, and their impact on the program's sampling rate. For example, you can use smaller capacitors for sensors other than QTIs that rely on RC decay. Also, instead of checking five additional sensors along with the QTIs between each servo pulse, your program can check one sensor along with the QTIs between each servo pulse. After five servo pulses, all five sensors will have been checked.
The next example program, LineFollowWithCheckQtis.bs2 does not check any other sensors. However, the Your Turn section following the program demonstrates how you can test various parts of a line following course to figure out how much room there is for sensor measurements in the sample interval.
' LineFollowWithCheckQtis.bs2 ' Navigates based on values acquired with the Check_Qtis subroutine. ' {$STAMP BS2} ' {$PBASIC 2.5} qtis VAR Nib ' qti black/white states OUTB = %1111 ' Set OUTB bits to 1 DO ' Main DO...LOOP GOSUB Check_Qtis ' Get QTI states SELECT qtis ' Control servo speeds/directions CASE %1000 ' Rotate right PULSOUT 13, 650 PULSOUT 12, 650 CASE %1100 ' Pivot right PULSOUT 13, 750 PULSOUT 12, 650 CASE %0100 ' Curve right PULSOUT 13, 800 PULSOUT 12, 650 CASE %0110 ' Straight ahead PULSOUT 13, 850 PULSOUT 12, 650 CASE %0010 ' Curve left PULSOUT 13, 850 PULSOUT 12, 700 CASE %0011 ' Pivot left PULSOUT 13, 850 PULSOUT 12, 750 CASE %0001 ' Rotate left PULSOUT 13, 850 PULSOUT 12, 850 CASE ELSE ' Do nothing PAUSE 3 ENDSELECT LOOP Check_Qtis: ' Result -> qtis variable. 0 means white surface, 1 means black surface. DIRB = %1111 ' P7..P4 -> output PAUSE 0 ' Delay = 230 us DIRB = %0000 ' P7..P4 -> input PAUSE 0 ' Delay = 230 us ' PULSOUT UnusedPin, 0 ' Delays = 208 + (Duration*2) us qtis = INB ' Store QTI outputs in INB RETURN
Figure 12 - Sample "S"-Shaped Boe-Bot Course
With line following competitions, it's important to test the various conditions your robot will encounter to determine how much room there is in the sample interval for other sensors. Here are just a few basic test examples. To fully prepare for a contest, you'll especially want to test the most severe obstacles the course might have.
With no PAUSE command in the DO...LOOP, the sample interval is 9 ms and the sampling rate.
Adapting the Calibrate_Qtis subroutine from Applied Robotics with the SumoBot (#27403) will take some work, but it's worth it. Start by reading Chapter 3, Activities #2, #3, and #4. The Calibrate_Qtis subroutine is in Activity #4. It takes RCTIME measurements on both of the SumoBot's QTIs. Then, it calculates the average of the two QTI measurements and divides that result by 4. When the QTI sees its infrared reflection, the voltage at the R pin decays very quickly. With a black surface, it takes a lot longer. By setting the threshold time at 1/4 of the average time it takes a QTI to discharge to 1.4 V over a black surface, it makes the QTIs fairly immune to fluctuations in ambient light that occur as the Boe-Bot changes direction.
Dividing the average dark QTI voltage decay time by 4 was arrived at by trial and error. With some more experimentation, you might find that you prefer to divide by 3, 5, or some other value instead.
Incorporating the Calibrate_Qtis subroutine into your program involves keeping a running total of four RCTIME measurements (one for each QTI) and dividing by 4 to take the average. After calculating the average amount of time it takes the QTIs to decay over black, divide the averaged result by 4 (a second time) to set the decay threshold time. Instead of dividing by 4 twice, you can alternately divide the sum of the dark QTI decay times by 16. The program's Check_Qtis subroutine should then use PULSOUT UnusedPin, threshold instead of PAUSE 0 between the DIRB = %1111 and DIRB = %0000 commands.
An initialization routine should also be added to a modified version of LineFollowWithCheckQtis.bs2. Whenever the program starts or is reset, its QTIs should all be over the black electrical tape until the calibration is done. The amount of time it takes to complete the calibration after a reset will vary depending on how large the program is. So, it's a good idea to add a speaker or LED circuit to signal when the calibration is complete and the Boe-Bot is done self-calibrating and ready to be placed on the course.
The QTIs in this activity were mounted very close together and right below the front of the Boe-Bot. This is just one example of sensor placement and coding strategy; there are many others you can (and should) try. For example, pairs of QTIs could be placed several inches apart and several inches in front of the Boe-Bot. The code could be modified so that the Boe-Bot rolls forward so long as it does not detect a line and adjusts when it detects that the line is either under its left or right QTIs. This is just one of many different setups that might turn out to be optimum for line following performance. Especially if you are preparing for a contest, build a variety of courses and then test various programs and mechanical setups to figure out what's going to work the best. Also make sure to verify that the sampling rate can keep up with the most extreme changes in line direction that you would expect to see on the course.
Links
[1] https://learn.parallax.com/sites/default/files/Files/Docs/Projects/FourQTI/Line-Follow-with-4-QTI.zip