Problems consistently turning 90 degrees with tracked robot

I am currently working on a project for school. The idea was to create a robot that could perform pathfinding functions using A*. The path is found using a virtual grid of nodes programmed into the Arduino where "X"s are non-passable objects, “0” as the start, and “1” as the destination.

Example:

char createPlane[][planeSizeX] = {{'.', '.', '.', '.', '.', 'X', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.'},
                                  {'.', 'X', '.', 'X', '.', 'X', '.', 'X', 'X', 'X', 'X', 'X', '.', '.', 'X', '.'},
                                  {'.', 'X', '.', '.', '.', '.', '.', 'X', '.', '.', '.', '.', '.', '.', 'X', '.'},
                                  {'.', '.', 'X', '.', 'X', 'X', 'X', 'X', '.', 'X', '.', '.', 'X', '.', '0', '.'},
                                  {'.', 'X', '.', '.', 'X', '.', '.', '.', '.', '.', '.', '.', '1', 'X', 'X', '.'},
                                  {'.', '.', '.', '.', '.', '.', 'X', '.', 'X', '.', 'X', '.', '.', '.', 'X', '.'},
                                  {'.', 'X', 'X', '.', '.', 'X', 'X', '.', 'X', '.', 'X', 'X', 'X', '.', 'X', '.'},
                                  {'.', '.', '.', '.', '.', '.', '.', '.', 'X', '.', '.', '.', '.', '.', '.', '.'}};

The instructions for the path above would be: left, up, left, left, down, down, right.

Current parts:
Dagu Rover 5 tracked robot (no encoders, 2 motors)
Arduino Mega 2560
Arduino Motor Shield
HMC5883L magnetometer compass

My pathfinding application works great and I know I am building the correct instructions for the robot to follow. At this point, I have already spent weeks just trying to get this thing to turn 90 degrees consistently. It is vital for my robot to turn as accurately as possible in order to arrive at the correct destination.

I bought the HMC5883L compass hoping that it would allow me to turn 90 degrees. I have gotten to the point where it will turn until the correct heading is reached (according to the compass), but it is not consistent at all. Sometimes too much, sometimes not enough.

Currently, I am taking an average of the X and Y readings along with the adjustment values for X and Y that I calculated using this blog. I believe I have correct declination for my area as well.

float getAvg(float array[])
{
  float average = 0;
  for (int i = 0; i < 5; i++)
    average += array[i];
  average /= 5;
  return average;
}

float getHeading(HMC5883L &compass)
{
   float declinationAngle = 12.183;
   float xScaled[5];
   float yScaled[5];
   MagnetometerScaled scaled;
   for (int i = 0; i < 5; i++)
   {
     scaled = compass.ReadScaledAxis();
     xScaled[i] = (scaled.XAxis - xAdj);
     yScaled[i] = (scaled.YAxis - yAdj);
   }
   float xFinal = getAvg(xScaled);
   float yFinal = getAvg(yScaled);
   float heading = atan2(yFinal, xFinal);
   heading += declinationAngle / 1000;

     if(heading < 0)
       heading += 2*PI;
     if(heading > 2*PI)
       heading -= 2*PI;
     heading *= 180/M_PI;

   return heading;
}

Here is the messy code I am currently using to turn:

void drive(Stack <short> thePath)
{
  short instruction;
  short temp;
  analogWrite(pwmA, 255);
  analogWrite(pwmB, 255);
  digitalWrite(brakeA, 0);
  digitalWrite(brakeB, 0);
 
  int err = 0;
  HMC5883L compass;
  Wire.begin();
  compass = HMC5883L();
  err = compass.SetScale(1.3);
  if(err != 0) //check for error
    Serial.println(compass.GetErrorText(err));
  err = compass.SetMeasurementMode(Measurement_Continuous);
  if(err != 0) //check for error
    Serial.println(compass.GetErrorText(err));
  
  instruction = thePath.pop();
  
  while (1)
  { 
    //checks if the robot needs to turn
    if (instruction != thePath.peek())
    {
      float heading = -1;
      float currentHeading = -1;
  
      heading = getHeading(compass);
      currentHeading = heading;
      
      temp = instruction - thePath.peek();

      if (temp == -1 || temp == 3)
      {
        int currentHeadCorrect = 0;
        heading += 90;
        if(heading > 360)
          heading -= 360;
        if (currentHeading >= 270)
          currentHeadCorrect = 360;

        while ((currentHeading - currentHeadCorrect) < heading)
        {
          currentHeading = getHeading(compass);

          if ((currentHeading - currentHeadCorrect) <= -300)
            currentHeadCorrect = 0;

          digitalWrite(directionA, bwd); //turn right
          digitalWrite(directionB, fwd);
        }
      }
      else if (temp == 1 || temp == -3)
      {
        int currentHeadCorrect = 0;
        heading -= 90;

        if(heading <= 0)
          heading += 360;
        if (currentHeading < 90)
          currentHeadCorrect = 360;

        while ((currentHeading + currentHeadCorrect) > heading) //if (temp == -1 || temp == 3)
        {
          currentHeading = getHeading(compass);

          if ((currentHeading + currentHeadCorrect) > 700)
            currentHeadCorrect = 0;
          
          digitalWrite(directionA, fwd); //turn left
          digitalWrite(directionB, bwd);
        }
      }
      else
        error();
    }
    
    //go straight
    analogWrite(pwmA, 255);
    analogWrite(pwmB, 255);
    digitalWrite(directionA, bwd);
    digitalWrite(directionB, bwd);
    delay(930);

    instruction = thePath.pop();
  }
}

Even with all of this, I am still not getting the precision I need for turning. Please let me know of any ideas any of you have that would allow me to have the precision in turning that I am looking for.

Lets see you have two nice big sources of magnetism (the motors) close to your magnetic compass and then you wonder why you can't follow a third smaller source of magnetism and you find you have a problem. Mmmm

Mark

If you look at my picture attached, you will see how I solved that problem... :stuck_out_tongue:

I have my compass on top of a long rod holding it far away from any conflicting magnetic fields.

I am still open to any alternative solutions.

Do you have control over the environment? Can you paint the north wall blue, the west wall red and use colour sensors? Can you put an IR LED at 'north' and have 4 IR sensors on the robot?

I would look at using a gyro to do this. The standard MEMS gyros aren't accurate enough to control an airliner but they're good enough to get within 1 degree accuracy on a robot like yours. I've done a bit of work with the MPU6050, which has 3 accelerometers and 3 gyros so it's way overkill for what you need but I expect that it should be possible to use one gyro axis to do what you want.

Thank you for the reply Morgan!

I do have control over the environment. I suppose I could use color, but I was hoping to be able to make it a bit more robust than that. I would like to be able to place it in any environment. However, for the sake of getting the project done, I may have to do that.

As far as the gyro goes, I have just been dismissing them as a possibility because I had hoped the compass would do just fine. My compass claims to have 1 degree of accuracy as well, but maybe the gyros are a little more consistent. I'll look into it further. Thanks!

As I have never used a gyro before, if you have any ideas for easy to use gyros, I would appreciate the direction. Thanks!

UPDATE: The MPU6050 looks pretty promising. My professor already has a how-to guide for that board. Is this the same board you were talking about? http://www.amazon.com/Kootek-Arduino-MPU-6050-gyroscope-accelerometer/dp/B008BOPN40/ref=sr_1_1?ie=UTF8&qid=1426744070&sr=8-1&keywords=MPU6050