BOE Bot: IR & Visible navigation code : 1

/*
 * AJM
 * Robotics with the BOE Shield - LightSeekingShieldBot
 * Roams toward light and away from shade and uses IRtriggers for 
 * contact navigation. 
 * The algorithm used is:
 * 1: IRtriggers override everything
 * 2: when no IRtriggers are pressed, photo navigation takes over.
 * 3: photo navigation accepts a delta_photo variable to avoid
 *    fluctuations. 20 seems a reasonable choice.
 * 4: LEDs indicate choices made. They should not fluctuate 
 *    excessively. 
 * 5: photo-navigation sensitivity is set using the photo_sensitivity
 *    variable. 3000.0 seems to be a good value.
 * 6: Attempting to get out of corners by reducing photo sensitivity
 *    when corners are encountered. 
 */

#include <Servo.h>                           // Include servo library
 
Servo servoLeft;                             // Declare left and right servos
Servo servoRight;

// Pins for buzzer, IR receivers and IR-LEDs 
int pinBuzz = 6;
int pinLir = 8;
int pinRir = 4;
int pinLrecv = 9;
int pinRrecv = 5;
int pinLled = 11;
int pinRled = 2;
// Pins for visible light sensors:
int pinLsensor = 10;
int pinRsensor = 3;

// some data for IR-navigation algorithm:
byte IRtriggersOld;                           
byte counter;                                // For counting alternate corners
int irFreq = 38000;

boolean photo_nav = true;                   // Set to false to disable photo-nav
int delta_photo = 40;                        // Used to suppress photo-fluctuations
float photo_sensitivity_default = 2000.0;
float photo_sensitivity = 2000.0;            // small == less sensitivity

/* 
 * Motor speeds are variable in the photo-navigation section,
 * but are fixed in the IRtriggers-navigation part. 
 */
int forwardL = 1700;
int forwardR = 1300;
int reverseL = 1300;
int reverseR = 1700;
int neutral  = 1500;

void setup()                                 // Built-in initialization block
{
  tone(pinBuzz, 3000, 1000);                 // Play tone for 1 second
  delay(1000);                               // Delay to finish tone

  servoLeft.attach(13);                      // Attach left signal to pin 13
  servoRight.attach(12);                     // Attach right signal to pin 12

  // Initialise the IR navigation variables:
  counter = 0;
  IRtriggersOld = 2;
  
  pinMode(pinLrecv, INPUT);
  pinMode(pinLir, OUTPUT);   // Left IR LED & Receiver
  pinMode(pinRrecv, INPUT);
  pinMode(pinRir, OUTPUT);   // Right IR LED & Receiver
  pinMode(pinLled, OUTPUT);  // Left and Right LEDs
  pinMode(pinRled, OUTPUT);
  
  Serial.begin(9600);                        // Output to Serial
  while (! Serial); // Wait untilSerial is ready
  Serial.println("BOE bot. Welcome!");

}  
 
