Arduino Robot 2 - Wandering Tank

My second robot was a bit more complex and ended up taking twice as long to build as originally intended. The budget changed, the designed changed, and my understanding of Robotics in general changed. The second robot is an attempt to make an obstacle avoidance robot that isn't overly simple. I will be outlining the goals, budget, build and code for Wandering Tank.

The goals for Wandering Tank were simple:
  • Should be able to scan an area in front of itself.
  • Should be able to avoid not only walls, but obstacles.
  • Avoid random decision making; the robot should use information when avoiding.
Wandering tank meets these goals, and also looks pretty cool doing it. The code for the robot will show how the third goal is met and the other two will be obvious in the video.

The budget for Wandering Tank was quite cheap. Since I started with Robot 1, the only additional cost for Wandering was the sensor and the servo.
 So Wandering Tank has an estimated cost of $120.00 to build.

Building Wandering Tank was pretty quick. The chassis was modified to hold a servo and the wiring for the motors was improved. Originally the original continuous servo was used, which you will see in the previous post about scanning. Eventually a real servo was purchased and replaced the scanning mechanism. The first thing I did, was improve the motor shield by adding headers for the Arduino ports that it wasn't using. You can see here on the lower right and upper right the additional headers:

 The next step was to modify the chassis to hold a standard size servo. The servo was lined up and used as a tracing template, I then cut out the area to make room. You will also notice in the first picture the improved running of the motor wires, and that the gear box is moved out slightly. This made the treads tighter and the robot moved better! Finally, the battery for the Arduino (9V DC) now fit underneath with room for the servo. In the middle on top is the velcro for the motor battery pack.

 Next the servo was mounted with the Velcro piece in place to hold the sensor. In the picture, the motor shield battery is in place as a test fitting.
 

Finally, everything is assembled and you have Wandering Tank (that does nothing):

With the bot built, it was time to write code to do the actual logic. The code for the scanner from the previous post couldn't be used and I ended up starting over. The code would work life this:
  1. Update Sensor Angle
  2. Scan with Sensor
  3. Move
Each of these steps is much more complicated than it is described. To update the sensor angle requires checking bounds and reversing the direction as necessary. When the sensor is checked, if there is a collision it records the sensor angle during collision (but lets the sensor angle continue to update!), and then records the collision. Movement works by checking for collision and sensor collision angle. If the robot sees something, it moves backwards until vision is clear; it then turns away from the object based on which side of the robot it saw the object. The robot then continues forward, scanning and avoiding as necessary. Here is the source code with comments:

/*
 * George Frick (george.frick@gmail.com)
 * Jan/Feb 2010.
 * Arduino Robot 2 - Wandering Tank
 * This robot will wander around, scanning with a very short range forward IR.
 * When an object is detected, the robot will turn based on the position of the IR sensor
 * during the detection. The robot will move backwards, then turn away from the detected object
 * and continue. The robot runs continuously.
 */
#include <AFMotor.h>
#include <Servo.h>

// Two motors, a servo, and an IR sensor.
AF_DCMotor motor1(1, MOTOR12_64KHZ); // create motor #1, 64KHz pwm
AF_DCMotor motor2(2, MOTOR12_64KHZ); // create motor #2, 64KHz pwm
Servo sensorServo;
const int irPin = 19; // pin 5 as digital is pin 19

// Timing. I don't like using delay.
unsigned long tCnt = 0;
unsigned long tStart = 0;
unsigned long tDelta = 0;
unsigned long tTurn = 0;

int state;               // Current Robot State
int lastState;           // Previous Robot State
int servoPos;            // Position to send servo
int servoDirection;      // Direction servo is turning
int lastDetectionAngle;  // Position of servo at last IR detect.

// Constants for state of tank tracks.
const int STATE_FORWARD = 1;
const int STATE_TURN_RIGHT = 2;
const int STATE_BACKWARD = 3;
const int STATE_TURN_LEFT = 4;

// Constants for Servo.
const int DIR_LEFT = 0;
const int DIR_RIGHT = 1;
const int MIN_DEGREE = 40;
const int MAX_DEGREE = 140;

/*
 * Initializes everything. Is run once.
 */
void setup() {
  Serial.begin(9600);           // set up Serial library at 9600 bps
  sensorServo.attach(10);  // attaches the servo on pin 10
  pinMode(irPin, INPUT);
  motor1.setSpeed(255);     // set the speed to 200/255
  motor2.setSpeed(255);     // set the speed to 200/255
  tStart = millis();
  lastState = state = STATE_FORWARD;
  servoPos = 90;
  servoDirection = DIR_RIGHT;
  sensorServo.write(servoPos);
  //state = 0; // Uncomment to have robot not move tank tracks.
}

