Fully Autonomous Golf Caddy

I'm getting pretty good at Arduino programming. For senior design we design and build a fully autonomous golf caddy. We're using a GPS and a compass for location awareness and a pre-programmed path based on our own optimal path calculations of the golf course and essentially you will be able to interact with the caddy remotely and just say “Go to point A” (ideally via text message, we're working on that right now)

Our IC components:

Arduino ATMega 1280 [main controller]

Parallax GPS [GPS receiver]

HMC6343 [Compass]

Narobo Dronecell [Cellular communication with a group member's SIM card installed.]

Sabertooth 2×25 [Motor controller]

various buttons, resistors, capacitors [input, testing, filters]

I've used lots of other people's programs and tuned them heavily, and I've written some of my own programs and protocols. I'll post up the full program to see how the full integration operates; it will be awesome :smiley:

I'll add in some horribly drawn wiring diagrams too.

so far I wrote the entire program but it didn't work properly, so I decided to just start from scratch and isolate as much as I could into functions and call them appropriately.

In this iteration (which is the 3rd) I've gotten the GPS to read and output the values I need into float variables (just latitude and longitude) and same with then HMC6343.

I'm going to start on the main logic control right now actually, and someone else is tackling the dronecell.

this is the program as of right now:

#include <String.h>
#include <ctype.h>
#include <Wire.h> 

int compassAddress = 0x32 >> 1;  // From datasheet compass address is 0x32 for write operations, 
                                 // or 0x33 for read operations.
                                 // shift the address 1 bit right, the Wire library only needs the 7
                                 // most significant bits for the address
int heading = 0;  // variable to hold the heading angle
int tilt = 0;     // variable to hold the tilt angle
int roll = 0;     // variable to hold the roll angle
int ledPin = 13;                  // LED test pin
int rxPin = 0;                    // RX PIN 
int txPin = 1;                    // TX TX
int byteGPS=-1;
char linea[300] = "";
char comandoGPR[7] = "$GPRMC";
int cont=0;
int bien=0;
int conta=0;
int indices[13];
char CurrentXascii[9];
char CurrentYascii[10];
float CurrentX;
float CurrentY;
float CurrentHeading;
byte responseBytes[6];  // for holding the sensor response bytes
int GPStimer=0;
  
void readSensor() 
{ 
  // step 1: instruct sensor to read echoes 
  Wire.beginTransmission(compassAddress);  // transmit to device
                           // the address specified in the datasheet is 66 (0x42) 
                           // but i2c adressing uses the high 7 bits so it's 33 
  Wire.send(0x50);         // Send a "Post Heading Data" (0x50) command to the HMC6343  
  Wire.endTransmission();  // stop transmitting 
 
  // step 2: wait for readings to happen 
  delay(2);               // datasheet suggests at least 1 ms 
  
  // step 3: request reading from sensor 
  Wire.requestFrom(compassAddress, 6);  // request 6 bytes from slave device #33 
 
  // step 4: receive reading from sensor 
  if(6 <= Wire.available())     // if six bytes were received 
  {
    for(int i = 0; i<6; i++) {
      responseBytes[i] = Wire.receive();
    }
  }
  
  heading = ((int)responseBytes[0]<<8) | ((int)responseBytes[1]);  // heading MSB and LSB
  tilt = (((int)responseBytes[2]<<8) | ((int)responseBytes[3]));  // tilt MSB and LSB
  roll = (((int)responseBytes[4]<<8) | ((int)responseBytes[5]));  // roll MSB and LSB
  CurrentHeading = heading/10;
} 

void readGPS()
{
  GPStimer=0;
  do{
    byteGPS=Serial2.read();         // Read a byte of the serial port
    if (byteGPS == -1) {           // See if the port is empty yet
      delay(100); 
    } else {
      linea[conta]=byteGPS;        // If there is serial port data, it is put in the buffer
      conta++;                      
//      Serial.print(byteGPS, BYTE); 
      if (byteGPS==13){            // If the received byte is = to 13, end of transmission
        digitalWrite(ledPin, LOW); 
        cont=0;
        bien=0;
        for (int i=1;i<7;i++){     // Verifies if the received command starts with $GPR
          if (linea[i]==comandoGPR[i-1]){
            bien++;
          }
        }
        if(bien==6){               // If yes, continue and process the data
          for (int i=0;i<300;i++){
            if (linea[i]==','){    // check for the position of the  "," separator
              indices[cont]=i;
              cont++;
            }
            if (linea[i]=='*'){    // ... and the "*"
              indices[12]=i;
              cont++;
            }
          }
          Serial.println("");      // ... and write to the serial port
          Serial.println("");
          Serial.println("---------------");
          for (int i=0;i<12;i++){
/*          switch(i){
              case 0 :Serial.print("Time in UTC (HhMmSs): ");break;
              case 1 :Serial.print("Status (A=OK,V=KO): ");break;
              case 2 :Serial.print("Latitude: ");break;
              case 3 :Serial.print("Direction (N/S): ");break;
              case 4 :Serial.print("Longitude: ");break;
              case 5 :Serial.print("Direction (E/W): ");break;
              case 6 :Serial.print("Velocity in knots: ");break;
              case 7 :Serial.print("Heading in degrees: ");break;
              case 8 :Serial.print("Date UTC (DdMmAa): ");break;
              case 9 :Serial.print("Magnetic degrees: ");break;
              case 10 :Serial.print("(E/W): ");break;
              case 11 :Serial.print("Mode: ");break;
              case 12 :Serial.print("Checksum: ");break;
            } */
            for (int j=indices[i],k=0;j<(indices[i+1]-1);j++,k++){
//              Serial.print(linea[j+1]); 
              switch(i){
                case 2 :CurrentXascii[k]=linea[j+1];break;
                case 4 :CurrentYascii[k]=linea[j+1];break;
              }
            }
//            Serial.println("");
          }
          CurrentX=atof(CurrentXascii);
          CurrentY=-atof(CurrentYascii);
/*          Serial.print("Current X Value: ");
          Serial.print(CurrentX, 10);
          Serial.print("\t");
          Serial.print("Current Y Value: ");
          Serial.println(CurrentY, 10);
          Serial.println("---------------"); */
        }
        conta=0;                    // Reset the buffer
        for (int i=0;i<300;i++){    //  
          linea[i]=' ';             
        }                 
      }
    }
    GPStimer++;
  }while(GPStimer<300);
}

void setup() 
{ 
  delay(500);  //Wait at least 500 milli-seconds for device initialization
  Wire.begin();        // join i2c bus (address optional for master) 
  Wire.send(0x82);
  delay(500);  //Wait at least 500 milli-seconds for device initialization
  Serial.begin(9600);  // start serial communication at 9600bps 
  pinMode(13, OUTPUT);  
  digitalWrite(13, HIGH);  // just turn ON the onboard LED 
  Serial2.begin(4800);
  for (int i=0;i<300;i++){       // Initialize a buffer for received data
    linea[i]=' ';
  }   
} 

void loop() 
{
  readSensor();  // read data from the HMC6343 sensor
  // Note that heading, tilt and roll values are in tenths of a degree, for example
  // if the value of heading is 1234 would mean 123.4 degrees, that's why the result
  // is divided by 10 when printing.
  Serial.print("Heading: ");
  Serial.print(CurrentHeading); 
  Serial.print("\t Tilt: ");
  Serial.print(tilt / 10, DEC);
  Serial.print("\t Roll: ");
  Serial.println(roll / 10, DEC);
  delay(250);                   // wait for half a second 
  readGPS();
  Serial.print("Current X Value: ");
  Serial.print(CurrentX, 10);
  Serial.print("\t");          
  Serial.print("Current Y Value: ");
  Serial.println(CurrentY, 10);
  Serial.println("---------------");
}

Just wondering what that GPS is like.

For something like that I'd have thought you need one with a pretty darn high accuracy.

Mowcius

Yes, that's a big problem. The gps outputs in the format 1234.5678, 12 is degrees, 34 is minutes, and 5678 are fractional seconds (multiply by 60 to get number of seconds, we instead just converted our coordinates to this format for ease of programming)

In our location our dimensional accuracy is approximately +/- 3 ft east/west and +/- 6 ft north/south WITHOUT calibration.

By calibrating it you can actually get it down to about +/-2 ft for both. This is pretty vital because some of the golf paths are as thin as 5 feet wide with dangers of hitting a tree or driving into a hazard on either side.

This is for conceptual design, it's likely it will never actually be patented or sold but it will be functional.

we were very tired and this was a video from a couple weeks ago our first motion test. Just spinning, essentially just connected to the batteries but it is a little more complicated than that. In any case, things have changed dramatically since then.

You could up the accuracy even more with an onboard IMU (properly filtered and integrated with your gps), so that could be your next expansion on the project.

well actually the HMC6343 does have a tilt and roll detection, a pretty decent accelerometer actually.

Ideally instead of using a simple GPS map we'd be using a GIS map and get much more detailed results but right now we're going for "works" then we'll go for "works well" and one day perhaps "works perfectly"

Does it really? I picked one of these up for a project months ago, but my friend and I never got it to work. I'll have to give it another look - I wasn't aware it can output accelerometer values as well (I thought they were internally integrated into the tilt compensation and that's it).

I actually had a bit of trouble with mine as well, Not sure what the problem exactly was but I ended soldering wires directly to the breakout and I added a
Wire.send(0x82);
in the setup, which resets the processor every time.

that has worked well for us, we actually got a HMC 5843 too and I thought it was broken, but now I'm starting to wonder if it was broken at all.

I did try using a header first, and it worked immediately and then had tons of problems, so I popped it off checked everything with a multi, soldered wires directly on and voila.

full size link:
http://a.yfrog.com/img837/6780/68ism.jpg

Updated caddy picture, top step is not there and none of the ICs are in the picture. Truth be told, since we don't have any enclosures with all the circuitry in place it looks pretty uggo.

north facing robot!

code for this exact build coming soon, I'm leaving my house and heading back to the lab in a few minutes.

as promised, my snippet

#include <String.h>
#include <ctype.h>
#include <Wire.h>

int compassAddress = 0x32 >> 1;
int heading = 0;
int tilt = 0;
int roll = 0;
int ledPin = 13;
int rxPin = 0;
int txPin = 1;
int byteGPS=-1;
char linea[300] = "";
char comandoGPR[7] = "$GPRMC";
int cont=0;
int bien=0;
int conta=0;
int indices[13];
char CurrentXascii[9];
char CurrentYascii[10];
float CurrentX;
float CurrentY;
float CurrentHeading;
byte responseBytes[6];
int GPStimer=0;
double DeltaX=0.000;
double DeltaY=0.000;
int Instance=0;
int InstanceSize=0;
int buttonState=0;
int buttonPin = 22;
int lastButtonState=0;
int buttonPushCounter=0;
float DesiredHeading=0.0;
int LeftSpeed=0;
int RightSpeed=0;
int Stall=0;
double ParkingLotX[4] = {4031.24041, 4031.24603, 4031.24053, 4031.23490};
double ParkingLotY[4] = {-7427.58333, -7427.59596, -7427.60489, -7427.59194};
void setEngineSpeed1(signed char cNewMotorSpeedH)
{
unsigned char cSpeedVal_Motor1 = 0;
if(cNewMotorSpeedH==0)
{
Serial3.print(0,BYTE);
return;
}
cSpeedVal_Motor1 = map(cNewMotorSpeedH,-100,100,1,127);
Serial3.print(cSpeedVal_Motor1, BYTE);
}

void setEngineSpeed2(signed char cNewMotorSpeed2)
{
unsigned char cSpeedVal_Motor2 = 0;
if(cNewMotorSpeed2 == 0)
{
Serial3.print(0,BYTE);
return;
}
cSpeedVal_Motor2 = map(cNewMotorSpeed2,-100,100,128,255);
Serial3.print(cSpeedVal_Motor2, BYTE);
}

void readSensor()
{
Wire.beginTransmission(compassAddress);
Wire.send(0x50);
Wire.endTransmission();
delay(2);
Wire.requestFrom(compassAddress, 6);
if(6 <= Wire.available())
{
for(int i = 0; i<6; i++) {
responseBytes* = Wire.receive();*

  • }*
  • }*
  • heading = ((int)responseBytes[0]<<8) | ((int)responseBytes[1]);*
  • tilt = (((int)responseBytes[2]<<8) | ((int)responseBytes[3]));*
  • roll = (((int)responseBytes[4]<<8) | ((int)responseBytes[5]));*
  • CurrentHeading = heading/10;*
    *} *
    void readGPS()
    {
  • GPStimer=0;*
  • do{*
    _ byteGPS=Serial2.read();_
  • if (byteGPS == -1)*
  • {*
  • delay(50);*
  • } *
  • else {*
  • linea[conta]=byteGPS;*
  • conta++;*
  • if (byteGPS==13){*
  • digitalWrite(ledPin, LOW);*
  • cont=0;*
  • bien=0;*
  • for (int i=1;i<7;i++){*
    _ if (linea*==comandoGPR[i-1]){_
    _
    bien++;_
    _
    }_
    _
    }_
    _
    if(bien==6){_
    _
    for (int i=0;i<300;i++){_
    _ if (linea==','){
    indices[cont]=i;
    cont++;
    }
    if (linea==''){

    * indices[12]=i;
    cont++;
    }
    }
    Serial.println("");
    Serial.println("");
    Serial.println("---------------");
    for (int i=0;i<12;i++){
    for (int j=indices,k=0;j<(indices[i+1]-1);j++,k++){
    switch(i){
    case 2 :
    CurrentXascii[k]=linea[j+1];
    break;
    case 4 :
    CurrentYascii[k]=linea[j+1];
    break;
    }
    }
    }
    CurrentX=atof(CurrentXascii);
    CurrentY=-atof(CurrentYascii);
    }
    conta=0;
    for (int i=0;i<300;i++){
    linea=' ';
    }
    }
    }
    GPStimer++;
    }
    while(GPStimer<300);
    }
    void setup()
    {
    delay(500);
    Wire.begin();
    Wire.send(0x82);
    delay(500);
    Serial.begin(9600);
    pinMode(13, OUTPUT);
    digitalWrite(13, HIGH);
    Serial2.begin(4800);
    Serial3.begin(9600);
    delay(2000);
    setEngineSpeed1(0);
    setEngineSpeed2(0);
    for (int i=0;i<300;i++){
    linea=' ';
    }
    }
    void loop()
    {
    buttonState = digitalRead(buttonPin);
    if (buttonState != lastButtonState) {
    if (buttonState == HIGH) {
    buttonPushCounter++;
    Serial.println("on");
    Serial.print("number of button pushes: ");
    Serial.println(buttonPushCounter, DEC);
    }
    else {
    Serial.println("off");
    }
    lastButtonState = buttonState;
    }
    readSensor();
    Serial.print("Heading: ");
    Serial.print(CurrentHeading);
    Serial.print("\t Tilt: ");
    Serial.print(tilt / 10, DEC);
    Serial.print("\t Roll: ");
    Serial.println(roll / 10, DEC);
    delay(5);
    // readGPS();
    Serial.print("Current X Value: ");
    Serial.print(CurrentX, 10);
    Serial.print("\t");
    Serial.print("Current Y Value: ");
    Serial.println(CurrentY, 10);
    Serial.println("---------------");
    InstanceSize=sizeof(ParkingLotX);
    if((CurrentHeading>6)&&(CurrentHeading<354)){
    LeftSpeed=25;
    RightSpeed=-25;
    Serial.println("Turning!");
    Serial.print("Heading: ");
    Serial.println(CurrentHeading);
    }else{
    LeftSpeed=0;
    RightSpeed=0;
    Stall=1;
    Serial.print("North!");
    }
    setEngineSpeed1(LeftSpeed);
    setEngineSpeed2(RightSpeed);
    if(Stall==1){
    delay(1500);
    Stall=0;
    }
    }
    [/quote]*_

and now if anyone feels like reversing the coordinate conversion, you can actually find out exactly where I am :stuck_out_tongue:

maybe at this location?
http://maps.google.com/maps?q=37.771008,+-122.41175

not even close! wrong coast entirely haha

more pics
with improved wheel hubs and bag attached:

Added in some CYLON!!!

:smiley:

void setup() {                
  pinMode(43, OUTPUT);     
  pinMode(45, OUTPUT);
  pinMode(47, OUTPUT);
  pinMode(49, OUTPUT);
  pinMode(51, OUTPUT);
  pinMode(53, OUTPUT);  
}

void loop() {
  for (int i=43;i<54;i++){
    digitalWrite(i+1, HIGH);
    delay(30);
    digitalWrite(i-1, LOW);
  }
  for (int i=54;i>42;i--){
    digitalWrite(i-1, HIGH);
    delay(30);
    digitalWrite(i+1, LOW);
  }
}

video coming as soon as youtube processes.