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
--- /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