Crude navigation
Dependencies: GPS2 L3GD20 LSM303DLHC2 PID mbed SDFileSystem
Fork of GPSNavigation by
IMUDataAndFilters.h
- Committer:
- Spilly
- Date:
- 2015-05-09
- Revision:
- 17:ba45eaae96b3
- Parent:
- 14:fd20b7ac8de8
File content as of revision 17:ba45eaae96b3:
/*************************************************************************************************************************************************************/ // Created by: Ryan Spillman // // Last updated 4/29/2015 // // Contains several functions such as calculations for roll, pitch, and yaw (tilt compensated compass) // Also contains various filtering and calibration functions // // Requires L3GD20 gyro and LSM303DLHC magnetometer and accelerometer /*************************************************************************************************************************************************************/ #include "mbed.h" #include "math.h" #include "L3GD20.h" #include "LSM303DLHC.h" #define IMUDataAndFilters_h L3GD20 gyro(PTE25, PTE24); //L3GD20/L3GD20.cpp LSM303DLHC compass(PTE25, PTE24); //LSM303DLHC/LSM303DLHC.cpp #define ALPHA_H 0.2f //Heading alpha (variable for low pass filter) #define ALPHA_A 0.1f //Heading accelerometer (variable for low pass filter) #define EARTHRADIUS 6378100.000000000000000000f //Radius of the earth in meters (DO NOT MODIFY) #define MAGN_PERIOD (1 / 15.0f) //magnetometer polling period (15 Hz) must manually update hardware ODR registers if chaging this value #define ACCEL_PERIOD (1 / 50.0f) //accelerometer polling period (50 Hz) must manually update hardware ODR registers if chaging this value #define GYRO_SAMPLE_PERIOD 0.01f //gyro sample period in seconds #define M_PI 3.1415926535897932384626433832795f #define TWO_PI 6.283185307179586476925286766559f #define RADTODEGREE 57.295779513082320876798154814105f #define DEGREETORAD 0.01745329251994329576923690768489f #define GRAVITY 1.0f #define CALIBRATION_COUNT 64 #define AVG_COUNT 10 // "accel x,y,z (min/max) = X_MIN/X_MAX Y_MIN/Y_MAX Z_MIN/Z_MAX" #define ACCEL_X_MIN -1.000000000f #define ACCEL_X_MAX 1.023437500f #define ACCEL_Y_MIN -1.015625000f #define ACCEL_Y_MAX 1.023437500f #define ACCEL_Z_MIN -1.023437500f #define ACCEL_Z_MAX 0.960937500f // "magn x,y,z (min/max) = X_MIN/X_MAX Y_MIN/Y_MAX Z_MIN/Z_MAX" #define MAGN_X_MIN (-0.370909f) #define MAGN_X_MAX (0.569091f) #define MAGN_Y_MIN (-0.516364f) #define MAGN_Y_MAX (0.392727f) #define MAGN_Z_MIN (-0.618367f) #define MAGN_Z_MAX (0.408163f) // Sensor calibration scale and offset values #define ACCEL_X_OFFSET ((ACCEL_X_MIN + ACCEL_X_MAX) / 2.0f) #define ACCEL_Y_OFFSET ((ACCEL_Y_MIN + ACCEL_Y_MAX) / 2.0f) #define ACCEL_Z_OFFSET ((ACCEL_Z_MIN + ACCEL_Z_MAX) / 2.0f) #define ACCEL_X_SCALE (GRAVITY / (ACCEL_X_MAX - ACCEL_X_OFFSET)) #define ACCEL_Y_SCALE (GRAVITY / (ACCEL_Y_MAX - ACCEL_Y_OFFSET)) #define ACCEL_Z_SCALE (GRAVITY / (ACCEL_Z_MAX - ACCEL_Z_OFFSET)) #define MAGN_X_OFFSET ((MAGN_X_MIN + MAGN_X_MAX) / 2.0f) #define MAGN_Y_OFFSET ((MAGN_Y_MIN + MAGN_Y_MAX) / 2.0f) #define MAGN_Z_OFFSET ((MAGN_Z_MIN + MAGN_Z_MAX) / 2.0f) //not sure what the "normal" value is, thus not using scale for magnetometer //#define MAGN_X_SCALE (??? / (MAGN_X_MAX - MAGN_X_OFFSET)) //#define MAGN_Y_SCALE (??? / (MAGN_Y_MAX - MAGN_Y_OFFSET)) //#define MAGN_Z_SCALE (??? / (MAGN_Z_MAX - MAGN_Z_OFFSET)) float magnetom[3] = {0,0,0}; float accel[3] = {0,0,0}; float degGyro[3] = {0,0,0}; float prevDegGyro[3]= {0,0,0}; float gyroOffset[3] = {0,0,0}; float roll = 0, pitch = 0, yaw = 0; float totalAccel = 0; float normalizedGravity; float lowPassAccel[3] = {0.0000001f,0.0000001f,0.0000001f}; float prevYaw = 0; /********************************************************************************************************************************/ // Low Pass /********************************************************************************************************************************/ float lowPass(float input, float output, int select) { //Magnetometer Heading alpha if(select == 0) { if (output == 0.0000001f) return input; output = output + ALPHA_H * (input - output); return output; } //Accelerometer alpha else if(select == 1) { if (output == 0.0000001f) return input; output = output + ALPHA_A * (input - output); return output; } //GPS heading alpha else { if (output == 0.0000001f) return input; output = output + ALPHA_A * (input - output); return output; } } /********************************************************************************************************************************/ // getMagn /********************************************************************************************************************************/ void getMagn() { //LSM303DLHC/LSM303DLHC.cpp compass.ReadMagnOnly(&magnetom[0], &magnetom[1], &magnetom[2]); //Compensate magnetometer error //magnetom[0] -= MAGN_X_OFFSET; //magnetom[1] -= MAGN_Y_OFFSET; //magnetom[2] -= MAGN_Z_OFFSET; } /********************************************************************************************************************************/ // getAccel /********************************************************************************************************************************/ void getAccel() { //LSM303DLHC/LSM303DLHC.cpp compass.ReadAccOnly(&accel[0], &accel[1], &accel[2]); //Compensate accelerometer error //TODO: verify compensation calculations accel[0] = (accel[0] - ACCEL_X_OFFSET) * ACCEL_X_SCALE; accel[1] = (accel[1] - ACCEL_Y_OFFSET) * ACCEL_Y_SCALE; accel[2] = (accel[2] - ACCEL_Z_OFFSET) * ACCEL_Z_SCALE; for(int i = 0; i <= 3; i++) { lowPassAccel[i] = lowPass(accel[i], lowPassAccel[i], 1); } } /********************************************************************************************************************************/ // Integrate Yaw With Gyro Data /********************************************************************************************************************************/ //not used void gyroIntegral() { float yawDiff = yaw - prevYaw; float absYawDiff = sqrt(yawDiff * yawDiff); //vC[x] = vP[x] + ((aP[x] + ((inertialAccel[x] - aP[x]) / 2)) * deltaT); float gyroInt = prevYaw + ((prevDegGyro[2] + ((degGyro[2] - prevDegGyro[2]) / 2)) * MAGN_PERIOD); float absGyroInt = sqrt(gyroInt * gyroInt); prevDegGyro[2] = degGyro[2]; if(absYawDiff > absGyroInt) { yaw = prevYaw + gyroInt; } } /********************************************************************************************************************************/ // Compass Heading /********************************************************************************************************************************/ //see http://cache.freescale.com/files/sensors/doc/app_note/AN4248.pdf for more info on these formulas void Compass_Heading() { float mag_x; float mag_y; float cos_roll; float sin_roll; float cos_pitch; float sin_pitch; //saves a few processor cycles by calculating and storing values cos_roll = cos(roll); sin_roll = sin(roll); cos_pitch = cos(pitch); sin_pitch = sin(pitch); // Tilt compensated magnetic field X mag_x = magnetom[0] * cos_pitch + magnetom[1] * sin_roll * sin_pitch + magnetom[2] * cos_roll * sin_pitch; // Tilt compensated magnetic field Y mag_y = magnetom[1] * cos_roll - magnetom[2] * sin_roll; // Magnetic Heading yaw = atan2(-mag_x, mag_y); //make negative angles positive if(yaw < 0) yaw = yaw + TWO_PI; //yaw = yaw + M_PI; yaw = yaw * RADTODEGREE; } /********************************************************************************************************************************/ // getAttitude /********************************************************************************************************************************/ //see http://cache.freescale.com/files/sensors/doc/app_note/AN4248.pdf for more info on these formulas void getAttitude() { float temp1[3]; float temp2[3]; float xAxis[3] = {1.0f, 0.0f, 0.0f}; // GET PITCH // Using y-z-plane-component/x-component of gravity vector pitch = -atan2(accel[0], sqrt((accel[1] * accel[1]) + (accel[2] * accel[2]))); //printf("P = %f", pitch); // GET ROLL // Compensate pitch of gravity vector temp1[0] = (accel[1] * xAxis[2]) - (accel[2] * xAxis[1]); temp1[1] = (accel[2] * xAxis[0]) - (accel[0] * xAxis[2]); temp1[2] = (accel[0] * xAxis[1]) - (accel[1] * xAxis[0]); temp2[0] = (xAxis[1] * temp1[2]) - (xAxis[2] * temp1[1]); temp2[1] = (xAxis[2] * temp1[0]) - (xAxis[0] * temp1[2]); temp2[2] = (xAxis[0] * temp1[1]) - (xAxis[1] * temp1[0]); // Since we compensated for pitch, x-z-plane-component equals z-component: roll = atan2(temp2[1], temp2[2]); } /********************************************************************************************************************************/ // Filtered_Attitude() /********************************************************************************************************************************/ void Filtered_Attitude() { float temp1[3]; float temp2[3]; float xAxis[3] = {1.0f, 0.0f, 0.0f}; // GET PITCH // Using y-z-plane-component/x-component of gravity vector pitch = -atan2(lowPassAccel[0], sqrt((lowPassAccel[1] * lowPassAccel[1]) + (lowPassAccel[2] * lowPassAccel[2]))); //printf("P = %f", pitch); // GET ROLL // Compensate pitch of gravity vector temp1[0] = (lowPassAccel[1] * xAxis[2]) - (lowPassAccel[2] * xAxis[1]); temp1[1] = (lowPassAccel[2] * xAxis[0]) - (lowPassAccel[0] * xAxis[2]); temp1[2] = (lowPassAccel[0] * xAxis[1]) - (lowPassAccel[1] * xAxis[0]); temp2[0] = (xAxis[1] * temp1[2]) - (xAxis[2] * temp1[1]); temp2[1] = (xAxis[2] * temp1[0]) - (xAxis[0] * temp1[2]); temp2[2] = (xAxis[0] * temp1[1]) - (xAxis[1] * temp1[0]); // Since we compensated for pitch, x-z-plane-component equals z-component: roll = atan2(temp2[1], temp2[2]); } /********************************************************************************************************************************/ // updateAngles /********************************************************************************************************************************/ void updateAngles() { Filtered_Attitude(); Compass_Heading(); } /********************************************************************************************************************************/ // normalizeGravity /********************************************************************************************************************************/ //not used void normalizeGravity() { float accumulator = 0; int sampleCount = 0; //We'll take a certain number of samples and then average them to calculate the average while (sampleCount < AVG_COUNT) { getAccel(); //add current sample to previous samples accumulator += sqrt((accel[0] * accel[0]) + (accel[1] * accel[1]) + (accel[2] * accel[2])); sampleCount++; //Make sure the IMU has had enough time to take a new sample. wait(0.06); } //divide by number of samples to get average offset normalizedGravity = accumulator / AVG_COUNT; } /********************************************************************************************************************************/ // gyroAvg /********************************************************************************************************************************/ //not used void gyroAvg() { float accumulator[3] = {0,0,0}; int sampleCount = 0; //We'll take a certain number of samples and then average them to calculate the offset while (sampleCount < AVG_COUNT) { //get gyro data gyro.read(°Gyro[0], °Gyro[1], °Gyro[2]); //add current sample to previous samples accumulator[2] += degGyro[2]; sampleCount++; //Make sure the gyro has had enough time to take a new sample. wait(GYRO_SAMPLE_PERIOD); } //divide by number of samples to get average offset degGyro[2] = accumulator[2] / AVG_COUNT; } /********************************************************************************************************************************/ // gyroCal /********************************************************************************************************************************/ //not used void gyroCal() { float accumulator[3] = {0,0,0}; int sampleCount = 0; //We'll take a certain number of samples and then average them to calculate the offset while (sampleCount < CALIBRATION_COUNT) { float gyroData[3]; //Make sure the gyro has had enough time to take a new sample. wait(GYRO_SAMPLE_PERIOD); //get gyro data gyro.read(&gyroData[0],&gyroData[1],&gyroData[2]); for(int i = 0; i < 3; i++) { //add current sample to previous samples accumulator[i] += gyroData[i]; } sampleCount++; } for(int i = 0; i < 3; i++) { //divide by number of samples to get average offset gyroOffset[i] = accumulator[i] / CALIBRATION_COUNT; } } /********************************************************************************************************************************/ // Only Get Gyro Data /********************************************************************************************************************************/ //not used void gyroOnly() { gyro.read(°Gyro[0], °Gyro[1], °Gyro[2]); //compensate for gyro offset for(int i=0;i<=3;i++) { degGyro[i] -= gyroOffset[i]; } }