Countdown

Hi everybody, my 11 yo is competing in a couple of robot competitions and i am trying (struggling) to understand what he is up too :slight_smile:

for one of the line follower competitions, i was planning to buy a zumo 32u4 but in the rules of the comp, it is stated that there should be a 5 seconds countdown. I read that on the zumo there is an audible countdown, but i was wondering if it is possible to program a 5 sec countdown on the display or by some LED’s of any sort?

not looking for the actual programming, just wondering if it is possible at all, before buying one? :slight_smile:

and can it also be programmed for combination of track ling / rescue (pushing a can out of a field?)

many thanks and pardon my probably ridiculous questions…

Hello.

Yes, it is possible to program the Zumo32U4 to display stuff on its LCD. Our SumoProximitySensors sketch displays a 5 second countdown on its LCD. Since you are new to using the LCD, we also recommend getting started by looking at the LCDBasics sketch. Additionally, the Zumo32U4 has three LEDs. So, if you want to use those instead of or alongside the LCD, I recommend looking at BlinkLEDs. All of those sketches can be found in our Arduino library for the Zumo32U4. A link to the GitHub page for that library, as well as many other resources like a user’s guide, can be found under the Resources tab of the Zumo 32U4’s product page.

-Jon

Hi Jon,

I have uploaded straight form the library the code for line following and i also tried the code beneath… for the one from the library, the zumo robot just spins around a bit , does not seem to align with the black line (stands diagonally) and does not move…

Any pointers on what i am doing wrong? i have the 50:1 motors

/*
Simple around the box program
- Brian Erickson
*/

#include <Wire.h>
#include <Zumo32U4.h>
#include "TurnSensor.h"

// This is the maximum speed the motors will be allowed to turn.
// A maxSpeed of 400 lets the motors go at top speed.  Decrease
// this value to impose a speed limit.
const int16_t max_speed = 400;

Zumo32U4LCD lcd;
Zumo32U4ButtonA buttonA;
Zumo32U4ButtonC buttonC;
Zumo32U4Motors motors;
Zumo32U4Encoders encoders;
Zumo32U4ProximitySensors proximity_sensors;
Zumo32U4LineSensors line_sensors;
#define NUM_SENSORS 5
uint16_t line_sensor_values[NUM_SENSORS];
int line_position;


struct Location {double x; double y;} location = {0.,0.};

L3G gyro;

int start_millis;



void setup() {
  turnSensorSetup();
  turnSensorReset();
  //proximity_sensors.initThreeSensors();
  line_sensors.initFiveSensors();
  
  start_millis = millis();
}



// returns true if loop time passes through n ms boundary
bool every_n_ms(unsigned long last_loop_ms, unsigned long loop_ms, unsigned long ms) {
  return (last_loop_ms % ms) + (loop_ms - last_loop_ms) >= ms;
}

// returns theta in [-180,180)
double standardized_degrees(double theta) {
  return fmod((theta + 180 + 3600), 360.) - 180.;
}

void show_stats_on_lcd() {
//    lcd.clear();
//    lcd.print(standardized_degrees(turn_degrees));
    // lcd.gotoXY(0,1);
    //lcd.print(proximity_sensors.countsRightWithLeftLeds());
    //lcd.print(",");
    //lcd.print(proximity_sensors.countsRightWithRightLeds());
    /*
    lcd.print((int)location.x);
    lcd.print(",");
    lcd.print((int)location.y);
    */
    lcd.clear();
    lcd.gotoXY(0,0);
    lcd.print((int)line_position);
    lcd.gotoXY(0,1);
    lcd.print((int)line_sensor_values[2]);
}


void go_forward(double desired_degrees, double speed = max_speed) {
  double heading_error = standardized_degrees(desired_degrees - turn_degrees);

  int left_speed = constrain(speed - 20 * heading_error,-max_speed,max_speed);
  int right_speed = constrain(speed + 20 * heading_error,-max_speed,max_speed);
  
  
  motors.setSpeeds(left_speed, right_speed);
}

