Help - with additional code line follower with QTR8RC

Hello,

this is my first post on the forum
I am building a fast line follower
using:
Arduino Nano, pololu QTR8RC and DRV8835 with N20 metal gear motors.

Actually, i managed to make it run using a PID…It works great when following curves and straight lines but there are too many errors when taking sharp turns ( angle 45, or 90 deg); the robot is turning in the wrong directions sometimes ( because the robot is not exactly at the middle of the line at the beginning of some turns…so it goes in the wrong direction sometimes).

After some research on internet…i saw someone suggesting to add an additional code
https://www.researchgate.net/publication/356027000_PID_Controller_Optimization_for_Low-cost_Line_Follower_Robots

As i am new in coding…is there anyone who can help me to point to the correct directions…or to writte the codes. Here is the flow chart:

What i want to do are marked in red inside the flow chart
I need to modify my actual code because even if i make a fine tune of Kp,Ki,Kd variables…there will still have too much errors when taking sharp angles…
so i need to solve few problems, unfortunately, my knowledge with programing is limited.
Does anyone can help with:
1- write a code to check if the robot still detect the black line
2- how to check since how long the robot had missed the line? so i can make it turn accordingly
3- Checking which one between far right and far left sensors saw the line the last( to know the right direction where to turn)

4-Using PID…if the error is 0…, the speed is limited to the base speed…Is there any kind of function which accelerate the robot when the error is null ( the robot is centered) and slow it down when negotiating the turns?

Thank you for your precious help.

Here is the actual code i uploaded inside my robot

#include<DRV8835MotorShield.h>
#include<QTRSensors.h>

void CALIBRATE(void);
void CALCULATE_PID(void);
void MOTOR_CONTROL(void);
void DEBUG_POSITION(void);

//QTR sensors
QTRSensors qtr;
const uint8_t SensorCount = 8;
uint16_t sensorValues[SensorCount];

//PID parameter
float Kp = 0.004 , Ki = 40, Kd = 0;
//PID variables
long P, I, D, PID_value, last_error, error = 0, last_I;

//MOTORS
DRV8835MotorShield motors(5,6,3,11);
const float BASE_SPEED = 100, MAX_SPEED = 300, BACKWARD_SPEED = -300; //Max 400
int turn_delay = 0 ; //The time spent ingnoring the sensors
int mode = 2;

void setup()
{
  delay(1000);
  Serial.begin(9600);
  
//  motors.flipM1(true);
//  motors.flipM2(true);
  pinMode(mode, OUTPUT);
  digitalWrite(mode, HIGH);
  CALIBRATE();
  delay(1000);
}

void loop()
{
  CALCULATE_PID();

//  MOTOR_CONTROL();

  DEBUG_POSITION();
}

void CALCULATE_PID(void){
  
  error = (qtr.readLineBlack(sensorValues)) - (3500);
 
  P = error;
  I = I + error;
  D = error - last_error;

  PID_value = (Kp*P) + (Ki*I) + (Kd*D);
  last_I = I;
  last_error = error;
 
  if(true/*DEBUG*/){
    Serial.print(PID_value);
//    Serial.print(error);
//    Serial.println();
  }
}

void MOTOR_CONTROL(void){
    //Motor speed
    int M1Speed = BASE_SPEED - PID_value;
    int M2Speed = BASE_SPEED + PID_value;
    M1Speed  = constrain(M1Speed, BACKWARD_SPEED, MAX_SPEED);
    M2Speed  = constrain(M2Speed, BACKWARD_SPEED, MAX_SPEED);
    motors.setM1Speed(M1Speed);
    motors.setM2Speed(M2Speed);

  if(false/*DEBUG*/){
    Serial.print("left:");
    Serial.print(M1Speed);
    Serial.print("  ");
    Serial.print("right:");
    Serial.print(M2Speed);
    Serial.println();
  }
  delay(turn_delay);
}

void CALIBRATE(void){
    // configure the sensors
  qtr.setTypeRC();
  qtr.setSensorPins((const uint8_t[]){14, 4, 7, 8, 9, 10, 12, 15}, SensorCount);
  qtr.setEmitterPin(13);

  delay(500);
  pinMode(LED_BUILTIN, OUTPUT);
  
  digitalWrite(LED_BUILTIN, HIGH); // turn on Arduino's LED to indicate we are in calibration mode
  for (uint16_t i = 0; i < 200; i++)
  {
    qtr.calibrate();
  }
  digitalWrite(LED_BUILTIN, LOW); // turn off Arduino's LED to indicate we are through with calibration

  if(true/*DEBUG*/){
    // print the calibration minimum values measured when emitters were on
    for (uint8_t i = 0; i < SensorCount; i++)
    {
      Serial.print(qtr.calibrationOn.minimum[i]);
      Serial.print(' ');
    }
    Serial.println();
  
    // print the calibration maximum values measured when emitters were on
    for (uint8_t i = 0; i < SensorCount; i++)
    {
      Serial.print(qtr.calibrationOn.maximum[i]);
      Serial.print(' ');
    }
    Serial.println();
    Serial.println();
  }
}

void DEBUG_POSITION(){
  // read calibrated sensor values and obtain a measure of the line position
  // from 0 to 5000 (for a white line, use readLineWhite() instead)
  uint16_t position = qtr.readLineBlack(sensorValues);

  // print the sensor values as numbers from 0 to 1000, where 0 means maximum
  // reflectance and 1000 means minimum reflectance, followed by the line
  // position
  for (uint8_t i = 0; i < SensorCount; i++)
  {
    Serial.print(sensorValues[i]);
    Serial.print('\t');
  }
  Serial.println(position);

}

Hello.

In general, there are a lot of factors that go into how well your robot can follow a tight turn, but if you are talking about sharp 90° turns (e.g. not smooth and with no radius), that is something that is commonly handled by breaking out of your main line-following algorithm when a turn is detected, performing a pre-programmed 90° turn, then going back into the line following algorithm. One way to detect a 90° turn might be to check if multiple channels on the QTR sensor are above the line at the same time. For example, if the three left-most sensors all see the line at the same time, your robot is likely at a 90° left turn.

To answer your questions, the readLineBlack() and readLineWhite() functions in our QTR reflectance sensor Arduino library remember where they last saw the line and continue to report the last known position until the line is reacquired. So, for example, if your robot veers off to the left (meaning your right-most sensor was the last one to see the line), the reading will return 7000 until it sees the line again, and if it veers off to the right, it will return 0. So, that gives you a way to know if the robot has lost the line and which side it last saw it. To time how long it has been since it last saw the line, you can use the built-in Arduino millis() function to clock a start time if you get one of those readings and compare it to the current millis() reading to see how much time has gone by.

Lastly, for increasing the speed when the robot is going straight, you might try simply making your BASE_SPEED constant into a variable and gradually increasing it as long as the error is between some reasonable range. Then if the error goes outside of the allowable range, you can set it back to the original BASE_SPEED.

Brandon

Hello Brandon,

thank you very much, i will try your solution , then i will update here.

regards
Mickael