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)