// turns toward desired angle, returns true when done
bool turn_to_angle(float desired_degrees) {

  double turn_error = standardized_degrees(desired_degrees - turn_degrees);
  lcd.clear();
  lcd.print(turn_error);
  int32_t motor_speed = turn_error * 10 - turn_degrees_per_second*0.5;

  // Constrain our motor speeds to be between
  // -maxSpeed and maxSpeed.
  motor_speed= constrain(motor_speed, -max_speed, max_speed);

  if(abs(turn_error)<1.0 && abs(turnRate) < 10) {
    motors.setSpeeds(-0,0);
    return true;
  }

  // enforce minimum motor speed
  if(abs(motor_speed) < 50 && abs(turnRate) < 10) {
    if(motor_speed < 0) 
      motor_speed = -100;
    else
      motor_speed = 100;
  }
  motors.setSpeeds(-motor_speed, motor_speed);
  return false;
  
}

double heading_degrees_to(double x, double y) {
  double dx = x - location.x;
  double dy = y - location.y;
  return atan2(dy, dx) * 180./PI;
}

bool move_to(double x, double y) {
  double dx = x - location.x;
  double dy = y - location.y;
  double heading_sp = atan2(dy, dx) * 180./PI;
  double remaining = sqrt(dx*dx+dy*dy);
  if(remaining < 100) {
    motors.setSpeeds(0,0);
    return true;
  }
 
  double speed = remaining*0.5;
  go_forward(heading_sp, speed);
  return false;
}

void follow_line() {
  // center of line is at 2000
  // 1000 is centor left sensor
  // 3000 is center right sensor
  // 0 means that it cannot determine reading
  if(line_position <1000 && line_position > 3000) {
    return;
  }
  int left_speed = constrain(map(line_position,1500,2100,0,max_speed),0,max_speed);
  int right_speed = constrain(map(line_position,2500, 1900,0,max_speed),0,max_speed);
  
  
  motors.setSpeeds(left_speed, right_speed);
  
  
}

void loop()
{
  static unsigned int last_loop_ms = 0;
  static int current_step = 0;
  static bool box_to_right = false;
  static int go_angle = 0;

  // update loop timers
  unsigned int loop_ms = millis();
  bool every_1_s = every_n_ms(last_loop_ms, loop_ms, 1000);
  bool every_100_ms = every_n_ms(last_loop_ms, loop_ms, 100);

  // update all sensors
  turnSensorUpdate();
  //proximity_sensors.read();


  // update robot location 
  {
    double forward_distance = encoders.getCountsAndResetLeft() + encoders.getCountsAndResetRight();
    location.x += forward_distance * cos(turn_degrees * PI/180.);
    location.y += forward_distance * sin(turn_degrees * PI/180.);
  }

  
  if(every_100_ms)  {
    show_stats_on_lcd();
  }



  //bool last_box_to_right = box_to_right;
  //box_to_right = (proximity_sensors.countsRightWithRightLeds() > 3);
  
  line_position = line_sensors.readLine(line_sensor_values);
  // try using only the three center sensors
  //line_position = (line_sensor_values[1]*1000+line_sensor_values[2]*2000+line_sensor_values[3]*3000)/(line_sensor_values[1]+line_sensor_values[2]+line_sensor_values[3]);

  // main state logic
  switch(current_step) {
    case 0:
      turnSensorReset();
      go_angle = 0;
      lcd.clear();
      ++current_step;
      break;
    case 1:
      lcd.print((String)"line cal");
      ++current_step;
      break;
    case 2:
      line_sensors.calibrate();
      if(turn_to_angle(90))  {
        ++current_step;
      }
      break;
    case 3:
      line_sensors.calibrate();
      if(turn_to_angle(180))  {
        ++current_step;
      }
      break;
    case 4:
      line_sensors.calibrate();
      if(turn_to_angle(270))  {
        ++current_step;
      }
      break;
    case 5:
      line_sensors.calibrate();
      if(turn_to_angle(360))  {
        ++current_step;
      }
      break;
    case 6:
      line_sensors.calibrate();
      if(turn_to_angle(360))  {
        ++current_step;
      }
      break;
    case 7:
      if(buttonA.getSingleDebouncedRelease()) {
        ++current_step;
      }
    case 9:
      follow_line();
      if(buttonA.getSingleDebouncedRelease()) {
        current_step=0;
      }
      
        
      break;
    default: // final state, do nothing
      break;
   
  }


  // remember last loop time
  last_loop_ms = loop_ms;
}

