Autonomous Car with Arduino, in progress write-up

Thread Starter

poopscoop

Joined Dec 12, 2012
140
Objective: Build an autonomous car which avoids obstacles by means of an ultrasonic sensor. Properly document the build.

Specific Goals/Intent: The car, powered by Arduino, will map its surroundings using an ultrasonic sensor mounted to a stepper motor. If this were a tank (Which it mildly resembles), the turret would be the ultrasonic sensor. The Arduino will identify the best direction to travel by finding the 22 degree sweep with the longest distance to an object, and then find the particular 5 degree sweep within that set and turn towards that direction. The Arduino will process the correction on the fly by altering motor speeds to affect the turn.

The car will include multiple IR sensor to ensure it doesn't drive into object the ultrasonic sensor misses, and to prevent it from driving off stairs. These will be wired to interrupt pins on the Arduino.

Parts List:
Tamiya Tracked Vehicle Chassis Kit
Tamiya 70168 Double Gearbox L/R Independ 4-Speed
[URL='http://smile.amazon.com/gp/product/B004U8TOE6/ref=oh_aui_detailpage_o04_s00?ie=UTF8&psc=1']SainSmart HC-SR04 Ranging Detector Mod Distance Sensor (Blue) [/URL]
[URL='http://smile.amazon.com/gp/product/B004U8TOE6/ref=oh_aui_detailpage_o04_s00?ie=UTF8&psc=1'][URL='http://smile.amazon.com/gp/product/B0089JV2OM/ref=oh_aui_detailpage_o04_s00?ie=UTF8&psc=1']RioRand Stepper Motor 5V DC 4-Phase 5-Wire with ULN2003 Driver Board [/URL][/URL]

