non tactile maze robot

For a school project, we are designing a non-tactile maze robot, designed to navigate its way thru a maze without touching the walls.
We are attempting this with one IR sensor in the front and two on the right side, designed to follow the right wall.

We are using the arduino duemilanove 328 and the ardumotomotor driver shield.

we cannot get the robot to re-adjust to make it drive in a straight line.
when we try to make it do 90-deg right and left hand turn, sometimes it will do a 270-deg turn.

we cannot figure out what we're doing wrong here.

please help!

below is our current code in its entirety.

//ONU MAZE ROBOT 2012

#include <EEPROM.h>

const int STATE_FORWARD = 1;
const int STATE_TURN_RIGHT = 2;
const int STATE_BACKWARD = 3;
const int STATE_TURN_LEFT = 4;
const int STATE_ADJ_RIGHT = 5;
const int STATE_ADJ_LEFT = 6;


int IRpin1 = 1;  
int IRpin2 = 2;
int IRpin3 = 3;
int IRpin4 = 4;

int pwm_a = 3;  //PWM control for motor outputs 1 and 2 is on digital pin 3
int pwm_b = 11;  //PWM control for motor outputs 3 and 4 is on digital pin 11
int dir_a = 12;  //direction control for motor outputs 1 and 2 is on digital pin 12
int dir_b = 13;  //direction control for motor outputs 3 and 4 is on digital pin 13

int state; //current state
int lastState; //previous state

void setup() 
{
  Serial.begin(9600);       // start the serial port
  pinMode(pwm_a, OUTPUT);  //Set control pins to be outputs
  pinMode(pwm_b, OUTPUT);
  pinMode(dir_a, OUTPUT);
  pinMode(dir_b, OUTPUT);
  
  analogWrite(pwm_a, 80);  //set both motors to run at (100/255 = 39)% duty cycle (slow)
  analogWrite(pwm_b, 80);
  lastState = state = STATE_FORWARD;
}

void loop() 
{
 
  float volts1 = analogRead(IRpin1)*0.0048828125;   // value from sensor * (5/1024) - if running 3.3.volts then change 5 to 3.3
  float Front = 65*pow(volts1, -1.10);          // worked out from graph 65 = theretical distance / (1/Volts1)S - luckylarry.co.uk
  Serial.println(Front);                       // print the distance
  delay(20);                                      // arbitary wait time.
  
  float volts2 = analogRead(IRpin2)*0.0048828125;   // value from sensor * (5/1024) - if running 3.3.volts then change 5 to 3.3
  float FrontSide = 65*pow(volts2, -1.10);          // worked out from graph 65 = theretical distance / (1/Volts1)S - luckylarry.co.uk
  Serial.println(FrontSide);                       // print the distance
  delay(20);                                      // arbitary wait time.
  
  float volts3 = analogRead(IRpin3)*0.0048828125;   // value from sensor * (5/1024) - if running 3.3.volts then change 5 to 3.3
  float BackSide = 65*pow(volts3, -1.10);          // worked out from graph 65 = theretical distance / (1/Volts1)S - luckylarry.co.uk
  Serial.println(BackSide);                       // print the distance
  delay(20);                                      // arbitary wait time.
  
  float volts4 = analogRead(IRpin4)*0.0048828125;   // value from sensor * (5/1024) - if running 3.3.volts then change 5 to 3.3
  float LeftSide = 65*pow(volts3, -1.10);          // worked out from graph 65 = theretical distance / (1/Volts1)S - luckylarry.co.uk
  Serial.println(LeftSide);                       // print the distance
  delay(20);                                      // arbitary wait time.

  Serial.print('\n');                              // skips a line
  
  if(state == 0) 
  {
    moveRobot();
    return;
  }
  
if (FrontSide < 90 && BackSide < 90 && Front > 100)
{
  state = STATE_FORWARD;
  analogWrite(pwm_a, 80);  
  analogWrite(pwm_b, 80);
  if (FrontSide > (BackSide+3))
  {
    state = STATE_ADJ_RIGHT;
  }
 else if (FrontSide < (BackSide+3))
  {
    state = STATE_ADJ_LEFT;  
  }
}
else if (FrontSide > 90 && BackSide > 90)
{
  analogWrite(pwm_a, 80);  
  analogWrite(pwm_b, 80);
  state = STATE_TURN_RIGHT;
}
else if (Front < 90)
{
  analogWrite(pwm_a, 80);  
  analogWrite(pwm_b, 80);
  state = STATE_TURN_LEFT;
}

Serial.println(state);
  moveRobot();
}

