A question about useing 3pi to run on a white line and the b


I was looking at page pololu.com/docs/0J18/19, and

I am trying to use 3pi to run on a white line and the background is black.

I edit the all example program from [unsigned int read_line] into [unsigned int read_line_white].

should that already be done?

Or I still need to edit the “if statement” down here↓

something like changing “200” into “800”?

I need to run on a white line and the background is black.

Would you please tell me how to judgment the value about the sensors like “100” “200” “600”?


  1. [follow-segment.c]
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.



                     else if(sensors[0] > 200 || sensors[4] > 200)


                                // Found an intersection.



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.




                     // Check for a straight exit.


                     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)



Mark Liao

Hi, Mark.

I moved this post to the 3pi and m3pi Robots discussion.

The only change you should have to make to run the example 3pi code on a white line with a black background is replacing the read_line function calls with read_line_white.

As for the sensor values, they are scaled values from 0 to 1000 read from the 3pi’s IR sensors, where 0 and 1000 correspond to the minimum and maximum values read by the sensors during calibration. There is no need to change any of the values in the if statements.


Hi Claire,
Thanks for explain the question about sensor values.

I did try that.
I edit the “3pi-mazesolver example program”.
I Only change all the program from"read_line(sensors,IR_EMITTERS_ON);" into “read_line_white(sensors,IR_EMITTERS_ON);”

But the 3pi robot still can’t follow the white line.
this is my 3pi robot movie on YouTube↓

Actually I did reference the solution down here that Ryan said.
(3pi following white line on black blackground).

But it still could not follow white line, too.

Please help me solve this problem.

By the way ,are the sensor values about white line and black line in opposite?


Sorry, I took a look at the read_line_white code and it actually does not adjust the actual sensor readings; it only adjusts the value for the position of the line. Therefore, to use the code you posted earlier, you will need to invert all of your sensor values by subtracting them from the maximum possible value. An easy way to do that is to add a loop that inverts the sensor values after each time you call readLine or readLineSensorsCalibrated.

	sensor_values[i] = 1000-sensor_values[i];


Thank Claire, you help me a lot.

I try to follow ypur tips and change my program like this↓

         unsigned int sensors[5];
     unsigned char i;

	sensors[i] = 1000-sensors[i];	

I’m so happy that my 3pi robot can follow the white line. It can work. I’m so excited!!
This is my 3pi robot movie on YouTube↓

But it still have 2 little problem

  1. In the movie my robot can follow the white straight line and the whole robot can get into the white block.
    But when I press botton B again, the 3pi will stop befor the white block. It won’t get in.
    Neither the white line program nor the black lin program have this problem.

  2. In the movie I change my map into another one which have branch road.
    3pi will go for a little while then stop and show “solve”.
    After I add [sensors[i] = 1000-sensors[i];] into the program, I think the “if statement” don’t have to change. It might be work as same as the black line. But it doesn’t. It can not scan the exit.


With the added for loop after every read statement, you should not need to change the values in the if statements. Could you post your entire code? That might help me find the problem.


Dear Claire,

Because the program is too large, may not be the entirely post on.
I already sent the E-mail to you. The attachment file (3pi-mazesolver (Mark.Liao).rar) is my 3pi robot project there have my program inside.


Hello, good, I’ve been working with the pololu and have had the same problem, it would be kind enough to help.
wish the 3pi follow the white line on black background please


As Brandon mentions in this thread, with a few minor adjustments to the example code, white line following on a black surface can be achieved. One such change is replacing the read_line command in the example code with read_line_white. You can read more about that command in “3pi Robot Functions” section of our AVR library command reference.

- Grant

Such Goodnight, thank you for taking the time to answer my question, try changing the command line but still does not recognize the white line and easily sidetracked, I’m using the demo code 3pi PID, please I would greatly appreciate if I can I say that more needs to be done … please

Could you describe the surface you are using the 3pi on? What material is it made of? How thick is the line you are trying to follow? If you could post a picture, that would be helpful. Could you also load the 3pi demo program and select the sensors demo? When you scan the 3pi sensors over your line, what do the bars on the LCD do?

- Grant

Good evening, sorry for the inconvenience it caused. I’m using the 3pi on a wooden surface in two funds and 2 types of lines, when in black with white line going at a good speed, but when it does the opposite case (black bottom white line) gets lost and goes fast at a slow speed, when canea black line in the LCD Current transition a full bar and two small side to her, and blank if it shows nothing, the width of the line is 0.8 inches.I show the image of the track … and programming code

//		3piLineFollower1.c

// 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 = "Control PID";
const char demo_name_line2[] PROGMEM = "Seguidor";

// 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 = {

// 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...
	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.

// 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.
	load_custom_characters(); // load the custom characters
	// Play welcome music and display a message


	// Display battery voltage and wait for button press
		int bat = read_battery_millivolts();

		print("Press B");


	// Always wait for the button to be released so that 3pi doesn't
	// start moving until your hand is away from it.

	// Auto-calibration: turn right and left while calibrating the
	// sensors.
		if(counter < 20 || counter >= 60)

		// 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.

		// Since our counter runs to 80, the total delay will be
		// 80*20 = 1600 ms.
		delay_ms(20); //aqui modifique

	// Display calibrated values as a bar graph.
		// Read the sensor values and get the position measurement.
		unsigned int position = read_line(sensors,IR_EMITTERS_ON);//aqui

		// 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.




	// Play music and wait for it to finish before we start driving.

// This is the main function, where the code starts.  All C programs
// must have a main() function defined somewhere.
int main()
	int speed = 255;						// set initial speed

	unsigned int sensors[5]; // an array to hold sensor values
	unsigned int last_proportional=0;
	long integral=0;

	// set up the 3pi

	// This is the "main loop" - it will run forever.
			// 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);//dos

			// The "proportional" term should be 0 when we are on the line.
			int proportional = ((int)position) - 2000;//segunda modificacion 

			// 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 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/3 + integral/50000 + derivative*4/1;

			// Compute the actual motor settings.  We never set either motor
			// to a negative value.
			const int max = 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);
				set_motors(max, max-power_difference);
		set_motors(0,0);										// stop the motors
		delay_ms(200);											// debounce button B
			print("Down  Up");
			print("A ");
			if( speed < 100 )
				print(" ");	
			print_long((long) speed);
				speed -= 5;
				if( speed < 30 )
					speed = 30;
				delay_ms(200);									// debounce button A
				speed += 5;
				if( speed > 255 )
					speed = 255;
				delay_ms(200);									// debounce button C
		delay_ms(2500);											// wait 2.5 seconds before resuming

	// 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);


It sounds like you want your 3pi to follow a course with both black lines on white surfaces and white lines on black surfaces. In order to do that you will need to some how signal to your program that the course is changing from one line color to the other. Then the program can decide which function to use to read the sensors. When following the white line your program should switch to using the read_line_white function.

- Grant