It sounds like you are using LineFollower.ino, which starts by running an auto-calibration routine. When the routine is finished, the Zumo’s LCD will display bar graphs of the sensor readings. At this point, you can move the Zumo across your line and the bar heights will adjust to give you a sense for how well each of the five line sensors is detecting the line. When you are ready, you can place your robot on your course where you would like it to start following a line and press the A button.

By the way, you can read through our comments on the sketches you are running to get an idea of what to expect the code to do.

If you still want help with the script your wrote, can you explain in more detail what the issue is? What do you expect your code to do, and what does the Zumo actually do? Sharing a link to a video that shows the behavior of your robot would help.

-Jon

hi Jon,

we now put the code you see below… i put the max speed at 200 as we use the 50:1 motors…
i made a quick video to show the issue… https://youtu.be/r1eVi76eIis
i cant figure out with it goes so far out of the line… PID??

/* This example uses the line sensors on the Zumo 32U4 to follow
a black line on a white background, using a PID-based algorithm.
It works decently on courses with smooth, 6" radius curves and
has been tested with Zumos using 75:1 HP motors.  Modifications
might be required for it to work well on different courses or
with different motors.

This demo requires a Zumo 32U4 Front Sensor Array to be
connected, and jumpers on the front sensor array must be
installed in order to connect pin 4 to DN4 and pin 20 to DN2. */

#include <Wire.h>
#include <Zumo32U4.h>

// This is the maximum speed the motors will be allowed to turn.
// A maxSpeed of 400 lets the motors go at top speed.  Decrease
// this value to impose a speed limit.
const uint16_t maxSpeed = 200;
Zumo32U4Buzzer buzzer;
Zumo32U4LineSensors lineSensors;
Zumo32U4Motors motors;
Zumo32U4ButtonA buttonA;
Zumo32U4LCD lcd;

int16_t lastError = 0;

#define NUM_SENSORS 5
unsigned int lineSensorValues[NUM_SENSORS];

// Sets up special characters in the LCD so that we can display
// bar graphs.
void loadCustomCharacters()
{
  static const char levels[] PROGMEM = {
    0, 0, 0, 0, 0, 0, 0, 63, 63, 63, 63, 63, 63, 63
  };
  lcd.loadCustomCharacter(levels + 0, 0);  // 1 bar
  lcd.loadCustomCharacter(levels + 1, 1);  // 2 bars
  lcd.loadCustomCharacter(levels + 2, 2);  // 3 bars
  lcd.loadCustomCharacter(levels + 3, 3);  // 4 bars
  lcd.loadCustomCharacter(levels + 4, 4);  // 5 bars
  lcd.loadCustomCharacter(levels + 5, 5);  // 6 bars
  lcd.loadCustomCharacter(levels + 6, 6);  // 7 bars
}

void printBar(uint8_t height)
{
  if (height > 8) { height = 8; }
  const char barChars[] = {' ', 0, 1, 2, 3, 4, 5, 6, 255};
  lcd.print(barChars[height]);
}

void calibrateSensors()
{
  lcd.clear();

  // Wait 1 second and then begin automatic sensor calibration
  // by rotating in place to sweep the sensors over the line
  delay(1000);
  for(uint16_t i = 0; i < 120; i++)
  {
    if (i > 30 && i <= 90)
    {
      motors.setSpeeds(-200, 200);
    }
    else
    {
      motors.setSpeeds(200, -200);
    }

    lineSensors.calibrate();
  }
  motors.setSpeeds(0, 0);
}