void loop()
{
  // Main loop. This auto-repeats.
  // =========
  // The idea here is to give the IRtriggers precedence. If there is no
  // input from the IRtriggers then use photo-navigation
  //
  
  int IRtriggers;
  
  IRtriggers = read_IRreceivers();

  if (Serial.available())                    // In general, output to Serial
  {                                          // only if input is detected.
    Serial.print("IR triggers = ");
    Serial.println(IRtriggers);
  }

  /* 
   * The Get-Out-Of-A-Corner code:
   * Works by saving the previous IR trigger states. If an alternation
   * is detected for a certain number (3 or 4) of times, BOEbot is in
   * a corner (likely!). In that case, best to try a near-reverse.
   */
  if((IRtriggers == 1) || (IRtriggers == 2)) // One side triggered?
  {                                          // Alternate from last time?
    if (IRtriggers != IRtriggersOld)  
    {                                       
      counter++;                             // Increase count by one
      IRtriggersOld = IRtriggers;
      photo_sensitivity -= 100.0;
      if(counter == 3)                       // Stuck in a corner?
      {
        IRtriggers == 0;                       // Set up for U-turn
        counter = 0;                         // Clear alternate corner count
        photo_sensitivity = photo_sensitivity_default;
      }
    }  
    else                                     // Not alternate from last time
    {
      counter = 0;                           // Clear alternate corner count
    }
  }  

  /*
   * Basic IRtriggers navigation code:
   * Control is passed to the photo-navigation function only if
   * neither IR detector is triggered.
   */
  if(IRtriggers == 0)          // If both IRtriggers contact
  {
    digitalWrite(pinLled, HIGH);
    digitalWrite(pinRled, HIGH);
    backward(500);                          // Back up 0.5 second
    turnLeft(50);                          // Turn left about 60 degrees
  }
  else if(IRtriggers == 1)                   // If only left detector triggered
  {
    digitalWrite(pinLled, HIGH);
    digitalWrite(pinRled, LOW);
    //backward(200);                           // Back up 0.2 second
    turnRight(50);
    //pivotRight(100);
  }
  else if(IRtriggers == 2)                   // If only right detector triggered
  {
    digitalWrite(pinRled, HIGH);
    digitalWrite(pinLled, LOW);
    //backward(200);                           // Back up 0.2 second
    turnLeft(50);
    //pivotLeft(100);
  }
  else                                       // Otherwise, no IR trigger
  {
    // No IR trigger so use photo-navigation (if allowed):
    digitalWrite(pinLled, LOW);
    digitalWrite(pinRled, LOW);
    if (photo_nav)
    {
      photo_navigate(100);
    }
    else
    {
      forward(100);
    }
  }
}

// IR Object Detection Function

int irDetect(int irLedPin, int irReceiverPin, long frequency)
{
  tone(irLedPin, frequency, 8);              // IRLED 38 kHz for at least 1 ms
  delay(1);                                  // Wait 1 ms
  int ir = digitalRead(irReceiverPin);       // IR receiver -> ir variable
  delay(1);                                  // Down time before recheck
  return ir;                                 // Return 1 no detect, 0 detect
} 
byte read_IRreceivers()
{
  // Return:
  // 0 : both trigger
  // 1 : left
  // 2 : right
  // 3 : neither trigger
  byte IRtriggers;
  int irL = irDetect(pinLir, pinLrecv, irFreq);
  int irR = irDetect(pinRir, pinRrecv, irFreq);
  IRtriggers = 2 * irL;
  IRtriggers += irR;
  //Serial.println(IRtriggers);
  return(IRtriggers);
}

void forward(int time)                       // Forward function
{
  servoLeft.writeMicroseconds(forwardL);     // Left wheel counterclockwise
  servoRight.writeMicroseconds(forwardR);    // Right wheel clockwise
  delay(time);                               // Maneuver for time ms
}

void turnLeft(int time)                      // Left turn function
{
  servoLeft.writeMicroseconds(reverseL);     // Left wheel clockwise
  servoRight.writeMicroseconds(forwardR);    // Right wheel clockwise
  delay(time);                               // Maneuver for time ms
}

void turnRight(int time)                     // Right turn function
{
  servoLeft.writeMicroseconds(forwardL);     // Left wheel counterclockwise
  servoRight.writeMicroseconds(reverseR);    // Right wheel counterclockwise
  delay(time);                               // Maneuver for time ms
}

void backward(int time)                      // Backward function
{
  servoLeft.writeMicroseconds(reverseL);     // Left wheel clockwise
  servoRight.writeMicroseconds(reverseR);    // Right wheel counterclockwise
  delay(time);                               // Maneuver for time ms
}
 
void pivotLeft(int time)                      // Left pivot function
{
  servoLeft.writeMicroseconds(reverseL);     // Left wheel clockwise
  servoRight.writeMicroseconds(neutral);     // Right wheel stops
  delay(time);                               // Maneuver for time ms
}

void pivotRight(int time)                    // Right pivot function
{
  servoLeft.writeMicroseconds(neutral);      // Left wheel stops
  servoRight.writeMicroseconds(reverseR);    // Right wheel counterclockwise
  delay(time);                               // Maneuver for time ms
}

