Attempt at controlling a specific single-axle robot to balance, by driving motors using filtered IMU data
Dependencies: mbed LSM9DS1_Library Motor
Diff: main.cpp
- Revision:
- 0:3cafef4c2519
diff -r 000000000000 -r 3cafef4c2519 main.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/main.cpp Wed Dec 11 01:39:42 2019 +0000 @@ -0,0 +1,198 @@ +#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); +} \ No newline at end of file