Documents »Pololu 3pi Robot User's Guide
View document on multiple pages.
1. IntroductionThe Pololu 3pi robot is a small, high-performance, autonomous robot designed to excel in line-following and line-maze-solving competitions. Powered by four AAA batteries (not included) and a unique power system that runs the motors at a regulated 9.25 V, 3pi is capable of speeds up to 100 cm/second while making precise turns and spins that don’t vary with the battery voltage. This results in highly consistent and repeatable performance of well-tuned code even as the batteries run low. The robot comes fully assembled with two micro metal gearmotors, five reflectance sensors, an 8×2 character LCD, a buzzer, three user pushbuttons, and more, all connected to a user-programmable AVR microcontroller. The 3pi measures approximately 3.7 inches (9.5 cm) in diameter and weighs 2.9 oz (83 g) without batteries. The 3pi is based on an Atmel ATmega168 microcontroller running at 20 MHz with 16KB of flash program memory and 1KB data memory. The use of the ATmega168 microcontroller makes the 3pi compatible with the popular Arduino development platform. Free C and C++ development tools are also available, and an extensive set of libraries make it a breeze to interface with all of the integrated hardware. Sample programs are available to show how to use the various 3pi components, as well as how to perform more complex behaviors such as line following and maze solving. For a Spanish version of this document, please see Pololu 3pi Robot Guia Usuario (2483k pdf) (provided by customer Jaume B.). 2. Contacting PololuYou can check the 3pi product page for additional information, including pictures, videos, example code, and other resources. We would be delighted to hear from you about any of your projects and about your experience with the 3pi robot. You can contact us directly or post on our forum. Tell us what we did well, what we could improve, what you would like to see in the future, or share your code with other 3pi users. 3. Important Safety Warning and Handling PrecautionsThe 3pi robot is not intended for young children! Younger users should use this product only under adult supervision. By using this product, you agree not to hold Pololu liable for any injury or damage related to the use or to the performance of this product. This product is not designed for, and should not be used in, applications where the malfunction of the product could cause injury or damage. Please take note of these additional precautions:
4. Getting Started with Your 3pi RobotGetting started with your 3pi can be as simple as taking it out of the box, adding batteries, and turning it on. The 3pi ships with a demo program that will give you a brief tour of its features.
The following subsections will give you all the information you need to get your 3pi up and running! 4.a. What You Will NeedThe following materials are necessary for getting started with your 3pi:
You might find the following materials useful in creating an environment for your robot to explore:
4.b. Powering Up Your 3pi
The first step in using your new 3pi robot is to insert four AAA batteries into the battery holders. To do this you will need to remove the LCD. Pay attention to the LCD’s orientation as you will want to plug it back in this way when you are done. With the LCD removed your 3pi should look like the picture to the right. Once the batteries are in place, you should return the LCD to its position over the rear battery holder. Make sure each male LCD header pin goes into a corresponding female socket. Next, push the power button (located on the left side of the rear battery pack) to turn on your 3pi. You should see the two blue power LEDs on the underside of the 3pi light, and the 3pi should begin running its preloaded demo program. You can simply push the power button again to turn the 3pi off, and you can push the reset button (located just below the power button) to reset the program the robot is running. 4.c. Using the Preloaded Demo ProgramYour 3pi comes preloaded with a program that demonstrates most of its features and allows you to test that it is working correctly. When you first turn on your 3pi, you will hear a beep and see the words “Pololu 3pi Robot”, then “Demo Program” appear, indicating that you are running the demo program. If you hear a beep but do not see any text on the LCD, you may need to adjust the contrast potentiometer on the underside of the board. When the program has started successfully, press the B button to proceed to the main menu. Press C or A to scroll forward or backward through the menu, and press B to make a selection or to exit one of the demos. There are seven demos accessible from the menu:
The source code for the demo program is included with the Pololu AVR C/C++ Library described in Section 6. After downloading and unpacking the library zip file, the demo program can be found in the folder 4.d. Included Accessories
The 3pi robot ships with two through-hole red LEDs and two through-hole green LEDs. There are connection points for three optional LEDs on your 3pi: one next to the power button to indicate when the 3pi is on and two user-controllable LED ports near the front edge of the robot. Using these LEDs is completely optional as the 3pi will function just fine without them. You can customize your 3pi by choosing your desired combination of red and green LEDs, or you can even use your own LEDs if you want more color/brightness options. Note that you should only add LEDs if you are comfortable soldering, and you should take care to avoid desoldering any of the components near the through-hole LED pads. LEDs are polarized, so be sure to solder them such that the longer lead connects to the pad marked with the +. Before you solder them in you can press-fit them in place and check to make sure they light as expected. Once soldered in place, carefully trim off the excess portion of the LED leads. Your 3pi also ships with three shorting blocks of each color: blue, red, yellow, black. This means you can customize your 3pi by selecting the shorting block color you most prefer, or you can use a mixture of colors! 5. How Your 3pi Works5.a. BatteriesIntroduction to Batteries
The power system on the 3pi begins with the batteries, so it is important to understand how your batteries work. A battery contains a carefully controlled chemical reaction that pulls electrons in from the positive (+) terminal and pushes them out of the negative (-) terminal. The most common type is the alkaline battery, which is based on a reaction between zinc and manganese through a potassium hydroxide solution. Once alkaline batteries are completely discharged, they cannot be reused. For the 3pi, we recommend rechargeable nickel-metal-hydride (NiMH) batteries, which can be recharged over and over. NiMH batteries are based on a different chemical reaction from alkaline batteries, but you don’t need to know anything about the chemical details to use a battery: everything you need to know about it is measured with a few simple numbers. The first is the strength with which the electrons are pushed, which we measure in volts (V), the units of electric potential. An NiMH battery has a voltage of about 1.2 V. To understand how much power you can get out of a battery, you also need to know how many electrons the battery can push per second – this is the electric current, measured in amps (A). A current of 1 A corresponds to about 6×1018 electrons flowing out one side and in to the other each second, which is such a huge number that it’s easier to talk about it just in terms of amps. 1 A is also a typical current that a medium-sized motor might use, and it’s a current that will put a significant strain on small (AAA) batteries. For any battery, if you attempt to draw more and more current, the voltage produced by the battery will drop, eventually dropping all the way to zero at the short circuit current: the current that flows if you connect one side directly to the other with a thick wire. (Don’t try this! The wire might overheat and melt, and the battery could explode.) The following graph shows a good model of how the voltage on a typical battery drops as the current goes up:
The power put out by a battery is measured by multiplying the volts by the amps, giving a measurement in watts (W). For example, at the point marked in the graph, we have a voltage of 0.9 V and a current of 0.6 A, this means that the power output is 0.54 W. If you want more power, you need to add more batteries, and there are two ways to do it: parallel and series configurations. When batteries are connected in parallel, with all of their positive terminals tied together and all of their negative terminals tied together, the voltage stays the same, but the maximum current output is multiplied by the number of batteries. When they are connected in series, with the positive terminal of one connected to the negative terminal of the next, the maximum current stays the same while the voltage multiplies. Either way, the maximum power output will be multiplied by the number of batteries. Think about two people using two buckets to lift water from a lake to higher ground. If they stand next to each other (working in parallel), they will be able to lift the water to the same height as before, while delivering twice the amount of water. If one of them stands uphill from the other, they can work together (in series) to life the water twice as high, but at the same rate as a single person. In practice, we only connect batteries in series. This is because different batteries will always have slightly different voltages, and if they are connected in parallel, the stronger battery will deliver current to the weaker battery, wasting power even when there is nothing else in the circuit. If we want more current, we can use bigger batteries: AAA, AA, C, and D batteries of the same type all have the same voltage, but they can put out very different amounts of current. The total amount of energy in any battery is limited by the chemical reaction: once the chemicals are exhausted, the battery will stop producing power. This happens gradually: the voltage and current produced by a battery will steadily drop until the energy runs out, as shown in the graph below:
A rough measure of the amount of energy stored in a battery is given by its milliamp-hour (mAH) rating, which specifies how long the battery will last at a given discharge rate. The mAH rating is the discharge rate multiplied by how long the battery lasts: if you draw current at a rate of 200 mA (0.2 A), and the battery lasts for 3 hours, you would call it a 600 mAH battery. If you discharge the same battery at 600 mA, you would get about an hour of operation (however, battery capacity tends to decline with faster discharge rates, so you might only get 50 minutes). 5.b. Power managementBattery voltage drops as the batteries are used up, but many electrical components require a specific voltage. A special kind of component called a voltage regulator helps out by converting the battery voltage to a constant, specified voltage. For a long time, 5 V has been the most common regulated voltage used in digital electronics; this is also called TTL level. The microcontroller and most of the circuitry in the 3pi operate at 5 V, so voltage regulation is essential. There are two basic types of voltage regulators:
The power management subsystem built into the 3pi is shown in this block diagram:
The voltage of 4 x AAA cells can vary between 3.5 – 5.5 V (and even to 6 V if alkalines are used). This means it’s not possible simply to regulate the voltage up or down to get 5 V. Instead, in the 3pi, a switching regulator first boosts the battery voltage up to 9.25 V (Vboost), and a linear regulator regulates Vboost back down to 5 V (VCC). Vboost powers the motors and the IR LEDs in the line sensors, while VCC is used for the microcontroller and all digital signals. Using Vboost for the motors and sensors gives the 3pi three unique performance advantages over typical robots, which use battery power directly:
One other interesting thing about this power system is that instead of gradually running out of power like most robots, the 3pi will operate at maximum performance until it suddenly shuts off. This can take you by surprise, so you might want your 3pi to monitor its battery voltage.
A simple circuit for monitoring battery voltage is built in to the 3pi. Three resistors, shown in the circuit at right, comprise a voltage divider that outputs a voltage equal to two-thirds of the battery voltage, which will always be safely below the main microcontroller’s maximum analog input voltage of 5 V. For example, at a battery voltage of 4.8 V, the battery voltage monitor port ADC6 will be at a level of 3.2 V. Using 10-bit analog-to-digital conversion, where 5 V is read as a value of 1023, 3.2 V is read as a value of 655. To convert it back to the actual battery voltage, multiply this number by 5000 mV×3/2 and divide by 1023. This is handled conveniently by the
unsigned int read_battery_millivolts()
{
return readAverage(6,10)*5000L*3/2/1023;
}
5.c. Motors and Gearboxes
A motor is a machine that converts electrical energy to motion. There are many different kinds of motors, but the most important for low-cost robotics is the brushed DC motor, which is the type used on the 3pi. A brushed DC motor typically has permanent magnets on the outside and several electromagnetic coils mounted on the motor shaft (armature). The “brushes” are sliding pieces of metal that switch the power from one coil to the next as the shaft turns so that magnetic attraction between the coil and the magnets continuously pulls the motor in the same direction. The primary values that describe a running motor are its speed, measured in rpm, and its torque, measured in kg·cm or oz·in (pronounced “ounce-inches”). The units for torque show the dependence on both force and distance; for example, a motor that produces 6 oz·in of torque can product a force of 6 oz. with a 1-inch lever arm, 3 oz. with a 2-inch lever, and so on. Multiplying the torque and speed (measured at the same time) give us the power delivered by a motor. We see, therefore, that a motor with twice the speed and half the torque as another has the same power output. Every motor has a maximum speed (when no force is applied) and a maximum torque (when the motor is completely stopped). We call these the free-running speed and the stall torque. Naturally, a motor uses the least current when no force is applied to it, and the current drawn from the batteries goes up until it stalls, so the free-running current and stall current are also important parameters characterizing the motor. The stall current is usually much higher than the free-running current, as shown in the graph below:
The free-running speed of a small DC motor is usually many thousands of rotations per minute (rpm), much high than the speed we want the wheels of a robot to turn. A gearbox is a system of gears that converts the high-speed, low-torque output of the motor into a lower-speed, higher-torque output that is a much better suited for driving a robot. The gear ratio used on the 3pi is 30:1, which means that for every 30 turns of the motor shaft, the output shaft turns once. This reduces the speed by a factor of 30, and (ideally) increases the torque by a factor of 30. The resulting parameters of the 3pi motors are summarized in this table:
The two wheels of the 3pi each have a radius of 0.67 in, which means that the maximum force it can produce with two motors when driving forward is 2×6/0.67 = 18 oz. The 3pi weighs about 7 oz with batteries, so the motors are strong enough to lift the 3pi up a vertical slope or accelerate it at 2 g (twice the acceleration of gravity). The actual performance is limited by the friction of the tires: on a steep enough slope, the wheels will slip before they stall – in practice, this happens when the slope is around 30-40°. Driving a motor with speed and direction controlOne nice thing about a DC motor is that you can change the direction of rotation by switching the polarity of the applied voltage. If you have a loose battery and motor, you can see this for yourself by making connections one way and then turning the battery around to make the motor spin in reverse. Of course, you don’t want take the batteries out of your 3pi and reverse them every time it needs to back up – instead, a special arrangement of four switches, called an H-bridge, allows the motor to spin either backwards or forwards. Here is a diagram that shows how the H-bridge works:
If switches 1 and 4 are closed (the center picture), current flows through the motor from left to right, and the motor spins forward. Closing switches 2 and 3 causes the current to reverse direction and the motor to spin backward. An H-bridge can be constructed with mechanical switches, but most robots, including the 3pi, use transistors to switch the current electronically. The H-bridges for both motors on the 3pi are all built into a single motor driver chip, the TB6612FNG, and output ports of the main microcontroller operate the switches through this chip. Here is a table showing how output ports PD5 and PD6 on the microcontroller control the transistors of motor M1:
Motor M2 is controlled through the same logic by ports PD3 and PB3:
Speed control is achieved by rapidly switching the motor between two states in the table. Suppose we keep PD6 high (at 5 V, also called a logical “1”) and have PD5 alternate quickly between low (0 V or “0”) and high. The motor driver will switch between the “forward” and “brake” states, causing M1 to turn forward at a reduced speed. For example, if PD6 is high two thirds of the time (a 67% duty cycle), then M1 will turn at approximately 67% of its full speed. Since the motor voltage is a series of pulses of varying width, this method of speed control is called pulse-width modulation (PWM). An example series of PWM pulses is shown in the graph at right: as the size of the pulses decreases from 100% duty cycle down to 0%, the motor speed decreases from full speed down to a stop. In the 3pi, speed control is accomplished using special PWM outputs of the main microcontroller that are linked to the internal timers Timer0 and Timer2. This means that you can set the PWM duty cycle of the two motors once, and the hardware will continue to produce the PWM signal, in the background, without any further attention. The set_motors(171,84); To get a slowly decreasing PWM sequence like the one shown in the graph, you would need to write a loop that gradually decreases the motor speed over time. Turning with a differential driveThe 3pi has an independent motor and wheel on each side, which enables a method of locomotion called differential drive. It is also known as a “tank drive” since this is how a tank drives. It is completely unlike the steering system of automobile, which uses a single drive motor and steerable front wheels. Turning with a differential drive is accomplished by running the two motors at different speeds. In the
5.d. Digital inputs and sensorsThe microcontroller at the heart of the 3pi, an Atmel AVR mega168, has a number of pins which can be configured as digital inputs: they are read by your program as a 1 or a 0 depending on whether the voltage is high (above about 2 V) or low. Here is the circuit for one of the pushbutton inputs:
Normally, the pull-up resistor R (20-50 k) brings the voltage on the input pin to 5 V, so it reads as a 1, but pressing the button connects the input to ground (0 V) through a 1 k resistor, which is much lower than the value of R. This brings the input voltage very close to 0 V, so the pin reads as a 0. Without the pull-up resistor, the input would be “floating” when the button is not pressed, and the value read could be affected by residual voltage on the line, interference from nearby electrical signals, or even distant lightning. Don’t leave an input floating unless you have a good reason. Since the pull-up resistors are important, they are included within the AVR – the resistor R in the picture represents this internal pull-up, not a discrete part on the 3pi circuit board. A more complicated use for the digital inputs is in the reflectance sensors. Here is the circuit for the 3pi’s leftmost reflectance sensor, which is connected to pin PC0:
The sensing element of the reflectance sensor is the phototransistor shown in the left half of U4, which is connected in series with capacitor C21. A separate connection leads through resistor R12 to pin PC0. This circuit takes advantage of the fact the digital inputs of the AVR can be reconfigured as digital outputs on the fly. A digital output presents a voltage of 5 V or 0 V, depending on whether it is set to a 1 or a 0 by your program. The way it works is that the pin is alternately set to a 5 V output and then a digital input. The capacitor stores charge temporarily, so that the input reads as a 1 until most of the stored charge has flowed through the phototransistor. Here is an oscilloscope trace showing the voltage on the capacitor (yellow) dropping as its charge flows through the phototransistor, and the resulting digital input value of pin PC0 (blue):
The rate of current flow through the phototransistor depends on the light level, so that when the robot is over a bright white surface, the value returns to 0 much more quickly than when it is over a black surface. The trace shown above was taken when the sensor was on the edge between a black surface and a white one – this is what it looks like on pure white:
The length of time that the digital input stays at 1 is very short when over white, and very long when over black. The function
time = 0;
last_time = TCNT2;
while (time < _maxValue)
{
// Keep track of the total time.
// This implicity casts the difference to unsigned char, so
// we don't add negative values.
unsigned char delta_time = TCNT2 - last_time;
time += delta_time;
last_time += delta_time;
// continue immediately if there is no change
if (PINC == last_c)
continue;
// save the last observed values
last_c = PINC;
// figure out which pins changed
for (i = 0; i < _numSensors; i++)
{
if (sensor_values[i] == 0 && !(*_register[i] & _bitmask[i]))
sensor_values[i] = time;
}
}
This piece of code is found in the file 6. Programming Your 3piTo do more with your 3pi than explore the demo program, you will need to program it, which requires an external AVR ISP programmer such as our Orangutan USB programmer. Your first step should be to set up your programmer by following its installation instructions. If you are using the Orangutan USB programmer, please see its user’s guide. Next you will need software that can compile your programs and transfer them to your 3pi via your programmer. We recommend you download two software packages for this:
Note: You can also program your 3pi using the Arduino IDE and an external ICSP programmer, such as our Orangutan USB programmer. For instructions on this approach, please see our guide: Programming Orangutans and the 3pi Robot from the Arduino Environment. The rest of this guide will be based on AVR Studio. For more general instructions on using the Pololu C/C++ libraries with our AVR-based robot boards, including Linux installation instructions, see the Pololu AVR C/C++ Library User’s Guide. 6.a. Downloading and Installing the C/C++ LibraryThe Pololu C/C++ AVR Library makes it easy for you to use the advanced features of your 3pi; the library is used for all of the examples in the following sections. For your convenience, the source code for these examples and the demo program is included with the library. To begin the installation process for the Pololu AVR C/C++ Library, you will need to download one of the following zip files:
We recommend the precompiled version, which will be easier to install for most people. If you have trouble installing the precompiled version, or you are interested in taking a closer look at the source code, please download the source distribution and see Section 4 of the Pololu AVR C/C++ Library User’s Guide for compilation instructions. Open the .zip file and click “Extract all” to extract the Pololu AVR Library files. A directory called “libpololu-avr” will be created. Determine the location of your avr-gcc files. In Windows, they will usually be in a folder such as: Next, copy Finally, copy the entire
You are now ready to use the Pololu AVR library with your 3pi. Note: To learn more about the functions in the Pololu AVR Library and how to use them, please see the Pololu AVR Library Command Reference. 6.b. Compiling a Simple ProgramA very simple demo program for the 3pi is available in the folder
#include <pololu/3pi.h>
int main()
{
print("Hello!");
play("L16 ceg>c");
while(1)
{
red_led(0);
green_led(1);
delay_ms(100);
red_led(1);
green_led(0);
delay_ms(100);
}
return 0;
}
Navigate to the
To compile this program, select Build > Build or press F7. Look for warnings and errors (indicated by yellow and red dots) in the output displayed below. If the program compiles successfully, the message “Build succeeded with 0 Warnings…” will appear at the end of the output, and a file
Connect your programmer to your computer and to the ISP port of your 3pi, and turn on the 3pi’s power by pressing the button labeled POWER. If you are using the Pololu Orangutan Programmer, the green status LED close to the USB connector should be on, while the other two LEDs should be off, indicating that the programmer is ready. Your programmer must be installed correctly before you use it. If you are using the Orangutan USB programmer, please see its user’s guide for installation instructions.
Select Tools > Program AVR > Connect to connect to the programmer. For the Orangutan Programmer, the default options of “STK500 or AVRISP” and “Auto” should be fine, so click Connect and the AVRISP programming window should appear. You will use AVRISP to load
If your 3pi was successfully programmed, you should hear a short tune, see the message “Hello!” on the LCD, and the LEDs on the board should blink. If you hear the tune and see the lights flashing, but nothing appears on the LCD, make sure that the LCD is correctly plugged in to the 3pi, and try adjusting the contrast using the small potentiometer on the underside of the 3pi, closest to the ball caster. 7. Example Project #1: Line Following7.a. About Line Following
Now that you have learned how to compile a simple program for the 3pi, it’s time to teach your robot do something more complicated. In this example project, we’ll show you how to make your 3pi follow a black line on a white background, by coordinating its sensors and motors. Line following is a great introduction to robot programming, and it makes a great contest: it’s easy to build a line-following course, the rules are simple to understand, and it’s not hard to program your 3pi to follow a line. Optimizing your program to make your 3pi zoom down the line at the highest speed possible, however, is a challenge that can introduce you to some advanced programming concepts. A great looking line following course can be constructed for a few dollars in a couple of hours at home. For information on building your own course, see our tutorial on Building Line Following and Line Maze Courses. 7.b. A Simple Line-Following Algorithm for 3piA simple line following program for the 3pi is available in the folder Note: An Arduino-compatible version of this sample program can be downloaded as part of the Pololu Arduino Libraries (see Section 5.g). The source code demonstrates a variety of different features of the 3pi, including the line sensors, motors, LCD, battery voltage monitor, and buzzer. The program has two phases. The first phase of the program is the initialization and calibration phase, which is handled by the function intitialize(). This function is called once, at the beginning of the main() function, before anything else happens, and it takes care of the following steps:
In the second phase of the program, your 3pi will take a sensor reading and set the motor speed appropriately based on the reading. The general idea is that if the robot is off on either side, it should turn to get back on, but if it’s on the line, it should try to drive straight ahead. The following steps occur inside of a
To open the program in AVR studio, you may go to
You might also want to:
The entire source code to this simple line following program is presented below, for your reference.
/*
* 3pi-linefollower - demo code for the Pololu 3pi Robot
*
* This code will follow a black line on a white background, using a
* very simple algorithm. It demonstrates auto-calibration and use of
* the 3pi IR sensors, motor control, bar graphs using custom
* characters, and music playback, making it a good starting point for
* developing your own more competitive line follower.
*
* http://www.pololu.com/docs/0J21
* http://www.pololu.com
* http://forum.pololu.com
*
*/
// The 3pi include file must be at the beginning of any program that
// uses the Pololu AVR library and 3pi.
#include <pololu/3pi.h>
// This include file allows data to be stored in program space. The
// ATmega168 has 16k of program space compared to 1k of RAM, so large
// pieces of static data should be stored in program space.
#include <avr/pgmspace.h>
// Introductory messages. The "PROGMEM" identifier causes the data to
// go into program space.
const char welcome_line1[] PROGMEM = " Pololu";
const char welcome_line2[] PROGMEM = "3\xf7 Robot";
const char demo_name_line1[] PROGMEM = "Line";
const char demo_name_line2[] PROGMEM = "follower";
// A couple of simple tunes, stored in program space.
const char welcome[] PROGMEM = ">g32>>c32";
const char go[] PROGMEM = "L16 cdegreg4";
// Data for generating the characters used in load_custom_characters
// and display_readings. By reading levels[] starting at various
// offsets, we can generate all of the 7 extra characters needed for a
// bargraph. This is also stored in program space.
const char levels[] PROGMEM = {
0b00000,
0b00000,
0b00000,
0b00000,
0b00000,
0b00000,
0b00000,
0b11111,
0b11111,
0b11111,
0b11111,
0b11111,
0b11111,
0b11111
};
// This function loads custom characters into the LCD. Up to 8
// characters can be loaded; we use them for 7 levels of a bar graph.
void load_custom_characters()
{
lcd_load_custom_character(levels+0,0); // no offset, e.g. one bar
lcd_load_custom_character(levels+1,1); // two bars
lcd_load_custom_character(levels+2,2); // etc...
lcd_load_custom_character(levels+3,3);
lcd_load_custom_character(levels+4,4);
lcd_load_custom_character(levels+5,5);
lcd_load_custom_character(levels+6,6);
clear(); // the LCD must be cleared for the characters to take effect
}
// This function displays the sensor readings using a bar graph.
void display_readings(const unsigned int *calibrated_values)
{
unsigned char i;
for(i=0;i<5;i++) {
// Initialize the array of characters that we will use for the
// graph. Using the space, an extra copy of the one-bar
// character, and character 255 (a full black box), we get 10
// characters in the array.
const char display_characters[10] = {' ',0,0,1,2,3,4,5,6,255};
// The variable c will have values from 0 to 9, since
// calibrated values are in the range of 0 to 1000, and
// 1000/101 is 9 with integer math.
char c = display_characters[calibrated_values[i]/101];
// Display the bar graph character.
print_character(c);
}
}
// Initializes the 3pi, displays a welcome message, calibrates, and
// plays the initial music.
void initialize()
{
unsigned int counter; // used as a simple timer
unsigned int sensors[5]; // an array to hold sensor values
// This must be called at the beginning of 3pi code, to set up the
// sensors. We use a value of 2000 for the timeout, which
// corresponds to 2000*0.4 us = 0.8 ms on our 20 MHz processor.
pololu_3pi_init(2000);
load_custom_characters(); // load the custom characters
// Play welcome music and display a message
print_from_program_space(welcome_line1);
lcd_goto_xy(0,1);
print_from_program_space(welcome_line2);
play_from_program_space(welcome);
delay_ms(1000);
clear();
print_from_program_space(demo_name_line1);
lcd_goto_xy(0,1);
print_from_program_space(demo_name_line2);
delay_ms(1000);
// Display battery voltage and wait for button press
while(!button_is_pressed(BUTTON_B))
{
int bat = read_battery_millivolts();
clear();
print_long(bat);
print("mV");
lcd_goto_xy(0,1);
print("Press B");
delay_ms(100);
}
// Always wait for the button to be released so that 3pi doesn't
// start moving until your hand is away from it.
wait_for_button_release(BUTTON_B);
delay_ms(1000);
// Auto-calibration: turn right and left while calibrating the
// sensors.
for(counter=0;counter<80;counter++)
{
if(counter < 20 || counter >= 60)
set_motors(40,-40);
else
set_motors(-40,40);
// This function records a set of sensor readings and keeps
// track of the minimum and maximum values encountered. The
// IR_EMITTERS_ON argument means that the IR LEDs will be
// turned on during the reading, which is usually what you
// want.
calibrate_line_sensors(IR_EMITTERS_ON);
// Since our counter runs to 80, the total delay will be
// 80*20 = 1600 ms.
delay_ms(20);
}
set_motors(0,0);
// Display calibrated values as a bar graph.
while(!button_is_pressed(BUTTON_B))
{
// Read the sensor values and get the position measurement.
unsigned int position = read_line(sensors,IR_EMITTERS_ON);
// Display the position measurement, which will go from 0
// (when the leftmost sensor is over the line) to 4000 (when
// the rightmost sensor is over the line) on the 3pi, along
// with a bar graph of the sensor readings. This allows you
// to make sure the robot is ready to go.
clear();
print_long(position);
lcd_goto_xy(0,1);
display_readings(sensors);
delay_ms(100);
}
wait_for_button_release(BUTTON_B);
clear();
print("Go!");
// Play music and wait for it to finish before we start driving.
play_from_program_space(go);
while(is_playing());
}
// This is the main function, where the code starts. All C programs
// must have a main() function defined somewhere.
int main()
{
unsigned int sensors[5]; // an array to hold sensor values
// set up the 3pi
initialize();
// This is the "main loop" - it will run forever.
while(1)
{
// Get the position of the line. Note that we *must* provide
// the "sensors" argument to read_line() here, even though we
// are not interested in the individual sensor readings.
unsigned int position = read_line(sensors,IR_EMITTERS_ON);
if(position < 1000)
{
// We are far to the right of the line: turn left.
// Set the right motor to 100 and the left motor to zero,
// to do a sharp turn to the left. Note that the maximum
// value of either motor speed is 255, so we are driving
// it at just about 40% of the max.
set_motors(0,100);
// Just for fun, indicate the direction we are turning on
// the LEDs.
left_led(1);
right_led(0);
}
else if(position < 3000)
{
// We are somewhat close to being centered on the line:
// drive straight.
set_motors(100,100);
left_led(1);
right_led(1);
}
else
{
// We are far to the left of the line: turn right.
set_motors(100,0);
left_led(0);
right_led(1);
}
}
// This part of the code is never reached. A robot should
// never reach the end of its program, or unpredictable behavior
// will result as random code starts getting executed. If you
// really want to stop all actions at some point, set your motors
// to 0,0 and run the following command to loop forever:
//
// while(1);
}
7.c. Advanced Line Following with 3pi: PID ControlA more advanced line following program for the 3pi is available in the folder Note: An Arduino-compatible version of this sample program can be downloaded as part of the Pololu Arduino Libraries (see Section 5.g). The technique used in this example program, known as PID control, addresses some of the problems that you might have noticed with the previous example, and it should allow you to greatly increase your robot’s line following speed. Most importantly, PID control uses continuous functions to compute the motor speeds, so that the jerkiness of the previous example can be replaced by a smooth response. PID stands for Proportional, Integral, Derivative; these are the three input values used in a simple formula to compute the speed that your robot should turn left or right.
Here is the section of code that computes the PID input values:
// Get the position of the line. Note that we *must* provide
// the "sensors" argument to read_line() here, even though we
// are not interested in the individual sensor readings.
unsigned int position = read_line(sensors,IR_EMITTERS_ON);
// The "proportional" term should be 0 when we are on the line.
int proportional = ((int)position) - 2000;
// Compute the derivative (change) and integral (sum) of the
// position.
int derivative = proportional - last_proportional;
integral += proportional;
// Remember the last position.
last_proportional = proportional;
Note that we cast the variable Each of these input values provides a different kind of information. The next step is a simple formula that combines all of the values into one variable, which is then used to determine the motor speeds:
// Compute the difference between the two motor power settings,
// m1 - m2. If this is a positive number the robot will turn
// to the right. If it is a negative number, the robot will
// turn to the left, and the magnitude of the number determines
// the sharpness of the turn.
int power_difference = proportional/20 + integral/10000 + derivative*3/2;
// Compute the actual motor settings. We never set either motor
// to a negative value.
const int max = 60;
if(power_difference > max)
power_difference = max;
if(power_difference < -max)
power_difference = -max;
if(power_difference < 0)
set_motors(max+power_difference, max);
else
set_motors(max, max-power_difference);
The values 1/20, 1/10000, and 3/2 are adjustable parameters that determine how your 3pi will react to the line. In general, increasing these PID parameters will make 8. Example Project #2: Maze Solving8.a. Solving a Line Maze
The next step up from simple line following is to teach your 3pi to navigate paths with sharp turns, dead ends, and intersections. Make a complicated network of intersecting black lines, add a circle to represent the goal, and you have a line maze, which is a challenging environment for a robot to explore. In a line maze contest, robots travel as quickly as possible along the lines from a designated start to the goal, keeping track of the intersections that they pass along the way. Robots are given several chances to run the maze, so that they can follow the fastest possible path after learning about all of the dead ends. The mazes that we will teach you to solve in this tutorial have one special feature: they have no loops. That is, there is no way to re-visit any point on the maze without retracing your steps. Solving this type of maze is much easier than solving a looped maze, since a simple strategy allows you to explore the entire maze. We’ll talk about that strategy in the next section. We also usually construct our mazes using only straight lines drawn on a regular grid, but this is mostly just to make the course easy to reproduce – the maze-solving strategy described in this tutorial does not require these features. For information on building your own course, see our tutorial on Building Line Following and Line Maze Courses. 8.b. Working with Multiple C Files in AVR StudioThe C source code for an example line maze solver is available in the folder Note: An Arduino-compatible version of this sample program can be downloaded as part of the Pololu Arduino Libraries (see Section 5.g) The Arduino sample sketch is all contained within a single file. This program is much more complicated than the examples you have seen so far, so we have split it up into multiple files. Using multiple files makes it easier for you to keep track of your code. For example, the file
#include <pololu/3pi.h>
// Turns according to the parameter dir, which should be 'L', 'R', 'S'
// (straight), or 'B' (back).
void turn(char dir)
{
switch(dir)
{
case 'L':
// Turn left.
set_motors(-80,80);
delay_ms(200);
break;
case 'R':
// Turn right.
set_motors(80,-80);
delay_ms(200);
break;
case 'B':
// Turn around.
set_motors(80,-80);
delay_ms(400);
break;
case 'S':
// Don't do anything!
break;
}
}
The first line of the file, like any C file that you will be writing for the 3pi, contains an include command that gives you access to the functions in the Pololu AVR Library. Within turn(), we then use the library functions delay_ms() and set_motors() to perform left turns, right turns, and U-turns. Straight “turns” are also handled by this function, though they don’t require us to take any action. The motor speeds and the timings for the turns are parameters that needed to be adjusted for the 3pi; as you work on making your maze solver faster, these are some of the numbers that you might need to adjust. To access this function from other C files, we need a “header file”, which is called void turn(char dir); This line declares the turn() function without actually including a copy of its code. To access the declaration, each C file that needs to call turn() adds the following line: #include "turn.h" Note the double-quotes being used instead of angle brackets. This signifies to the C compiler that the header file is in the project directory, rather than being a system header file like The file
void follow_segment()
{
int last_proportional = 0;
long integral=0;
while(1)
{
// Normally, we will be following a line. The code below is
// similar to the 3pi-linefollower-pid example, but the maximum
// speed is turned down to 60 for reliability.
// Get the position of the line.
unsigned int sensors[5];
unsigned int position = read_line(sensors,IR_EMITTERS_ON);
// The "proportional" term should be 0 when we are on the line.
int proportional = ((int)position) - 2000;
// Compute the derivative (change) and integral (sum) of the
// position.
int derivative = proportional - last_proportional;
integral += proportional;
// Remember the last position.
last_proportional = proportional;
// Compute the difference between the two motor power settings,
// m1 - m2. If this is a positive number the robot will turn
// to the left. If it is a negative number, the robot will
// turn to the right, and the magnitude of the number determines
// the sharpness of the turn.
int power_difference = proportional/20 + integral/10000 + derivative*3/2;
// Compute the actual motor settings. We never set either motor
// to a negative value.
const int max = 60; // the maximum speed
if(power_difference > max)
power_difference = max;
if(power_difference < -max)
power_difference = -max;
if(power_difference < 0)
set_motors(max+power_difference,max);
else
set_motors(max,max-power_difference);
// We use the inner three sensors (1, 2, and 3) for
// determining whether there is a line straight ahead, and the
// sensors 0 and 4 for detecting lines going to the left and
// right.
if(sensors[1] < 100 && sensors[2] < 100 && sensors[3] < 100)
{
// There is no line visible ahead, and we didn't see any
// intersection. Must be a dead end.
return;
}
else if(sensors[0] > 200 || sensors[4] > 200)
{
// Found an intersection.
return;
}
}
}
Between the PID code and the intersection detection, there are now about six more parameters that could be adjusted. We’ve picked values here that allow 3pi to solve the maze at a safe, controlled speed; try increasing the speed and you will quickly run in to lots of problems that you’ll have to handle with more complicated code. Putting the C files and header files into your project is easy with AVR Studio. In the left column of your screen, you should see options for “Source Files” and “Header Files”. Right click on either one and you will have the option to add or remove files from the list. When you build your project, AVR Studio will automatically compile all C files in the project together to produce a single hex file. 8.c. Left Hand on the WallThe basic strategy for solving a non-looped maze is called “left hand on the wall”. Imagine walking through a real labyrinth – a human-sized maze built with stone walls – while keeping your left hand on the wall at all times. You’ll turn left whenever possible and only turn right at an intersection if there is no other exit. Sometimes, when you reach a dead end, you’ll turn 180 degrees to the right and start walking back the way you came. Eventually, as long as there are no loops, your hand will travel along each length of wall in the entire labyrinth exactly once, and you’ll find your way back to the entrance. If there is a room somewhere in the labyrinth with a monster or some treasure, you’ll find that on the way, since you’ll travel down every hallway exactly twice. We use this simple, reliable strategy in our 3pi maze solving example:
// This function decides which way to turn during the learning phase of
// maze solving. It uses the variables found_left, found_straight, and
// found_right, which indicate whether there is an exit in each of the
// three directions, applying the "left hand on the wall" strategy.
char select_turn(unsigned char found_left, unsigned char found_straight, unsigned char found_right)
{
// Make a decision about how to turn. The following code
// implements a left-hand-on-the-wall strategy, where we always
// turn as far to the left as possible.
if(found_left)
return 'L';
else if(found_straight)
return 'S';
else if(found_right)
return 'R';
else
return 'B';
}
The values returned by select_turn() correspond to the values used by turn(), so these functions will work nicely together in our main loop. 8.d. The Main Loop(s)The strategy of our program is expressed in the file char path[100] = ""; unsigned char path_length = 0; // the length of the path Our “main loop” is found in the function maze_solve(), which is called after calibration, from
// This function is called once, from main.c.
void maze_solve()
{
while(1)
{
// FIRST MAIN LOOP BODY
// (when we find the goal, we use break; to get out of this)
}
// Now enter an infinite loop - we can re-run the maze as many
// times as we want to.
while(1)
{
// Beep to show that we finished the maze.
// Wait for the user to press a button...
int i;
for(i=0;i<path_length;i++)
{
// SECOND MAIN LOOP BODY
}
// Follow the last segment up to the finish.
follow_segment();
// Now we should be at the finish! Restart the loop.
}
}
The first main loop needs to drive down a segment of the course, decide how to turn, and record the turn in the path variable. To pass the correct arguments to select_turn(), we need to carefully examine the intersection as we cross it. Note that there is a special exception for finding the end of the maze. The following code works pretty well, at least at the slow speeds that we’re using:
// FIRST MAIN LOOP BODY
follow_segment();
// Drive straight a bit. This helps us in case we entered the
// intersection at an angle.
// Note that we are slowing down - this prevents the robot
// from tipping forward too much.
set_motors(50,50);
delay_ms(50);
// These variables record whether the robot has seen a line to the
// left, straight ahead, and right, whil examining the current
// intersection.
unsigned char found_left=0;
unsigned char found_straight=0;
unsigned char found_right=0;
// Now read the sensors and check the intersection type.
unsigned int sensors[5];
read_line(sensors,IR_EMITTERS_ON);
// Check for left and right exits.
if(sensors[0] > 100)
found_left = 1;
if(sensors[4] > 100)
found_right = 1;
// Drive straight a bit more - this is enough to line up our
// wheels with the intersection.
set_motors(40,40);
delay_ms(200);
// Check for a straight exit.
read_line(sensors,IR_EMITTERS_ON);
if(sensors[1] > 200 || sensors[2] > 200 || sensors[3] > 200)
found_straight = 1;
// Check for the ending spot.
// If all three middle sensors are on dark black, we have
// solved the maze.
if(sensors[1] > 600 && sensors[2] > 600 && sensors[3] > 600)
break;
// Intersection identification is complete.
// If the maze has been solved, we can follow the existing
// path. Otherwise, we need to learn the solution.
unsigned char dir = select_turn(found_left, found_straight, found_right);
// Make the turn indicated by the path.
turn(dir);
// Store the intersection in the path variable.
path[path_length] = dir;
path_length ++;
// You should check to make sure that the path_length does not
// exceed the bounds of the array. We'll ignore that in this
// example.
// Simplify the learned path.
simplify_path();
// Display the path on the LCD.
display_path();
We’ll discuss the call to simplify_path() in the next section. Before that, let’s take a look at the second main loop, which is very simple. All we do is drive to the next intersection and turn according to our records. After doing the last recorded turn, the robot will be one segment away from the finish, which explains the final follow_segment() call in the outline of maze_solve() above.
// SECOND MAIN LOOP BODY
follow_segment();
// Drive straight while slowing down, as before.
set_motors(50,50);
delay_ms(50);
set_motors(40,40);
delay_ms(200);
// Make a turn according to the instruction stored in
// path[i].
turn(path[i]);
8.e. Simplifying the SolutionAfter every turn, the length of the recorded path increases by 1. If your maze, for example, has a long zigzag passageway with no side exits, you’ll see a sequence like ‘RLRLRLRL’ appear on the 3pi’s LCD. There’s no shortcut that would get you through this section of the path faster than just following the left hand on the wall strategy. However, whenever we encounter a dead end, we can simplify the path to something shorter. Consider the sequence ‘LBL’, where ‘B’ stands for “back” and is the action taken when a dead end is encountered. This is what happens if there is a left turn that branches off of a straight path and leads immediately to a dead end. After turning 90° left, 180°, and 90° left again, the net effect is that the robot is heading in its original direction again. The path can be simplified to a 0° turn: a single ‘S’. The following diagram depicts this scenario, showing the two functionally equivalent paths from start to end:
Another example is a T-intersection with a dead end on the left: ‘LBS’. The turns are 90° left, 180°, and 0°, for a total of 90° right. The sequence should be replaced with a single ‘R’. In fact, whenever we have a sequence like ‘xBx’, we can replace all three turns with a turn corresponding to the total angle, eliminating the U-turn and speeding up our solution. Here’s the code to handle this:
// Path simplification. The strategy is that whenever we encounter a
// sequence xBx, we can simplify it by cutting out the dead end. For
// example, LBL -> S, because a single S bypasses the dead end
// represented by LBL.
void simplify_path()
{
// only simplify the path if the second-to-last turn was a 'B'
if(path_length < 3 || path[path_length-2] != 'B')
return;
int total_angle = 0;
int i;
for(i=1;i<=3;i++)
{
switch(path[path_length-i])
{
case 'R':
total_angle += 90;
break;
case 'L':
total_angle += 270;
break;
case 'B':
total_angle += 180;
break;
}
}
// Get the angle as a number between 0 and 360 degrees.
total_angle = total_angle % 360;
// Replace all of those turns with a single one.
switch(total_angle)
{
case 0:
path[path_length - 3] = 'S';
break;
case 90:
path[path_length - 3] = 'R';
break;
case 180:
path[path_length - 3] = 'B';
break;
case 270:
path[path_length - 3] = 'L';
break;
}
// The path is now two steps shorter.
path_length -= 2;
}
One interesting point about this code is that there are some sequences that should never be encountered by a left-turning robot, like ‘RBR’, which would be replaced by ‘S’ according to this code. In a more advanced program, you might want to keep track of inconsistencies like this, since they indicate some kind of a problem that could cause the robot to get lost. Now let’s step through a slightly more complicated maze, showing how we can simplify the path as we explore it: Fully explore the maze using a left-hand-on-the-wall strategy.
The above list of actions is a record of all the steps we took to fully explore the maze while looking for the end, which is marked by the large black circle. Our goal is to now reduce this list to represent the shortest path from start to finish by weeding out all of the dead ends. One option is to perform this pruning when we finish the maze, but the better approach is to perform the pruning as we go to keep our list from growing excessively large and taking up more memory than we have available. Prune out the first dead end as we identify it.
When we encounter the first intersection after our first “back” action, we know we have reached a dead end that can be removed from our list of actions. In this case, the most recent actions in our list is the sequence ‘SBL’, and the diagram shows that this sequence can be simplified into a single right turn ‘R’. Prune out the rest of this dead-end branch as we back-track.
We next end up with the sequence ‘RBL’, which reduces to a single back ‘B’, and this combines with the next action to produce the sequence ‘LBL’, which reduces to a single straight ‘S’. Prune out the final dead-end branch to leave us with the shortest path from start to finish.
The last dead end gives us the sequence ‘SBL’, which reduces to a sigle right turn ‘R’. Our action list is now just ‘R’ and represents the shortest path from start to finish. As we drove the maze, our action list would have looked like the following:
8.f. Improving the Maze Solving CodeWe have gone over the most important parts of the code; the other bits and pieces (like the function display_path(), the start-up sequence and calibration, etc.) can be found with everything else in the folder
9. Pin Assignment Tables
Pin Assignment Table Sorted by Function
Pin Assignment Table Sorted by Pin
10. Expansion Information | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||