RHX 10 Pcs TCRT5000L TCRT5000 Reflective Optical Sensor Infrared 950mm 5V 3A New
Arduino Uno (Or Mini, we'll see how it works out)
Arduino Motor Shield (Easy to use H-Bridge)

Particular Implementation Details:
This is a work in progress, and given my school schedule, it will take about 6 months to complete. I intend to place this project on my resume.

Assembly is ongoing, pictures will be up later.
Pin use is as follows:
4 Digital pins for motor control (2 for direction, 2 for speed)
2 Analog pins for current sensing of the drive motors
2 interrupt pins for IR sensors
2 Digital pins for Ultrasonic Sensor
4 Digital pins for the Stepper Motor
1 Digital pin for "start"
1 Digital pin for home position on the 'turret'.

I'm working on the code as able, none of it is tested yet. I haven't used C++ before, but I've had classes in Java and MATLAB, and know how to use Google. I write code like an engineer so I'm sure there are several places where the language could simplify things for me, and I appreciate any input.

The operation is broken into 2 parts; Start-up and Run.

When initially started the car will:
1. Find home position on the turret.
2. Sweep a full rotation counter clockwise and map surroundings.
3. Choose the best direction.
4. Turn to face that direction. ***(Need help here, details at end)

When running the car will:
1. Sweep the front 90 degrees continuously.
2. Adjust drive path to follow the best direction.
3. If IR sensor trips, stop. Reverse direction.


Here's the code I slapped together, nothing complete, just a few functions I intend to call multiple times later.
Code:
//Function that collects the inputs from the distance sensor sweep.
//Just assigns the input distance value to a corresponding spot in the range array.
//Outputs true when array is filled.
boolean totalS(double dist){
  if (arrayPos <= 64){ //arrayPos is a global var that keeps track of array position
    if (dist == 0)//Sensor outputs 0 when it times out, such as at long range
      dist = 400;//400 cm is about the max length it detects, so assume anything that returns 0 is equiv to 400
    ranges[arrayPos] = dist;//Assign input distance to array
    arrayPos++;
    return (arrayPos < 64);
  }
  else
    return true;
}









//Given a 64 input array (When the initial scan is done), choose best direction
int bestDirection(int arr[64]){
  for (int i=0;i<64;i++){//Ensures no 0 values in array
    if (arr[i] == 0);
    arr[i] = 400;

  }
  //The sensor doesn't do well with walls at angles, so as a way to aid it in making
  //good decisions, it averages every group of 4 ranges together into an array with 16 entries.
  int groups[16];
  int indexer = 0;

  for (int i = 0; i< 16; i++){//iterate through groups array
    double avg = 0;
    int sum = 0;
    //collect 4 entires in the 64 length array, average them
    for (int k = 1; k<=4; k++){//This just sums the 4 vectors, indexer retains its last value outside of the loop.
      sum += arr[indexer];
      indexer++;
    }
    avg = sum/4; //average the sum that comes out of the loop
    groups[i] = avg; //place it in groups.

  }//end for

  //Now find the greatest average distance, using the average values found in groups.
  int maximum = 0;
  int indexOfMax = 0;
  for (int i=0;i<16;i++){ //Your basic max finding loop
    if (maximum < groups[i]){
      maximum = groups[i];
      indexOfMax = i;//The index of the max is what I really care about
    }
  }//end for

  //Using the highest average, find that group of 4 in the original array.
  int selectGroup[4];
  int startPoint = indexOfMax * 4; //max index in groups corresponds to the starting index in the original array
  for (int i = 0; i < 4; i++){
    selectGroup[i] = arr[startPoint + i]; //populate selectGroup with the group of 4 values from the OG array

  }

  int specificMax = 0;
  int specificMaxIndex = 0;

  //Find the specific max in the group of 4, and its position in the OG array
  for (int i = 0;i<4; i++){
    if (specificMax < selectGroup[i]){//max finding loop
      specificMax = selectGroup[i] ;
      specificMaxIndex = i + startPoint;//translate that index into an index in the OG array.
    }

  }

  return specificMaxIndex; //Return the index of the max in the original array.

}





//Translate index into an angle in radians.
//Must rotate counter clockwise
double directionfindStart(int index){
  double angle = 0;
  //If the index is less than 32, its in the first or second quadrant,
  // and to the left of the car.
  if (index <32)
    angle = PI*index/32; //Maps the index into an angle
   
  //I want negative angles if the value is on the right side, this ensures I get a negative
  if (index >= 32)
    angle =  -1*(64-index)*PI/32; //If index = 64, value is straight head, angle = 0. 32, angle is -PI.

  return angle;

}

Code:
//Translates an angle into motor speeds, outputs a 1x2 double array, left motor and right motor speeds.
////This is intended for when the car is lready moving and its only scanning a 90 degree angle, thus a 16 entry array.
double[2] motorSpeed(int angle, int frontArr[16]){
  //Maxspeed is dependent on the distance to the object directly in front.
  int maxSpeed = round(frontArr[7]/400 * 180);//Any closer than 400cm and the car slows.
  int rightMotor = 0;
  int leftMotor = 0;
 
  //****Might Need to swap right and left motor on all of these***
  //If best direction if of to the left.
  if (angle >0 && angle < PI/2){
    rightMotor = maxSpeed - angle*2; //Right motor moves at max speed minus how extreme the turn needs to be.
    leftMotor = cos(angle) * maxSpeed//Left motor slows down corresponding the the cos of the angle.
   
  }
  //When the best direction is behind the car, the negative cos will tell the motor to spin backwards.
  if (angle >= PI/2){
    rightMotor = maxSpeed - angle;
    leftMotor = cos(angle) * maxspeed;
   
  }
  //The angle is negative for the collowing inputs, so a few things must be changed.
  if (angle <0 && angle > (-1)*PI/2){
    leftMotor = maxSpeed + angle*2;
    rightMotor = cos(angle) * maxSpeed
   
  }
  //Same as above.
  if (angle <= -PI/2){
    leftMotor = maxSpeed + angle;
    rightMotor = cos(angle)*maxspeed;
   
  }
 
 
}











//Finds the best direction when moving, when the sweep is only 90*
int bestDirectionMoving(ranges[16]){
for (int i=0;i<16;i++){
   if (arr[i] == 0);
     arr[i] = 400;
}
int groups[4];
int indexer = 0;
  for (int i = 0; i< 4; i++){
    double avg = 0;
   
    int sum = 0;
    for (int k = 1; k<=4; k++){
      sum += arr[indexer];
      indexer++;
    }
    avg = sum/4;
    groups[i] = avg;
   
  }
 
int maximum = 0;
int indexOfMax = 0;
for (int i=0;i<4;i++){
   if (maximum < groups[i]){
     maximum = groups[i];
     indexOfMax = i;
   }
  
}
 
int selectGroup[4];
int startPoint = indexOfMax * 4;
for (int i = 0; i < 4; i++){
selectGroup[i] = arr[startPoint + i];
 
}

int specificMax = 0;
int specificMaxIndex = 0;
  for (int i = 0;i<4; i++){
   if (specificMax < selectGroup[i]){
    specificMax = selectGroup[i] ;
    specificMaxIndex = i + startPoint;
   }
   
  }

return specificMaxIndex; 
 
}
Where I need help: How to turn the car to the proper direction upon startup? How do I create a feedback loop using the ultrasonic sensor?
 

Thread Starter

poopscoop

Joined Dec 12, 2012
140
Update:

That stepper motor is wired incorrectly, and after a few minutes fiddling a google search turned up the solution: Swap the center 2 wires. It takes about 2100 steps per revolution, at a max speed of 9 RPM. Not blazing, but it should work.

Got the distance sensor up and running, its reasonably accurate but I'm using it in a 4 iteration loop to average the values for each direction.

Settled on a magnetometer to get turning feedback. http://smile.amazon.com/365buying-G...8&qid=1412473977&sr=8-1&keywords=magnetometer

I was originally going to use the Arduino Motorshield as my H-bridge for the drive motors. Unfortunately, it seems to be dead. I have 0 ohms between the Vin and Gnd pins, it doesn't turn the motors, and pulls 3 amps when connected to the battery pack. It's $25 to replace, so I intend use an L298N H-bridge (~$8) instead.
 
Top