IMU 10dof MEMS from DR Robot adapted from HK10DOF Changed gyro to ITG3200

Fork of HK10DOF by Aloïs Wolff

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers IMU10DOF.cpp Source File

IMU10DOF.cpp

00001 /*
00002 FreeIMU.cpp - A libre and easy to use orientation sensing library for Arduino
00003 Copyright (C) 2011-2012 Fabio Varesano <fabio at varesano dot net>
00004 ported for HobbyKing's 10DOF stick on a mbed platform by Aloïs Wolff(wolffalois@gmail.com)
00005 
00006 Development of this code has been supported by the Department of Computer Science,
00007 Universita' degli Studi di Torino, Italy within the Piemonte Project
00008 http://www.piemonte.di.unito.it/
00009 
00010 
00011 This program is free software: you can redistribute it and/or modify
00012 it under the terms of the version 3 GNU General Public License as
00013 published by the Free Software Foundation.
00014 
00015 This program is distributed in the hope that it will be useful,
00016 but WITHOUT ANY WARRANTY; without even the implied warranty of
00017 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00018 GNU General Public License for more details.
00019 
00020 You should have received a copy of the GNU General Public License
00021 along with this program.  If not, see <http://www.gnu.org/licenses/>.
00022 
00023  Now ported to support DFRobot.com 10DOF MEMS IMU
00024  by Larry Littlefield svkatielee@yahoo.com
00025  
00026 */
00027 
00028 
00029 
00030 //#define DEBUG
00031 
00032 
00033 #include "IMU10DOF.h"
00034 #include <math.h> 
00035 #include "helper_3dmath.h"
00036  
00037 
00038 // #include "WireUtils.h"
00039 //#include "DebugUtils.h"
00040 //#include "vector_math.h"
00041 
00042 #ifndef M_PI
00043 #define     M_PI 3.1415926535897932384626433832795
00044 #endif
00045 
00046 IMU10DOF::IMU10DOF(PinName sda, PinName scl) : acc(sda,scl), magn(sda,scl),gyro(sda,scl), baro(sda,scl),pc(USBTX,USBRX)
00047 {
00048 
00049     pc.baud(115200);
00050     
00051     // initialize quaternion
00052     q0 = 1.0f;
00053     q1 = 0.0f;
00054     q2 = 0.0f;
00055     q3 = 0.0f;
00056     exInt = 0.0;
00057     eyInt = 0.0;
00058     ezInt = 0.0;
00059     twoKp = twoKpDef;
00060     twoKi = twoKiDef;
00061     integralFBx = 0.0f,  integralFBy = 0.0f, integralFBz = 0.0f;
00062 
00063 
00064 #ifndef CALIBRATION_H
00065     // initialize scale factors to neutral values
00066     acc_scale_x = 1;
00067     acc_scale_y = 1;
00068     acc_scale_z = 1;
00069     magn_scale_x = 1;
00070     magn_scale_y = 1;
00071     magn_scale_z = 1;
00072 #else
00073     // get values from global variables of same name defined in calibration.h
00074     acc_off_x = ::acc_off_x;
00075     acc_off_y = ::acc_off_y;
00076     acc_off_z = ::acc_off_z;
00077     acc_scale_x = ::acc_scale_x;
00078     acc_scale_y = ::acc_scale_y;
00079     acc_scale_z = ::acc_scale_z;
00080     magn_off_x = ::magn_off_x;
00081     magn_off_y = ::magn_off_y;
00082     magn_off_z = ::magn_off_z;
00083     magn_scale_x = ::magn_scale_x;
00084     magn_scale_y = ::magn_scale_y;
00085     magn_scale_z = ::magn_scale_z;
00086 #endif
00087 }
00088 
00089 
00090 
00091 
00092 /**
00093  * Initialize the FreeIMU I2C bus, sensors and performs gyro offsets calibration
00094 */
00095 
00096 void IMU10DOF::init(bool fastmode)
00097 {
00098 
00099 
00100          //Sensors Initialization
00101      pc.printf("Initializing sensors...\n\r");
00102      magn.init();
00103      baro.getCalibrationData();
00104      acc.setPowerControl(0x00);
00105      wait_ms(10);
00106      acc.setDataFormatControl(0x0B);
00107      wait_ms(10);
00108      acc.setPowerControl(MeasurementMode);
00109      wait_ms(10);
00110      gyro.init();
00111     pc.printf("Sensors OK!\n\r");
00112 
00113     update.start();
00114     int dt_us=0;
00115     pc.printf("ZeroGyro\n\r");
00116     // zero gyro
00117     //zeroGyro();
00118     gyro.zeroCalibrate(128,5);
00119     pc.printf("ZeroGyro OK, CalLoad...\n\r");
00120     // load calibration from eeprom
00121     calLoad();
00122 
00123 }
00124 
00125 void IMU10DOF::calLoad()
00126 {
00127 
00128     
00129     acc_off_x = 0;
00130     acc_off_y = 0;
00131     acc_off_z = 0;
00132     acc_scale_x = 1;
00133     acc_scale_y = 1;
00134     acc_scale_z = 1;
00135 
00136     magn_off_x = 0.21;
00137     magn_off_y = -0.175;
00138     magn_off_z = 0;
00139     magn_scale_x = 1;
00140     magn_scale_y = 1;
00141     magn_scale_z = 1;
00142 }
00143 
00144 void IMU10DOF::getRawValues(int16_t * raw_values)
00145 {
00146     int16_t raw3[3];
00147     int raw[3];
00148     
00149     pc.printf("GetRaw IN \n\r");
00150 
00151     acc.getOutput(&raw_values[0], &raw_values[1], &raw_values[2]);
00152     pc.printf("GotACC\n\r");
00153     
00154     gyro.read(raw);
00155     
00156     raw_values[3]=(int16_t)raw[0];
00157     raw_values[4]=(int16_t)raw[1];
00158     raw_values[5]=(int16_t)raw[2];
00159     pc.printf("GotGYRO\n\r");
00160     
00161 
00162     magn.getXYZ(raw3);
00163     raw_values[6]=raw3[0];
00164     raw_values[7]=raw3[1];
00165     raw_values[8]=raw3[2];
00166     pc.printf("GotMAG\n\r");
00167     
00168 
00169 
00170     baro.readSensor();
00171     raw_values[9]=(int16_t)baro.temp();
00172     raw_values[10]=(int16_t)baro.press();
00173     pc.printf("GotBARO\n\r");
00174     
00175 
00176 }
00177 
00178 
00179 /**
00180  * Populates values with calibrated readings from the sensors
00181 */
00182 void IMU10DOF::getValues(float * values)
00183 {
00184 
00185     int16_t accval[3];
00186     float     gyrval[3];
00187     acc.getOutput(accval);
00188     values[0] = (float) accval[0];
00189     values[1] = (float) accval[1];
00190     values[2] = (float) accval[2];
00191     
00192     gyro.readFin(gyrval);
00193     values[3] =  gyrval[0];
00194     values[4] =  gyrval[1];
00195     values[5] =  gyrval[2];
00196     
00197     magn.getXYZ(accval);
00198     values[6]=(float)accval[0];
00199     values[7]=(float)accval[1];
00200     values[8]=(float)accval[2];
00201 
00202 
00203 #warning Accelerometer calibration active: have you calibrated your device?
00204     // remove offsets and scale accelerometer (calibration)
00205     values[0] = (values[0] - acc_off_x) / acc_scale_x;
00206     values[1] = (values[1] - acc_off_y) / acc_scale_y;
00207     values[2] = (values[2] - acc_off_z) / acc_scale_z;
00208 
00209     // calibration
00210 #warning Magnetometer calibration active: have you calibrated your device?
00211     values[6] = (values[6] - magn_off_x) / magn_scale_x;
00212     values[7] = (values[7] - magn_off_y) / magn_scale_y;
00213     values[8] = (values[8] - magn_off_z) / magn_scale_z;
00214 
00215     
00216     
00217 }
00218 
00219 
00220 /**
00221  * Computes gyro offsets
00222 */
00223 void IMU10DOF::zeroGyro()
00224 {
00225     const int totSamples = 3;
00226     int16_t raw[11];
00227     float tmpOffsets[] = {0,0,0};
00228 
00229     for (int i = 0; i < totSamples; i++) {
00230         getRawValues(raw);
00231         tmpOffsets[0] += raw[3];
00232         tmpOffsets[1] += raw[4];
00233         tmpOffsets[2] += raw[5];
00234     }
00235 
00236     gyro_off_x = tmpOffsets[0] / totSamples;
00237     gyro_off_y = tmpOffsets[1] / totSamples;
00238     gyro_off_z = tmpOffsets[2] / totSamples;
00239 }
00240 
00241 
00242 /**
00243  * Quaternion implementation of the 'DCM filter' [Mayhony et al].  Incorporates the magnetic distortion
00244  * compensation algorithms from Sebastian Madgwick's filter which eliminates the need for a reference
00245  * direction of flux (bx bz) to be predefined and limits the effect of magnetic distortions to yaw
00246  * axis only.
00247  *
00248  * @see: http://www.x-io.co.uk/node/8#open_source_ahrs_and_imu_algorithms
00249 */
00250 //#if IS_9DOM()
00251 void  IMU10DOF::AHRSupdate(float gx, float gy, float gz, float ax, float ay, float az, float mx, float my, float mz)
00252 {
00253 
00254     float recipNorm;
00255     float q0q0, q0q1, q0q2, q0q3, q1q1, q1q2, q1q3, q2q2, q2q3, q3q3;
00256     float halfex = 0.0f, halfey = 0.0f, halfez = 0.0f;
00257     float qa, qb, qc;
00258 
00259     // Auxiliary variables to avoid repeated arithmetic
00260     q0q0 = q0 * q0;
00261     q0q1 = q0 * q1;
00262     q0q2 = q0 * q2;
00263     q0q3 = q0 * q3;
00264     q1q1 = q1 * q1;
00265     q1q2 = q1 * q2;
00266     q1q3 = q1 * q3;
00267     q2q2 = q2 * q2;
00268     q2q3 = q2 * q3;
00269     q3q3 = q3 * q3;
00270 
00271     // Use magnetometer measurement only when valid (avoids NaN in magnetometer normalisation)
00272     if((mx != 0.0f) && (my != 0.0f) && (mz != 0.0f)) {
00273         float hx, hy, bx, bz;
00274         float halfwx, halfwy, halfwz;
00275 
00276         // Normalise magnetometer measurement
00277         recipNorm = invSqrt(mx * mx + my * my + mz * mz);
00278         mx *= recipNorm;
00279         my *= recipNorm;
00280         mz *= recipNorm;
00281 
00282         // Reference direction of Earth's magnetic field
00283         hx = 2.0f * (mx * (0.5f - q2q2 - q3q3) + my * (q1q2 - q0q3) + mz * (q1q3 + q0q2));
00284         hy = 2.0f * (mx * (q1q2 + q0q3) + my * (0.5f - q1q1 - q3q3) + mz * (q2q3 - q0q1));
00285         bx = sqrt(hx * hx + hy * hy);
00286         bz = 2.0f * (mx * (q1q3 - q0q2) + my * (q2q3 + q0q1) + mz * (0.5f - q1q1 - q2q2));
00287 
00288         // Estimated direction of magnetic field
00289         halfwx = bx * (0.5f - q2q2 - q3q3) + bz * (q1q3 - q0q2);
00290         halfwy = bx * (q1q2 - q0q3) + bz * (q0q1 + q2q3);
00291         halfwz = bx * (q0q2 + q1q3) + bz * (0.5f - q1q1 - q2q2);
00292 
00293         // Error is sum of cross product between estimated direction and measured direction of field vectors
00294         halfex = (my * halfwz - mz * halfwy);
00295         halfey = (mz * halfwx - mx * halfwz);
00296         halfez = (mx * halfwy - my * halfwx);
00297     }
00298 
00299 
00300     // Compute feedback only if accelerometer measurement valid (avoids NaN in accelerometer normalisation)
00301     if((ax != 0.0f) && (ay != 0.0f) && (az != 0.0f)) {
00302         float halfvx, halfvy, halfvz;
00303 
00304         // Normalise accelerometer measurement
00305         recipNorm = invSqrt(ax * ax + ay * ay + az * az);
00306         ax *= recipNorm;
00307         ay *= recipNorm;
00308         az *= recipNorm;
00309 
00310         // Estimated direction of gravity
00311         halfvx = q1q3 - q0q2;
00312         halfvy = q0q1 + q2q3;
00313         halfvz = q0q0 - 0.5f + q3q3;
00314 
00315         // Error is sum of cross product between estimated direction and measured direction of field vectors
00316         halfex += (ay * halfvz - az * halfvy);
00317         halfey += (az * halfvx - ax * halfvz);
00318         halfez += (ax * halfvy - ay * halfvx);
00319     }
00320 
00321     // Apply feedback only when valid data has been gathered from the accelerometer or magnetometer
00322     if(halfex != 0.0f && halfey != 0.0f && halfez != 0.0f) {
00323         // Compute and apply integral feedback if enabled
00324         if(twoKi > 0.0f) {
00325             integralFBx += twoKi * halfex * (1.0f / sampleFreq);  // integral error scaled by Ki
00326             integralFBy += twoKi * halfey * (1.0f / sampleFreq);
00327             integralFBz += twoKi * halfez * (1.0f / sampleFreq);
00328             gx += integralFBx;  // apply integral feedback
00329             gy += integralFBy;
00330             gz += integralFBz;
00331         } else {
00332             integralFBx = 0.0f; // prevent integral windup
00333             integralFBy = 0.0f;
00334             integralFBz = 0.0f;
00335         }
00336 
00337         // Apply proportional feedback
00338         gx += twoKp * halfex;
00339         gy += twoKp * halfey;
00340         gz += twoKp * halfez;
00341     }
00342 
00343     // Integrate rate of change of quaternion
00344     gx *= (0.5f * (1.0f / sampleFreq));   // pre-multiply common factors
00345     gy *= (0.5f * (1.0f / sampleFreq));
00346     gz *= (0.5f * (1.0f / sampleFreq));
00347     qa = q0;
00348     qb = q1;
00349     qc = q2;
00350     q0 += (-qb * gx - qc * gy - q3 * gz);
00351     q1 += (qa * gx + qc * gz - q3 * gy);
00352     q2 += (qa * gy - qb * gz + q3 * gx);
00353     q3 += (qa * gz + qb * gy - qc * gx);
00354 
00355     // Normalise quaternion
00356     recipNorm = invSqrt(q0 * q0 + q1 * q1 + q2 * q2 + q3 * q3);
00357     q0 *= recipNorm;
00358     q1 *= recipNorm;
00359     q2 *= recipNorm;
00360     q3 *= recipNorm;
00361 }
00362 
00363 
00364 /**
00365  * Populates array q with a quaternion representing the IMU orientation with respect to the Earth
00366  *
00367  * @param q the quaternion to populate
00368 */
00369 void IMU10DOF::getQ(float * q)
00370 {
00371     float val[9];
00372     getValues(val);
00373     /*
00374         DEBUG_PRINT(val[3] * M_PI/180);
00375         DEBUG_PRINT(val[4] * M_PI/180);
00376         DEBUG_PRINT(val[5] * M_PI/180);
00377         DEBUG_PRINT(val[0]);
00378         DEBUG_PRINT(val[1]);
00379         DEBUG_PRINT(val[2]);
00380         DEBUG_PRINT(val[6]);
00381         DEBUG_PRINT(val[7]);
00382         DEBUG_PRINT(val[8]);
00383     */
00384 
00385     dt_us=update.read_us();
00386     sampleFreq = 1.0 / ((dt_us) / 1000000.0);
00387     update.reset();
00388 
00389 
00390 
00391     // gyro values are expressed in deg/sec, the * M_PI/180 will convert it to radians/sec
00392     AHRSupdate(val[3] * M_PI/180, val[4] * M_PI/180, val[5] * M_PI/180, val[0], val[1], val[2], val[6], val[7], val[8]);
00393 
00394 
00395 
00396     q[0] = q0;
00397     q[1] = q1;
00398     q[2] = q2;
00399     q[3] = q3;
00400 }
00401 
00402 
00403 
00404 const float def_sea_press = 1013.25;
00405 
00406 /**
00407  * Returns an altitude estimate from baromether readings only using sea_press as current sea level pressure
00408 */
00409 float IMU10DOF::getBaroAlt(float sea_press)
00410 {
00411     //float temp,press,res;
00412     baro.readSensor();
00413     //temp=(float)baro.temp();
00414     //press=(float)baro.press();
00415     
00416     
00417 
00418     return baro.altitud();
00419     //return(temp,press);
00420 }
00421 
00422 /**
00423  * Returns an altitude estimate from baromether readings only using a default sea level pressure
00424 */
00425 float IMU10DOF::getBaroAlt()
00426 {
00427     return getBaroAlt(def_sea_press);
00428 }
00429 
00430 
00431 /**
00432  * Compensates the accelerometer readings in the 3D vector acc expressed in the sensor frame for gravity
00433  * @param acc the accelerometer readings to compensate for gravity
00434  * @param q the quaternion orientation of the sensor board with respect to the world
00435 */
00436 void IMU10DOF::gravityCompensateAcc(float * acc, float * q)
00437 {
00438     float g[3];
00439 
00440     // get expected direction of gravity in the sensor frame
00441     g[0] = 2 * (q[1] * q[3] - q[0] * q[2]);
00442     g[1] = 2 * (q[0] * q[1] + q[2] * q[3]);
00443     g[2] = q[0] * q[0] - q[1] * q[1] - q[2] * q[2] + q[3] * q[3];
00444 
00445     // compensate accelerometer readings with the expected direction of gravity
00446     acc[0] = acc[0] - g[0];
00447     acc[1] = acc[1] - g[1];
00448     acc[2] = acc[2] - g[2];
00449 }
00450 
00451 
00452 
00453 /**
00454  * Returns the Euler angles in radians defined in the Aerospace sequence.
00455  * See Sebastian O.H. Madwick report "An efficient orientation filter for
00456  * inertial and intertial/magnetic sensor arrays" Chapter 2 Quaternion representation
00457  *
00458  * @param angles three floats array which will be populated by the Euler angles in radians
00459 */
00460 void IMU10DOF::getEulerRad(float * angles)
00461 {
00462     float q[4]; // quaternion
00463     getQ(q);
00464     angles[0] = atan2(2 * q[1] * q[2] - 2 * q[0] * q[3], 2 * q[0]*q[0] + 2 * q[1] * q[1] - 1); // psi
00465     angles[1] = -asin(2 * q[1] * q[3] + 2 * q[0] * q[2]); // theta
00466     angles[2] = atan2(2 * q[2] * q[3] - 2 * q[0] * q[1], 2 * q[0] * q[0] + 2 * q[3] * q[3] - 1); // phi
00467 }
00468 
00469 
00470 /**
00471  * Returns the Euler angles in degrees defined with the Aerospace sequence.
00472  * See Sebastian O.H. Madwick report "An efficient orientation filter for
00473  * inertial and intertial/magnetic sensor arrays" Chapter 2 Quaternion representation
00474  *
00475  * @param angles three floats array which will be populated by the Euler angles in degrees
00476 */
00477 void IMU10DOF::getEuler(float * angles)
00478 {
00479     getEulerRad(angles);
00480     arr3_rad_to_deg(angles);
00481 }
00482 
00483 
00484 /**
00485  * Returns the yaw pitch and roll angles, respectively defined as the angles in radians between
00486  * the Earth North and the IMU X axis (yaw), the Earth ground plane and the IMU X axis (pitch)
00487  * and the Earth ground plane and the IMU Y axis.
00488  *
00489  * @note This is not an Euler representation: the rotations aren't consecutive rotations but only
00490  * angles from Earth and the IMU. For Euler representation Yaw, Pitch and Roll see IMU10DOF::getEuler
00491  *
00492  * @param ypr three floats array which will be populated by Yaw, Pitch and Roll angles in radians
00493 */
00494 void IMU10DOF::getYawPitchRollRad(float * ypr)
00495 {
00496     float q[4]; // quaternion
00497     float gx, gy, gz; // estimated gravity direction
00498     getQ(q);
00499 
00500     gx = 2 * (q[1]*q[3] - q[0]*q[2]);
00501     gy = 2 * (q[0]*q[1] + q[2]*q[3]);
00502     gz = q[0]*q[0] - q[1]*q[1] - q[2]*q[2] + q[3]*q[3];
00503 
00504     ypr[0] = atan2(2 * q[1] * q[2] - 2 * q[0] * q[3], 2 * q[0]*q[0] + 2 * q[1] * q[1] - 1);
00505     ypr[1] = atan(gx / sqrt(gy*gy + gz*gz));
00506     ypr[2] = atan(gy / sqrt(gx*gx + gz*gz));
00507 }
00508 
00509 
00510 /**
00511  * Returns the yaw pitch and roll angles, respectively defined as the angles in degrees between
00512  * the Earth North and the IMU X axis (yaw), the Earth ground plane and the IMU X axis (pitch)
00513  * and the Earth ground plane and the IMU Y axis.
00514  *
00515  * @note This is not an Euler representation: the rotations aren't consecutive rotations but only
00516  * angles from Earth and the IMU. For Euler representation Yaw, Pitch and Roll see IMU10DOF::getEuler
00517  *
00518  * @param ypr three floats array which will be populated by Yaw, Pitch and Roll angles in degrees
00519 */
00520 void IMU10DOF::getYawPitchRoll(float * ypr)
00521 {
00522     getYawPitchRollRad(ypr);
00523     arr3_rad_to_deg(ypr);
00524 }
00525 
00526 
00527 /**
00528  * Converts a 3 elements array arr of angles expressed in radians into degrees
00529 */
00530 void arr3_rad_to_deg(float * arr)
00531 {
00532     arr[0] *= 180/M_PI;
00533     arr[1] *= 180/M_PI;
00534     arr[2] *= 180/M_PI;
00535 }
00536 
00537 
00538 /**
00539  * Fast inverse square root implementation
00540  * @see http://en.wikipedia.org/wiki/Fast_inverse_square_root
00541 */
00542 float invSqrt(float number)
00543 {
00544     volatile long i;
00545     volatile float x, y;
00546     volatile const float f = 1.5F;
00547 
00548     x = number * 0.5F;
00549     y = number;
00550     i = * ( long * ) &y;
00551     i = 0x5f375a86 - ( i >> 1 );
00552     y = * ( float * ) &i;
00553     y = y * ( f - ( x * y * y ) );
00554     return y;
00555 }
00556 
00557 
00558