Navigation

Hovercraft


Team Members:
  • Joseph Kopacz 
  • Brian Bybee 
  • Ryan Clouse
  • Corey Barringer
Sponsored By:


Technical Details:
  • Thrust Fans
    • 3.3 Volts
    • 1.3 Amps Peak
    • 8300 RPM
    • 3x3 Two-Blade Propeller
  • Battery
    • 11.1 Volts
    • 6600 mAh
    • ~11lb
  • Electronics
    • Xbee (wireless communication)
      • 300ft (line of sight)
      • 3.3 Volts
      • 50 mA
    • Compass
      • 5 Volts
      • 1 mA
      • .5 Degree Resolution
    • GPS
      • 3.3 Volts
      • 41 mA
      • 66 Channels
    • Humidity Sensor
      • 5 Volts
      • 200 uA
      • Analog Output
    • Temperature and Pressure Sensor
      • 3.3 Volts
      • 200 uA
      • I2C Protocol
    • Arduino
      • Atmel Processor
      • 1 Hardware Serial Port
      • Multiple Digital and Analog Ports
      • I2C Capable


Video:







Pictures:


Interactive GUI

PCB Layout:
Power Management Circuit

Controls Circuit








Arduino Source Code:
#include <Wire.h>
#include <BMP085.h>
#include <NewSoftSerial.h>
#include <TinyGPS.h>
#include <PID_v1.h>
#include <avr/pgmspace.h>

//Global Vars
int HMC6352SlaveAddress = 0x42;
int HMC6352ReadAddress = 0x41; //"A" in hex, A command is:
BMP085 dps = BMP085();
long Temperature = 0, Pressure = 0, Altitude = 0;
#define line 1
#define no_line 0

//speak function
prog_char string_0[] PROGMEM = "Finished Initializing Ready for commands";   // "String 0" etc are strings to store - change to suit.
prog_char string_1[] PROGMEM = "Temp(C):";
prog_char string_2[] PROGMEM = "Pressure(Pa):";
prog_char string_3[] PROGMEM = "Humidity ";
prog_char string_4[] PROGMEM = "Compass Heading = ";
prog_char string_5[] PROGMEM = " degrees";
prog_char string_6[] PROGMEM = "Please Enter New Values for Kp,Ki,Kd";
prog_char string_7[] PROGMEM = "60.0,20.0,30.0";
prog_char string_8[] PROGMEM = "Kp = ";
prog_char string_9[] PROGMEM = "Ki = ";
prog_char string_10[] PROGMEM = "Kd = ";
prog_char string_11[] PROGMEM = "Enter Heading";
prog_char string_12[] PROGMEM = "The Desired Heading is ";
prog_char string_13[] PROGMEM = "Enter motor power";
prog_char string_14[] PROGMEM = "Motor Power Set:  ";
prog_char string_15[] PROGMEM = "Enter Gps Coordinates";
prog_char string_16[] PROGMEM = "Format - (LAT),(LON) ";
prog_char string_17[] PROGMEM = "/";
prog_char string_18[] PROGMEM = "Maintaining Straight Course";
prog_char string_19[] PROGMEM = "Turning Left 45 Degrees";
prog_char string_20[] PROGMEM = "Turning Right 45 Degrees";
prog_char string_21[] PROGMEM = "Kill Motors";
prog_char string_22[] PROGMEM = "Flip 180 Degrees";
prog_char string_23[] PROGMEM = "Information Page (Manual)";
prog_char string_24[] PROGMEM = "v - Display Temp, Pressure, Humidity, Heading";
prog_char string_25[] PROGMEM = "c - Enter New PID Constants";
prog_char string_26[] PROGMEM = "p - Adjust Motor Power";
prog_char string_27[] PROGMEM = "h - Enter Heading Menu";
prog_char string_28[] PROGMEM = "g - Enter GPS Heading Menu";
prog_char string_29[] PROGMEM = "w,a,d,s - Manual controls";
prog_char string_30[] PROGMEM = "q - Kill All Motors";


PROGMEM const char *string_table[] =     // change "string_table" name to suit
{   
  string_0, string_1, string_2, string_3, string_4, string_5, string_6,
  string_7, string_8, string_9, string_10, string_11, string_12,
  string_13, string_14, string_15, string_16, string_17, string_18,
  string_19, string_20, string_21, string_22, string_23, string_24,
  string_25, string_26, string_27, string_28, string_29,  string_30,
};


//Initialize GPS
NewSoftSerial nss(2, 3);
TinyGPS gps;

//Initialize PID
double Setpoint, Input, Output;
PID myPID(&Input, &Output, &Setpoint,30,0,30, DIRECT);
float Ki = 30, Kp = 0, Kd = 30;

//Motor Power setting
float motor_power = .40;