void moveRobot() 
{
  switch( state ) {
    case 1: //forward
    {
      digitalWrite(dir_a, LOW);  //Set motor direction, 1 low, 2 high
      digitalWrite(dir_b, LOW);  //Set motor direction, 3 high, 4 low
      break;
    }
    case 3: //backward
    {
      digitalWrite(dir_a, HIGH);  //Set motor direction, 1 low, 2 high
      digitalWrite(dir_b, HIGH);  //Set motor direction, 3 high, 4 low
      delay (1000);
      break;
    }
    case 2: //right
    {
      delay(300);
      digitalWrite(dir_a, LOW);  //turn right
      digitalWrite(dir_b, HIGH);  //
      delay (550);
      digitalWrite(dir_a, LOW);  //straight
      digitalWrite(dir_b, LOW);  //Set motor direction, 3 high, 4 low
      delay (1300);
      digitalWrite(dir_a, LOW);  //turn right
      digitalWrite(dir_b, HIGH);  //Set motor direction, 3 high, 4 low
      delay (600);
      digitalWrite(dir_a, LOW);  //straight
      digitalWrite(dir_b, LOW);  //Set motor direction, 3 high, 4 low
      delay (2200);
      break;
    }
    case 4: //left
    {
      digitalWrite(dir_a, HIGH);  //Set motor direction, 1 low, 2 high
      digitalWrite(dir_b, LOW);  //Set motor direction, 3 high, 4 low
      delay (300);
      break;
    }
    case 5:  //state adj right
    {
     analogWrite(pwm_a, 82); 
     delay (400);
     break;
    }
    case 6: //state adj left
    {
     analogWrite(pwm_a, 75);
     delay(400);
     break;    
   }
  }
}

//END OF LINE
  1. I assume you're using GP2D12s or somesuch, but it's not indicated.

  2. first off, I would make the debug printouts a little more descriptive,
    maybe add a string label in front of each IRR reading, as
    Serial.print("Front ");
    Serial.println(Front);

  3. also, your case statements look a little weird to me, they probably work
    ok, but are usually written "without" the braces. ???

  4. one usually adds a default: line at the end of the switch to handle nonmatches.

  5. the statement if(state == 0) {...} doesn't do anything once inside the switch;
    is it supposed to?

  6. switch case 2: //right executes for almost 5-seconds, that seems an awfully
    long time to turn, and may explain the 270-deg turns.

  7. also, if you analyze your decision structure, you'll see there are large holes
    in it, eg for Front in 91...99, and for Frontside<=89 AND Backside=anything,
    and vice versa. These are logical holes that may be causing behavioral holes.
    Best to try and plug the logical holes.

if (FrontSide < 90 && BackSide < 90 && Front > 100) { }
else if (FrontSide > 90 && BackSide > 90) { }
else if (Front < 90) { }

It might work better to say ... else if (Front <= 100) { }.

  1. It also might help to have a default at the end of the decision structure,
    eg ... else { setting for go_straight }.

  2. Also, for if (FrontSide > 90 && BackSide > 90) { }, I might try using a
    right_turn less than a full turn, more like a hard_adjust_right, so just
    bear over towards the wall rather than try to turn a full 90-deg, and
    possibly overshooting 90-deg.

Other than that, I don't have the robot here in front of me to play with
until I get it to work. :slight_smile:

  1. first off, I would make the debug printouts a little more descriptive,

I second this.

  1. also, your case statements look a little weird to me, they probably work
    ok, but are usually written "without" the braces. ???

The braces are fine. They are not required, but adding them doesn't hurt.

  1. one usually adds a default: line at the end of the switch to handle nonmatches.

The default case is optional, and can be left out if it would be empty anyway.

  float volts4 = analogRead(IRpin4)*0.0048828125;   // value from sensor * (5/1024) - if running 3.3.volts then change 5 to 3.3
  float LeftSide = 65*pow(volts3, -1.10);          // worked out from graph 65 = theretical distance / (1/Volts1)S - luckylarry.co.uk

Does the actual distance matter? There is a value read from the pin that the sensor is attached to. It might be simpler to just use that value, instead of all this mucking around.

In any case, why is LeftSide based on volts3? Should it be a function of volts4?

why is LeftSide based on volts3? Should it be a function of volts4?

Good catch, I missed it, although it isn't used in the decision structure.

Another thing occurred to me this morning that may have bearing on the
overall behavior. If these are GP2D12 type devices being used, and it still
isn't clear if this is the case, they take about 50-msec to produce a new
output reading, and each reading is based upon the average of 10 triangulations
made over those 50-msec.

If the bot is moving fast, or especially if "rotating" fast, GP2 devices can
produce large errors in distance measurement. And this is worse if some
of the readings are taken at oblique angles, such as the bot near a wall
and the sensor pickup angle turned almost parallel to it.

FWIW, Joe Jones in his book Robot Programming goes on and on about the
general unreliability of most sensor readings, and how it pays to have different
types backing up each other. If you only have one temperature probe on your
nuclear reactor, and the probe fails, then what? Just something to think about.