Looking for some pointers on a obstacle avoiding car.

Hi all.

I’m pretty new to electronic stuff, and I am wondering if anyone knows of any sites/shops that are good for smaller orders.

I looked at CPC-farnell but there postage seems to be really expensive compared to the size/value of things I am ordering.

I am part way though my first project, which is going to be a obsticle avoiding car, not very original I know but its just enough to get me going.

So far I have got:
arduion Uno r3 + bread board, a handful of resistors + leds and a few cables with pins for use with the bread board.
2 * HC-SR04 ultrasonic range finders
An old car from when I was a kid. (bycmo subaru http://www.deagostini.com.au/radiocontrolcar/build.php i never completed the build, I got to the part where the “guts” were there but never got the case.)
a 7.2 4000mah battery, standard remote control car battery.

the car is one of the kind that is controlled using 2 servos, one changes the position of the front wheals for stearing and the other turns some kind of controller device that controls how much power to send to the drive motor from the 7.2v battery.

I still need to buy a handful of things:
A charger for the NiMh battery
some kind of power source for the Arduino and servos, I was thinking of using power from the 7.2v battery putting it though a 5v regulator to power the servos, and doing the same with a different 5v regulator to power the arduino, is this feasible? or would I be better to power just the servos from the 7.2v battery with a regulator and power the arduino using a standard 9v battery?

I have started working on the code, its pretty rough right now and doesn’t do much, I will edit this post in a few min to include the code so far.

edit: code start.

//Robot Car by markp1989. Work in progress.
#include "Servo.h"
#include "NewPing.h"

Servo stearing;  // create servo object to control stearing
Servo throttle;  // create servo object to control throttle

//ranges or the servo as the car I am using has object near extreme ends of each servo. 
#define MAX_SPEED 165
#define MIN_SPEED 15
#define MAX_ANGLE 145
#define MIN_ANGLE 35

//current speed status 
int curSpeed=0; 
int curAngle=0; 

#define MAX_DISTANCE 100 //max distance in CM. shorted max distance speeds up ping replys.
#define PING_SPEED 75 // Ping frequency (in milliseconds), fastest we should ping is about 35ms per sensor

unsigned long pingTimerL, pingTimerR;  // 2 longs used to store the times of ultrasonic readings. 
unsigned long TurnTimer, ReverseTimer; 
//NewPing (TRIG, ECHO, MAXDistance); Left sensor, Right Sensor. 
NewPing lSensor = NewPing(4,5,MAX_DISTANCE);
NewPing rSensor = NewPing(7,6,MAX_DISTANCE);
//used to store the distance of any objects detected by sensor, 0=nothing detected
int rRange = 0;
int lRange = 0;
//flags to see if the ranges have been updated since the last check. 
boolean lRangeUpdated = false;
boolean rRangeUpdated = false;
boolean reversing = false;

#define CLOSE 20 //the distance where we cosnider things to be "close"     


void setup() 
{ 
  Serial.begin(115200); //set up serial port for debuging.  
  throttle.attach(9);
  stearing.attach(10);
  throttle.write(90);
  stearing.write(90); 
  //timing restrictions for the ping sensors. 
  pingTimerL = millis() + 1000; // wait 1 second before we do anything. 
  pingTimerR = pingTimerL + (PING_SPEED / 2); // Sensor 2 fires 50ms later
} 



void loop() 
{ 
  //check the sensor ranges if enough time has passed since the last read.
  if (millis() >= pingTimerL) {
    pingTimerL += PING_SPEED; // Make sensor 1 fire again after PING_SPEED
    lRange=lSensor.ping_cm();
    lRangeUpdated=true; //set the updated flag to true
  }
  if (millis() >= pingTimerR) {
    pingTimerR = pingTimerL + (PING_SPEED / 2); // Make sensor 2 fire again (half PING_SPEED) after sensor 1 fires
    rRange=rSensor.ping_cm();
    rRangeUpdated=true; //set the updated flag to true
  }  

  //straighten up the wheals if turn timer is up, this enables me to turn the wheels for a set time without delaying the program.
  if (millis() >= TurnTimer){
    turn(0);
    //dont do rest wheals again for 1hour //this prevents the weals constantly jittering around 0 when TurnTimer doesnt get updated exterally. 
      TurnTimer+=3600000;
    ////Serial.println("Reseting Stearing");
  }

  //stop reversing after reverse timer is up, this enables us to reverse for a set time without delaying the program
  if (millis() >= ReverseTimer){
    drive(0);
    //dont do rest wheals again for 1hou //this stops us stopping if we havnt set the reversetimer.. 
    ReverseTimer+=3600000;
    reversing=false;
    //Serial.println("Reseting reverse");
  }


  /**
   * run distanceChecks() only if both ranges have been updated since last loop completed
   * this stops the robot reacting multiple times to old data. 
   * when we are reverseing the sensors are meaningless so dont bother checking them
   **/
  if(lRangeUpdated && rRangeUpdated && !reversing){
    Serial.print(lRange);
    Serial.print("---------");
    Serial.println(rRange);
    //distanceChecks();    
    //reseting the updated flags
    lRangeUpdated=false;
    rRangeUpdated=false;
  }  
}//end of loop. 


//drive 0 stopped, -100 fill reverse, 100 full forward
void drive(int speed){
  speed=constrain(speed,-100,100);
  curSpeed=speed;
  //Serial.print("Changing the speed to :    ");
  //Serial.println(speed);
  int mappedSpeed = map(speed,-100,100,MIN_SPEED,MAX_SPEED);
  throttle.write(mappedSpeed);

}

//stear, 0 is straight  -100 full left , 100 full right. 
void turn(int angle){
  angle=constrain(angle,-100,100);
  int mappedAngle=map(angle,-100,100,MIN_ANGLE,MAX_ANGLE);
  curAngle=angle;
  stearing.write(mappedAngle);
}

//reads values of both sensors and reacts depending on readings. 
// * May need a bit of refactoring as it looks a bit complex considering the amount of work it does * 
void distanceChecks(){
  //if both either is true (not 0) either sensor has an obstruction 
  if(lRange || rRange){
    //if both sensors read betwen 1 and "Close"
    if((lRange <= CLOSE && lRange > 0) && (rRange <= CLOSE && rRange > 0)){
      //Serial.println("both Close reversing and turning");
      // do something when both sensors are close.
      //break, turn weals, reverse, break, straighten wheals. 
      drive(0); 
      //stear towards the nearest object 
      if(lRange<rRange){
        turn(100); // turn left
      }
      else{
        turn(100); // turn right
      }

      TurnTimer = millis() + 1500; //keep turned for 1.5sec
      drive(-40);
      ReverseTimer = millis() + 1500; //keep reversing for 1.5sec
      reversing=true;
    } 
    else{
      //if either sensors read betwen 1 and "Close"
      if((lRange <= CLOSE && lRange > 0) || (rRange <= CLOSE && rRange > 0) ) {
        //Serial.println("one of the sensors is close, avoding object");
        //drive(curSpeed*0.5); //slow down. 
        //stear away from the obsticle/ full lock.
 
        autoTurn(100);
       TurnTimer = millis() + 1500; //keep wturned for 1.5sec

      }
      else{
        //Serial.println("obsticle but not very close"); 
        //do something if there is an obsticle between 21 and 100cm on either sensor 
        //stear away from the obsticle
        autoTurn(50);
        //turn(0);
        drive(curSpeed+10); //speedup by 10%
        TurnTimer = millis() + 500; //keep wturned for 0.5 seconds. 
      }
    }

  }
  else{
    //no obsticles detected. 
    //Serial.print("No obstructions Speeding Up alot, Current Speed :");
    //Serial.println(curSpeed);
    drive(curSpeed+20); //speeding up by 10. 
  } 
}


//decides to turn left or right by "turnBy" depending which sensor has the most space. 
void autoTurn(int turnBy){
        //if lRange is clear
        if (!lRange){
          //convert "turn by" in to a negative number 
          turn(turnBy * -1); 
          // turn left
        }
        else{
          //if rRange is clear
          if(!rRange){
            turn(turnBy); 
            // turn right
          }
          else {
            //else point to the one with the most space.
            if(lRange<rRange){
              turn(turnBy); 
              // turn right
            }
            else {
              turn(turnBy * -1); 
              // turn left
            }

          }
        }  
}

I have attached a pic of the car with the non essentials remove, radio receiver

Thanks for reading.

markp1989: the car is one of the kind that is controlled using 2 servos, one changes the position of the front wheals for stearing and the other turns some kind of controller device that controls how much power to send to the drive motor from the 7.2v battery.

Hmm - sounds like it is using either an old-school style rheostat-like controller, or it is using a potentiometer controlled PWM ESC (which is a strange way to go about doing it). For now, this is ok - but I would suggest later, if you decide to keep this chassis, to look into using a real ESC that is controlled directly by the servo PPM pulse train (with no mechanical linkage); I don't know what kind of motor that chassis you have uses (brushed or brushless; likely the former), so you will need to match the ESC to the motor of course, or replace the motor as well (take it to a good local hobby shop, they might be able to help you retrofit it).

markp1989: some kind of power source for the Arduino and servos, I was thinking of using power from the 7.2v battery putting it though a 5v regulator to power the servos, and doing the same with a different 5v regulator to power the arduino, is this feasible? or would I be better to power just the servos from the 7.2v battery with a regulator and power the arduino using a standard 9v battery?

Did the original car chassis use some other kind of arrangement? Servos are generally limited to 4.5 - 6.0 volt range (there are servos now though that can operate on higher voltages). What I would do is purchase something called a BEC (or SBEC) - "Battery Eliminator Circuit". It's a small device that you plug into the battery via a Y-cable to allow you to power servos and other devices from a higher voltage source (essentially, they are a small DC/DC converter). Get one that gives you about 5 amps or so of 5 or 6 volt output. Power the servos from that. Power the Arduino directly from the battery -into- the barrel jack of the Arduino (the on-board regulator will take care of the rest).

Thanks for replying

cr0sh:
Hmm - sounds like it is using either an old-school style rheostat-like controller, or it is using a potentiometer controlled PWM ESC (which is a strange way to go about doing it). For now, this is ok - but I would suggest later, if you decide to keep this chassis, to look into using a real ESC that is controlled directly by the servo PPM pulse train (with no mechanical linkage); I don’t know what kind of motor that chassis you have uses (brushed or brushless; likely the former), so you will need to match the ESC to the motor of course, or replace the motor as well (take it to a good local hobby shop, they might be able to help you retrofit it).

I have taken a photo of the components that control the speed of the motor.

It looks to me like depending on how far you have turned it it dumps power in to the two very large resistors next to it to slow the motor down? which seems like a inefficient way to slow down a motor.

edit: this is the motor on the car rs-540sh . http://www.robotstorehk.com/motors/doc/rs_540rhsh.pdf

cr0sh:
Did the original car chassis use some other kind of arrangement? Servos are generally limited to 4.5 - 6.0 volt range (there are servos now though that can operate on higher voltages). What I would do is purchase something called a BEC (or SBEC) - “Battery Eliminator Circuit”. It’s a small device that you plug into the battery via a Y-cable to allow you to power servos and other devices from a higher voltage source (essentially, they are a small DC/DC converter). Get one that gives you about 5 amps or so of 5 or 6 volt output. Power the servos from that. Power the Arduino directly from the battery -into- the barrel jack of the Arduino (the on-board regulator will take care of the rest).

It looks like the “BEC” was part of the RF receiver, I attached a picture of the receiver it even has BEC written on it.
I just tried connecting up the 2 power pins to the servo to the servo output on the RF receiver and connecting the control pin of the servo to the Arduino. I am able to control the servo perfectly fine whilst powering the servo from a different power source, which should save me needing to buy something extra to power the servos. it also leaves open the possibility of reading the servo control output from the RF receiver if I decide to incorporate some kind of remote control to the “bot” in the future.

I ran in to a bit of a “snag” on the first run, the remote control car I was using has a problem, some of the cogs in the differential have broken, so only one of the real wheels gets power, during fast acceleration this caused the car to pretty much spin on the spot and crash in to a chair, luckily enough nothing broke accept the make shift “shelf” i made at the front of a car out of an old blank dvd.

I have revised the code to accelerate more gradual.

//Robot Car by markp1989. Work in progress.
#include "Servo.h"
#include "NewPing.h"
#include <avr/wdt.h>

Servo stearing;  // create servo object to control stearing
Servo throttle;  // create servo object to control throttle

//ranges or the servo as the car I am using has objects near extreme ends of each servo,using these so the servo doesnt hit things. 
#define MAX_SPEED 165
#define MIN_SPEED 15
#define MAX_ANGLE 170
#define MIN_ANGLE 10

//current speed status range min=-100 , max=100  
int curSpeed=0; 
int curAngle=0; 


#define MAX_DISTANCE 200 //max distance in CM. shorted max distance, saves the sensor listening to a reply when it wont get one. 
#define PING_SPEED 75 // Ping frequency (in milliseconds), fastest we should ping is about 35ms per sensor

unsigned long pingTimerL, pingTimerR;  // 2 longs used to store the times of ultrasonic readings. 
unsigned long TurnTimer, ReverseTimer; 
unsigned long SpeedupTimer;

//NewPing (TRIG, ECHO, MAXDistance); Left sensor, Right Sensor. 
NewPing lSensor = NewPing(4,5,MAX_DISTANCE);
NewPing rSensor = NewPing(7,6,MAX_DISTANCE);

//used to store the distance of any objects detected by sensor, 0=all clear, possible values 0 - MAX_DISTANCE 
int rRange = 0;
int lRange = 0;

//flags to see if the ranges have been updated since the last check. 
boolean lRangeUpdated = false;
boolean rRangeUpdated = false;
boolean reversing = false;
boolean turning = false;

#define CLOSE 40 //the distance where we cosnider things to be "close"     

void setup() 
{ 
  Serial.begin(115200); //set up serial port for debuging.  
  throttle.attach(9);
  stearing.attach(10);
  throttle.write(90);
  stearing.write(90); 
  delay(10000); //wait 10sec after boot up before doing anything. 
  //timing restrictions for the ping sensors. 
  pingTimerL = millis() + 1000; // wait 1 second before we do anything. 
  pingTimerR = pingTimerL + (PING_SPEED / 2); // Sensor 2 fires half of PING_SPEED later. 
  pinMode(13,OUTPUT);
  //reboot if we dont reset the timer within 2sec, just incase we have a crash whilst the servos are set to drive the car. 
  wdt_enable(WDTO_2S);
}

void loop() 
{ 
  wdt_reset(); //reset the "watch dog" so we know there are no problems with this loop.

  digitalWrite(13,LOW); 
  //check the sensor ranges if enough time has passed since the last read.
  if (millis() >= pingTimerL) {
    pingTimerL += PING_SPEED; // Make sensor 1 fire again after PING_SPEED
    lRange=lSensor.ping_cm();
    lRangeUpdated=true; //set the updated flag to true  
  }

  if (millis() >= pingTimerR) {
    pingTimerR = pingTimerL + (PING_SPEED / 2); // Make sensor 2 fire again (half PING_SPEED) after sensor 1 fires
    rRange=rSensor.ping_cm();
    rRangeUpdated=true; //set the updated flag to true
  }  

  //straighten up the wheals if turn timer is up, this enables me to turn the wheels for a set time without delaying the program.
  if (turning && millis() >= TurnTimer ){
    turn(0);
    turning=false;  
    Serial.println("Reseting Stearing");
  }

  //stop reversing after reverse timer is up, this enables us to reverse for a set time without delaying the program
  if (reversing && millis() >= ReverseTimer){
    drive(0);
    reversing=false;
    Serial.println("Restting servo");
  }


  /**
   * run distanceChecks() only if both ranges have been updated since last loop completed
   * this stops the robot reacting multiple times to old data,
   **/
  if(lRangeUpdated && rRangeUpdated){
    digitalWrite(13,HIGH);  //turn the led on so I can see when it is analysing the distances and making decisions 
    distanceChecks();    
    //reseting the updated flags
    lRangeUpdated=false;
    rRangeUpdated=false;
  }  
}//end of loop. 


//added "inline" on all functions that are not in the main loop
//makes the binary a bit bigger but removes overhead of calling a funcion at run time, should result in a slight speed bost.

//drive 0 stopped, -100 fill reverse, 100 full forward
inline void drive(int speed){
  //for testing constrain from -50 to 50 so that things are a lot slower.
  //speed=constrain(speed,-50,50);
  speed=constrain(speed,-100,100);
  curSpeed=speed;
  int mappedSpeed = map(speed,-100,100,MIN_SPEED,MAX_SPEED);
  throttle.write(mappedSpeed);

}

//stear, 0 is straight  -100 full left , 100 full right. 
inline void turn(int angle){
  angle=constrain(angle,-100,100);
  int mappedAngle=map(angle,-100,100,MIN_ANGLE,MAX_ANGLE);
  curAngle=angle;
  stearing.write(mappedAngle);
}

//reads values of both sensors and reacts depending on readings. 
// * May need a bit of refactoring as it looks a bit complex considering the amount of work it does * 
inline void distanceChecks(){

  //no obsticles detected speed up at a maximum rate of 15 every 5 seconds. 
  if(!lRange && !rRange){
    if (millis() >= SpeedupTimer) {
      drive(curSpeed+15); //speeding up by 15. 
      SpeedupTimer=millis()+5000;
      //dont speed up again for 5000ms. 
    }
    return; 
  }

  //if both sensors read betwen 1 and CLOSE(25)
  if((lRange <= CLOSE && lRange >= 1) && (rRange <= CLOSE && rRange >= 1)){
    //Serial.println("both Close reversing and turning");
    // do something when both sensors are close.
    //break, turn weals, reverse, 
    drive(-100); //reverse at full speed
    ReverseTimer = millis() + 250; //as this is caled by the main loop, it will keep reversing untill 250ms after the obstruction is out of the way. 
    reversing=true;
    //stear towards the nearest object 
    if(lRange<rRange){
      turn(100); // turn left
    }
    else{
      turn(100); // turn right
    }

    TurnTimer = millis() + 250; 
    //as this is called by the main loop, it will keep the wheels turned untill 250ms after the obsticle is out of the way. 
    turning=true;
    return; //exit function 
  } 

  //if either sensors read betwen CLOSE(25) and 1
  if((lRange <= CLOSE && lRange >= 1) || (rRange <= CLOSE && rRange >= 1) ) {
    //Serial.println("one is close");
    //stear away from the obsticle/ full lock.
    drive(curSpeed*0.99); //slow down  as this is called by the main loop ,it will lose 1% speed untill the obsticle is out of the way. 
    autoTurn(100); //stere the wheels towards the area with the most space. 
    turning=true;
    TurnTimer = millis() + 250; //as this is called by loop it will keep turning untill 0.25 sec after the obsticle is out of the way.
    return; //exit function  
  }

  //thre is an obsticle on either sensor but its not very close
  if (millis() >= SpeedupTimer) {
    drive(curSpeed+5); //speedup by 5
    SpeedupTimer=millis()+5000;
    //dont speed up again for 5000ms. 
  }
    //turn by 50%
    autoTurn(50);
    turning=true;
    TurnTimer = millis() + 500; //keep wturned for 0.5 seconds. 
}



//decides to turn left or right by "turnBy" depending which sensor has the most space. 
//inline to get rid of the over head of calling the function at run time. 
inline void autoTurn(int turnBy){
  //if lRange is clear
  if (!lRange){
    //convert "turn by" in to a negative number 
    turn(turnBy * -1); 
    // turn left
  }
  else{
    //if rRange is clear
    if(!rRange){
      turn(turnBy); 
      // turn right
    }
    else {
      //else point to the one with the most space.
      if(lRange<rRange){
        turn(turnBy); 
        // turn right
      }
      else {
        turn(turnBy * -1); 
        // turn left
      }

    }
  }  
}

now I am going to have to look for something else to be the base of the robot car, I volunteer at a charity shop which only stock electrical items, so tomorrow I will have a rummage in the stock room to see if they have any cars with missing receivers that I can use, currently my car does still drive, but it doesn’t accelerate in a straight line.

Hi,
Just a quick note of caution - It is normally electronic speed controllers that have the BEC, that socket on your receiver that is labelled BEC is just there to tell you where to plug one in, it is very unlikley that the receiver itself has a BEC inside.

How does this effect you ? Well receivers and servos expect to be powered at 4.8 to 6 volts. If you power them directly with 7.2 or higher you can significantly shorten thier lives.

Three solutions -

  1. power your reciver and servos with 4 aa batteries plugged into the BEC socket of your receiver this is probably how your car was designed to operate given its age.

  2. Buy a standalone BEC, you can get them for around 10 dollars

  3. The mechanical speed controller is old technology, it will jerk between three preset speeds, generating lots of electrical noise in the process,for smoother control, get a modern electronic speed controller most of which have a nice BEc built in solving two problems in one go.

It. MIght be worth taking your stripped gears to a hobby shop, they might be copies of a mainstream brand in which case they may have stock still.

Duane B
rcarduino.blogspot.com

DuaneB: Hi, Just a quick note of caution - It is normally electronic speed controllers that have the BEC, that socket on your receiver that is labelled BEC is just there to tell you where to plug one in, it is very unlikley that the receiver itself has a BEC inside.

How does this effect you ? Well receivers and servos expect to be powered at 4.8 to 6 volts. If you power them directly with 7.2 or higher you can significantly shorten thier lives.

The receiver has 3 ports, "BAT" "CH1" and "CH2". When it was a remote control car the Bat port went straight to the battery, and CH1 and CH2 went to each of the servos, which is how I have the servos powered, when I get home I will see if i can find a multimeter and measure the voltage coming out of the power pins of CH1 and CH2, If I can't find a multimeter will pop the case of the receiver to see if I can see any sort of voltage regulator in it.

it does say "Battery Eliminator Circuit" no wear near any of the ports, gonna have to see if i can find a spec sheet for it anywhere as I'm worried that you may be correct and I don't want to damage anything.

I had a search around a few car forums and it looks like the car I have is known for having bad quality gears, a few people get a replacement direct from the manufacturer as they couldnt find them very easily, but as its a car from 2004 Im not sure its worth the effort to repair it.

you do seem right about the speed controller only having 3 actual speeds, as it seems to go from being really slow pretty fast with little movement which isn't really ideal for project.

looks like I am going to go back to the drawing board, Im sure I have some more cars around here as I had quite a few when i was young and I dont remember throwing them away, gonna have a look in the bottom of the wardrobe to see if i can find anything

thanks for replying to me.