void setup()  
{
  pinMode(7, OUTPUT);
  digitalWrite(7, HIGH);
  
  HMC6352SlaveAddress = HMC6352SlaveAddress >> 1;
  Serial.begin(9600);
  Wire.begin();
  delay(1000);
  
  //initialize gps soft serial
  nss.begin(57600);
  
  //Initialize temp and pressure sensor
  dps.init();
  
  //Initialize PID
  myPID.SetMode(AUTOMATIC);
  
  //Show finished initialization
  speak(0,line);
  digitalWrite(7, LOW);
  delay(300);
  digitalWrite(7, HIGH);
  delay(300);
  digitalWrite(7, LOW);
  delay(300);
  digitalWrite(7, HIGH);
}

void loop()      // run over and over again
{
  //Control Vars
  char var;
  #define motor_left 10
  #define motor_right 11
 
  //Humidity Vars
  int val = 0, humidity = 0;
  float realhumidity;
    
  //PID
  char buffer_const[14];
  
  //Heading vars
  int i = 0;
  char buffer[3];
  float current_heading, desired_heading = 0;
  
  //GPS
   long lat_gps, lon_gps,lat_head, lon_head, lat, lon;
   unsigned long age_gps;
   char buffer_gps[17];
  
  //Stabalize craft while not doing anything
  analogWrite(motor_left, 0);
  analogWrite(motor_right, 255 * .26);
  
  if (Serial.available()) 
  {
    var = Serial.read();
    delay(6);
  }

switch (var) 
 {
   case 'v':
   
   Serial.print("Avalible memory ");
   Serial.println(availableMemory());

      //read and print temp and pressure
      dps.getTemperature(&Temperature); 
      dps.getPressure(&Pressure);
      Serial.println();
      speak(1,no_line);
      Temperature = Temperature * .1;
      Serial.println(Temperature);
      speak(2,no_line);
      Serial.println(Pressure);
      
     // read and print humidity
      val = analogRead(0);
      humidity = val*0.157380254-25.8;  //Decimal from 161/1023
      realhumidity =humidity/(1.0546-.0026*Temperature);
      speak(3,no_line);
      Serial.print(realhumidity);
      Serial.println("%");
      
      //read and print compass heading
      desired_heading = Heading();
      speak(4,no_line);
      Serial.print(desired_heading);
      speak(5,no_line);
    break;
   
   case 'c':
   //Adjust PID Constants
   Serial.println();
   speak(6,line);
   speak(7,line);
   
   while(Serial.available() < 14) //Wait for 14 bytes to be availible
     {
       delay(5);
     }
     while(Serial.available()) //Read `4 bytes into array
     {
       for(i=0; i<14; i++)
       {
         buffer_const[i] = Serial.read();
       }
     }
     
     Kp = atof(strtok(buffer_const, ","));
     Ki = atof(strtok (NULL, ","));
     Kd = atof(strtok (NULL, ","));

    myPID.SetTunings(Kp, Ki, Kd);
    
    speak(8,no_line);
    Serial.println(Kp);
    speak(9,no_line);
    Serial.println(Ki);
    speak(10,no_line);
    Serial.println(Kd);
    
   break;
   
   case 'h':
     Serial.println();
     speak(11,line);   
   
     while(Serial.available() < 3) //Wait for three bytes to be availible
     {
       delay(5);
     }
   
     while(Serial.available()) //Read 3 bytes into array
     {
       for(i=0; i<3; i++)
       {
         buffer[i] = Serial.read();
       }
     }
       desired_heading = atoi(buffer); //convert array into int
       Serial.println();
       speak(12,no_line); 
       Serial.println(desired_heading);
       
     while(Serial.available()==0) //while there are no other commands maintain entered heading
     {
       motor_control(desired_heading);
     }
   break;
   
   case 'p':
     Serial.println();   
     speak(13,line);
   
     while(Serial.available() < 3) //Wait for three bytes to be availible
     {
       delay(5);
     }
   
     while(Serial.available()) //Read 3 bytes into array
     {
       for(i=0; i<3; i++)
       {
         buffer[i] = Serial.read();
       }
     }
       motor_power = atof(buffer); //convert array into int
       speak(14,no_line);
       Serial.println(motor_power);
     break;
   
   case 'g':
     Serial.println();   
     speak(15,line);
     speak(16,line);   
   
         while(Serial.available() < 17) //Wait for 17 bytes to be availible
           {
             delay(5);
           }
         while(Serial.available()) //Read 17 bytes into array
           {
             for(i=0; i<17; i++)
             {
               buffer_gps[i] = Serial.read();
             }
           }
     
          lat = atol(strtok(buffer_gps, ","));
          lon = atol(strtok (NULL, ","));
          
      
      while(Serial.available()==0) //while there are no other commands maintain entered heading
       {
        
        //Get data from GPS
        
         while (nss.available())
         {
           gps.encode(nss.read());
         }
        
          gps.get_position(&lat_gps, &lon_gps, &age_gps);
          desired_heading = atan2(lon-lon_gps,lat-lat_gps);
          desired_heading = (desired_heading*(180.0/3.14)-360.0);
          desired_heading = ((int)desired_heading)% 360;
          desired_heading = desired_heading * -1;
          motor_control(desired_heading);
          
            current_heading = Heading();
            Serial.print(current_heading);
            
            val = analogRead(0);
            humidity = val*0.157380254-25.8;  //Decimal from 161/1023
            realhumidity = humidity/(1.0546-.0026*Temperature);
            speak(17,no_line);
            Serial.print(realhumidity);
            
            dps.getTemperature(&Temperature); 
            dps.getPressure(&Pressure);
            dps.getAltitude(&Altitude);
            Temperature = Temperature * .1;
            speak(17,no_line);
            Serial.print(Temperature);
            speak(17,no_line);
            Serial.print(Pressure);
            speak(17,no_line);
            Serial.print(lat_gps);
            speak(17,no_line);
            Serial.println(lon_gps);     
            
       }
   break;
   
   case 'w':
     //maintain straight course
     Serial.println();
     speak(18,line);
     desired_heading = Heading();
      
     while(Serial.available()==0) //while there are no other commands maintain entered heading
     {  
       motor_control(desired_heading);
     }
   break;
     
   case 'a':
     //Turn left 45 degrees
     Serial.println();
     speak(19,line);
     desired_heading = Heading();
     desired_heading = desired_heading + 45.0;
     if (desired_heading > 360)
     {
       desired_heading = desired_heading - 360;
     }
     while(Serial.available()==0) //while there are no other commands maintain entered heading
     {
       motor_control(desired_heading);
     }
   break;
     
   case 'd':
     //Turn right 45 degrees
     Serial.println();
     speak(20,line);
     desired_heading = Heading();
     desired_heading = desired_heading - 45.0;
     if (desired_heading < 0)
     {
       desired_heading = desired_heading + 360;
     }
     while(Serial.available()==0) //while there are no other commands maintain entered heading
     {
       motor_control(desired_heading);
     }
   break;
     
   case 'q':
     Serial.println();
     speak(21,line);
     analogWrite(motor_left,0);
     analogWrite(motor_right,0);
   break;
     
   case 's':
     Serial.println();
     speak(22,line);
     
     desired_heading = Heading();
     desired_heading = desired_heading + 180;
     if (desired_heading > 360)
     {
       desired_heading = desired_heading - 360;
     }
     while(Serial.available()==0) //while there are no other commands maintain entered heading
     {
       motor_control(desired_heading);
     }
   break;
     
   case 'm':
     Serial.println();
     speak(23,line);
     speak(24,line);
     speak(25,line);
     speak(26,line);
     speak(27,line);
     speak(28,line);
     speak(29,line);
     speak(30,line);
   break;
 }
}

