Introduction: ArduBot

About: Mechatronic Engineering

The foundation for this robot comes from the Escape Robot kit. I purchased mine at Fry's. You can buy one for $25 online here: http://www.electronickits.com/robot/CK21886.htm

The kit includes the components and the PCB to make a maze solving robot. The kit works fairly well and is worth building if you buy it, but if you want it to be fully programmable and capable of supporting more sensors, you can use an arduino uno for the brain. I spent a week trying to build an h-bridge from 5v relays for motor control. But I ended buying the arduino motorshield for about $35 from radioshack. 

My robot uses the parallax PING sensor to sense the distance in front of the robot. I bought mine for $30, again at Frys.
   http://www.parallax.com/Store/Sensors/ObjectDetection/tabid/176/CategoryID/51/List/0/SortField/0/Level/a/ProductID/92/Default.aspx

I also added the parallax PIR (passive infrared) to the robot. The idea being the robot would start up when someone entered the room.

http://www.parallax.com/Store/Sensors/ObjectDetection/tabid/176/CategoryID/51/List/0/SortField/0/Level/a/ProductID/83/Default.aspx

 Later in this instructable I will explain how the code works for the robot, and how I made sense of the sensor data. I found this part of the project the most engaging, and I am eager to see how my program measures up.


Step 1: Chassis & Gearbox

Again, the chassis and gearbox used on this robots come from the Escape Robot kit.

The included instructions explain how to build to the gearbox. The platform is easily adapted for use with our arduino.

For the most part, the gearbox and platform work fine given the price EXCEPT:
    1. The clearance underneath is low, and the gears can hit the carpet and the fibers get tangled in the gears.
    2. The wheels have a tendency to stall if the motor mount is not mounted evenly between the two sets. 

Step 2: Arduino Box Mounting

I used one Airtronics servo packaging boxes to house the arduino boards. They seemed easy to adapt for mounting and protecting arduino boards. 

The boxes are transparent, so it is easy to position and mark where you need to cut and drill. 

I cut a hole for the USB connection so that I could program the arduino easily once the robot was assembled.

I also drilled holes for the motor wires and sensor wires to pass through, as well as holes for mounting the box to the platform and the board to the box.


Step 3: Arduino Motorshield

Once your arduino board is mounted, the motorshield easily slips in. Connect the motorwires to the boards terminals.

Once your motor shield is all connected, it becomes a matter of programming your arduino:


const int DIRB = 13; // sets left motor direction
const int DIRA = 12; // sets right motor direction
const int PWMB = 11; // analog; sets motor speed
const int PWMA = 3;  // analog; sets motor speed

int PWMAfwd, PWMArev, PWMAleft, PWMAright, PWMAhalt, PWMBfwd, PWMBrev, PWMBleft, PWMBright;

void setup() {
  pinMode(DIRB, OUTPUT);
  pinMode(PWMB, OUTPUT);
  pinMode(PWMA, OUTPUT);
  pinMode(DIRA, OUTPUT);
   Serial.begin(9600);
     PWMAfwd = 200;      //used to trim motorspeeds for different robot executions
     PWMBfwd = 255;
     PWMArev = 200;
     PWMBrev = 255;
     PWMAleft = 200;
     PWMBleft = 255;
     PWMAright = 200;
     PWMBright = 255;
}

void fwd () {                  //sets motors to forward
  digitalWrite(DIRA, HIGH);
  digitalWrite(DIRB, HIGH);
  analogWrite(PWMA, PWMAfwd);
  analogWrite(PWMB, PWMBfwd);

}

void rev (){                     //sets motors to reverse
   digitalWrite(DIRB, LOW);
   digitalWrite(DIRA, LOW);
   analogWrite(PWMB, PWMBrev);
   analogWrite(PWMA, PWMArev);
}

void left () {                  //turns bot left on spot
   digitalWrite(DIRB, LOW);
   digitalWrite(DIRA, HIGH);
   analogWrite(PWMB, PWMBleft);
   analogWrite(PWMA, PWMAleft);
}