// Displays a bar graph of sensor readings on the LCD.
// Returns after the user presses A.
void showReadings()
{
  lcd.clear();

  while(!buttonA.getSingleDebouncedPress())
  {
    lineSensors.readCalibrated(lineSensorValues);

    lcd.gotoXY(0, 0);
    for (uint8_t i = 0; i < NUM_SENSORS; i++)
    {
      uint8_t barHeight = map(lineSensorValues[i], 0, 1000, 0, 8);
      printBar(barHeight);
    }
  }
}

void setup()
{
  // Uncomment if necessary to correct motor directions:
  //motors.flipLeftMotor(true);
  //motors.flipRightMotor(true);

  lineSensors.initFiveSensors();

  loadCustomCharacters();

  // Play a little welcome song
  buzzer.play(">g32>>c32");

  // Wait for button A to be pressed and released.
  lcd.clear();
  lcd.print(F("Press A"));
  lcd.gotoXY(0, 1);
  lcd.print(F("to calb"));
  buttonA.waitForButton();

  calibrateSensors();

  showReadings();

  // Play music and wait for it to finish before we start driving.
  lcd.clear();
  lcd.print(F("Go!"));
  buzzer.play("L16 cdegreg4");
  while(buzzer.isPlaying());
}

void loop()
{
  // Get the position of the line.  Note that we *must* provide
  // the "lineSensorValues" argument to readLine() here, even
  // though we are not interested in the individual sensor
  // readings.
  int16_t position = lineSensors.readLine(lineSensorValues);

  // Our "error" is how far we are away from the center of the
  // line, which corresponds to position 2000.
  int16_t error = position - 2000;

  // Get motor speed difference using proportional and derivative
  // PID terms (the integral term is generally not very useful
  // for line following).  Here we are using a proportional
  // constant of 1/4 and a derivative constant of 6, which should
  // work decently for many Zumo motor choices.  You probably
  // want to use trial and error to tune these constants for your
  // particular Zumo and line course.
  int16_t speedDifference = error / 1 + 2 * (error - lastError);

  lastError = error;

  // Get individual motor speeds.  The sign of speedDifference
  // determines if the robot turns left or right.
  int16_t leftSpeed = (int16_t)maxSpeed + speedDifference;
  int16_t rightSpeed = (int16_t)maxSpeed - speedDifference;

  // Constrain our motor speeds to be between 0 and maxSpeed.
  // One motor will always be turning at maxSpeed, and the other
  // will be at maxSpeed-|speedDifference| if that is positive,
  // else it will be stationary.  For some applications, you
  // might want to allow the motor speed to go negative so that
  // it can spin in reverse.
  leftSpeed = constrain(leftSpeed, 0, (int16_t)maxSpeed);
  rightSpeed = constrain(rightSpeed, 0, (int16_t)maxSpeed);

  motors.setSpeeds(leftSpeed, rightSpeed);
}

Yes, to get smooth line following, you need to tune your PID constants. In general, we recommend starting by just using a proportional (Kp) term, i.e.:

int16_t speedDifference = error / 4;

Then you can start experimenting by trial and error with the value of that coefficient (try a series of slightly bigger and smaller terms), and find the value that makes the Zumo follow the line best. Then, you can add in the derivative (Kd) term. For example:

int16_t speedDifference = error / 4 + 6 * (error - lastError);

Note that Kd is being multiplied by the difference in successive errors, which will generally be much smaller than the error itself, so Kd usually needs to be much bigger than Kp for it to have a comparable effect on the motor speed differential. Starting with a Kd constant that is 10 to 20 times bigger than Kp is reasonable. You can read more about choosing a good Kp and Kd in this blog post by Ben.

I also recommend reading the Wikipedia page on PID control.

Additionally, the paneled floor you placed your line on top of might be interfering with the Zumo’s ability to detect the line (i.e. it could be getting false readings from the darker parts of your floor). It would help to create a course on a surface that has a stark contrast to the black line (i.e. a reflective white surface).

-Jon

HI Jon,

Thanks for the answers; we have managed to put in the LED countdown before starting… as you can see in the youtube video, we have reduced to speed to half (200) and then it is more or less ok, but as in the following video, running at 400 max speed, it just goes nuts… we have tried changing the standard 4 + 6 to anything higher or lower but we dont see any difference… https://youtu.be/GT9uE8g3p6Q

