Avoid calibrating

Hello, in competitions in Spain it is not permitted calibrating the robot before it starts. How can i avoid this step? Thanks.

Hi rashield,

Are you using the 3pi-linefollower demo program in the 3pi user’s guide?

Take a look at the part around line 140:

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

Commenting out this section should stop the 3pi from calibrating.

Not calibrating will make it harder for the 3pi’s sensors to determine black from white. To circumvent this you could calibrate your 3pi on a sample course. One way is to have a sample course with you at the competition and calibrate the 3pi on it. Then, without turning your 3pi off, run it on the competition course. If you are not allowed to bring a sample course, you could calibrate 3pi before the competition, store the results in EPROM, and read the calibration data out of EPROM during the competition.

Does this answer your question?

-Ryan

rashield, what do the contest rules say? Surely your robot can learn from the course and use that knowledge to its advantage, once the competition has started. Removing the calibration sequence could potentially cause problems, but you could easily make the robot calibrate itself and then automatically start driving.

If you are using the sample 3pi code, then remove the “while” loop that waits for user-input after the calibration sequence:

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

After you have removed that code from your program, your robot will calibrate itself and then start driving shortly after the calibration is done.

Here’s the code I use to save my calibration values to EEPROM. You can use the buttons to trigger calibration only when you want to do it, for example when testing your robot before the contest, then call save_calibration() to save the values. Call load_calibration() when your robot is turned on, and it will be ready to run.

#include <pololu/3pi.h>
#include <avr/eeprom.h>

unsigned int calibrated_maximum_on[5] EEMEM;
unsigned int calibrated_minimum_on[5] EEMEM;

void save_calibration()
{
  int i;
  for(i=0;i<5;i++)
  {
    eeprom_write_word(
      &calibrated_minimum_on[i],
      get_line_sensors_calibrated_minimum_on()[i]
    );

    eeprom_write_word(
      &calibrated_maximum_on[i],
      get_line_sensors_calibrated_maximum_on()[i]
    );
  }
}

void load_calibration()
{
  calibrate_line_sensors(IR_EMITTERS_ON); // need to do this to allocate the arrays

  int i;
  for(i=0;i<5;i++)
  {
    get_line_sensors_calibrated_minimum_on()[i] =
      eeprom_read_word(&calibrated_minimum_on[i]);

    get_line_sensors_calibrated_maximum_on()[i] = 
      eeprom_read_word(&calibrated_maximum_on[i]);
  }
}

-Paul

Hi thanks to all, I think all of the solution you propose are good. The eprom solution is ideal as you only have to turn on the robot. The competition says that someone of the competition will turn on your robot so you can “loose” so many time calibrating it as its time that its counting…Im using a modification of the 3pi pid demo program.Thanks

Paul, where is eeprom_write_word function coming from? I did not find it in Pololu library.

The functions eeprom_read_word and eeprom_write_word come from <avr/eeprom.h> (i.e. from WinAVR, not the Pololu library), which Paul includes at the top of his code.

- Ben

I can’t seem to get this working perfectly. When I load the calibration from eeprom, the readings are slightly off, can’t tell where the problem is. Anyone see anything wrong with my code?

#include <pololu/3pi.h>
#include <avr/pgmspace.h>
#include <avr/eeprom.h>

unsigned int calibrated_maximum_on[5] EEMEM;
unsigned int calibrated_minimum_on[5] EEMEM;

const char levels[] PROGMEM = {
	0b00000,
	0b00000,
	0b00000,
	0b00000,
	0b00000,
	0b00000,
	0b00000,
	0b11111,
	0b11111,
	0b11111,
	0b11111,
	0b11111,
	0b11111,
	0b11111
};

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();
}

void display_readings(const unsigned int *calibrated_values){
	unsigned char i;
	for(i=0;i<5;i++) {
		const char display_characters[10] = {' ',0,0,1,2,3,4,5,6,255};
		char c = display_characters[calibrated_values[i]/101];
		print_character(c);
	}
}

void save_calibration(){
	int i;
	for(i=0;i<5;i++){
		eeprom_write_word(
		&calibrated_minimum_on[i],
		get_line_sensors_calibrated_minimum_on()[i]
		);

		eeprom_write_word(
		&calibrated_maximum_on[i],
		get_line_sensors_calibrated_maximum_on()[i]
		);
	}
}

void load_calibration(){
	calibrate_line_sensors(IR_EMITTERS_ON);

	int i;
	for(i=0;i<5;i++){
		get_line_sensors_calibrated_minimum_on()[i] =
		eeprom_read_word(&calibrated_minimum_on[i]);

		get_line_sensors_calibrated_maximum_on()[i] =
		eeprom_read_word(&calibrated_maximum_on[i]);
	}
}

int main(){
	unsigned int counter;
	unsigned int sensors[5];
	load_custom_characters();
	pololu_3pi_init(2000);
	load_calibration();
	
	play_frequency(1000, 200, 10);
	
	clear();
	print("Ready");
	
	while(1){
		if (button_is_pressed(BUTTON_A)){
			delay_ms(1000);
			for(counter=0;counter<80;counter++){
				if(counter < 20 || counter >= 60){
					set_motors(40,-40);
				} else {
					set_motors(-40,40);
				}
				calibrate_line_sensors(IR_EMITTERS_ON);
				delay_ms(20);
			}
			set_motors(0,0);
			save_calibration();
		} else if(button_is_pressed(BUTTON_C)){
			while(1){
				unsigned int position = read_line(sensors,IR_EMITTERS_ON);
				clear();
				print_long(position);
				lcd_goto_xy(0,1);
				display_readings(sensors);

				delay_ms(100);
			}
		} else if(button_is_pressed(BUTTON_B)){
			
		}
	}
}

After playing around, I found after saving the calibration all the sensors always pick up a reading greater than 0 when they shouldn’t…

Hello.

Can you describe the surface you are calibrating your sensors on? What material is it made of? What do you mean by values “greater than 0” ?

Can you to post a video showing everything you are doing to reproduce the problem? The video should show what buttons you are pressing, the surface you are trying to calibrate on, how you are saving and loading the calibrated values to and from the EEPROM, and the outputs that you are getting.

- Amanda

I managed to get it working by lowering the tolerance levels in my PID checks. The movement isn’t 100% accurate and it seems to slightly go to the side at the end of a segment but it seems to be working well enough for my needs.

I’d do everything you said but the problem is no longer blocking.