void right (){                  // turns bot right on spot
   digitalWrite(DIRB, HIGH);
   digitalWrite(DIRA, LOW);
   analogWrite(PWMB, PWMBright);
   analogWrite(PWMA, PWMAright);
}

void halt (){                  //stops motors
    digitalWrite(PWMB, LOW);
    digitalWrite(PWMA, LOW);


}

void directiontest (){

  fwd ();
    delay (1000);
      halt ();
        delay (1000);
  rev ();
    delay (1000);
      halt ();
        delay (1000);
  left ();
    delay (1000);
      halt ();
        delay (1000);
  right ();
    delay (1000);
      halt ();
        delay (1000);

}

void loop (){
  directiontest();
}


If your bot's movements does not correspond to your function names you can re position the motor wires so that writing the direction pin HIGH sets the motors forward, or you can adjust it within your program.

It is not necessary to define variables for every pulse width modulation process, but I wanted to be able to tweak the motor speeds in different functions.

Step 4: Wiring

I am sorry I do not have a picture, but it all connects smoothly.

Cut the "Vin" pin off of the motorshield so that the motors and arduino are running off of different batteries. It might be possible to run both off the same power source at about 5v, but it seems at higher voltages the motorshield overheats.

Make sure your output pins from the sensors are plugged into the correct input on the arduino- as described in your code.

You can use a mini bread board to ground and power all the sensors to the 5v output.

Step 5: Sensors

The parallax and arduino community have ample example codes on how to run the PIR and PING sensors used in this robot. So it should only be a matter of assigning and independent functions for each sensor so that we can call for them whenever we want.
Here are a couple functions I defined for the PING sensor:


////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

unsigned long duration, inches, cm, dir;   //duration measures the time between PINGs
long minsafe = 15;                         //minimum safe distance in front of robot
int count;                                 //used to help identify when the robot is in a corner

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
unsigned long PING() {                         //triggers the ping sensor once and returns the value                           
pinMode(pingPin, OUTPUT);              // Make the Pingpin to output
digitalWrite(pingPin, LOW);                 //Send a low pulse
delayMicroseconds(2);                        // wait for two microseconds
digitalWrite(pingPin, HIGH);               // Send a high pulse
delayMicroseconds(5);                       // wait for 5 micro seconds
digitalWrite(pingPin, LOW);               // send a low pulse
pinMode(pingPin,INPUT);                 // switch the Pingpin to input
duration = pulseIn(pingPin, HIGH); //listen for echo

/*Convert micro seconds to Inches
*/

inches = microsecondsToInches(duration);
cm = microsecondsToCentimeters(duration);
Serial.print( cm);
Serial.print(" CM");
Serial.println ();

  return cm;

}