second part of the assignment for the competition is that after following the line, a can must be detected and pushed out… we have the code on how to scan for an object within the correct distance and to push it… how can we merge both code? meaning how to get the zumo to stop when there is no more line and go to the second command?

i put the codes below…

the line following:

#include <Wire.h>
#include <Zumo32U4.h>

// This is the maximum speed the motors will be allowed to turn.
// A maxSpeed of 400 lets the motors go at top speed.  Decrease
// this value to impose a speed limit.
const uint16_t maxSpeed = 250;
Zumo32U4Buzzer buzzer;
Zumo32U4LineSensors lineSensors;
Zumo32U4Motors motors;
Zumo32U4ButtonA buttonA;
Zumo32U4ButtonB buttonB;
Zumo32U4LCD lcd;
Zumo32U4ProximitySensors proxSensors;

int16_t lastError = 0;

#define NUM_SENSORS 5
unsigned int lineSensorValues[NUM_SENSORS];

// Sets up special characters in the LCD so that we can display
// bar graphs.
void loadCustomCharacters()
{
  static const char levels[] PROGMEM = {
    0, 0, 0, 0, 0, 0, 0, 63, 63, 63, 63, 63, 63, 63
  };
  lcd.loadCustomCharacter(levels + 0, 0);  // 1 bar
  lcd.loadCustomCharacter(levels + 1, 1);  // 2 bars
  lcd.loadCustomCharacter(levels + 2, 2);  // 3 bars
  lcd.loadCustomCharacter(levels + 3, 3);  // 4 bars
  lcd.loadCustomCharacter(levels + 4, 4);  // 5 bars
  lcd.loadCustomCharacter(levels + 5, 5);  // 6 bars
  lcd.loadCustomCharacter(levels + 6, 6);  // 7 bars
}

void printBar(uint8_t height)
{
  if (height > 8) { height = 8; }
  const char barChars[] = {' ', 0, 1, 2, 3, 4, 5, 6, 255};
  lcd.print(barChars[height]);
}

void calibrateSensors()
{
  lcd.clear();

  // Wait 1 second and then begin automatic sensor calibration
  // by rotating in place to sweep the sensors over the line
  delay(1000);
  for(uint16_t i = 0; i < 120; i++)
  {
    if (i > 30 && i <= 90)
    {
      motors.setSpeeds(-200, 200);
    }
    else
    {
      motors.setSpeeds(200, -200);
    }

    lineSensors.calibrate();
  }
  motors.setSpeeds(0, 0);
}

void setup()
{
  // Uncomment if necessary to correct motor directions:
  //motors.flipLeftMotor(true);
  //motors.flipRightMotor(true);

  lineSensors.initFiveSensors();

  loadCustomCharacters();

  // Play a little welcome song
  buzzer.play(">g32>>c32");

  // Wait for button A to be pressed and released.
  lcd.clear();
  lcd.print(F("Press A"));
  lcd.gotoXY(0, 1);
  lcd.print(F("to calb"));
  buttonA.waitForButton();

  calibrateSensors();


  lcd.clear();
  lcd.print(F("Press B"));
  lcd.gotoXY(0, 1);
  lcd.print(F("to start"));
  buttonB.waitForButton();

  {
    
    delay(1000);
   // Turn the LEDs on.
  ledRed(1);

  // Wait for a second.
  delay(1000);

   // Turn the LEDs off.
  ledRed(0);
  
  // Turn the LEDs on.
  ledYellow(1);

  // Wait for a second.
  delay(1000);
  
  // Turn the LEDs off.
  ledYellow(0);
  
  // Turn the LEDs on.
  ledGreen(1);

  // Wait for a second.
  delay(1000);

  // Turn the LEDs off.
  ledGreen(0);

  // Turn the LEDs on.
  ledRed(1);

  // Wait for a second.
  delay(1000);

   // Turn the LEDs off.
  ledRed(0);
  
  // Turn the LEDs on.
  ledYellow(1);

  // Wait for a second.
  delay(1000);
  
  // Turn the LEDs off.
  ledYellow(0);

}
  
  
  lcd.clear();
  lcd.print(F("Go!"));
}