float Heading()
{
   //Compass Vars
  byte MSB;
  byte LSB;
  float headingSum;
  float headingInt;
  
  //read and send compass heading
      Wire.beginTransmission(HMC6352SlaveAddress);
      Wire.send(HMC6352ReadAddress);              // The "Get Data" command
      Wire.endTransmission();

      //time delays required by HMC6352 upon receipt of the command
      delay(6);

      Wire.requestFrom(HMC6352SlaveAddress, 2); //get the two data bytes, MSB and LSB

      //"The heading output data will be the value in tenths of degrees from zero to 3599 and provided in binary format over the two bytes."
      MSB = Wire.receive();
      LSB = Wire.receive();

      headingSum = (MSB << 8) + LSB; //(MSB / LSB sum)
      headingInt = headingSum / 10; 
     return headingInt;
}


void motor_control(float desired_heading)
{
float current_heading;
int output;

current_heading = Heading();
  
  Setpoint = 0;
  
  if ((current_heading - desired_heading) > 180)
  {
    Input = ((360-current_heading)+desired_heading)*-1;
  }
  if((current_heading - desired_heading) < -180)
  {
    Input = (360-desired_heading)+current_heading;
  }
  
  if((current_heading - desired_heading)> -180 && (current_heading - desired_heading) < 180)
  {
    Input = current_heading - desired_heading;
  }
  
         myPID.Compute();
         
         //Works to maintain heading
        if (Output > 0)
         {
           if (Output + (255*motor_power) >= 255)
           {
            output=255;
           }
           else
           {
            output = Output +(255*motor_power);
           }
           
           analogWrite(10,255*motor_power);
           analogWrite(11,output);
         }
         
         if (Output < 0)
         {
           if (Output - (255*motor_power) <= -255)
           {
            output=-255;
           }
           else
           {
            output = Output -(255*motor_power);
           }
           
           analogWrite(11,255*motor_power);
           analogWrite(10, (output * -1));
         }
}

int availableMemory() {
  int size = 1024; // Use 2048 with ATmega328
  byte *buf;

  while ((buf = (byte *) malloc(--size)) == NULL)
    ;

  free(buf);

  return size;
}

void speak(int string_num, int ln)
{
  char write_buffer[44];
  strcpy_P(write_buffer, (char *)pgm_read_word(string_table + string_num));    
    if (ln == 1) 
    {
      Serial.println(write_buffer);
    }
    
    if (ln == 0)
    {
      Serial.print(write_buffer); 
    }
  
}



























No comments:

Post a Comment