long microsecondsToInches(long microseconds) {
return microseconds / 74 / 2;
}
long microsecondsToCentimeters(long microseconds) {
return microseconds / 29 / 2;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
unsigned long leftping (){                   //records distance to the left of the robot and returns it to center
  int left;                                            //used to record the distance measured by the PING ()
   LEFT ();
  delay (420);
  STOP ();
  delay(10); 

    Serial.print ("Left: ") ;
    left = PING ();
    delay (420);

  RIGHT ();
  delay (420);
  STOP ();
    return left;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
unsigned long rightping (){ //records distance to the right of the robot and returns it to center
  int right;
  RIGHT ();
  delay (420);
  STOP (); 
  delay (10);

    Serial.print ("Right: ");
    right = PING ();
    delay (420);

  LEFT ();
  delay (420);
  STOP();
    return right ;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

unsigned long forwardping () { //essentiallhy PING
  int middle;
  STOP ();
  delay (10);
   Serial.print ("Middle: ");
   middle = PING ();
     return middle;

}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

unsigned long choose (){       //used to choose between our two ping functions
  leftping();
  forwardping();
  rightping();
if(leftping > rightping)
    if (leftping > forwardping)
      LEFT ();
      delay (420);
      STOP();
      dir = forwardping ();
if (rightping > leftping)
   if ( rightping > forwardping){
      RIGHT ();
      delay (420);
      STOP();
      dir = forwardping ();
}else
    STOP ();
    dir = forwardping ();

}
/////////////////////////////////////////////////////////////////////////////////////////////////////

Step 6: Physics

The next step is interpreting the data we are able to gather. 
The bots entire navigation is done by the PING sensor. This information will appear as a long string of integers. It is on us to use our knowledge of our robot and mechanics to make this data relevant.

An easy out would be to to just send the robot forward until it approached some minimally safe distance and then to turn it until the distance in front of it is greater than the minsafe and repeat. I was trying to have more fun with my arduino than  this :)

If we make the robot spin a circle and collect data points from our PING function,  we could use this data to represent the floor plan of a room in polar coordinates if we could associate a corresponding angle. 

We solve for that associated angle by measuring the angular velocity of one of the robots center wheels, multiplying by the circumference and the amount of time the wheel spent spinning. By relating this to arc length we can solve for change in angle (picture 1).

A simpler way, more practical way to do this would be to measure the amount of times it takes the robot to make one complete turn and realizing that its angular velocity (omega r) multiplied by change in time is equal the robots change in angle (picture 2).

Just for fun, we can convert these polar coordinates to cartesian coordinates (with the use of our calculated theta values) by realizing that the sin of the included angle is equal to the x component divided by the distance from PING sensor (P1 and P2 on picture 3). Solve for x. We can derive a value for y the same using cosine. 

Putting it all together we can calculate x and y coordinates for every data point captured by our PING sensor (picture 4). Lets start with 120 data points for one full scan.

My trouble is, when I change the batteries, the robot will turn at different rate. Likewise, as the battery dies the robot will turn slower. Making the time it takes to do a full 360 into a global constant makes it easier to measure and reenter the correct delay into your program.

It is possible to monitor the current the motors are drawing, but if you are using pulse width modulation on your motors, the current will move in a wave.

Is there a way to plot the robots location more accurately by monitoring this data(current, voltage etc)? 

Step 7: Program

We will need to define some arrays in order to store the polar and cartesian coordinates gathered to represent the floor plan of whatever room the robot is in. We also will need another array to indicate the delay time between pings in order to calculate theta.


const int pingPin = 7;
const int DIRA = 13;   //sets direction of motor a
const int DIRB = 12;   //sets direction of motor b
const int PWMA = 11;   //pulse width modulation for motor a
const int PWMB = 3;    //pulse width modulation for motor b
int n = 16;            //number of data points recorded for scan (); - 1
int ping[16];          //stores PING data for complete
int theta[16];         //stores polar data for data point n
int x[16];             //stores cartesian x value for data point n
int y[16];             //stores cartesian y value for data point n
int t[16];             //used to calculate theta; stores change in time from begining of scan for data point n
int spindelay = 3200;  // time in ms that it takes robot to spin one full rotation
int delta = 3200/16;             //delta = spindelay/n; amount of time for one rotation
unsigned long duration, inches, cm, dir;   //duration measures the time between PINGs
long minsafe = 15;                         //minimum safe distance in front of robot
int count;                                 //used to help identify when the robot is in a corner
int omega =2*3.1416/3.2;             //angular velocity of turning robot = 2pi/spindelay


void setup() {
  pinMode(DIRA, OUTPUT);//Sets direction of motor A
  pinMode(DIRB, OUTPUT);//Sets direction of motor B
  pinMode(PWMA, OUTPUT);//Turns A on and off
  pinMode(PWMB, OUTPUT);//Turns B on and off

  /* If DIRA = LOW, Motor A spins FWD
     If DIRB = LOW, Motor B spins FWD
  */

  // initialize serial communication:
  Serial.begin(9600);
}

void FWD () {                    //sets motors to forward
      digitalWrite(DIRA, HIGH);
      digitalWrite(PWMA, HIGH);
      digitalWrite(DIRB, HIGH);
      digitalWrite(PWMB, HIGH);
}

void REV (){                     //sets motors to reverse
      digitalWrite(DIRA, LOW);
      digitalWrite(PWMA, HIGH);
      digitalWrite(DIRB, LOW);
      digitalWrite(PWMB, HIGH);
}

void RIGHT (){                  //turns right on spot
      digitalWrite(DIRA, HIGH);
      digitalWrite(PWMA, HIGH);
      digitalWrite(DIRB, LOW);
      digitalWrite(PWMB, HIGH);
}

void LEFT (){                   //turns left on spot
      digitalWrite(DIRA, LOW);
      digitalWrite(PWMA, HIGH);
      digitalWrite(DIRB, HIGH);
      digitalWrite(PWMB, HIGH);
}

void STOP (){
      digitalWrite(PWMA, LOW);
      digitalWrite(PWMB, LOW);
     }


unsigned long PING() {
pinMode(pingPin, OUTPUT); // Make the Pingpin to output
digitalWrite(pingPin, LOW); //Send a low pulse
delayMicroseconds(2); // wait for two microseconds
digitalWrite(pingPin, HIGH); // Send a high pulse
delayMicroseconds(5); // wait for 5 micro seconds
digitalWrite(pingPin, LOW); // send a low pulse
pinMode(pingPin,INPUT); // switch the Pingpin to input
duration = pulseIn(pingPin, HIGH); //listen for echo

/*Convert micro seconds to Inches
*/

inches = microsecondsToInches(duration);
cm = microsecondsToCentimeters(duration);
Serial.print( cm);
Serial.print(" CM");
Serial.println ();

  return cm;

}


long microsecondsToInches(long microseconds) {
return microseconds / 74 / 2;
}
long microsecondsToCentimeters(long microseconds) {
return microseconds / 29 / 2;
}

unsigned long leftping (){ //records distance to the left of the robot and returns it to center
  int left;
   LEFT ();
  delay (420);
  STOP ();
  delay(10); 

    Serial.print ("Left: ") ;
    left = PING ();
    delay (420);

  RIGHT ();
  delay (420);
  STOP ();
    return left;
}

unsigned long rightping (){ //records distance to the right of the robot and returns it to center
  int right;
  RIGHT ();
  delay (420);
  STOP (); 
  delay (10);

    Serial.print ("Right: ");
    right = PING ();
    delay (420);

  LEFT ();
  delay (420);
  STOP();
    return right ;
}

unsigned long forwardping () { //essentiallhy PING
  int middle;
  STOP ();
  delay (10);
   Serial.print ("Middle: ");
   middle = PING ();
     return middle;


}

unsigned long choose (){
  leftping();
  forwardping();
  rightping();
if(leftping > rightping)
    if (leftping > forwardping)
      LEFT ();
      delay (420);
      STOP();
      dir = forwardping ();
if (rightping > leftping)
   if ( rightping > forwardping){
      RIGHT ();
      delay (420);
      STOP();
      dir = forwardping ();
}else
    STOP ();
    dir = forwardping ();

}

void scan (){
  delta = spindelay/n;
  int c;
  int i;
  int t;

  for (t = 1; t < 17; t ++){    
    theta[t] = t/8*3.1416;
   }


  for (c = 0; c < 16; c++){
    LEFT ();
    delay(delta);
    STOP ();
    delay (200);

    ping[c] = PING ();

    x[c] = ping[c]*sin(theta[c]);
    y[c] = ping[c]*cos(theta[c]);
  }

  for (i = 0; i <16; i ++){
    Serial.println(theta[i]);

}

void loop  (){
scan();
delay(10000);
}


Step 8: Motor Driver

So when I split the boards between two batteries, I accidentally overheated the motorshield running 12v through it. Now motor "B" on the shield does not run. I did not think that this voltage was outside of the boards capabilities. I am only using 4.8v nimh now...

In effort to repair the robot, I tried to construct an H-bridge for the second time. My bridge works, but does not allow the motor to turn nearly as fast as the one connected to the motorshield- even though they were using equal power sources.
A probable problem may be that I am using 4 different transistors...
I built my hbridge using:
PNP
2n 4403-331
mps 2907a 331

NPN
2n 3904 a19
2n 3904 020

Diodes:
IN400 MIC

What should I do in order to ensure adequate preformance?!

Arduino Challenge

Participated in the
Arduino Challenge