ECE 4180 Mini Project Balancing Robot Nico van Duijn Samer Mabrouk 3/6/2015 This project consists of a robot balancing on two wheels. We use the 9-axis IMU LSM9DS0 to give us Accelerometer and Gyroscope data about the current angle and angular velocity of the robot. This data is then filtered using complementary filters and PID control to drive the two motors. The motors are controlled through digital outputs in their direction and their seepd by PWM using an H-bridge

Dependencies:   LSM9DS0 mbed

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers main.cpp Source File

main.cpp

00001 /*////////////////////////////////////////////////////////////////
00002 ECE 4180 Final Project
00003 Balancing Robot
00004 
00005 Nico van Duijn
00006 Samer Mabrouk
00007 Anthony Agnone
00008 Jay 
00009 4/20/2015
00010 
00011 This project consists of a robot balancing on two wheels. We use
00012 the 9-axis IMU LSM9DS0 to give us Accelerometer and Gyroscope data
00013 about the current angle and angular velocity of the robot. This
00014 data is then filtered using complementary filters and PID control
00015 to drive the two motors. The motors are controlled through digital
00016 outputs in their direction and their seepd by PWM using an H-bridge.
00017 The robot receives steering commands via the XBee module which is
00018 interfaced with from a C# GUI that runs on a desktop computer.
00019 
00020 */////////////////////////////////////////////////////////////////
00021 
00022 //////////////////////////////////////////////////////////////////
00023 // Includes
00024 #include "mbed.h"                                               // mbed library
00025 #include "LSM9DS0.h"                                            // 9axis IMU
00026 #include "math.h"                                               // We need to be able to do trig
00027 
00028 //////////////////////////////////////////////////////////////////
00029 // Constants
00030 #define LSM9DS0_XM_ADDR  0x1D                                   // Would be 0x1E if SDO_XM is LOW
00031 #define LSM9DS0_G_ADDR   0x6B                                   // Would be 0x6A if SDO_G is LOW
00032 //#define DEBUG                                                   // Comment this out for final version
00033 
00034 //////////////////////////////////////////////////////////////////
00035 // I/O and object instatiation
00036 PwmOut motorSpeedLeft(p21);                                     // PWM port to control speed of left motor
00037 PwmOut motorSpeedRight(p22);                                    // PWM port to control speed of right motor
00038 DigitalOut motorDirLeft(p24);                                   // Digital pin for direction of left motor
00039 DigitalOut NmotorDirLeft(p23);                                  // Usually inverse of motorDirLeft
00040 DigitalOut motorDirRight(p26);                                  // Digital pin for direction of right motor
00041 DigitalOut NmotorDirRight(p25);                                 // Usually inverse of motorDirRight
00042 LSM9DS0 imu(p9, p10, LSM9DS0_G_ADDR, LSM9DS0_XM_ADDR);          // Creates on object for IMU
00043 Ticker start;                                                   // Initialize ticker to call control function
00044 #ifdef DEBUG
00045 Serial pc(USBTX, USBRX);                                        // Creates virtual Serial connection though USB
00046 #endif
00047 
00048 //////////////////////////////////////////////////////////////////
00049 // Globals
00050 float kp = 98;   //40                                              // 145 Proportional gain kU 400-500
00051 float kd = 200;   //2 was quite good                              // Derivative gain
00052 float ki = 935;    //30                                              // Integrative gain
00053 float OVERALL_SCALAR = 170;                                    // Overall scalar that speed is divided by
00054 float accBias = 0;                                              // Accelerometer Bias
00055 float gyroBias = 0;                                             // Gyro Bias
00056 float accAngle = 0;                                             // Global to hold angle measured by Accelerometer
00057 float gyroAngle = 0;                                            // This variable holds the amount the angle has changed
00058 float speed = 0;                                                // Variable for motor speed
00059 float iAngle = 0;                                               // Integral value of angle-error (sum of gyro-angles)NOT EQUAL TO gyroAngle
00060 float dAngle = 0;                                               // Derivative value for angle, angular velocity, how fast angle is changing
00061 float pAngle = 0;                                               // Proportional value for angle, current angle (best measurement)
00062 float desiredAngle=0;                                           // Setpoint. Set unequal zero to drive
00063 // float turnspeed=0;
00064 
00065 //////////////////////////////////////////////////////////////////
00066 // Function Prototypes
00067 void drive(float);                                              // Function declaration for driving the motors
00068 void calibrate();                                               // Function to calibrate gyro and accelerometer
00069 void control();                                                 // Function implementing PID control
00070 #ifdef DEBUG
00071 void callback();                                                // Interrupt triggered function for serial communication
00072 #endif
00073 
00074 //////////////////////////////////////////////////////////////////
00075 // Main function
00076 int main()
00077 {
00078     uint16_t status = imu.begin();                              // Use the begin() function to initialize the LSM9DS0 library.
00079 #ifdef DEBUG
00080     pc.printf("LSM9DS0 WHO_AM_I's returned: 0x%X\n", status);   // Make sure communication is working
00081     pc.printf("Should be 0x49D4\n\n");                          // Check if we're talking to the right guy
00082     pc.attach(&callback);                                       // Attach interrupt to serial communication
00083 #endif
00084     calibrate();                                                // Calibrate gyro and accelerometer
00085     start.attach(&control, 0.01);                               // Looptime 10ms ca. 100Hz
00086     while(1) {                                                  // Stay in this loop forever
00087 #ifdef DEBUG
00088         pc.printf("%f\n",speed);                                // Print to USB the speed
00089 #endif
00090     }
00091 }
00092 
00093 /////////////////////////////////////////////////////////////////
00094 // Control function, implements PID
00095 void control()
00096 {
00097     dAngle=pAngle;// remember old p-value
00098     imu.readGyro();                                             // Read the gyro
00099     imu.readAccel();                                            // Read the Accelerometer
00100     accAngle=(-1)*atan2(imu.ay,imu.az)*180/3.142-90-accBias;    // Like this, 0 is upright, forward is negative, backward positive
00101     gyroAngle=-(imu.gx-gyroBias)*0.01;                          // This is deltaangle, how much angle has changed
00102     pAngle=0.98*(pAngle+gyroAngle)+0.02*accAngle-desiredAngle;               // Complementary filter yields best value for current angle
00103     //iAngle=(0.9*iAngle+0.1*gyroAngle);     DOESNT ACTUALLY INTEGRATE ERROR      // Sorta- running average-integral thing
00104     dAngle=pAngle-dAngle;                                       //Ang. Veloc. less noisy than dAngle = -(imu.gx-gyroBias);
00105     iAngle+=(pAngle*.01);// integrate the angle (multiply by timestep to get dt!)
00106     //if((ki*iAngle/OVERALL_SCALAR)>=3)iAngle=3*OVERALL_SCALAR/ki;// if ki dominates three-fold
00107     //if((ki*iAngle/OVERALL_SCALAR)<=-3)iAngle=-3*OVERALL_SCALAR/ki;//50 is an arbitrary cap - kind of worked
00108     // try angle dev. .55 and find value for imu.gx
00109     if(abs(pAngle-desiredAngle)>=0.6&&abs(pAngle-desiredAngle)<=15) {                    // If it is tilted enough, but not too much
00110         speed=-(ki*iAngle+kd*dAngle+kp*pAngle)/OVERALL_SCALAR;  // drive to correct
00111         if(speed<-1) speed=-1;                                  // Cap if undershoot
00112         else if(speed>1) speed=1;                               // Cap if overshoot
00113     } else speed=0;                                             // If we've fallen over or are steady on top
00114     drive(speed);                                               // Write speed to the motors
00115     
00116 }
00117 
00118 //////////////////////////////////////////////////////////////////
00119 // Drive function
00120 void drive(float speed)
00121 {
00122     int direction=0;                                            // Variable to hold direction we want to drive
00123     if (speed>0)direction=1;                                    // Positive speed indicates forward
00124     if (speed<0)direction=2;                                    // Negative speed indicates backwards
00125     if(speed==0)direction=0;                                    // Zero speed indicates stopping
00126     switch( direction) {                                        // Depending on what direction was passed
00127         case 0:                                                 // Stop case
00128             motorSpeedLeft=0;                                   // Set the DigitalOuts to stop the motors
00129             motorSpeedRight=0;
00130             motorDirLeft=0;
00131             NmotorDirLeft=0;
00132             motorDirRight=0;
00133             NmotorDirRight=0;
00134             break;
00135         case 1:                                                 // Forward case
00136             motorSpeedLeft=speed;                               // Set the DigitalOuts to run the motors forward
00137             motorSpeedRight=speed;
00138             motorDirLeft=1;
00139             NmotorDirLeft=0;
00140             motorDirRight=1;
00141             NmotorDirRight=0;
00142             break;
00143         case 2:                                                 // Backwards
00144             motorSpeedLeft=-speed;                              // Set the DigitalOuts to run the motors backward
00145             motorSpeedRight=-speed;
00146             motorDirLeft=0;
00147             NmotorDirLeft=1;
00148             motorDirRight=0;
00149             NmotorDirRight=1;
00150             break;
00151         default:                                                // Catch-all (Stop)
00152             motorSpeedLeft=0;                                   // Set the DigitalOuts to stop the motors
00153             motorSpeedRight=0;
00154             motorDirLeft=0;
00155             NmotorDirLeft=0;
00156             motorDirRight=0;
00157             NmotorDirRight=0;
00158             break;
00159     }
00160 }
00161 
00162 //////////////////////////////////////////////////////////////////
00163 // Calibrate function to find gyro drift and accelerometer bias accbias: -0.3 gyrobias: +0.15
00164 void calibrate()
00165 {
00166     for(int i=0; i<100; i++) {                                 // Read a thousand values
00167         imu.readAccel();                                        // Read the Accelerometer
00168         imu.readGyro();                                         // Read the gyro
00169         accBias=accBias+(-1)*atan2(imu.ay,imu.az)*180/3.142-90; // Like this, 0 is upright, forward is negative, backward positive
00170         gyroBias=gyroBias+imu.gx;                               // Add up all the gyro Biases
00171     }
00172     accBias=accBias/100;                                       // Convert sum to average
00173     gyroBias=gyroBias/100;                                     // Convert sum to average
00174 #ifdef DEBUG
00175     pc.printf("accBias: %f gyroBias: %f\n",accBias, gyroBias);  // Print biases to USB
00176 #endif
00177 }
00178 
00179 /////////////////////////////////////////////////////////////////
00180 // Callback function to change values/gains
00181 #ifdef DEBUG
00182 void callback()
00183 {
00184     char val;                                                   // Needed for Serial communication (need to be global?)
00185     val = pc.getc();                                            // Reat the value from Serial
00186     pc.printf("%c\n", val);                                     // Print it back to the screen
00187     if( val =='p') {                                            // If character was a 'p'
00188         pc.printf("enter kp \n");                               // Adjust kp
00189         val = pc.getc();                                        // Wait for kp value
00190         if(val == 0x2b) {                                       // If character is a plus sign
00191             kp=kp+10;                                           // Increase kp
00192         } else if (val == 0x2d) {                               // If recieved character is the minus sign
00193             kp=kp-10;                                           // Decrease kp
00194         } else {
00195             kp = val - 48;                                      // Cast char to float
00196         }
00197         pc.printf(" kp = %f \n",kp);                            // Print current kp value to screen
00198     } else if( val == 'd') {                                    // Adjust kd
00199         pc.printf("enter kd \n");                               // Wait for kd
00200         val= pc.getc();                                         // Read value from serial
00201         if(val == '+') {                                        // If given plus sign
00202             kd++;                                               // Increase kd
00203         } else if (val == '-') {                                // If given negative sign
00204             kd--;                                               // Decrease kd
00205         } else {                                                // If given some other ascii (a number?)
00206             kd = val - 48;                                      // Set derivative gain
00207         }
00208         pc.printf(" kd = %f \n",kd);                            // Print kd back to screen
00209     } else if( val == 'i') {                                    // If given i - integral gain
00210         pc.printf("enter ki \n");                               // Prompt on screen to ask for ascii
00211         val= pc.getc();                                         // Get the input
00212         if(val == '+') {                                        // If given the plus sign
00213             ki++;                                               // Increase ki
00214         } else if (val == '-') {                                // If given the minus sign
00215             ki--;                                               // Decrease ki
00216         } else {                                                // If given some other ascii
00217             ki = val - 48;                                      // Set ki to that number
00218         }
00219         pc.printf(" ki = %f \n",ki);
00220     } else if( val == 'o') {
00221         pc.printf("enter OVERALL_SCALAR \n");
00222         val= pc.getc();
00223         if(val == '+') {
00224             OVERALL_SCALAR=OVERALL_SCALAR+1000;
00225         } else if (val == '-') {
00226             OVERALL_SCALAR=OVERALL_SCALAR-1000;
00227         } else {
00228             OVERALL_SCALAR=(val-48)*1000;;
00229         }
00230         pc.printf(" OVERALL_SCALAR = %f \n",OVERALL_SCALAR);
00231     }
00232 }
00233 #endif