Attempt at controlling a specific single-axle robot to balance, by driving motors using filtered IMU data

Dependencies:   mbed LSM9DS1_Library Motor

main.cpp

Committer:
Solomon_Martin
Date:
2019-12-11
Revision:
0:3cafef4c2519

File content as of revision 0:3cafef4c2519:

#include "mbed.h"
#include "Motor.h"
#include "LSM9DS1.h"
#include "math.h"

#define PI 3.141592653589793
#define PERIOD 0.001 //NOTE: Max sample rate is 952 Hz

Serial pc(USBTX, USBRX);
LSM9DS1 imu(p28, p27, 0xD6, 0x3C);
Motor L(p22, p5, p6); // pwm, fwd, rev
Motor R(p21, p8, p7);
DigitalOut myled(LED1);

float angle;
float gOffset = 0;
float aOffset = 0;

int kp = 48; //Best performance so far acheived with kp and speedCorrection
int ki;
int kd;
int factor;
float speedCorrection = 2.0;
float speed;

void myCalibrate();
void control();
void drive();
void callback();

int main() {
    //LSM9DS1 imu(p9, p10, 0x6B, 0x1E);
    imu.begin();
    if (!imu.begin()) {
        pc.printf("Failed to communicate with LSM9DS1.\n");
    }
    imu.calibrate();
    imu.setAccelScale(2);
    imu.setGyroScale(2);
    pc.attach(&callback);
    myCalibrate();
    drive();
}

void control() {
}
    
void drive() {
    float accelAngle;
    float gyroAngle;
    float pastAccAngle = 0;
    float pastRawGyro = 0;
    float pastGyroAngle = 0;
    float temp;
    int counter = 0;
    float alpha = 0.50; // = T/(T+dt) where T is response time
    float beta = 1; // Decides how much the gyroscope data is trusted. b/t 0.9 & 1
    float integral = 0;
    float derivative = 0;
    float lastAngle = 0;
    factor = 1000;
    while(1) {
        counter++;
        accelAngle = 0;
        for (int i = 0; i < 10; i++) {
            imu.readAccel();
            imu.readGyro();
            accelAngle += ::atan2((float) imu.ax, (float) imu.az)*180/PI - 90;
            //The max we will see for this application is about 16380 (1 G), but max is 2 G's (32767 in 16 bits):
            gyroAngle += (1/2)*imu.gy + gOffset;
            wait(PERIOD);
        }
        accelAngle = accelAngle/10.0 + aOffset; //Averaging accel to get rid of some noise
        gyroAngle = (gyroAngle)*PERIOD - pastGyroAngle;         //Sum over a set time (10 ms) and multiply by differential time (1 ms) to integrate angular velocity

        temp = gyroAngle;
        accelAngle = (1 - alpha)*accelAngle + alpha*pastAccAngle; //Low Pass Filter
        //gyroAngle = (1 - alpha)*pastGyroAngle + (1 - alpha)*(gyroAngle - pastRawGyro); //High pass filter
        pastRawGyro = temp;
        angle = accelAngle*beta + gyroAngle*(1 - beta);
        angle -= 2; //Attempt to correct for weight distribution bias
        
        myled = 1;
        if (counter % (int) (0.5/(PERIOD*10)) == 0) {
            pc.printf("Angle by acc: %.3f\n\r", accelAngle);
            pc.printf("Angle by gyro: %.3f\n\r", gyroAngle);
            pc.printf("Overall %.3f\n\r", angle);
        }
        integral += angle*PERIOD;
        derivative = angle - lastAngle;
        speed = (kp*angle + ki*integral + kd*derivative)/factor;
        speed = abs(angle) > 80 ? 0 : speed;
        pastAccAngle = accelAngle;
        pastGyroAngle = gyroAngle;

        if (speed < 0) {
            speed *= speedCorrection; //Speed Correction attempts to correct weight distribution issue
        }                             //by driving faster when falling on the heavier side
        if (speed > 1) {
            speed = 1;
        } else if(speed < -1) {
            speed = -1;
        }
        if (counter % (int) (0.5/(PERIOD*10)) == 0) {
            pc.printf("speed: %.3f\n\n\r", speed);
        }
        speed *= -1;        //Had to correct direction after moving accelerometer to opposite side
        L.speed(speed);
        R.speed(speed);
        lastAngle = angle;
    }
}
        
void myCalibrate() {
    //Get linear offsets for accelerometer and gyroscope
    float gSum = 0;
    for (int i = 0; i < 100; i++) {
        imu.readAccel();
        imu.readGyro();
        gSum += imu.gy;
        aOffset += ::atan2((float) imu.ax, (float) imu.az)*180/PI;
        wait(PERIOD);
    }
    aOffset = - aOffset/100 ;
    gOffset = - gSum/100 ;
    pc.printf("Accelerometer offset: %.3f \n\r", aOffset);
    pc.printf("Gyroscope offset: %.3f \n\r", gOffset);
    wait(2);
}

//This function is a revised version of that from:
// https://os.mbed.com/teams/ECE-4180-Spring-15/code/balance2/
void callback()
{
    speed = 0;
    char val;                                                   // Needed for Serial communication (need to be global?)
    val = pc.getc();                                            // Reat the value from Serial
    pc.printf("%c\n", val);                                     // Print it back to the screen
    if( val =='p') {                                            // If character was a 'p'
        pc.printf("enter kp \n");                               // Adjust kp
        val = pc.getc();                                        // Wait for kp value
        if(val == 0x2b) {                                       // If character is a plus sign
            kp++;                                           // Increase kp
        } else if (val == 0x2d) {                               // If recieved character is the minus sign
            kp--;
        } else if (val == '(') {
            kp-= 10;
        } else if (val == ')') {
            kp += 10;                                           // Decrease kp
        } else {
            kp = val - 48;                                      // Cast char to float
        }
        pc.printf(" kp = %d \n",kp);                            // Print current kp value to screen
    } else if( val == 'd') {                                    // Adjust kd
        pc.printf("enter kd \n");                               // Wait for kd
        val= pc.getc();                                         // Read value from serial
        if(val == '+') {                                        // If given plus sign
            kd++;                                               // Increase kd
        } else if (val == '-') {                                // If given negative sign
            kd--;                                               // Decrease kd
        } else {                                                // If given some other ascii (a number?)
            kd = val - 48;                                      // Set derivative gain
        }
        pc.printf(" kd = %d \n",kd);                            // Print kd back to screen
    } else if( val == 'i') {                                    // If given i - integral gain
        pc.printf("enter ki \n");                               // Prompt on screen to ask for ascii
        val= pc.getc();                                         // Get the input
        if(val == '+') {                                        // If given the plus sign
            ki++;                                               // Increase ki
        } else if (val == '-') {                                // If given the minus sign
            ki--;                                               // Decrease ki
        } else {                                                // If given some other ascii
            ki = val - 48;                                      // Set ki to that number
        }
        pc.printf(" ki = %d \n",ki);
    } else if( val == 'o') {
        pc.printf("enter factor \n");
        val= pc.getc();
        if(val == '+') {
            factor=factor+1000;
        } else if (val == '-') {
            factor=factor-1000;
        } else {
            factor=(val-48)*1000;;
        }
        pc.printf(" factor = %d \n",factor);
    } else if (val == 'a') {
        pc.printf("enter speed correction \n");
        val= pc.getc();
        if(val == '+') {
            speedCorrection += 0.1;
        } else if (val == '-') {
            speedCorrection -= 0.1;
        }
         pc.printf("speedCorrect = %f \n",speedCorrection);
    }
    wait(1);
}