First trial

Dependencies:   MPU6050 Motor ledControl2 mbed

Fork of BalancingRobotPS3 by Kristian Lauszus

Committer:
Lauszus
Date:
Tue Feb 14 18:31:05 2012 +0000
Revision:
1:01295228342f
Parent:
0:f5c02b620688
Child:
2:caec5534774d

        

Who changed what in which revision?

UserRevisionLine numberNew contents of line
Lauszus 1:01295228342f 1 /*
Lauszus 1:01295228342f 2 * The code is released under the GNU General Public License.
Lauszus 1:01295228342f 3 * Developed by Kristian Lauszus
Lauszus 1:01295228342f 4 * This is the algorithm for my balancing robot/segway.
Lauszus 1:01295228342f 5 * It is controlled by a PS3 Controller via bluetooth.
Lauszus 1:01295228342f 6 * The remote control code can be found at my other github repository: https://github.com/TKJElectronics/BalancingRobot
Lauszus 1:01295228342f 7 * For details, see http://blog.tkjelectronics.dk
Lauszus 1:01295228342f 8 */
Lauszus 1:01295228342f 9
Lauszus 0:f5c02b620688 10 #include "BalancingRobot.h"
Lauszus 0:f5c02b620688 11
Lauszus 0:f5c02b620688 12 /* Serial communication */
Lauszus 0:f5c02b620688 13 Serial xbee(p13,p14); // For wireless debugging
Lauszus 0:f5c02b620688 14 Serial ps3(p9,p10); // For remote control
Lauszus 0:f5c02b620688 15 Serial debug(USBTX, USBRX); // USB
Lauszus 0:f5c02b620688 16
Lauszus 0:f5c02b620688 17 /* Timer instance */
Lauszus 0:f5c02b620688 18 Timer t;
Lauszus 0:f5c02b620688 19
Lauszus 0:f5c02b620688 20 int main() {
Lauszus 0:f5c02b620688 21 xbee.baud(115200);
Lauszus 0:f5c02b620688 22 ps3.baud(115200);
Lauszus 0:f5c02b620688 23 debug.baud(115200);
Lauszus 0:f5c02b620688 24
Lauszus 0:f5c02b620688 25 leftPWM.period(0.00005); //The motor driver can handle pwm values up to 20kHz (1/20000=0.00005)
Lauszus 0:f5c02b620688 26 rightPWM.period(0.00005);
Lauszus 0:f5c02b620688 27
Lauszus 0:f5c02b620688 28 calibrateSensors(); // Calibrate the gyro and accelerometer relative to ground
Lauszus 0:f5c02b620688 29
Lauszus 0:f5c02b620688 30 xbee.printf("Initialized\n");
Lauszus 0:f5c02b620688 31
Lauszus 0:f5c02b620688 32 // Start timing
Lauszus 0:f5c02b620688 33 t.start();
Lauszus 0:f5c02b620688 34 loopStartTime = t.read_us();
Lauszus 0:f5c02b620688 35 timer = loopStartTime;
Lauszus 0:f5c02b620688 36
Lauszus 0:f5c02b620688 37 while (1) {
Lauszus 0:f5c02b620688 38 // See my guide for more info about calculation the angles and the Kalman filter: http://arduino.cc/forum/index.php/topic,58048.0.html
Lauszus 0:f5c02b620688 39 accYangle = getAccY();
Lauszus 0:f5c02b620688 40 gyroYrate = getGyroYrate();
Lauszus 0:f5c02b620688 41
Lauszus 0:f5c02b620688 42 pitch = kalman(accYangle, gyroYrate, t.read_us() - timer); // calculate the angle using a Kalman filter
Lauszus 0:f5c02b620688 43 timer = t.read_us();
Lauszus 0:f5c02b620688 44
Lauszus 0:f5c02b620688 45 if (ps3.readable()) // Check if there's any incoming data
Lauszus 0:f5c02b620688 46 receiveSerial();
Lauszus 0:f5c02b620688 47
Lauszus 0:f5c02b620688 48 //debug.printf("Pitch: %f, accYangle: %f\n",pitch,accYangle);
Lauszus 0:f5c02b620688 49
Lauszus 1:01295228342f 50 if (pitch < 75 || pitch > 105) // Stop if falling or laying down
Lauszus 0:f5c02b620688 51 stopAndReset();
Lauszus 0:f5c02b620688 52 else
Lauszus 0:f5c02b620688 53 PID(targetAngle);
Lauszus 0:f5c02b620688 54
Lauszus 0:f5c02b620688 55 /* Used a time fixed loop */
Lauszus 0:f5c02b620688 56 lastLoopUsefulTime = t.read_us() - loopStartTime;
Lauszus 0:f5c02b620688 57 if (lastLoopUsefulTime < STD_LOOP_TIME)
Lauszus 0:f5c02b620688 58 wait_us(STD_LOOP_TIME - lastLoopUsefulTime);
Lauszus 0:f5c02b620688 59 lastLoopTime = t.read_us() - loopStartTime; // only used for debugging
Lauszus 0:f5c02b620688 60 loopStartTime = t.read_us();
Lauszus 0:f5c02b620688 61
Lauszus 0:f5c02b620688 62 //debug.printf("%i,%i\n",lastLoopUsefulTime,lastLoopTime);
Lauszus 0:f5c02b620688 63 }
Lauszus 0:f5c02b620688 64 }
Lauszus 0:f5c02b620688 65 void stopAndReset() {
Lauszus 0:f5c02b620688 66 stop(both);
Lauszus 0:f5c02b620688 67 lastError = 0;
Lauszus 0:f5c02b620688 68 iTerm = 0;
Lauszus 0:f5c02b620688 69 }
Lauszus 0:f5c02b620688 70 void receiveSerial() {
Lauszus 0:f5c02b620688 71 char input[16]; // The serial buffer is only 16 characters, so the data has to be split up
Lauszus 1:01295228342f 72 int i = 0;
Lauszus 0:f5c02b620688 73 while (ps3.readable()) {
Lauszus 1:01295228342f 74 input[i] = ps3.getc();
Lauszus 1:01295228342f 75 if(input[i] == ';')
Lauszus 1:01295228342f 76 break;
Lauszus 1:01295228342f 77 i++;
Lauszus 1:01295228342f 78 }
Lauszus 0:f5c02b620688 79 //debug.printf("Input: %s\n",input);
Lauszus 0:f5c02b620688 80
Lauszus 0:f5c02b620688 81 /* For remote control */
Lauszus 0:f5c02b620688 82 if (input[0] == 'F') { // Forward
Lauszus 0:f5c02b620688 83 steerForward = true;
Lauszus 0:f5c02b620688 84 steerBackward = false;
Lauszus 0:f5c02b620688 85 steerLeft = false;
Lauszus 0:f5c02b620688 86 steerRotateLeft = false;
Lauszus 0:f5c02b620688 87 steerRight = false;
Lauszus 0:f5c02b620688 88 steerRotateRight = false;
Lauszus 0:f5c02b620688 89 } else if (input[0] == 'B') { // Backward
Lauszus 0:f5c02b620688 90 steerForward = false;
Lauszus 0:f5c02b620688 91 steerBackward = true;
Lauszus 0:f5c02b620688 92 steerLeft = false;
Lauszus 0:f5c02b620688 93 steerRotateLeft = false;
Lauszus 0:f5c02b620688 94 steerRight = false;
Lauszus 0:f5c02b620688 95 steerRotateRight = false;
Lauszus 0:f5c02b620688 96 } else if (input[0] == 'L') { // Left
Lauszus 0:f5c02b620688 97 if (input[1] == 'R') { // Left Rotate
Lauszus 0:f5c02b620688 98 steerLeft = false;
Lauszus 0:f5c02b620688 99 steerRotateLeft = true;
Lauszus 0:f5c02b620688 100 } else {
Lauszus 0:f5c02b620688 101 steerLeft = true;
Lauszus 0:f5c02b620688 102 steerRotateLeft = false;
Lauszus 0:f5c02b620688 103 }
Lauszus 0:f5c02b620688 104 steerForward = false;
Lauszus 0:f5c02b620688 105 steerBackward = false;
Lauszus 0:f5c02b620688 106 steerRight = false;
Lauszus 0:f5c02b620688 107 steerRotateRight = false;
Lauszus 0:f5c02b620688 108 } else if (input[0] == 'R') { // Right
Lauszus 0:f5c02b620688 109 if (input[1] == 'R') { // Right Rotate
Lauszus 0:f5c02b620688 110 steerRight = false;
Lauszus 0:f5c02b620688 111 steerRotateRight = true;
Lauszus 0:f5c02b620688 112 } else {
Lauszus 0:f5c02b620688 113 steerRight = true;
Lauszus 0:f5c02b620688 114 steerRotateRight = false;
Lauszus 0:f5c02b620688 115 }
Lauszus 0:f5c02b620688 116 steerForward = false;
Lauszus 0:f5c02b620688 117 steerBackward = false;
Lauszus 0:f5c02b620688 118 steerLeft = false;
Lauszus 0:f5c02b620688 119 steerRotateLeft = false;
Lauszus 0:f5c02b620688 120 } else if (input[0] == 'S') { // Stop
Lauszus 0:f5c02b620688 121 steerForward = false;
Lauszus 0:f5c02b620688 122 steerBackward = false;
Lauszus 0:f5c02b620688 123 steerLeft = false;
Lauszus 0:f5c02b620688 124 steerRotateLeft = false;
Lauszus 0:f5c02b620688 125 steerRight = false;
Lauszus 0:f5c02b620688 126 steerRotateRight = false;
Lauszus 0:f5c02b620688 127 }
Lauszus 0:f5c02b620688 128
Lauszus 0:f5c02b620688 129 else if (input[0] == 'T') { // Set the target angle
Lauszus 0:f5c02b620688 130 strtok(input, ","); // Ignore 'T'
Lauszus 1:01295228342f 131 targetAngle = atof(strtok(NULL, ";")); // read until the end and then convert from string to double
Lauszus 0:f5c02b620688 132 if (targetAngle < 75 || targetAngle > 105) // The serial communication sometimes behaves weird
Lauszus 1:01295228342f 133 targetAngle = lastTargetAngle;
Lauszus 1:01295228342f 134 lastTargetAngle = targetAngle;
Lauszus 0:f5c02b620688 135 xbee.printf("%f\n",targetAngle); // Print targetAngle for debugging
Lauszus 0:f5c02b620688 136 } else if (input[0] == 'A') { // Abort
Lauszus 0:f5c02b620688 137 stopAndReset();
Lauszus 0:f5c02b620688 138 while (ps3.getc() != 'C'); // Wait until continue is send
Lauszus 0:f5c02b620688 139 }
Lauszus 0:f5c02b620688 140 }
Lauszus 0:f5c02b620688 141 void PID(double restAngle) {
Lauszus 0:f5c02b620688 142 if (steerForward)
Lauszus 1:01295228342f 143 restAngle -= 1.5;
Lauszus 0:f5c02b620688 144 else if (steerBackward)
Lauszus 1:01295228342f 145 restAngle += 1.5;
Lauszus 0:f5c02b620688 146
Lauszus 0:f5c02b620688 147 double error = (restAngle - pitch)/100;
Lauszus 0:f5c02b620688 148 double pTerm = Kp * error;
Lauszus 0:f5c02b620688 149 iTerm += Ki * error;
Lauszus 0:f5c02b620688 150 double dTerm = Kd * (error - lastError);
Lauszus 0:f5c02b620688 151 lastError = error;
Lauszus 0:f5c02b620688 152
Lauszus 0:f5c02b620688 153 double PIDValue = pTerm + iTerm + dTerm;
Lauszus 0:f5c02b620688 154
Lauszus 0:f5c02b620688 155 //debug.printf("Pitch: %5.2f\tPIDValue: %5.2f\tpTerm: %5.2f\tiTerm: %5.2f\tdTerm: %5.2f\tKp: %5.2f\tKi: %5.2f\tKd: %5.2f\n",pitch,PIDValue,pTerm,iTerm,dTerm,Kp,Ki,Kd);
Lauszus 0:f5c02b620688 156
Lauszus 0:f5c02b620688 157 double PIDLeft;
Lauszus 0:f5c02b620688 158 double PIDRight;
Lauszus 0:f5c02b620688 159 if (steerLeft) {
Lauszus 0:f5c02b620688 160 PIDLeft = PIDValue-(0.1);
Lauszus 0:f5c02b620688 161 PIDRight = PIDValue+(0.1);
Lauszus 0:f5c02b620688 162 } else if (steerRotateLeft) {
Lauszus 0:f5c02b620688 163 PIDLeft = PIDValue-(0.2);
Lauszus 0:f5c02b620688 164 PIDRight = PIDValue+(0.2);
Lauszus 0:f5c02b620688 165 } else if (steerRight) {
Lauszus 0:f5c02b620688 166 PIDLeft = PIDValue-(-0.1);
Lauszus 0:f5c02b620688 167 PIDRight = PIDValue+(-0.1);
Lauszus 0:f5c02b620688 168 } else if (steerRotateRight) {
Lauszus 0:f5c02b620688 169 PIDLeft = PIDValue-(-0.2);
Lauszus 0:f5c02b620688 170 PIDRight = PIDValue+(-0.2);
Lauszus 0:f5c02b620688 171 } else {
Lauszus 0:f5c02b620688 172 PIDLeft = PIDValue;
Lauszus 0:f5c02b620688 173 PIDRight = PIDValue;
Lauszus 0:f5c02b620688 174 }
Lauszus 0:f5c02b620688 175 PIDLeft *= 0.9; // compensate for difference in the motors
Lauszus 0:f5c02b620688 176
Lauszus 0:f5c02b620688 177 //Set PWM Values
Lauszus 0:f5c02b620688 178 if (PIDLeft >= 0)
Lauszus 0:f5c02b620688 179 move(left, forward, PIDLeft);
Lauszus 0:f5c02b620688 180 else
Lauszus 0:f5c02b620688 181 move(left, backward, PIDLeft * -1);
Lauszus 0:f5c02b620688 182 if (PIDRight >= 0)
Lauszus 0:f5c02b620688 183 move(right, forward, PIDRight);
Lauszus 0:f5c02b620688 184 else
Lauszus 0:f5c02b620688 185 move(right, backward, PIDRight * -1);
Lauszus 0:f5c02b620688 186 }
Lauszus 0:f5c02b620688 187 double kalman(double newAngle, double newRate, double dtime) {
Lauszus 0:f5c02b620688 188 // KasBot V2 - Kalman filter module - http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1284738418 - http://www.x-firm.com/?page_id=145
Lauszus 0:f5c02b620688 189 // with slightly modifications by Kristian Lauszus
Lauszus 0:f5c02b620688 190 dt = dtime / 1000000; // Convert from microseconds to seconds
Lauszus 0:f5c02b620688 191
Lauszus 0:f5c02b620688 192 // Discrete Kalman filter time update equations - Time Update ("Predict")
Lauszus 0:f5c02b620688 193 // Update xhat - Project the state ahead
Lauszus 0:f5c02b620688 194 angle += dt * (newRate - bias);
Lauszus 0:f5c02b620688 195
Lauszus 0:f5c02b620688 196 // Update estimation error covariance - Project the error covariance ahead
Lauszus 0:f5c02b620688 197 P_00 += -dt * (P_10 + P_01) + Q_angle * dt;
Lauszus 0:f5c02b620688 198 P_01 += -dt * P_11;
Lauszus 0:f5c02b620688 199 P_10 += -dt * P_11;
Lauszus 0:f5c02b620688 200 P_11 += +Q_gyro * dt;
Lauszus 0:f5c02b620688 201
Lauszus 0:f5c02b620688 202 // Discrete Kalman filter measurement update equations - Measurement Update ("Correct")
Lauszus 0:f5c02b620688 203 // Calculate Kalman gain - Compute the Kalman gain
Lauszus 0:f5c02b620688 204 S = P_00 + R_angle;
Lauszus 0:f5c02b620688 205 K_0 = P_00 / S;
Lauszus 0:f5c02b620688 206 K_1 = P_10 / S;
Lauszus 0:f5c02b620688 207
Lauszus 0:f5c02b620688 208 // Calculate angle and resting rate - Update estimate with measurement zk
Lauszus 0:f5c02b620688 209 y = newAngle - angle;
Lauszus 0:f5c02b620688 210 angle += K_0 * y;
Lauszus 0:f5c02b620688 211 bias += K_1 * y;
Lauszus 0:f5c02b620688 212
Lauszus 0:f5c02b620688 213 // Calculate estimation error covariance - Update the error covariance
Lauszus 0:f5c02b620688 214 P_00 -= K_0 * P_00;
Lauszus 0:f5c02b620688 215 P_01 -= K_0 * P_01;
Lauszus 0:f5c02b620688 216 P_10 -= K_1 * P_00;
Lauszus 0:f5c02b620688 217 P_11 -= K_1 * P_01;
Lauszus 0:f5c02b620688 218
Lauszus 0:f5c02b620688 219 return angle;
Lauszus 0:f5c02b620688 220 }
Lauszus 0:f5c02b620688 221 double getGyroYrate() {
Lauszus 0:f5c02b620688 222 // (gyroAdc-gyroZero)/Sensitivity (In quids) - Sensitivity = 0.00333/3.3*4095=4.132227273
Lauszus 0:f5c02b620688 223 double gyroRate = -(((gyroY.read()*4095) - zeroValues[0]) / 4.132227273);
Lauszus 0:f5c02b620688 224 return gyroRate;
Lauszus 0:f5c02b620688 225 }
Lauszus 0:f5c02b620688 226 double getAccY() {
Lauszus 0:f5c02b620688 227 // (accAdc-accZero)/Sensitivity (In quids) - Sensitivity = 0.33/3.3*4095=409.5
Lauszus 0:f5c02b620688 228 double accXval = ((accX.read()*4095) - zeroValues[1]) / 409.5;
Lauszus 0:f5c02b620688 229 double accYval = ((accY.read()*4095) - zeroValues[2]) / 409.5;
Lauszus 0:f5c02b620688 230 accYval--;//-1g when lying down
Lauszus 0:f5c02b620688 231 double accZval = ((accZ.read()*4095) - zeroValues[3]) / 409.5;
Lauszus 0:f5c02b620688 232
Lauszus 0:f5c02b620688 233 double R = sqrt(pow(accXval, 2) + pow(accYval, 2) + pow(accZval, 2)); // Calculate the force vector
Lauszus 0:f5c02b620688 234 double angleY = acos(accYval / R) * RAD_TO_DEG;
Lauszus 0:f5c02b620688 235
Lauszus 0:f5c02b620688 236 return angleY;
Lauszus 0:f5c02b620688 237 }
Lauszus 0:f5c02b620688 238 void calibrateSensors() {
Lauszus 0:f5c02b620688 239 onboardLED1 = 1;
Lauszus 0:f5c02b620688 240 onboardLED2 = 1;
Lauszus 0:f5c02b620688 241 onboardLED3 = 1;
Lauszus 0:f5c02b620688 242 onboardLED4 = 1;
Lauszus 0:f5c02b620688 243
Lauszus 0:f5c02b620688 244 double adc[4];
Lauszus 0:f5c02b620688 245 for (uint8_t i = 0; i < 100; i++) { // Take the average of 100 readings
Lauszus 0:f5c02b620688 246 adc[0] += gyroY.read()*4095;
Lauszus 0:f5c02b620688 247 adc[1] += accX.read()*4095;
Lauszus 0:f5c02b620688 248 adc[2] += accY.read()*4095;
Lauszus 0:f5c02b620688 249 adc[3] += accZ.read()*4095;
Lauszus 0:f5c02b620688 250 wait_ms(10);
Lauszus 0:f5c02b620688 251 }
Lauszus 0:f5c02b620688 252 zeroValues[0] = adc[0] / 100; // Gyro X-axis
Lauszus 0:f5c02b620688 253 zeroValues[1] = adc[1] / 100; // Accelerometer X-axis
Lauszus 0:f5c02b620688 254 zeroValues[2] = adc[2] / 100; // Accelerometer Y-axis
Lauszus 0:f5c02b620688 255 zeroValues[3] = adc[3] / 100; // Accelerometer Z-axis
Lauszus 0:f5c02b620688 256
Lauszus 0:f5c02b620688 257 onboardLED1 = 0;
Lauszus 0:f5c02b620688 258 onboardLED2 = 0;
Lauszus 0:f5c02b620688 259 onboardLED3 = 0;
Lauszus 0:f5c02b620688 260 onboardLED4 = 0;
Lauszus 0:f5c02b620688 261 }
Lauszus 0:f5c02b620688 262 void move(Motor motor, Direction direction, float speed) { // speed is a value in percentage (0.0f to 1.0f)
Lauszus 0:f5c02b620688 263 if (motor == right) {
Lauszus 0:f5c02b620688 264 leftPWM = speed;
Lauszus 0:f5c02b620688 265 if (direction == forward) {
Lauszus 0:f5c02b620688 266 leftA = 0;
Lauszus 0:f5c02b620688 267 leftB = 1;
Lauszus 0:f5c02b620688 268 } else if (direction == backward) {
Lauszus 0:f5c02b620688 269 leftA = 1;
Lauszus 0:f5c02b620688 270 leftB = 0;
Lauszus 0:f5c02b620688 271 }
Lauszus 0:f5c02b620688 272 } else if (motor == left) {
Lauszus 0:f5c02b620688 273 rightPWM = speed;
Lauszus 0:f5c02b620688 274 if (direction == forward) {
Lauszus 0:f5c02b620688 275 rightA = 0;
Lauszus 0:f5c02b620688 276 rightB = 1;
Lauszus 0:f5c02b620688 277 } else if (direction == backward) {
Lauszus 0:f5c02b620688 278 rightA = 1;
Lauszus 0:f5c02b620688 279 rightB = 0;
Lauszus 0:f5c02b620688 280 }
Lauszus 0:f5c02b620688 281 } else if (motor == both) {
Lauszus 0:f5c02b620688 282 leftPWM = speed;
Lauszus 0:f5c02b620688 283 rightPWM = speed;
Lauszus 0:f5c02b620688 284 if (direction == forward) {
Lauszus 0:f5c02b620688 285 leftA = 0;
Lauszus 0:f5c02b620688 286 leftB = 1;
Lauszus 0:f5c02b620688 287
Lauszus 0:f5c02b620688 288 rightA = 0;
Lauszus 0:f5c02b620688 289 rightB = 1;
Lauszus 0:f5c02b620688 290 } else if (direction == backward) {
Lauszus 0:f5c02b620688 291 leftA = 1;
Lauszus 0:f5c02b620688 292 leftB = 0;
Lauszus 0:f5c02b620688 293
Lauszus 0:f5c02b620688 294 rightA = 1;
Lauszus 0:f5c02b620688 295 rightB = 0;
Lauszus 0:f5c02b620688 296 }
Lauszus 0:f5c02b620688 297 }
Lauszus 0:f5c02b620688 298 }
Lauszus 0:f5c02b620688 299 void stop(Motor motor) {
Lauszus 0:f5c02b620688 300 if (motor == left) {
Lauszus 0:f5c02b620688 301 leftPWM = 1;
Lauszus 0:f5c02b620688 302 leftA = 1;
Lauszus 0:f5c02b620688 303 leftB = 1;
Lauszus 0:f5c02b620688 304 } else if (motor == right) {
Lauszus 0:f5c02b620688 305 rightPWM = 1;
Lauszus 0:f5c02b620688 306 rightA = 1;
Lauszus 0:f5c02b620688 307 rightB = 1;
Lauszus 0:f5c02b620688 308 } else if (motor == both) {
Lauszus 0:f5c02b620688 309 leftPWM = 1;
Lauszus 0:f5c02b620688 310 leftA = 1;
Lauszus 0:f5c02b620688 311 leftB = 1;
Lauszus 0:f5c02b620688 312
Lauszus 0:f5c02b620688 313 rightPWM = 1;
Lauszus 0:f5c02b620688 314 rightA = 1;
Lauszus 0:f5c02b620688 315 rightB = 1;
Lauszus 0:f5c02b620688 316 }
Lauszus 0:f5c02b620688 317 }