/*
 * Runs continuously.
 * 1. Update Servo.
 * 2. Check IR sensor.
 * 3. Move Robot
 */
void loop() {
  tDelta = millis() - tStart;
  tCnt += tDelta;
  tStart += tDelta;

  // Tell the servo to move 2 degrees every 25 ticks.
  if( tCnt > 25 ) {
    tCnt = 0;
    if( servoDirection == DIR_LEFT ) {
      servoPos -= 2;
    } else if( servoDirection == DIR_RIGHT) {
      servoPos += 2;
    }

    // Servo position will be beyond desired angles, turn around.
    if( servoPos >= MAX_DEGREE ) {
      servoDirection = DIR_LEFT;
    } else if( servoPos <= MIN_DEGREE ) {
      servoDirection = DIR_RIGHT;
    }
    sensorServo.write(servoPos);
  }

  // Allows disabling of tracks by setting state to 0.
  if(state == 0) {
    moveRobot();
    return;
  }

  // Double read the pin, @see forums.adafruit.com
  digitalRead(irPin);
  delay(5);
  if( digitalRead(irPin) == 0 ) {
    lastDetectionAngle = servoPos;
    state = STATE_BACKWARD;
  } else {
    if( state == STATE_BACKWARD ) {
      if( lastDetectionAngle > 105 ) { // right
        state = STATE_TURN_LEFT;
        tTurn = 1000; // turn for ~1 seconds
      } else if( lastDetectionAngle < 75 ) { // left
        state = STATE_TURN_RIGHT;
        tTurn = 1000; // turn for ~1 seconds
      } else { // center
        state = STATE_TURN_RIGHT; // for now, turn right by default.
        tTurn = 1500; // turn for ~1 seconds
      }
    } else if ( state == STATE_TURN_RIGHT || state == STATE_TURN_LEFT ) {
      tTurn -= tDelta;
      if( tTurn <= 10 ) {
        state = STATE_FORWARD;
      }
    } else {
      state = STATE_FORWARD;
    }
  }

  moveRobot();
}

/*
 * Uses the state of the robot to move tank treads accordingly
 */
void moveRobot() {

  // The motors seemed to respond better if they receive a stop before a switch in direction.
  if( state != lastState ) {
    motor1.run(RELEASE);      // stopped
    motor2.run(RELEASE);      // stopped
  }

  switch( state ) {
    default:
      return; // helps test, state 0 = dont move.
    case STATE_FORWARD: {
      motor1.run(FORWARD);      // turn it on going forward
      motor2.run(FORWARD);      // turn it on going forward
      break;
    }
    case STATE_BACKWARD: {
      motor1.run(BACKWARD);      // turn it on going forward
      motor2.run(BACKWARD);      // turn it on going forward
      break;
    }
    case STATE_TURN_RIGHT: {
      motor1.run(FORWARD);      // turn it on going forward
      motor2.run(BACKWARD);      // turn it on going forward
      break;
    }
    case STATE_TURN_LEFT: {
      motor1.run(BACKWARD);      // turn it on going forward
      motor2.run(FORWARD);      // turn it on going forward
      break;
    }
  }

  lastState = state;
}

/* EOF */

And finally, here is Wandering Tank in action:

Comments

Eric said…
Very nice. I am looking forward for the next improvement!
shimniok said…
Wow, that's nicely done! Thanks for sharing the code etc. I was hoping to work harder on obstacle avoidance at some point soon so this will be a big help. Didn't know about those Pololu mini distance sensors--cool! --Michael
Matt V said…
Awesome job on the tank. I myself built a similar tank with the same gearbox and whatnot, but instead of it driving itself I have it remotely controlled via xbee modules with a wii nunchuck for input. I was looking to add an autonomous mode and I think I just found it, thanks for including the code.
Andy said…
Great job! Didn't knew about the Digital Distance Sensor either. I've learned a lot from your code, thank you for sharing!
Unknown said…
Can you show me how to build one... i wanna try also. If i build this tank can i control it using ipad or laptop through wifi or bluetooth?

Popular posts from this blog

Arduino Robot 1 - The Basic Tank

Fixing a dead Sansa MP3 player (Versions e200 e260 e280)