Last active
July 18, 2018 15:53
-
-
Save tuenhidiy/d25148fd64665902a98a218482566e0a to your computer and use it in GitHub Desktop.
This is main program for 2 wheel self balancing robot.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /******************************************************************************************* | |
| * Two Wheel Self Balancing Robot | |
| * | |
| * REVISION | |
| * ________________________________________________________________________________________ | |
| * | |
| * It is referred from many sources. | |
| * - I2Cdev library: https://github.com/jrowberg/i2cdevlib/tree/master/Arduino/I2Cdev | |
| * - MPU6050 library: http://github.com/jrowberg/i2cdevlib/tree/master/Arduino/MPU6050 | |
| * - LMotorController library: https://github.com/lukagabric/Franko/tree/master/libraries/LMotorController | |
| * - PID_v1 library: https://github.com/br3ttb/Arduino-PID-Library | |
| * - Franco Robot: https://github.com/lukagabric | |
| * | |
| ******************************************************************************************/ | |
| #include "PID_v1.h" | |
| #include "LMotorController.h" | |
| #include "I2Cdev.h" | |
| #include "MPU6050_6Axis_MotionApps20.h" | |
| #if I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE | |
| #include "Wire.h" | |
| #endif | |
| // uncomment "OUTPUT_TEAPOT" if you want output that matches the | |
| // format used for the InvenSense teapot demo | |
| #define OUTPUT_TEAPOT 0 // Only for testing with processing "Teapot" program | |
| #define LOG_INPUT 0 | |
| #define MANUAL_TUNING 1 | |
| #define LOG_PID_CONSTANTS 0 //MANUAL_TUNING must be 1 | |
| #define MOVE_BACK_FORTH 0 | |
| #define MIN_ABS_SPEED 5 | |
| //MPU | |
| #define MPU_YPR_SELECT 1 | |
| MPU6050 mpu; | |
| // MPU control/status vars | |
| bool dmpReady = false; // set true if DMP init was successful | |
| uint8_t mpuIntStatus; // holds actual interrupt status byte from MPU | |
| uint8_t devStatus; // return status after each device operation (0 = success, !0 = error) | |
| uint16_t packetSize; // expected DMP packet size (default is 42 bytes) | |
| uint16_t fifoCount; // count of all bytes currently in FIFO | |
| uint8_t fifoBuffer[64]; // FIFO storage buffer | |
| // orientation/motion vars | |
| Quaternion q; // [w, x, y, z] quaternion container | |
| VectorFloat gravity; // [x, y, z] gravity vector | |
| float ypr[3]; // [yaw, pitch, roll] yaw/pitch/roll container and gravity vector | |
| // packet structure for InvenSense teapot demo | |
| uint8_t teapotPacket[14] = { '$', 0x02, 0,0, 0,0, 0,0, 0,0, 0x00, 0x00, '\r', '\n' }; | |
| //PID | |
| #if MANUAL_TUNING | |
| double kp , ki, kd; | |
| double prevKp, prevKi, prevKd; | |
| #endif | |
| double originalSetpoint = 180.70;// 181.13 | |
| double setpoint = originalSetpoint; | |
| double movingAngleOffset = 0.15;// 0.3- OK, 0.15 - OK | |
| double input, output; | |
| int moveState=0; //0 = balance; 1 = back; 2 = forth | |
| #if MANUAL_TUNING | |
| PID pid(&input, &output, &setpoint, 0, 0, 0, DIRECT); | |
| #else | |
| PID pid(&input, &output, &setpoint, 10.50, 67.44, 0.88, DIRECT);// time 5ms & 10ms, sometimes Kp(17.35, 16.86) Ki(302.05, 301.05) Kd(1.21) | |
| #endif | |
| //MOTOR CONTROLLER | |
| int ENA = 3; | |
| int IN1 = 4; | |
| int IN2 = 8; | |
| int IN3 = 5; | |
| int IN4 = 7; | |
| int ENB = 6; | |
| LMotorController motorController(ENA, IN1, IN2, ENB, IN3, IN4, 1, 1); | |
| //timers | |
| long time1Hz = 0; | |
| long time5Hz = 0; | |
| volatile bool mpuInterrupt = false; // indicates whether MPU interrupt pin has gone high | |
| void dmpDataReady() | |
| { | |
| mpuInterrupt = true; | |
| } | |
| void setup() | |
| { | |
| // join I2C bus (I2Cdev library doesn't do this automatically) | |
| #if I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE | |
| Wire.begin(); | |
| TWBR = 24; // 400kHz I2C clock (200kHz if CPU is 8MHz) | |
| #elif I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_FASTWIRE | |
| Fastwire::setup(400, true); | |
| #endif | |
| // initialize serial communication | |
| // (115200 chosen because it is required for Teapot Demo output, but it's | |
| // really up to you depending on your project) | |
| Serial.begin(115200); | |
| while (!Serial); // wait for Leonardo enumeration, others continue immediately | |
| // initialize device | |
| Serial.println(F("Initializing I2C devices...")); | |
| mpu.initialize(); | |
| // verify connection | |
| Serial.println(F("Testing device connections...")); | |
| Serial.println(mpu.testConnection() ? F("MPU6050 connection successful") : F("MPU6050 connection failed")); | |
| // load and configure the DMP | |
| Serial.println(F("Initializing DMP...")); | |
| devStatus = mpu.dmpInitialize(); | |
| // supply your own gyro offsets here, scaled for min sensitivity | |
| mpu.setXGyroOffset(39); | |
| mpu.setYGyroOffset(14); | |
| mpu.setZGyroOffset(6); | |
| mpu.setZAccelOffset(1788); // 1688 factory default for my test chip | |
| // make sure it worked (returns 0 if so) | |
| if (devStatus == 0) | |
| { | |
| // turn on the DMP, now that it's ready | |
| Serial.println(F("Enabling DMP...")); | |
| mpu.setDMPEnabled(true); | |
| // enable Arduino interrupt detection | |
| Serial.println(F("Enabling interrupt detection (Arduino external interrupt 0)...")); | |
| attachInterrupt(0, dmpDataReady, RISING); | |
| mpuIntStatus = mpu.getIntStatus(); | |
| // set our DMP Ready flag so the main loop() function knows it's okay to use it | |
| Serial.println(F("DMP ready! Waiting for first interrupt...")); | |
| dmpReady = true; | |
| // get expected DMP packet size for later comparison | |
| packetSize = mpu.dmpGetFIFOPacketSize(); | |
| //setup PID | |
| pid.SetMode(AUTOMATIC); | |
| pid.SetSampleTime(5);// 10 - OK, 5 - GOOD, 1- CHANGE PID | |
| pid.SetOutputLimits(-255, 255);// 80 - OK Strong enough | |
| } | |
| else | |
| { | |
| // ERROR! | |
| // 1 = initial memory load failed | |
| // 2 = DMP configuration updates failed | |
| // (if it's going to break, usually the code will be 1) | |
| Serial.print(F("DMP Initialization failed (code ")); | |
| Serial.print(devStatus); | |
| Serial.println(F(")")); | |
| } | |
| } | |
| void loop() | |
| { | |
| // if programming failed, don't try to do anything | |
| if (!dmpReady) return; | |
| // wait for MPU interrupt or extra packet(s) available | |
| while (!mpuInterrupt && fifoCount < packetSize) | |
| { | |
| //no mpu data - performing PID calculations and output to motors | |
| pid.Compute(); | |
| motorController.move(output, MIN_ABS_SPEED); | |
| unsigned long currentMillis = millis(); | |
| if (currentMillis - time1Hz >= 1000) | |
| { | |
| loopAt1Hz(); | |
| time1Hz = currentMillis; | |
| } | |
| if (currentMillis - time5Hz >= 5000) | |
| { | |
| loopAt5Hz(); | |
| time5Hz = currentMillis; | |
| } | |
| } | |
| // reset interrupt flag and get INT_STATUS byte | |
| mpuInterrupt = false; | |
| mpuIntStatus = mpu.getIntStatus(); | |
| // get current FIFO count | |
| fifoCount = mpu.getFIFOCount(); | |
| // check for overflow (this should never happen unless our code is too inefficient) | |
| if ((mpuIntStatus & 0x10) || fifoCount == 1024) | |
| { | |
| // reset so we can continue cleanly | |
| mpu.resetFIFO(); | |
| Serial.println(F("FIFO overflow!")); | |
| // otherwise, check for DMP data ready interrupt (this should happen frequently) | |
| } | |
| else if (mpuIntStatus & 0x02) | |
| { | |
| // wait for correct available data length, should be a VERY short wait | |
| while (fifoCount < packetSize) fifoCount = mpu.getFIFOCount(); | |
| // read a packet from FIFO | |
| mpu.getFIFOBytes(fifoBuffer, packetSize); | |
| // track FIFO count here in case there is > 1 packet available | |
| // (this lets us immediately read more without waiting for an interrupt) | |
| fifoCount -= packetSize; | |
| mpu.dmpGetQuaternion(&q, fifoBuffer); | |
| mpu.dmpGetGravity(&gravity, &q); | |
| mpu.dmpGetYawPitchRoll(ypr, &q, &gravity); | |
| #if LOG_INPUT | |
| Serial.print("ypr\t"); | |
| Serial.print(ypr[0] * 180/M_PI); | |
| Serial.print("\t"); | |
| Serial.print(ypr[1] * 180/M_PI); | |
| Serial.print("\t"); | |
| Serial.println(ypr[2] * 180/M_PI); | |
| Serial.print("\tout: "); | |
| Serial.println(ypr[MPU_YPR_SELECT] * 180/M_PI + 180); | |
| #endif | |
| input = ypr[MPU_YPR_SELECT] * 180/M_PI + 180; | |
| #ifdef OUTPUT_TEAPOT | |
| // display quaternion values in InvenSense Teapot demo format: | |
| teapotPacket[2] = fifoBuffer[0]; | |
| teapotPacket[3] = fifoBuffer[1]; | |
| teapotPacket[4] = fifoBuffer[4]; | |
| teapotPacket[5] = fifoBuffer[5]; | |
| teapotPacket[6] = fifoBuffer[8]; | |
| teapotPacket[7] = fifoBuffer[9]; | |
| teapotPacket[8] = fifoBuffer[12]; | |
| teapotPacket[9] = fifoBuffer[13]; | |
| Serial.write(teapotPacket, 14); | |
| teapotPacket[11]++; // packetCount, loops at 0xFF on purpose | |
| #endif | |
| } | |
| } | |
| void loopAt1Hz() | |
| { | |
| #if MANUAL_TUNING | |
| setPIDTuningValues(); | |
| #endif | |
| } | |
| void loopAt5Hz() | |
| { | |
| #if MOVE_BACK_FORTH | |
| moveBackForth(); | |
| #endif | |
| } | |
| //move back and forth | |
| void moveBackForth() | |
| { | |
| moveState++; | |
| if (moveState > 2) moveState = 0; | |
| if (moveState == 0) | |
| setpoint = originalSetpoint; | |
| else if (moveState == 1) | |
| setpoint = originalSetpoint - movingAngleOffset; | |
| else | |
| setpoint = originalSetpoint + movingAngleOffset; | |
| } | |
| //PID Tuning (3 potentiometers) | |
| #if MANUAL_TUNING | |
| void setPIDTuningValues() | |
| { | |
| readPIDTuningValues(); | |
| if (kp != prevKp || ki != prevKi || kd != prevKd) | |
| { | |
| #if LOG_PID_CONSTANTS | |
| Serial.print(kp);Serial.print(", ");Serial.print(ki);Serial.print(", ");Serial.println(kd); | |
| #endif | |
| pid.SetTunings(kp, ki, kd); | |
| prevKp = kp; prevKi = ki; prevKd = kd; | |
| } | |
| } | |
| void readPIDTuningValues() | |
| { | |
| int potKp = analogRead(A0); | |
| int potKi = analogRead(A1); | |
| int potKd = analogRead(A2); | |
| kp = map(potKp, 0, 1023, 0, 25000) / 100.0; //0 - 250 | |
| ki = map(potKi, 0, 1023, 0, 100000) / 100.0; //0 - 1000 | |
| kd = map(potKd, 0, 1023, 0, 500) / 100.0; //0 - 5 | |
| } | |
| #endif |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment