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

Committer:
nicovanduijn
Date:
Mon Apr 20 16:33:09 2015 +0000
Revision:
3:dd0c62b586ea
Parent:
2:ad080363a22c
most current version;

Who changed what in which revision?

UserRevisionLine numberNew contents of line
nicovanduijn 0:67f5b4041c15 1 /*////////////////////////////////////////////////////////////////
nicovanduijn 3:dd0c62b586ea 2 ECE 4180 Final Project
nicovanduijn 0:67f5b4041c15 3 Balancing Robot
nicovanduijn 3:dd0c62b586ea 4
nicovanduijn 0:67f5b4041c15 5 Nico van Duijn
nicovanduijn 0:67f5b4041c15 6 Samer Mabrouk
nicovanduijn 3:dd0c62b586ea 7 Anthony Agnone
nicovanduijn 3:dd0c62b586ea 8 Jay
nicovanduijn 3:dd0c62b586ea 9 4/20/2015
nicovanduijn 0:67f5b4041c15 10
nicovanduijn 0:67f5b4041c15 11 This project consists of a robot balancing on two wheels. We use
nicovanduijn 0:67f5b4041c15 12 the 9-axis IMU LSM9DS0 to give us Accelerometer and Gyroscope data
nicovanduijn 0:67f5b4041c15 13 about the current angle and angular velocity of the robot. This
nicovanduijn 0:67f5b4041c15 14 data is then filtered using complementary filters and PID control
nicovanduijn 0:67f5b4041c15 15 to drive the two motors. The motors are controlled through digital
nicovanduijn 3:dd0c62b586ea 16 outputs in their direction and their seepd by PWM using an H-bridge.
nicovanduijn 3:dd0c62b586ea 17 The robot receives steering commands via the XBee module which is
nicovanduijn 3:dd0c62b586ea 18 interfaced with from a C# GUI that runs on a desktop computer.
nicovanduijn 3:dd0c62b586ea 19
nicovanduijn 0:67f5b4041c15 20 */////////////////////////////////////////////////////////////////
nicovanduijn 0:67f5b4041c15 21
nicovanduijn 0:67f5b4041c15 22 //////////////////////////////////////////////////////////////////
nicovanduijn 0:67f5b4041c15 23 // Includes
nicovanduijn 0:67f5b4041c15 24 #include "mbed.h" // mbed library
nicovanduijn 0:67f5b4041c15 25 #include "LSM9DS0.h" // 9axis IMU
nicovanduijn 0:67f5b4041c15 26 #include "math.h" // We need to be able to do trig
nicovanduijn 0:67f5b4041c15 27
nicovanduijn 0:67f5b4041c15 28 //////////////////////////////////////////////////////////////////
nicovanduijn 0:67f5b4041c15 29 // Constants
nicovanduijn 0:67f5b4041c15 30 #define LSM9DS0_XM_ADDR 0x1D // Would be 0x1E if SDO_XM is LOW
nicovanduijn 0:67f5b4041c15 31 #define LSM9DS0_G_ADDR 0x6B // Would be 0x6A if SDO_G is LOW
nicovanduijn 3:dd0c62b586ea 32 //#define DEBUG // Comment this out for final version
nicovanduijn 0:67f5b4041c15 33
nicovanduijn 0:67f5b4041c15 34 //////////////////////////////////////////////////////////////////
nicovanduijn 0:67f5b4041c15 35 // I/O and object instatiation
nicovanduijn 0:67f5b4041c15 36 PwmOut motorSpeedLeft(p21); // PWM port to control speed of left motor
nicovanduijn 0:67f5b4041c15 37 PwmOut motorSpeedRight(p22); // PWM port to control speed of right motor
nicovanduijn 3:dd0c62b586ea 38 DigitalOut motorDirLeft(p24); // Digital pin for direction of left motor
nicovanduijn 3:dd0c62b586ea 39 DigitalOut NmotorDirLeft(p23); // Usually inverse of motorDirLeft
nicovanduijn 0:67f5b4041c15 40 DigitalOut motorDirRight(p26); // Digital pin for direction of right motor
nicovanduijn 0:67f5b4041c15 41 DigitalOut NmotorDirRight(p25); // Usually inverse of motorDirRight
nicovanduijn 0:67f5b4041c15 42 LSM9DS0 imu(p9, p10, LSM9DS0_G_ADDR, LSM9DS0_XM_ADDR); // Creates on object for IMU
nicovanduijn 0:67f5b4041c15 43 Ticker start; // Initialize ticker to call control function
nicovanduijn 0:67f5b4041c15 44 #ifdef DEBUG
nicovanduijn 0:67f5b4041c15 45 Serial pc(USBTX, USBRX); // Creates virtual Serial connection though USB
nicovanduijn 0:67f5b4041c15 46 #endif
nicovanduijn 0:67f5b4041c15 47
nicovanduijn 0:67f5b4041c15 48 //////////////////////////////////////////////////////////////////
nicovanduijn 0:67f5b4041c15 49 // Globals
nicovanduijn 3:dd0c62b586ea 50 float kp = 98; //40 // 145 Proportional gain kU 400-500
nicovanduijn 3:dd0c62b586ea 51 float kd = 200; //2 was quite good // Derivative gain
nicovanduijn 3:dd0c62b586ea 52 float ki = 935; //30 // Integrative gain
nicovanduijn 3:dd0c62b586ea 53 float OVERALL_SCALAR = 170; // Overall scalar that speed is divided by
nicovanduijn 0:67f5b4041c15 54 float accBias = 0; // Accelerometer Bias
nicovanduijn 0:67f5b4041c15 55 float gyroBias = 0; // Gyro Bias
nicovanduijn 0:67f5b4041c15 56 float accAngle = 0; // Global to hold angle measured by Accelerometer
nicovanduijn 0:67f5b4041c15 57 float gyroAngle = 0; // This variable holds the amount the angle has changed
nicovanduijn 0:67f5b4041c15 58 float speed = 0; // Variable for motor speed
nicovanduijn 0:67f5b4041c15 59 float iAngle = 0; // Integral value of angle-error (sum of gyro-angles)NOT EQUAL TO gyroAngle
nicovanduijn 0:67f5b4041c15 60 float dAngle = 0; // Derivative value for angle, angular velocity, how fast angle is changing
nicovanduijn 0:67f5b4041c15 61 float pAngle = 0; // Proportional value for angle, current angle (best measurement)
nicovanduijn 3:dd0c62b586ea 62 float desiredAngle=0; // Setpoint. Set unequal zero to drive
nicovanduijn 3:dd0c62b586ea 63 // float turnspeed=0;
nicovanduijn 0:67f5b4041c15 64
nicovanduijn 0:67f5b4041c15 65 //////////////////////////////////////////////////////////////////
nicovanduijn 0:67f5b4041c15 66 // Function Prototypes
nicovanduijn 0:67f5b4041c15 67 void drive(float); // Function declaration for driving the motors
nicovanduijn 0:67f5b4041c15 68 void calibrate(); // Function to calibrate gyro and accelerometer
nicovanduijn 0:67f5b4041c15 69 void control(); // Function implementing PID control
nicovanduijn 0:67f5b4041c15 70 #ifdef DEBUG
nicovanduijn 0:67f5b4041c15 71 void callback(); // Interrupt triggered function for serial communication
nicovanduijn 0:67f5b4041c15 72 #endif
nicovanduijn 1:bf71a7bd2d3e 73
nicovanduijn 0:67f5b4041c15 74 //////////////////////////////////////////////////////////////////
nicovanduijn 0:67f5b4041c15 75 // Main function
nicovanduijn 0:67f5b4041c15 76 int main()
nicovanduijn 0:67f5b4041c15 77 {
nicovanduijn 0:67f5b4041c15 78 uint16_t status = imu.begin(); // Use the begin() function to initialize the LSM9DS0 library.
nicovanduijn 0:67f5b4041c15 79 #ifdef DEBUG
nicovanduijn 0:67f5b4041c15 80 pc.printf("LSM9DS0 WHO_AM_I's returned: 0x%X\n", status); // Make sure communication is working
nicovanduijn 0:67f5b4041c15 81 pc.printf("Should be 0x49D4\n\n"); // Check if we're talking to the right guy
nicovanduijn 0:67f5b4041c15 82 pc.attach(&callback); // Attach interrupt to serial communication
nicovanduijn 0:67f5b4041c15 83 #endif
nicovanduijn 0:67f5b4041c15 84 calibrate(); // Calibrate gyro and accelerometer
nicovanduijn 0:67f5b4041c15 85 start.attach(&control, 0.01); // Looptime 10ms ca. 100Hz
nicovanduijn 0:67f5b4041c15 86 while(1) { // Stay in this loop forever
nicovanduijn 0:67f5b4041c15 87 #ifdef DEBUG
nicovanduijn 0:67f5b4041c15 88 pc.printf("%f\n",speed); // Print to USB the speed
nicovanduijn 0:67f5b4041c15 89 #endif
nicovanduijn 0:67f5b4041c15 90 }
nicovanduijn 0:67f5b4041c15 91 }
nicovanduijn 1:bf71a7bd2d3e 92
nicovanduijn 0:67f5b4041c15 93 /////////////////////////////////////////////////////////////////
nicovanduijn 0:67f5b4041c15 94 // Control function, implements PID
nicovanduijn 0:67f5b4041c15 95 void control()
nicovanduijn 0:67f5b4041c15 96 {
nicovanduijn 3:dd0c62b586ea 97 dAngle=pAngle;// remember old p-value
nicovanduijn 0:67f5b4041c15 98 imu.readGyro(); // Read the gyro
nicovanduijn 0:67f5b4041c15 99 imu.readAccel(); // Read the Accelerometer
nicovanduijn 0:67f5b4041c15 100 accAngle=(-1)*atan2(imu.ay,imu.az)*180/3.142-90-accBias; // Like this, 0 is upright, forward is negative, backward positive
nicovanduijn 0:67f5b4041c15 101 gyroAngle=-(imu.gx-gyroBias)*0.01; // This is deltaangle, how much angle has changed
nicovanduijn 3:dd0c62b586ea 102 pAngle=0.98*(pAngle+gyroAngle)+0.02*accAngle-desiredAngle; // Complementary filter yields best value for current angle
nicovanduijn 3:dd0c62b586ea 103 //iAngle=(0.9*iAngle+0.1*gyroAngle); DOESNT ACTUALLY INTEGRATE ERROR // Sorta- running average-integral thing
nicovanduijn 3:dd0c62b586ea 104 dAngle=pAngle-dAngle; //Ang. Veloc. less noisy than dAngle = -(imu.gx-gyroBias);
nicovanduijn 3:dd0c62b586ea 105 iAngle+=(pAngle*.01);// integrate the angle (multiply by timestep to get dt!)
nicovanduijn 3:dd0c62b586ea 106 //if((ki*iAngle/OVERALL_SCALAR)>=3)iAngle=3*OVERALL_SCALAR/ki;// if ki dominates three-fold
nicovanduijn 3:dd0c62b586ea 107 //if((ki*iAngle/OVERALL_SCALAR)<=-3)iAngle=-3*OVERALL_SCALAR/ki;//50 is an arbitrary cap - kind of worked
nicovanduijn 3:dd0c62b586ea 108 // try angle dev. .55 and find value for imu.gx
nicovanduijn 3:dd0c62b586ea 109 if(abs(pAngle-desiredAngle)>=0.6&&abs(pAngle-desiredAngle)<=15) { // If it is tilted enough, but not too much
nicovanduijn 2:ad080363a22c 110 speed=-(ki*iAngle+kd*dAngle+kp*pAngle)/OVERALL_SCALAR; // drive to correct
nicovanduijn 0:67f5b4041c15 111 if(speed<-1) speed=-1; // Cap if undershoot
nicovanduijn 0:67f5b4041c15 112 else if(speed>1) speed=1; // Cap if overshoot
nicovanduijn 0:67f5b4041c15 113 } else speed=0; // If we've fallen over or are steady on top
nicovanduijn 0:67f5b4041c15 114 drive(speed); // Write speed to the motors
nicovanduijn 3:dd0c62b586ea 115
nicovanduijn 0:67f5b4041c15 116 }
nicovanduijn 1:bf71a7bd2d3e 117
nicovanduijn 0:67f5b4041c15 118 //////////////////////////////////////////////////////////////////
nicovanduijn 0:67f5b4041c15 119 // Drive function
nicovanduijn 0:67f5b4041c15 120 void drive(float speed)
nicovanduijn 0:67f5b4041c15 121 {
nicovanduijn 0:67f5b4041c15 122 int direction=0; // Variable to hold direction we want to drive
nicovanduijn 0:67f5b4041c15 123 if (speed>0)direction=1; // Positive speed indicates forward
nicovanduijn 0:67f5b4041c15 124 if (speed<0)direction=2; // Negative speed indicates backwards
nicovanduijn 0:67f5b4041c15 125 if(speed==0)direction=0; // Zero speed indicates stopping
nicovanduijn 0:67f5b4041c15 126 switch( direction) { // Depending on what direction was passed
nicovanduijn 0:67f5b4041c15 127 case 0: // Stop case
nicovanduijn 0:67f5b4041c15 128 motorSpeedLeft=0; // Set the DigitalOuts to stop the motors
nicovanduijn 0:67f5b4041c15 129 motorSpeedRight=0;
nicovanduijn 0:67f5b4041c15 130 motorDirLeft=0;
nicovanduijn 0:67f5b4041c15 131 NmotorDirLeft=0;
nicovanduijn 0:67f5b4041c15 132 motorDirRight=0;
nicovanduijn 0:67f5b4041c15 133 NmotorDirRight=0;
nicovanduijn 0:67f5b4041c15 134 break;
nicovanduijn 0:67f5b4041c15 135 case 1: // Forward case
nicovanduijn 0:67f5b4041c15 136 motorSpeedLeft=speed; // Set the DigitalOuts to run the motors forward
nicovanduijn 0:67f5b4041c15 137 motorSpeedRight=speed;
nicovanduijn 0:67f5b4041c15 138 motorDirLeft=1;
nicovanduijn 0:67f5b4041c15 139 NmotorDirLeft=0;
nicovanduijn 0:67f5b4041c15 140 motorDirRight=1;
nicovanduijn 0:67f5b4041c15 141 NmotorDirRight=0;
nicovanduijn 0:67f5b4041c15 142 break;
nicovanduijn 0:67f5b4041c15 143 case 2: // Backwards
nicovanduijn 0:67f5b4041c15 144 motorSpeedLeft=-speed; // Set the DigitalOuts to run the motors backward
nicovanduijn 0:67f5b4041c15 145 motorSpeedRight=-speed;
nicovanduijn 0:67f5b4041c15 146 motorDirLeft=0;
nicovanduijn 0:67f5b4041c15 147 NmotorDirLeft=1;
nicovanduijn 0:67f5b4041c15 148 motorDirRight=0;
nicovanduijn 0:67f5b4041c15 149 NmotorDirRight=1;
nicovanduijn 0:67f5b4041c15 150 break;
nicovanduijn 0:67f5b4041c15 151 default: // Catch-all (Stop)
nicovanduijn 0:67f5b4041c15 152 motorSpeedLeft=0; // Set the DigitalOuts to stop the motors
nicovanduijn 0:67f5b4041c15 153 motorSpeedRight=0;
nicovanduijn 0:67f5b4041c15 154 motorDirLeft=0;
nicovanduijn 0:67f5b4041c15 155 NmotorDirLeft=0;
nicovanduijn 0:67f5b4041c15 156 motorDirRight=0;
nicovanduijn 0:67f5b4041c15 157 NmotorDirRight=0;
nicovanduijn 0:67f5b4041c15 158 break;
nicovanduijn 0:67f5b4041c15 159 }
nicovanduijn 0:67f5b4041c15 160 }
nicovanduijn 0:67f5b4041c15 161
nicovanduijn 0:67f5b4041c15 162 //////////////////////////////////////////////////////////////////
nicovanduijn 3:dd0c62b586ea 163 // Calibrate function to find gyro drift and accelerometer bias accbias: -0.3 gyrobias: +0.15
nicovanduijn 0:67f5b4041c15 164 void calibrate()
nicovanduijn 0:67f5b4041c15 165 {
nicovanduijn 3:dd0c62b586ea 166 for(int i=0; i<100; i++) { // Read a thousand values
nicovanduijn 0:67f5b4041c15 167 imu.readAccel(); // Read the Accelerometer
nicovanduijn 0:67f5b4041c15 168 imu.readGyro(); // Read the gyro
nicovanduijn 0:67f5b4041c15 169 accBias=accBias+(-1)*atan2(imu.ay,imu.az)*180/3.142-90; // Like this, 0 is upright, forward is negative, backward positive
nicovanduijn 0:67f5b4041c15 170 gyroBias=gyroBias+imu.gx; // Add up all the gyro Biases
nicovanduijn 0:67f5b4041c15 171 }
nicovanduijn 3:dd0c62b586ea 172 accBias=accBias/100; // Convert sum to average
nicovanduijn 3:dd0c62b586ea 173 gyroBias=gyroBias/100; // Convert sum to average
nicovanduijn 0:67f5b4041c15 174 #ifdef DEBUG
nicovanduijn 0:67f5b4041c15 175 pc.printf("accBias: %f gyroBias: %f\n",accBias, gyroBias); // Print biases to USB
nicovanduijn 0:67f5b4041c15 176 #endif
nicovanduijn 0:67f5b4041c15 177 }
nicovanduijn 0:67f5b4041c15 178
nicovanduijn 0:67f5b4041c15 179 /////////////////////////////////////////////////////////////////
nicovanduijn 0:67f5b4041c15 180 // Callback function to change values/gains
nicovanduijn 1:bf71a7bd2d3e 181 #ifdef DEBUG
nicovanduijn 0:67f5b4041c15 182 void callback()
nicovanduijn 0:67f5b4041c15 183 {
nicovanduijn 0:67f5b4041c15 184 char val; // Needed for Serial communication (need to be global?)
nicovanduijn 0:67f5b4041c15 185 val = pc.getc(); // Reat the value from Serial
nicovanduijn 0:67f5b4041c15 186 pc.printf("%c\n", val); // Print it back to the screen
nicovanduijn 0:67f5b4041c15 187 if( val =='p') { // If character was a 'p'
nicovanduijn 0:67f5b4041c15 188 pc.printf("enter kp \n"); // Adjust kp
nicovanduijn 0:67f5b4041c15 189 val = pc.getc(); // Wait for kp value
nicovanduijn 0:67f5b4041c15 190 if(val == 0x2b) { // If character is a plus sign
nicovanduijn 0:67f5b4041c15 191 kp=kp+10; // Increase kp
nicovanduijn 0:67f5b4041c15 192 } else if (val == 0x2d) { // If recieved character is the minus sign
nicovanduijn 0:67f5b4041c15 193 kp=kp-10; // Decrease kp
nicovanduijn 0:67f5b4041c15 194 } else {
nicovanduijn 0:67f5b4041c15 195 kp = val - 48; // Cast char to float
nicovanduijn 0:67f5b4041c15 196 }
nicovanduijn 0:67f5b4041c15 197 pc.printf(" kp = %f \n",kp); // Print current kp value to screen
nicovanduijn 0:67f5b4041c15 198 } else if( val == 'd') { // Adjust kd
nicovanduijn 0:67f5b4041c15 199 pc.printf("enter kd \n"); // Wait for kd
nicovanduijn 0:67f5b4041c15 200 val= pc.getc(); // Read value from serial
nicovanduijn 0:67f5b4041c15 201 if(val == '+') { // If given plus sign
nicovanduijn 0:67f5b4041c15 202 kd++; // Increase kd
nicovanduijn 0:67f5b4041c15 203 } else if (val == '-') { // If given negative sign
nicovanduijn 0:67f5b4041c15 204 kd--; // Decrease kd
nicovanduijn 0:67f5b4041c15 205 } else { // If given some other ascii (a number?)
nicovanduijn 0:67f5b4041c15 206 kd = val - 48; // Set derivative gain
nicovanduijn 0:67f5b4041c15 207 }
nicovanduijn 0:67f5b4041c15 208 pc.printf(" kd = %f \n",kd); // Print kd back to screen
nicovanduijn 0:67f5b4041c15 209 } else if( val == 'i') { // If given i - integral gain
nicovanduijn 0:67f5b4041c15 210 pc.printf("enter ki \n"); // Prompt on screen to ask for ascii
nicovanduijn 0:67f5b4041c15 211 val= pc.getc(); // Get the input
nicovanduijn 0:67f5b4041c15 212 if(val == '+') { // If given the plus sign
nicovanduijn 0:67f5b4041c15 213 ki++; // Increase ki
nicovanduijn 0:67f5b4041c15 214 } else if (val == '-') { // If given the minus sign
nicovanduijn 0:67f5b4041c15 215 ki--; // Decrease ki
nicovanduijn 0:67f5b4041c15 216 } else { // If given some other ascii
nicovanduijn 0:67f5b4041c15 217 ki = val - 48; // Set ki to that number
nicovanduijn 0:67f5b4041c15 218 }
nicovanduijn 0:67f5b4041c15 219 pc.printf(" ki = %f \n",ki);
nicovanduijn 0:67f5b4041c15 220 } else if( val == 'o') {
nicovanduijn 0:67f5b4041c15 221 pc.printf("enter OVERALL_SCALAR \n");
nicovanduijn 0:67f5b4041c15 222 val= pc.getc();
nicovanduijn 0:67f5b4041c15 223 if(val == '+') {
nicovanduijn 0:67f5b4041c15 224 OVERALL_SCALAR=OVERALL_SCALAR+1000;
nicovanduijn 0:67f5b4041c15 225 } else if (val == '-') {
nicovanduijn 0:67f5b4041c15 226 OVERALL_SCALAR=OVERALL_SCALAR-1000;
nicovanduijn 0:67f5b4041c15 227 } else {
nicovanduijn 0:67f5b4041c15 228 OVERALL_SCALAR=(val-48)*1000;;
nicovanduijn 0:67f5b4041c15 229 }
nicovanduijn 0:67f5b4041c15 230 pc.printf(" OVERALL_SCALAR = %f \n",OVERALL_SCALAR);
nicovanduijn 0:67f5b4041c15 231 }
nicovanduijn 0:67f5b4041c15 232 }
nicovanduijn 0:67f5b4041c15 233 #endif