Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- #define MPU_addr 0x68
- #define BUTTON_PIN 12
- #include <EEPROM.h>
- #include "EEPROMAnything.h"
- #include <Wire.h>
- #include <math.h>
- #include <LiquidCrystal_I2C.h>
- LiquidCrystal_I2C lcd(0x27, 2, 1, 0, 4, 5, 6, 7,3,POSITIVE);
- float G = 9.81;
- float G_SQR = 96.2361;
- int16_t Tmp;
- float x[3],y[3],z[3];
- float Araw[3]; // acceleration from MPU, for calibration/alignment use
- float GyRaw[3]; // gyro readings from MPU, for use in getAraw function
- int signS; // either +1 or -1, sign of S based on gyros
- float S, ax, ay, az; // side acceleration (m/s^2), accelerations in chassis frame (m/s), theta in radians
- float csX = 1, csY = 1, csZ = 1, coX = 0, coY = 0, coZ = 0; // slope and offset calculated by calibrations. This should be stored in EEPROM.
- float GyXoffset = 0, GyYoffset = 0, GyZoffset = 0; // offsets for gyro
- int theta;
- int g;
- long thetaTime = 99999;
- long gTime = 99999;
- int maxTheta = 0;
- int maxG = 0;
- long timeOut = 4000; // make this user changable
- long timeOutArray[] = {5000, 10000, 15000, 25000, 50000, 120000, 100000000};
- // strings
- char angle[5];
- char accel[5];
- char maxAngle[5];
- char maxAccel[5];
- // declare functions
- void getA();
- void getTemp();
- void calibration();
- void getAraw();
- void alignment();
- void calibrateAraw();
- void getStheta(int average); // averages "average" number of values before it returns
- void setups();
- void cross(const float m[],const float n[], float p[]);
- boolean buttonPressed();
- void setup() {
- setups();
- lcd.setCursor(0,0); lcd.print("Now:");
- lcd.setCursor(0,1); lcd.print("Max:");
- }
- void loop() {
- getStheta(250);
- g = 100*az/G;
- if (abs(g) > abs(maxG)) {
- maxG = g; gTime = millis();
- }
- if (abs(theta) > abs(maxTheta)) {
- maxTheta = theta; thetaTime = millis();
- }
- if (millis() - gTime > timeOut) maxG = 0;
- if (millis() - thetaTime > timeOut) maxTheta = 0;
- sprintf(angle, "%i%c", theta, ((char) 223) );
- sprintf(accel, "%i%s", -g, "'g");
- sprintf(maxAngle, "%i%c", maxTheta, ((char) 223) );
- sprintf(maxAccel, "%i%s", -maxG, "'g");
- lcd.setCursor(5, 0); lcd.print(angle); lcd.print(" ");
- lcd.setCursor(11,0); lcd.print(accel); lcd.print(" ");
- lcd.setCursor(5, 1); lcd.print(maxAngle); lcd.print(" ");
- lcd.setCursor(11,1); lcd.print(maxAccel); lcd.print(" ");
- if (buttonPressed()) changeTimeout();
- }// end loop
- void alignment() //determines the x[] y[] and z[] which are used to transform from sensor frame (Araw from getAraw) to chassis frame (ax ay az from getA).
- {
- int steps = 1000;
- int delaytime = 5*1000/steps; // put in seconds to calibrate, want at least 1 sec or so.
- float ax1=0, ay1=0, az1=0, ax2=0, ay2=0, az2=0; // these are values in sensor frame (with calibration applied): ax1 on kick stand, ax2 on rear stand
- lcd.clear(); lcd.setCursor(0,0);
- lcd.print(" Entering"); lcd.setCursor(0,1); lcd.print(" alignment mode");
- delay(200);
- while(buttonPressed()); // wait for button to be released
- delay(1500);
- if (buttonPressed()) calibration();
- lcd.clear(); lcd.setCursor(0,0);
- lcd.print(" Place bike flat"); lcd.setCursor(0,1); lcd.print(" on KICKSTAND");
- while (!buttonPressed()); //wait for button press
- lcd.clear(); lcd.setCursor(1,0); lcd.print("Acquiring data"); lcd.setCursor(2,1); lcd.print("HOLD STEADY!");
- delay(1000); // make sure bike isn't being touched
- for(int k=0; k<steps ; k++)
- {
- delay(delaytime);
- getAraw(); //update accel values
- calibrateAraw(); // apply calibration to Araw[] array
- ax2=ax2+Araw[0]; // sum values to compute average (divide later)
- ay2=ay2+Araw[1];
- az2=az2+Araw[2];
- }
- ax2=ax2/steps; //divide to give the average
- ay2=ay2/steps;
- az2=az2/steps;
- lcd.clear(); lcd.setCursor(0,0);
- lcd.print("Place bike flat"); lcd.setCursor(0,1); lcd.print(" on REAR STAND");
- while (!buttonPressed()); //wait for button press
- lcd.clear(); lcd.print(" Acquiring in");
- for(int j=5;j>0;j--) // countdown until it starts taking data. Gives enough time to level rear stand
- {lcd.setCursor(14,0); lcd.print(j); delay(1000); }
- lcd.clear(); lcd.setCursor(1,0); lcd.print("Acquiring data"); lcd.setCursor(2,1); lcd.print("HOLD STEADY!");
- for(int k=0; k<steps ; k++)
- {
- delay(delaytime);
- getAraw();
- calibrateAraw();
- ax1=ax1+Araw[0];
- ay1=ay1+Araw[1];
- az1=az1+Araw[2];
- }
- ax1=ax1/steps;
- ay1=ay1/steps;
- az1=az1/steps;
- //***** that's it for data collection
- // compute vectors which become components of the rotation matrix
- float yvec[3];
- yvec[0]=ax1/G; yvec[1]=ay1/G; yvec[2]=az1/G;
- float a2magnitude=sqrt(ax2*ax2+ay2*ay2+az2*az2);
- // float a2[]={ax2/a2magnitude , ay2/a2magnitude , az2/a2magnitude};
- float a2[3]; a2[0]=ax2; a2[1]=ay2; a2[2]=az2;
- float zvec[3]; //initiate this so that the cross function can edit it
- cross(yvec,a2,zvec); // crosses yvec, a2, and writes it into zvec
- float zvecmagnitude=sqrt(zvec[0]*zvec[0]+zvec[1]*zvec[1]+zvec[2]*zvec[2]); // get it's magnitude because it's not a unit vector ( sin(alpha) is the length), alpha is angle of tilt on kickstand
- zvec[0]=zvec[0]/zvecmagnitude; zvec[1]=zvec[1]/zvecmagnitude; zvec[2]=zvec[2]/zvecmagnitude; // make zvec a unit vector
- float xvec[]={0,0,0};
- cross(yvec,zvec,xvec);
- // now update alignment constants which are loaded in memory in setups() and used by getA function
- for(int k = 0; k < 3; k++) {
- EEPROM_writeAnything(4*k,xvec[k]);
- EEPROM_writeAnything(4*k+12,yvec[k]);
- EEPROM_writeAnything(4*k+24,zvec[k]);
- }//end for loop
- EEPROM.write(72, B01100100); // sets the alignment check register (72) = to 100 = B01100100 to flag that it's been done.
- lcd.clear(); lcd.setCursor(0,0);
- lcd.print(" Alignment"); lcd.setCursor(0,1); lcd.print(" finished.");
- delay(3000);
- lcd.clear();
- }// end alignment function
- boolean buttonPressed() { // should include some sort of debouncing
- if (!digitalRead(BUTTON_PIN)) {
- delay(20);
- if (!digitalRead(BUTTON_PIN)) {
- return true;
- }
- }
- return false;
- }// end buttonPrssed() function
- void calibrateAraw()
- {
- // apply calibration to raw sensor values
- Araw[0]=Araw[0]*csX+coX;
- Araw[1]=Araw[1]*csY+coY;
- Araw[2]=Araw[2]*csZ+coZ;
- GyRaw[0]=GyRaw[0]-GyXoffset;
- GyRaw[1]=GyRaw[1]-GyYoffset;
- GyRaw[2]=GyRaw[2]-GyZoffset;
- } // end calibrateAraw function
- void calibration()
- {
- float xmin=0,xmax=0,ymin=0,ymax=0,zmin=0,zmax=0,GyroX=0,GyroY=0,GyroZ=0;
- int steps = 1000;
- int delaytime = 2.5*1000/steps;
- lcd.clear(); lcd.setCursor(0,0);
- lcd.print(" Entering"); lcd.setCursor(0,1); lcd.print("CALIBRATION mode");
- delay(1500);
- while(buttonPressed());
- lcd.clear(); lcd.setCursor(0,0); lcd.print("Align X vertical"); lcd.setCursor(0,1); lcd.print("& press button.");
- while (!buttonPressed());
- lcd.clear(); lcd.setCursor(0,0); lcd.print("Calibrating X"); lcd.setCursor(0,1); lcd.print("HOLD STEADY!");
- for(int k=0 ; k<steps ; k++)
- {
- delay(delaytime);
- getAraw(); //update accel values
- if(Araw[0]<0) xmin=xmin+Araw[0]; // figures out if you have x down or -x down
- else xmax=xmax+Araw[0];
- }
- lcd.clear(); lcd.setCursor(0,0); lcd.print("Flip X"); lcd.setCursor(0,1); lcd.print("& press button.");
- while (!buttonPressed()); //wait for button press
- lcd.clear(); lcd.setCursor(0,0); lcd.print("Calibrating X"); lcd.setCursor(0,1); lcd.print("HOLD STEADY!");
- for(int k=0; k<steps ; k++)
- {
- delay(delaytime);
- getAraw(); //update accel values
- if(Araw[0]<0) xmin=xmin+Araw[0]; // figures out if you have x down or -x down
- else xmax=xmax+Araw[0];
- }
- //now scale back
- xmax=xmax/steps;
- xmin=xmin/steps;
- lcd.clear(); lcd.setCursor(0,0); lcd.print("Align Y vertical"); lcd.setCursor(0,1); lcd.print("& press button.");
- while (!buttonPressed()); //wait for button press
- lcd.clear(); lcd.setCursor(0,0); lcd.print("Calibrating Y"); lcd.setCursor(0,1); lcd.print("HOLD STEADY!");
- for(int k=0; k<steps; k++)
- {
- delay(delaytime);
- getAraw(); //update accel values
- if(Araw[1]<0) ymin=ymin+Araw[1]; // figures out if you have y down or -y down
- else ymax=ymax+Araw[1];
- }
- lcd.clear(); lcd.setCursor(0,0); lcd.print("Flip Y"); lcd.setCursor(0,1); lcd.print("& press button.");
- while (!buttonPressed()); //wait for button press
- lcd.clear(); lcd.setCursor(0,0); lcd.print("Calibrating Y"); lcd.setCursor(0,1); lcd.print("HOLD STEADY!");
- for(int k=0; k<steps; k++)
- {
- delay(delaytime);
- getAraw(); //update accel values
- if(Araw[1]<0) ymin=ymin+Araw[1]; // figures out if you have y down or -y down
- else ymax=ymax+Araw[1];
- }
- ymax=ymax/steps;
- ymin=ymin/steps;
- // done with y calibration
- lcd.clear(); lcd.setCursor(0,0); lcd.print("Align Z vertical"); lcd.setCursor(0,1); lcd.print("& press button.");
- while (!buttonPressed()); //wait for button press
- lcd.clear(); lcd.setCursor(0,0); lcd.print("Calibrating Z"); lcd.setCursor(0,1); lcd.print("HOLD STEADY!");
- for(int k=0; k<steps; k++)
- {
- delay(delaytime);
- getAraw(); //update accel values
- if(Araw[2]<0) zmin=zmin+Araw[2]; // figures out if you have z down or -z down
- else zmax=zmax+Araw[2];
- }
- lcd.clear(); lcd.setCursor(0,0); lcd.print("Flip Z"); lcd.setCursor(0,1); lcd.print("& press button.");
- while (!buttonPressed()); //wait for button press
- lcd.clear(); lcd.setCursor(0,0); lcd.print("Calibrating Z"); lcd.setCursor(0,1); lcd.print("HOLD STEADY!");
- for(int k=0; k<steps; k++)
- {
- delay(delaytime);
- getAraw(); //update accel values
- if(Araw[2]<0) zmin=zmin+Araw[2]; // figures out if you have z down or -z down
- else zmax=zmax+Araw[2];
- }
- zmax=zmax/steps;
- zmin=zmin/steps;
- // done with z calibration
- // now do the gyro accelerations
- lcd.clear(); lcd.setCursor(0,0); lcd.print("Hold STILL"); lcd.setCursor(0,1); lcd.print("& press button.");
- while (!buttonPressed()); //wait for button press
- lcd.clear(); lcd.setCursor(0,0); lcd.print("Calibrating GYRO"); lcd.setCursor(0,1); lcd.print("HOLD STEADY!");
- for(int k=0; k<steps; k++)
- {
- getAraw();
- GyroX=GyroX+GyRaw[0];
- GyroY=GyroY+GyRaw[1];
- GyroZ=GyroZ+GyRaw[2];
- delay(delaytime);
- }
- csX=2*G/(xmax-xmin);
- coX=-G*(xmax+xmin)/(xmax-xmin);
- csY=2*G/(ymax-ymin);
- coY=-G*(ymax+ymin)/(ymax-ymin);
- csZ=2*G/(zmax-zmin);
- coZ=-G*(zmax+zmin)/(zmax-zmin);
- GyXoffset=GyroX/steps;
- GyYoffset=GyroY/steps;
- GyZoffset=GyroZ/steps;
- // write coefficients to EEPROM as in excel list
- EEPROM_writeAnything(36,GyXoffset);
- EEPROM_writeAnything(40,GyYoffset);
- EEPROM_writeAnything(44,GyZoffset);
- EEPROM_writeAnything(48,csX);
- EEPROM_writeAnything(52,coX);
- EEPROM_writeAnything(56,csY);
- EEPROM_writeAnything(60,coY);
- EEPROM_writeAnything(64,csZ);
- EEPROM_writeAnything(68,coZ);
- EEPROM.write(73, B01100100); // sets the calibration check register (73) = to 100 to flag that it's been done.
- lcd.clear(); lcd.setCursor(0,0); lcd.print("Calibration"); lcd.setCursor(0,1); lcd.print("complete."); delay(2000); lcd.clear();
- }// end calibration function
- void changeTimeout() {
- lcd.clear();
- lcd.setCursor(0,0);
- lcd.print("Set timeout:");
- while(buttonPressed());
- delay(200);
- long timer = millis();
- byte k = 0;
- char timerString[16];
- while (millis() - timer < 4000) {
- delay(150);
- if (buttonPressed()) {
- if ( k > 6 ) k = 0;
- timeOut = timeOutArray[k];
- sprintf(timerString, "%li%s", timeOut/1000, " seconds ");
- delay(200);
- lcd.setCursor(0,1);
- lcd.print(timerString);
- k++;
- timer = millis();
- }
- }
- EEPROM.write(75, k);
- lcd.clear();
- lcd.setCursor(0,0); lcd.print("Setting saved!");
- delay(2000);
- lcd.clear();
- lcd.setCursor(0,0); lcd.print("Now:");
- lcd.setCursor(0,1); lcd.print("Max:");
- }
- void cross(const float m[],const float n[], float p[]) // pass p[] and change it in the cross function because you can't return array in C. Funciton can change input arrays because they're pointers. Doing const float m[] means the function is not allowed to change m[].
- {
- p[0]=n[1]*m[2]-m[1]*n[2];
- p[1]=m[0]*n[2]-n[0]*m[2];
- p[2]=n[0]*m[1]-m[0]*n[1];
- }//end cross function
- void getA() // takes Araw[] data and transforms to chassis frame, both accel and gyro, also sets sign of S
- {
- getAraw(); // first, loads raw values straight from MPU6050
- calibrateAraw(); // apply calibration to Araw[] array
- // transorm to chassis frame, x[],y[],z[] computed from alignment
- ax=Araw[0]*x[0]+Araw[1]*x[1]+Araw[2]*x[2];
- ay=Araw[0]*y[0]+Araw[1]*y[1]+Araw[2]*y[2];
- az=Araw[0]*z[0]+Araw[1]*z[1]+Araw[2]*z[2];
- int16_t gyy=GyRaw[0]*y[0]+GyRaw[1]*y[1]+GyRaw[2]*y[2]; // only care about angular rate about y axis (bike is in a turn)
- //determine sign of S
- if(gyy>0) signS=1;
- else signS=-1;
- }// end getA function
- void getAraw()// gets raw sensor values for calibration and alignment
- {
- Wire.beginTransmission(MPU_addr);
- Wire.write(0x3B); // starting with register 0x3B (ACCEL_XOUT_H)
- if (Wire.endTransmission(true)) return;
- if(Wire.requestFrom(MPU_addr,14,true) == 14) // request a total of 14 registers
- {
- Araw[0]=Wire.read()<<8|Wire.read();
- Araw[1]=Wire.read()<<8|Wire.read();
- Araw[2]=Wire.read()<<8|Wire.read();
- Tmp = Wire.read()<<8|Wire.read();
- GyRaw[0]=Wire.read()<<8|Wire.read();
- GyRaw[1]=Wire.read()<<8|Wire.read();
- GyRaw[2]=Wire.read()<<8|Wire.read();
- }
- // note, Araw[] and GyRaw[] are float, but they come off as uint16_t from << thing, it gets re-cast.
- }// end getAraw
- void getStheta(int average) // updates the S, theta floats, and accelerations -- "int average" is the number of readings to be averaged
- {
- float axAxPlusAyay = 0; // this gets averaged
- float axaveraged=0, ayaveraged=0, azaveraged=0;
- int signSaveraged=0;
- for(int k=0; k < average ; k++) { // loop adds up "int average" number of readings
- getA();
- axaveraged = axaveraged + ax;
- ayaveraged = ayaveraged + ay;
- azaveraged = azaveraged + az;
- signSaveraged = signSaveraged + signS; // signS comes from getA function
- }// end for loop which averages readings
- // now divide by "int average" to get the average except for signS
- axaveraged = axaveraged/average;
- ayaveraged = ayaveraged/average;
- az = azaveraged/average; // this is a bit weird, bad style to overwrite az like this
- axAxPlusAyay = axaveraged*axaveraged + ayaveraged*ayaveraged;
- if (signSaveraged > 0) signSaveraged = 1;
- else if (signSaveraged < 0) signSaveraged = -1;
- else signSaveraged = 0;
- //Serial.print("ax^2 + ay^2= "); Serial.println(axaxplusayay);
- float absS_squared = axAxPlusAyay - G_SQR; // this should always be >=0 but inaccuracies can make it negative
- if (absS_squared <=0) S=0; // sometimes the sqrt gets negative argument which returns "nan" (no imaginary). This prevents it.
- else S = signSaveraged*sqrt( absS_squared ); // sqrtf is on floats, sqrt() is on doubles
- // S = signSaveraged * sqrt( absS_squared );
- //Serial.print("S= "); Serial.println(S);
- //theta = round(57.29578*acos( (axaveraged*G+ayaveraged*S)/axAxPlusAyay )-90);
- theta = round(57.29578*asin( (axaveraged*G-ayaveraged*S)/axAxPlusAyay ) ); // asin takes double, also converts to degrees
- //Serial.print("theta "); Serial.println(theta);
- } //end getStheta function
- void getTemp() // returns int value of temperature in Fahrengehgiehgeht
- {
- getAraw(); delay(100); getAraw();
- int tempF = Tmp*0.0052941+97.754; // converstion formula
- lcd.setCursor(0,0); lcd.print("Chip temp:");
- lcd.print(tempF); lcd.print((char) 223); lcd.print("F"); delay(250);
- } // end getTemp function
- void setups() { // sets the accelerometer settings, starts LCD
- // configure accelerometer registers
- Wire.begin();
- Wire.beginTransmission(MPU_addr);
- Wire.write(0x6B); // PWR_MGMT_1 register, sets clock reference, sleep mode, and temp sensor
- Wire.write(0b00000011); // set to zero (wakes up the MPU-6050), use gyro Z for clock ref
- Wire.endTransmission(true);
- Wire.beginTransmission(MPU_addr);
- Wire.write(26); // Frame Synchronization and Filtering register
- Wire.write(0b00000110); // set to pretty hardcore filtering (last 3 bits chose between 0-6)
- Wire.endTransmission(true);
- Wire.beginTransmission(MPU_addr);
- Wire.write(28); // Self-test and sensitivity (g-range) register.
- Wire.write(0b00000000); // last 3 bits are nothing. all 0 is +/- 2g
- Wire.endTransmission(true);
- // initialize LCD 16x2
- lcd.begin(16,2);
- lcd.backlight();
- lcd.setCursor(0,0); //Start at character 4 on line 0
- lcd.print("Starting...");
- delay(500);
- lcd.clear();
- pinMode(BUTTON_PIN,INPUT_PULLUP); // button pin default high
- //Serial.begin(9600);
- // load EEPROM, if EEPROM bytes are all 255, message that it needs alignment. Or make byte 72 = 0 if alignment done.
- /*byte checkCalibration;
- byte checkAlignment;
- byte checkCalibration=EEPROM.read(73);
- EEPROM.read(72, checkAlignment);*/
- if(EEPROM.read(73)!=B01100100){
- lcd.setCursor(0,0);
- lcd.print("Calibration not");
- lcd.setCursor(0,1);
- lcd.print("yet done.");
- delay(4000); calibration();
- }
- if(EEPROM.read(72) != B01100100){
- lcd.print("Alignment not");
- lcd.setCursor(0,1);
- lcd.print("yet done.");
- delay(4000); alignment();
- }
- // show temperature while waiting for any button presses
- getTemp();
- while (!buttonPressed() && millis() < 2000);
- lcd.clear();
- // now read calibration constants from EEPROM
- EEPROM_readAnything(36,GyXoffset);
- EEPROM_readAnything(40,GyYoffset);
- EEPROM_readAnything(44,GyZoffset);
- EEPROM_readAnything(48,csX);
- EEPROM_readAnything(52,coX);
- EEPROM_readAnything(56,csY);
- EEPROM_readAnything(60,coY);
- EEPROM_readAnything(64,csZ);
- EEPROM_readAnything(68,coZ);
- // perform alignment if button pressed
- if(buttonPressed()) alignment();
- // now read alignment constants from EEPROM
- for(int k=0; k < 3; k++) {
- EEPROM_readAnything(4*k,x[k]);
- EEPROM_readAnything(4*k+12,y[k]);
- EEPROM_readAnything(4*k+24,z[k]);
- }//end for loop
- // read timeOut preference
- int w = EEPROM.read(75);
- if (w > 6) w = 2;
- timeOut = timeOutArray[ w ];
- }//end setups function
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement