Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Dependencies: LSM9DS0 USBDevice mbed
Quaternion.cpp
00001 00002 #include "Quaternion.h" 00003 #include "mbed.h" 00004 #define M_PI 3.14159265 00005 00006 Timer t; 00007 /** 00008 * Default constructor. 00009 **/ 00010 Quaternion::Quaternion() { 00011 q0 = 1.0f; 00012 q1 = 0.0f; 00013 q2 = 0.0f; 00014 q3 = 0.0f; 00015 twoKp = twoKpDef; 00016 twoKi = twoKiDef; 00017 sampleFreq = 0.0f; 00018 lastUpdate = 0L; 00019 now = 0L; 00020 integralFBx = 0.0f; 00021 integralFBy = 0.0f; 00022 integralFBz = 0.0f; 00023 t.start(); 00024 } 00025 00026 /** 00027 * Updates the sample frequency based on the elapsed time. 00028 **/ 00029 void Quaternion::updateSampleFrequency() { 00030 now = t.read(); 00031 sampleFreq = 1.0 / ((now - lastUpdate)); 00032 lastUpdate = now; 00033 } 00034 /** 00035 * Returns the quaternion representation of the orientation. 00036 **/ 00037 void Quaternion::getQ(float * q) { 00038 q[0] = q0; 00039 q[1] = q1; 00040 q[2] = q2; 00041 q[3] = q3; 00042 } 00043 00044 /** 00045 * Gets the linear acceleration by estimating gravity and then subtracting it. All accelerations 00046 * need to be scaled to 1 g. So if at 1 g your accelerometer reads 245, divide it by 245 before passing it 00047 * to this function. 00048 * @param *linearAccel, pointer to float array for linear accelerations, 00049 * @param ax, the scaled acceleration in the x direction. 00050 * @param ay, the scaled acceleration in the y direction. 00051 * @param az, the scaled acceleration in the z direction. 00052 **/ 00053 void Quaternion::getLinearAcceleration(float * linearAccel, float ax, float ay, float az) { 00054 00055 float gravity[3]; 00056 getGravity(gravity); 00057 00058 00059 00060 float xwog = ax - gravity[0]; 00061 float ywog = ay - gravity[1]; 00062 float zwog = az - gravity[2]; 00063 00064 linearAccel[0] = xwog * 9.8; 00065 linearAccel[1] = ywog * 9.8; 00066 linearAccel[2] = zwog * 9.8; 00067 } 00068 00069 /** 00070 * Returns the euler angles psi, theta and phi. 00071 **/ 00072 void Quaternion::getEulerAngles(float * angles) { 00073 angles[0] = atan2(2 * q1 * q2- 2 * q0 * q3, 2 * q0*q0 + 2 * q1 * q1 - 1) * 180/M_PI; // psi 00074 angles[1] = -asin(2 * q1 * q3 + 2 * q0 * q2) * 180/M_PI; // theta 00075 angles[2] = atan2(2 * q2 * q3 - 2 * q0 * q1, 2 * q0 * q0 + 2 * q3 * q3 - 1) * 180/M_PI; // phi 00076 } 00077 00078 /** 00079 * Returns the yaw pitch and roll of the device. 00080 **/ 00081 void Quaternion::getYawPitchRoll(double * ypr) { 00082 00083 ypr[0] = atan2(double(2*q1*q2 + 2*q0*q3), double(q0*q0 + q1*q1 - q2*q2 - q3*q3)) * 180/M_PI; //yaw 00084 ypr[1] = -asin(2*q0*q2 - 2*q1*q3) * 180/M_PI; // pitch 00085 ypr[2] = -atan2(2*q2*q3 + 2*q0*q1, -q0*q0 + q1*q1 + q2*q2 - q3*q3) * 180/M_PI; //roll 00086 00087 } 00088 /** 00089 * Gets an estimation of gravity based on quaternion orientation representation. 00090 **/ 00091 void Quaternion::getGravity(float * gravity) { 00092 float q[4]; 00093 getQ(q); 00094 gravity[0] = 2 * (q[1] * q[3] - q[0] *q[2]); 00095 gravity[1] = 2 * (q[0] * q[1] + q[2] * q[3]); 00096 gravity[2] = q[0] * q[0] - q[1] * q[1] - q[2] * q[2] + q[3] * q[3]; 00097 } 00098 00099 /** 00100 * Calculates the quaternion representation based on a 6DOF sensor. 00101 * @param gx, the rotation about the x axis in rad/sec 00102 * @param gy, the rotation about the y axis in rad/sec 00103 * @param gz, the rotation about the z axis in rad/sec 00104 * @param ax, the raw acceleration in the x direction. 00105 * @param ay, the raw acceleration in the y direction. 00106 * @param az, the raw acceleration in the z direction. 00107 **/ 00108 void Quaternion::update6DOF(float gx, float gy, float gz, float ax, float ay, float az) { 00109 updateSampleFrequency(); 00110 float recipNorm; 00111 float halfvx, halfvy, halfvz; 00112 float halfex, halfey, halfez; 00113 float qa, qb, qc; 00114 00115 // Compute feedback only if accelerometer measurement valid (avoids NaN in accelerometer normalisation) 00116 if(!((ax == 0.0f) && (ay == 0.0f) && (az == 0.0f))) { 00117 00118 // Normalise accelerometer measurement 00119 recipNorm = invSqrt(ax * ax + ay * ay + az * az); 00120 ax *= recipNorm; 00121 ay *= recipNorm; 00122 az *= recipNorm; 00123 00124 // Estimated direction of gravity and vector perpendicular to magnetic flux 00125 halfvx = q1 * q3 - q0 * q2; 00126 halfvy = q0 * q1 + q2 * q3; 00127 halfvz = q0 * q0 - 0.5f + q3 * q3; 00128 00129 // Error is sum of cross product between estimated and measured direction of gravity 00130 halfex = (ay * halfvz - az * halfvy); 00131 halfey = (az * halfvx - ax * halfvz); 00132 halfez = (ax * halfvy - ay * halfvx); 00133 00134 // Compute and apply integral feedback if enabled 00135 if(twoKi > 0.0f) { 00136 integralFBx += twoKi * halfex * (1.0f / sampleFreq); // integral error scaled by Ki 00137 integralFBy += twoKi * halfey * (1.0f / sampleFreq); 00138 integralFBz += twoKi * halfez * (1.0f / sampleFreq); 00139 gx += integralFBx; // apply integral feedback 00140 gy += integralFBy; 00141 gz += integralFBz; 00142 } 00143 else { 00144 integralFBx = 0.0f; // prevent integral windup 00145 integralFBy = 0.0f; 00146 integralFBz = 0.0f; 00147 } 00148 00149 // Apply proportional feedback 00150 gx += twoKp * halfex; 00151 gy += twoKp * halfey; 00152 gz += twoKp * halfez; 00153 } 00154 00155 // Integrate rate of change of quaternion 00156 gx *= (0.5f * (1.0f / sampleFreq)); // pre-multiply common factors 00157 gy *= (0.5f * (1.0f / sampleFreq)); 00158 gz *= (0.5f * (1.0f / sampleFreq)); 00159 qa = q0; 00160 qb = q1; 00161 qc = q2; 00162 q0 += (-qb * gx - qc * gy - q3 * gz); 00163 q1 += (qa * gx + qc * gz - q3 * gy); 00164 q2 += (qa * gy - qb * gz + q3 * gx); 00165 q3 += (qa * gz + qb * gy - qc * gx); 00166 00167 // Normalise quaternion 00168 recipNorm = invSqrt(q0 * q0 + q1 * q1 + q2 * q2 + q3 * q3); 00169 q0 *= recipNorm; 00170 q1 *= recipNorm; 00171 q2 *= recipNorm; 00172 q3 *= recipNorm; 00173 00174 } 00175 00176 /** 00177 * Calculates the quaternion representation based on a 9DOF sensor. 00178 * @param gx, the rotation about the x axis in rad/sec 00179 * @param gy, the rotation about the y axis in rad/sec 00180 * @param gz, the rotation about the z axis in rad/sec 00181 * @param ax, the raw acceleration in the x direction. 00182 * @param ay, the raw acceleration in the y direction. 00183 * @param az, the raw acceleration in the z direction. 00184 * @param mx, the raw magnometer heading in the x direction. 00185 * @param my, the raw magnometer heading in the y direction. 00186 * @param mz, the raw magnometer heading in the z direction. 00187 **/ 00188 void Quaternion::update9DOF(float gx, float gy, float gz, float ax, float ay, float az, float mx, float my, float mz) { 00189 //update the frequency first. 00190 updateSampleFrequency(); 00191 float recipNorm; 00192 float q0q0, q0q1, q0q2, q0q3, q1q1, q1q2, q1q3, q2q2, q2q3, q3q3; 00193 float hx, hy, bx, bz; 00194 float halfvx, halfvy, halfvz, halfwx, halfwy, halfwz; 00195 float halfex, halfey, halfez; 00196 float qa, qb, qc; 00197 00198 // Use IMU algorithm if magnetometer measurement invalid (avoids NaN in magnetometer normalisation) 00199 if((mx == 0.0f) && (my == 0.0f) && (mz == 0.0f)) { 00200 update6DOF(gx, gy, gz, ax, ay, az); 00201 return; 00202 } 00203 00204 // Compute feedback only if accelerometer measurement valid (avoids NaN in accelerometer normalisation) 00205 if(!((ax == 0.0f) && (ay == 0.0f) && (az == 0.0f))) { 00206 00207 // Normalise accelerometer measurement 00208 recipNorm = invSqrt(ax * ax + ay * ay + az * az); 00209 ax *= recipNorm; 00210 ay *= recipNorm; 00211 az *= recipNorm; 00212 00213 // Normalise magnetometer measurement 00214 recipNorm = invSqrt(mx * mx + my * my + mz * mz); 00215 mx *= recipNorm; 00216 my *= recipNorm; 00217 mz *= recipNorm; 00218 00219 // Auxiliary variables to avoid repeated arithmetic 00220 q0q0 = q0 * q0; 00221 q0q1 = q0 * q1; 00222 q0q2 = q0 * q2; 00223 q0q3 = q0 * q3; 00224 q1q1 = q1 * q1; 00225 q1q2 = q1 * q2; 00226 q1q3 = q1 * q3; 00227 q2q2 = q2 * q2; 00228 q2q3 = q2 * q3; 00229 q3q3 = q3 * q3; 00230 00231 // Reference direction of Earth's magnetic field 00232 hx = 2.0f * (mx * (0.5f - q2q2 - q3q3) + my * (q1q2 - q0q3) + mz * (q1q3 + q0q2)); 00233 hy = 2.0f * (mx * (q1q2 + q0q3) + my * (0.5f - q1q1 - q3q3) + mz * (q2q3 - q0q1)); 00234 bx = sqrt(hx * hx + hy * hy); 00235 bz = 2.0f * (mx * (q1q3 - q0q2) + my * (q2q3 + q0q1) + mz * (0.5f - q1q1 - q2q2)); 00236 00237 // Estimated direction of gravity and magnetic field 00238 halfvx = q1q3 - q0q2; 00239 halfvy = q0q1 + q2q3; 00240 halfvz = q0q0 - 0.5f + q3q3; 00241 halfwx = bx * (0.5f - q2q2 - q3q3) + bz * (q1q3 - q0q2); 00242 halfwy = bx * (q1q2 - q0q3) + bz * (q0q1 + q2q3); 00243 halfwz = bx * (q0q2 + q1q3) + bz * (0.5f - q1q1 - q2q2); 00244 00245 // Error is sum of cross product between estimated direction and measured direction of field vectors 00246 halfex = (ay * halfvz - az * halfvy) + (my * halfwz - mz * halfwy); 00247 halfey = (az * halfvx - ax * halfvz) + (mz * halfwx - mx * halfwz); 00248 halfez = (ax * halfvy - ay * halfvx) + (mx * halfwy - my * halfwx); 00249 00250 // Compute and apply integral feedback if enabled 00251 if(twoKi > 0.0f) { 00252 integralFBx += twoKi * halfex * (1.0f / sampleFreq); // integral error scaled by Ki 00253 integralFBy += twoKi * halfey * (1.0f / sampleFreq); 00254 integralFBz += twoKi * halfez * (1.0f / sampleFreq); 00255 gx += integralFBx; // apply integral feedback 00256 gy += integralFBy; 00257 gz += integralFBz; 00258 } 00259 else { 00260 integralFBx = 0.0f; // prevent integral windup 00261 integralFBy = 0.0f; 00262 integralFBz = 0.0f; 00263 } 00264 00265 // Apply proportional feedback 00266 gx += twoKp * halfex; 00267 gy += twoKp * halfey; 00268 gz += twoKp * halfez; 00269 } 00270 00271 // Integrate rate of change of quaternion 00272 gx *= (0.5f * (1.0f / sampleFreq)); // pre-multiply common factors 00273 gy *= (0.5f * (1.0f / sampleFreq)); 00274 gz *= (0.5f * (1.0f / sampleFreq)); 00275 qa = q0; 00276 qb = q1; 00277 qc = q2; 00278 q0 += (-qb * gx - qc * gy - q3 * gz); 00279 q1 += (qa * gx + qc * gz - q3 * gy); 00280 q2 += (qa * gy - qb * gz + q3 * gx); 00281 q3 += (qa * gz + qb * gy - qc * gx); 00282 00283 // Normalise quaternion 00284 recipNorm = invSqrt(q0 * q0 + q1 * q1 + q2 * q2 + q3 * q3); 00285 q0 *= recipNorm; 00286 q1 *= recipNorm; 00287 q2 *= recipNorm; 00288 q3 *= recipNorm; 00289 } 00290 00291 /** 00292 * Super fast inverted square root. 00293 **/ 00294 float Quaternion::invSqrt(float x) { 00295 float halfx = 0.5f * x; 00296 float y = x; 00297 long i = *(long*)&y; 00298 i = 0x5f3759df - (i>>1); 00299 y = *(float*)&i; 00300 y = y * (1.5f - (halfx * y * y)); 00301 return y; 00302 }
Generated on Sat Jul 16 2022 19:55:07 by
1.7.2