void photo_navigate(int timems) 
{
  // Photo navigation function
  // 
  float tLeft = float(rcTime(pinLsensor));    // Get left light & make float
  float tRight = float(rcTime(pinRsensor));   // Get right light & make float
 
  float ndShade;                             // Normalized differential shade
  ndShade = tRight / (tLeft+tRight) - 0.5;   // Calculate it and subtract 0.5

  int speedLeft, speedRight;                 // Declare speed variables
 
  if (ndShade > 0.0)                         // Shade on right?
  {                                          // Slow down left wheel
    speedLeft = int(200.0 - (ndShade * photo_sensitivity));
    speedLeft = constrain(speedLeft, -200, 200);
    speedRight = 200;                        // Full speed right wheel
  }
  else                                       // Shade on Left?
  {                                          // Slow down right wheel
    speedRight = int(200.0 + (ndShade * photo_sensitivity));
    speedRight = constrain(speedRight, -200, 200);
    speedLeft = 200;                         // Full speed left wheel
  }  
  /*
   * Stutter Suppression
   * ====
   * If the left and right servo speeds are too close (i.e. not much
   * difference in light on the two sides) then we may encounter a 
   * stutter: that is, the motors can rapidly alternate in speed and 
   * direction. To suppress this we use the following code. If the 
   * two speeds are less than delta_photo apart, the two are assigned
   * the same speed, taken as the average of the two.
   */
  if (abs(speedLeft-speedRight) < delta_photo)
  {
    int avg = (speedLeft + speedRight)/2;
    speedLeft = avg;
    speedRight = avg;
  }
  /*
   * Write out the speeds if input is detected on the serial port
   */
  if (Serial.available())
  {
    Serial.print("Speeds L= ");
    Serial.print(speedLeft);
    Serial.print("  R= ");
    Serial.println(speedRight);
    Serial.print("   Reading L=");
    Serial.print(tLeft);
    Serial.print("   Reading R=");
    Serial.print(tRight);
    Serial.print("   ndShade =");
    Serial.println(ndShade);
  }

  /*
   * Get rolling!!!
   */
  maneuver(speedLeft, speedRight, timems);
}

long rcTime(int pin)                         // rcTime measures decay at pin
{
  /*
   * This is an RC-time operation. Because we need to measure the decay
   * time fairly rapidly, the capacitance in parallel with the photo-transistor
   * is quite small (in this case, 0.1 microFarad). 
   * Time constant = R * C
   *               = (V/I) * C
   * Here, V = 5 V (set by digitalWrite(pin, HIGH)
   * I varies depending on incident light. 
   *    = 1.75 mA (bright) => R = V/I =  3 kOhm  => RC = 3e-4 sec  =  300 msec
   *    = 0.25 ma (dim)    => R = V/I = 20 kOhm  => RC = 0.002 sec = 2000 msec
   * This is rough. In practice you will see a greater variation.
   */
  pinMode(pin, OUTPUT);                      // Charge capacitor
  digitalWrite(pin, HIGH);                   // ..by setting pin ouput-high
  delay(5);                                  // ..for 5 ms
  pinMode(pin, INPUT);                       // Set pin to input
  digitalWrite(pin, LOW);                    // ..with no pullup
  long time  = micros();                     // Mark the time
  while(digitalRead(pin));                   // Wait for voltage < threshold
  time = micros() - time;                    // Calculate decay time
  return time;                               // Returns decay time
}

// maneuver function
void maneuver(int speedLeft, int speedRight, int msTime)
{
  if (abs(speedLeft) < abs(speedRight))
  {
    digitalWrite(pinRled, HIGH);
    digitalWrite(pinLled, LOW);
  } 
  else if (abs(speedLeft) > abs(speedRight))
  {
    digitalWrite(pinRled, LOW);
    digitalWrite(pinLled, HIGH);
  }
  else
  {
    digitalWrite(pinRled, LOW);
    digitalWrite(pinLled, LOW);
  }
  servoLeft.writeMicroseconds(neutral + speedLeft);   // Set left servo speed
  servoRight.writeMicroseconds(neutral - speedRight); // Set right servo speed
  if(msTime==-1)                                   // if msTime = -1
  {                                  
    servoLeft.detach();                            // Stop servo signals
    servoRight.detach();   
  }
  delay(msTime);                                   // Delay for msTime
}

AJMPublic/teaching/arduino-pi/projects/arduino/boe-bot/IR-visible-code-1-AJM (last edited 2021-04-14 13:08:03 by apw109)