void loop()
{
  // Get the position of the line.  Note that we *must* provide
  // the "lineSensorValues" argument to readLine() here, even
  // though we are not interested in the individual sensor
  // readings
  int16_t position = lineSensors.readLine(lineSensorValues);

  // Our "error" is how far we are away from the center of the
  // line, which corresponds to position 2000.
  int16_t error = position - 2000;

  // Get motor speed difference using proportional and derivative
  // PID terms (the integral term is generally not very useful
  // for line following).  Here we are using a proportional
  // constant of 1/4 and a derivative constant of 6, which should
  // work decently for many Zumo motor choices.  You probably
  // want to use trial and error to tune these constants for your
  // particular Zumo and line course.
int16_t speedDifference = error / 4;

  lastError = error;
  ledRed(1);
  
  // Get individual motor speeds.  The sign of speedDifference
  // determines if the robot turns left or right.
  int16_t leftSpeed = (int16_t)maxSpeed + (speedDifference + 20);
  int16_t rightSpeed = (int16_t)maxSpeed - (speedDifference + 20);

  leftSpeed = constrain(leftSpeed, 0, (int16_t)maxSpeed);
  rightSpeed = constrain(rightSpeed, 0, (int16_t)maxSpeed);

  motors.setSpeeds(leftSpeed, rightSpeed);
}

for the scan and ram:

#include <Wire.h>
#include <Zumo32U4.h>

Zumo32U4LCD lcd;
Zumo32U4Motors motors;
Zumo32U4ProximitySensors proxSensors;
Zumo32U4ButtonA buttonA;

// A sensors reading must be greater than or equal to this
// threshold in order for the program to consider that sensor as
// seeing an object.
const uint8_t sensorThreshold = 4;

// The maximum speed to drive the motors while turning.  400 is
// full speed.
const uint16_t turnSpeedMax = 200;

// The minimum speed to drive the motors while turning.  400 is
// full speed.
const uint16_t turnSpeedMin = 100;

// The amount to decrease the motor speed by during each cycle
// when an object is seen.
const uint16_t deceleration = 10;

// The amount to increase the speed by during each cycle when an
// object is not seen.
const uint16_t acceleration = 10;

#define LEFT 0
#define RIGHT 1

// Stores the last indication from the sensors about what
// direction to turn to face the object.  When no object is seen,
// this variable helps us make a good guess about which direction
// to turn.
bool senseDir = RIGHT;

// True if the robot is turning left (counter-clockwise).
bool turningLeft = false;

// True if the robot is turning right (clockwise).
bool turningRight = false;

// If the robot is turning, this is the speed it will use.
uint16_t turnSpeed = turnSpeedMax;

// The time, in milliseconds, when an object was last seen.
uint16_t lastTimeObjectSeen = 0;

void setup()
{
  proxSensors.initFrontSensor();

  // Wait for the user to press A before driving the motors.
  lcd.clear();
  lcd.print(F("Press A"));
  buttonA.waitForButton();
  lcd.clear();
}

void turnRight()
{
  motors.setSpeeds(turnSpeed, -turnSpeed);
  turningLeft = false;
  turningRight = true;
}

void turnLeft()
{
  motors.setSpeeds(-turnSpeed, turnSpeed);
  turningLeft = true;
  turningRight = false;
}

void stop()
{
  motors.setSpeeds(0, 0);
  turningLeft = false;
  turningRight = false;
}

