Go Down

Topic: Fully Autonomous Golf Caddy (Read 5 times) previous topic - next topic

bleedscarlet

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 :D

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

bleedscarlet

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.

bleedscarlet

this is the program as of right now:

Code: [Select]
#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("---------------");
}

mowcius

#3
Nov 01, 2010, 02:25 pm Last Edit: Nov 01, 2010, 02:26 pm by mowcius Reason: 1
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

bleedscarlet

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.

bleedscarlet

#5
Nov 01, 2010, 11:24 pm Last Edit: Nov 01, 2010, 11:24 pm by bleedscarlet Reason: 1
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.

[media]http://www.youtube.com/watch?v=S5RsKJHwJfQ[/media]

jezuz

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.

bleedscarlet

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"

jezuz

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).

bleedscarlet

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.

bleedscarlet

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.

bleedscarlet

#11
Nov 03, 2010, 01:10 am Last Edit: Nov 03, 2010, 01:11 am by bleedscarlet Reason: 1


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.

bleedscarlet

#12
Nov 03, 2010, 11:47 pm Last Edit: Nov 03, 2010, 11:47 pm by bleedscarlet Reason: 1
north facing robot!

[media]http://www.youtube.com/watch?v=vp0woQTHmmw[/media]

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

bleedscarlet

#13
Nov 04, 2010, 12:18 am Last Edit: Nov 04, 2010, 12:21 am by bleedscarlet Reason: 1
as promised, my snippet
Quote

#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);
}

bleedscarlet


Quote

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;
  }
}

Go Up