void loop()
{
  // Read the front proximity sensor and gets its left value (the
  // amount of reflectance detected while using the left LEDs)
  // and right value.
  proxSensors.read();
  uint8_t leftValue = proxSensors.countsFrontWithLeftLeds();
  uint8_t rightValue = proxSensors.countsFrontWithRightLeds();

  // Determine if an object is visible or not.
  bool objectSeen = leftValue >= sensorThreshold || rightValue >= sensorThreshold;

  if (objectSeen)
  {
    // An object is visible, so we will start decelerating in
    // order to help the robot find the object without
    // overshooting or oscillating.
    turnSpeed -= deceleration;
  }
  else
  {
    // An object is not visible, so we will accelerate in order
    // to help find the object sooner.
    turnSpeed += acceleration;
  }

  // Constrain the turn speed so it is between turnSpeedMin and
  // turnSpeedMax.
  turnSpeed = constrain(turnSpeed, turnSpeedMin, turnSpeedMax);

  if (objectSeen)
  {
    // An object seen.
    ledYellow(1);
    motors.setSpeeds(200, 200);
    delay(300);

    lastTimeObjectSeen = millis();

    bool lastTurnRight = turnRight;

    if (leftValue < rightValue)
    {
      // The right value is greater, so the object is probably
      // closer to the robot's right LEDs, which means the robot
      // is not facing it directly.  Turn to the right to try to
      // make it more even.
      turnRight();
      senseDir = RIGHT;
    }
    else if (leftValue > rightValue)
    {
      // The left value is greater, so turn to the left.
      turnLeft();
      senseDir = LEFT;
    }
    else
    {
      // The values are equal, so stop the motors.
      stop();
    }
  }
  else
  {
    // No object is seen, so just keep turning in the direction
    // that we last sensed the object.
    ledYellow(0);

    if (senseDir == RIGHT)
    {
      turnRight();
    }
    else
    {
      turnLeft();
    }
  }

  lcd.gotoXY(0, 0);
  lcd.print(leftValue);
  lcd.print(' ');
  lcd.print(rightValue);
  lcd.gotoXY(0, 1);
  lcd.print(turningRight ? 'R' : (turningLeft ? 'L' : ' '));
  lcd.print(' ');
  lcd.print(turnSpeed);
  lcd.print(' ');
  lcd.print(' ');
}

Although it is slower, it looks like your Zumo is following the line (especially straighter segments) better than before. To get it to follow at higher speeds, you should incrementally increase the speed by small amounts, tuning your PD constants each time you increase the speed. For example, you can try running your line following code at a speed of 225 or 250. Observe the behavior of your robot as it follows the course and adjust the PD constants. Repeat until the line following behavior is as smooth as you can get it at that new speed. Then, you can incrementally increase the speed again, to maybe 250 or 275, and begin tuning there. If you are not able to notice a difference in performance when changing your PD constants, try using even bigger/smaller values.

As for performing the next phase of your competition after following the line, one thing you might do is keep track of how much time has passed since your robot was commanded to follow the line and compare that time to how long you expect your Zumo to take to reach the end of the line. If the robot loses the line (i.e. has driven past the end of the line) and it has been following the line for longer than you expect it to take, it should begin running code to search for the can. As for programming that approach, it is probably easiest to make the “scan and ram” part of your code into one or a few functions that get called when the conditions are appropriate to begin searching for the can.

-Jon

again many thanks Jon! i think i am finally starting to understand how to play with the PID values, instead of randomly trying anything :slight_smile: thanks to your useful lecture… we managed to fine tune a bit and will continue now as we slowly increase speed (it is a competition after all :slight_smile: )

so that part will be fine… however for integrating the code if “scan and ram”, i am completely clueless as to how… and now the bot turns to find the black line if the sensors lose all connection. can the code be changed that when i finds no line, it just stops and there the fuctions “scan ram” starts??

sorry for all the questions and cries for help but my 11 yo son is gone for a week on a field trip and i am trying to get along without him :slight_smile:

Taken by itself, that approach might not work because when the robot is following the line (especially during turns) there can be brief moments when none of the sensors are detecting the line. Those moments could be interpreted by your code as an appropriate time to stop and switch over to running your scan and ram routine. However, one thing you might do to prevent that from happening often is to implement some kind of condition that also checks to see if the robot has not seen the line for a relatively long amount of time (e.g. one second). You might also add the other condition I mentioned in my last reply and check to see if enough time has elapsed and you expect the robot to be near the end of the course.

-Jon

great, thanks Jon… i will have a look this evening at how i can implement the condition that when it does not see a line for a defined time, it stops…