4180 final project
Dependencies: LSM9DS0 USBDevice mbed
Quaternion/Quaternion.cpp@0:ebbc3cd3a61e, 2015-12-05 (annotated)
- Committer:
- jlee887
- Date:
- Sat Dec 05 18:39:33 2015 +0000
- Revision:
- 0:ebbc3cd3a61e
d
Who changed what in which revision?
| User | Revision | Line number | New contents of line |
|---|---|---|---|
| jlee887 | 0:ebbc3cd3a61e | 1 | |
| jlee887 | 0:ebbc3cd3a61e | 2 | #include "Quaternion.h" |
| jlee887 | 0:ebbc3cd3a61e | 3 | #include "mbed.h" |
| jlee887 | 0:ebbc3cd3a61e | 4 | #define M_PI 3.14159265 |
| jlee887 | 0:ebbc3cd3a61e | 5 | |
| jlee887 | 0:ebbc3cd3a61e | 6 | Timer t; |
| jlee887 | 0:ebbc3cd3a61e | 7 | /** |
| jlee887 | 0:ebbc3cd3a61e | 8 | * Default constructor. |
| jlee887 | 0:ebbc3cd3a61e | 9 | **/ |
| jlee887 | 0:ebbc3cd3a61e | 10 | Quaternion::Quaternion() { |
| jlee887 | 0:ebbc3cd3a61e | 11 | q0 = 1.0f; |
| jlee887 | 0:ebbc3cd3a61e | 12 | q1 = 0.0f; |
| jlee887 | 0:ebbc3cd3a61e | 13 | q2 = 0.0f; |
| jlee887 | 0:ebbc3cd3a61e | 14 | q3 = 0.0f; |
| jlee887 | 0:ebbc3cd3a61e | 15 | twoKp = twoKpDef; |
| jlee887 | 0:ebbc3cd3a61e | 16 | twoKi = twoKiDef; |
| jlee887 | 0:ebbc3cd3a61e | 17 | sampleFreq = 0.0f; |
| jlee887 | 0:ebbc3cd3a61e | 18 | lastUpdate = 0L; |
| jlee887 | 0:ebbc3cd3a61e | 19 | now = 0L; |
| jlee887 | 0:ebbc3cd3a61e | 20 | integralFBx = 0.0f; |
| jlee887 | 0:ebbc3cd3a61e | 21 | integralFBy = 0.0f; |
| jlee887 | 0:ebbc3cd3a61e | 22 | integralFBz = 0.0f; |
| jlee887 | 0:ebbc3cd3a61e | 23 | t.start(); |
| jlee887 | 0:ebbc3cd3a61e | 24 | } |
| jlee887 | 0:ebbc3cd3a61e | 25 | |
| jlee887 | 0:ebbc3cd3a61e | 26 | /** |
| jlee887 | 0:ebbc3cd3a61e | 27 | * Updates the sample frequency based on the elapsed time. |
| jlee887 | 0:ebbc3cd3a61e | 28 | **/ |
| jlee887 | 0:ebbc3cd3a61e | 29 | void Quaternion::updateSampleFrequency() { |
| jlee887 | 0:ebbc3cd3a61e | 30 | now = t.read(); |
| jlee887 | 0:ebbc3cd3a61e | 31 | sampleFreq = 1.0 / ((now - lastUpdate)); |
| jlee887 | 0:ebbc3cd3a61e | 32 | lastUpdate = now; |
| jlee887 | 0:ebbc3cd3a61e | 33 | } |
| jlee887 | 0:ebbc3cd3a61e | 34 | /** |
| jlee887 | 0:ebbc3cd3a61e | 35 | * Returns the quaternion representation of the orientation. |
| jlee887 | 0:ebbc3cd3a61e | 36 | **/ |
| jlee887 | 0:ebbc3cd3a61e | 37 | void Quaternion::getQ(float * q) { |
| jlee887 | 0:ebbc3cd3a61e | 38 | q[0] = q0; |
| jlee887 | 0:ebbc3cd3a61e | 39 | q[1] = q1; |
| jlee887 | 0:ebbc3cd3a61e | 40 | q[2] = q2; |
| jlee887 | 0:ebbc3cd3a61e | 41 | q[3] = q3; |
| jlee887 | 0:ebbc3cd3a61e | 42 | } |
| jlee887 | 0:ebbc3cd3a61e | 43 | |
| jlee887 | 0:ebbc3cd3a61e | 44 | /** |
| jlee887 | 0:ebbc3cd3a61e | 45 | * Gets the linear acceleration by estimating gravity and then subtracting it. All accelerations |
| jlee887 | 0:ebbc3cd3a61e | 46 | * need to be scaled to 1 g. So if at 1 g your accelerometer reads 245, divide it by 245 before passing it |
| jlee887 | 0:ebbc3cd3a61e | 47 | * to this function. |
| jlee887 | 0:ebbc3cd3a61e | 48 | * @param *linearAccel, pointer to float array for linear accelerations, |
| jlee887 | 0:ebbc3cd3a61e | 49 | * @param ax, the scaled acceleration in the x direction. |
| jlee887 | 0:ebbc3cd3a61e | 50 | * @param ay, the scaled acceleration in the y direction. |
| jlee887 | 0:ebbc3cd3a61e | 51 | * @param az, the scaled acceleration in the z direction. |
| jlee887 | 0:ebbc3cd3a61e | 52 | **/ |
| jlee887 | 0:ebbc3cd3a61e | 53 | void Quaternion::getLinearAcceleration(float * linearAccel, float ax, float ay, float az) { |
| jlee887 | 0:ebbc3cd3a61e | 54 | |
| jlee887 | 0:ebbc3cd3a61e | 55 | float gravity[3]; |
| jlee887 | 0:ebbc3cd3a61e | 56 | getGravity(gravity); |
| jlee887 | 0:ebbc3cd3a61e | 57 | |
| jlee887 | 0:ebbc3cd3a61e | 58 | |
| jlee887 | 0:ebbc3cd3a61e | 59 | |
| jlee887 | 0:ebbc3cd3a61e | 60 | float xwog = ax - gravity[0]; |
| jlee887 | 0:ebbc3cd3a61e | 61 | float ywog = ay - gravity[1]; |
| jlee887 | 0:ebbc3cd3a61e | 62 | float zwog = az - gravity[2]; |
| jlee887 | 0:ebbc3cd3a61e | 63 | |
| jlee887 | 0:ebbc3cd3a61e | 64 | linearAccel[0] = xwog * 9.8; |
| jlee887 | 0:ebbc3cd3a61e | 65 | linearAccel[1] = ywog * 9.8; |
| jlee887 | 0:ebbc3cd3a61e | 66 | linearAccel[2] = zwog * 9.8; |
| jlee887 | 0:ebbc3cd3a61e | 67 | } |
| jlee887 | 0:ebbc3cd3a61e | 68 | |
| jlee887 | 0:ebbc3cd3a61e | 69 | /** |
| jlee887 | 0:ebbc3cd3a61e | 70 | * Returns the euler angles psi, theta and phi. |
| jlee887 | 0:ebbc3cd3a61e | 71 | **/ |
| jlee887 | 0:ebbc3cd3a61e | 72 | void Quaternion::getEulerAngles(float * angles) { |
| jlee887 | 0:ebbc3cd3a61e | 73 | angles[0] = atan2(2 * q1 * q2- 2 * q0 * q3, 2 * q0*q0 + 2 * q1 * q1 - 1) * 180/M_PI; // psi |
| jlee887 | 0:ebbc3cd3a61e | 74 | angles[1] = -asin(2 * q1 * q3 + 2 * q0 * q2) * 180/M_PI; // theta |
| jlee887 | 0:ebbc3cd3a61e | 75 | angles[2] = atan2(2 * q2 * q3 - 2 * q0 * q1, 2 * q0 * q0 + 2 * q3 * q3 - 1) * 180/M_PI; // phi |
| jlee887 | 0:ebbc3cd3a61e | 76 | } |
| jlee887 | 0:ebbc3cd3a61e | 77 | |
| jlee887 | 0:ebbc3cd3a61e | 78 | /** |
| jlee887 | 0:ebbc3cd3a61e | 79 | * Returns the yaw pitch and roll of the device. |
| jlee887 | 0:ebbc3cd3a61e | 80 | **/ |
| jlee887 | 0:ebbc3cd3a61e | 81 | void Quaternion::getYawPitchRoll(double * ypr) { |
| jlee887 | 0:ebbc3cd3a61e | 82 | |
| jlee887 | 0:ebbc3cd3a61e | 83 | ypr[0] = atan2(double(2*q1*q2 + 2*q0*q3), double(q0*q0 + q1*q1 - q2*q2 - q3*q3)) * 180/M_PI; //yaw |
| jlee887 | 0:ebbc3cd3a61e | 84 | ypr[1] = -asin(2*q0*q2 - 2*q1*q3) * 180/M_PI; // pitch |
| jlee887 | 0:ebbc3cd3a61e | 85 | ypr[2] = -atan2(2*q2*q3 + 2*q0*q1, -q0*q0 + q1*q1 + q2*q2 - q3*q3) * 180/M_PI; //roll |
| jlee887 | 0:ebbc3cd3a61e | 86 | |
| jlee887 | 0:ebbc3cd3a61e | 87 | } |
| jlee887 | 0:ebbc3cd3a61e | 88 | /** |
| jlee887 | 0:ebbc3cd3a61e | 89 | * Gets an estimation of gravity based on quaternion orientation representation. |
| jlee887 | 0:ebbc3cd3a61e | 90 | **/ |
| jlee887 | 0:ebbc3cd3a61e | 91 | void Quaternion::getGravity(float * gravity) { |
| jlee887 | 0:ebbc3cd3a61e | 92 | float q[4]; |
| jlee887 | 0:ebbc3cd3a61e | 93 | getQ(q); |
| jlee887 | 0:ebbc3cd3a61e | 94 | gravity[0] = 2 * (q[1] * q[3] - q[0] *q[2]); |
| jlee887 | 0:ebbc3cd3a61e | 95 | gravity[1] = 2 * (q[0] * q[1] + q[2] * q[3]); |
| jlee887 | 0:ebbc3cd3a61e | 96 | gravity[2] = q[0] * q[0] - q[1] * q[1] - q[2] * q[2] + q[3] * q[3]; |
| jlee887 | 0:ebbc3cd3a61e | 97 | } |
| jlee887 | 0:ebbc3cd3a61e | 98 | |
| jlee887 | 0:ebbc3cd3a61e | 99 | /** |
| jlee887 | 0:ebbc3cd3a61e | 100 | * Calculates the quaternion representation based on a 6DOF sensor. |
| jlee887 | 0:ebbc3cd3a61e | 101 | * @param gx, the rotation about the x axis in rad/sec |
| jlee887 | 0:ebbc3cd3a61e | 102 | * @param gy, the rotation about the y axis in rad/sec |
| jlee887 | 0:ebbc3cd3a61e | 103 | * @param gz, the rotation about the z axis in rad/sec |
| jlee887 | 0:ebbc3cd3a61e | 104 | * @param ax, the raw acceleration in the x direction. |
| jlee887 | 0:ebbc3cd3a61e | 105 | * @param ay, the raw acceleration in the y direction. |
| jlee887 | 0:ebbc3cd3a61e | 106 | * @param az, the raw acceleration in the z direction. |
| jlee887 | 0:ebbc3cd3a61e | 107 | **/ |
| jlee887 | 0:ebbc3cd3a61e | 108 | void Quaternion::update6DOF(float gx, float gy, float gz, float ax, float ay, float az) { |
| jlee887 | 0:ebbc3cd3a61e | 109 | updateSampleFrequency(); |
| jlee887 | 0:ebbc3cd3a61e | 110 | float recipNorm; |
| jlee887 | 0:ebbc3cd3a61e | 111 | float halfvx, halfvy, halfvz; |
| jlee887 | 0:ebbc3cd3a61e | 112 | float halfex, halfey, halfez; |
| jlee887 | 0:ebbc3cd3a61e | 113 | float qa, qb, qc; |
| jlee887 | 0:ebbc3cd3a61e | 114 | |
| jlee887 | 0:ebbc3cd3a61e | 115 | // Compute feedback only if accelerometer measurement valid (avoids NaN in accelerometer normalisation) |
| jlee887 | 0:ebbc3cd3a61e | 116 | if(!((ax == 0.0f) && (ay == 0.0f) && (az == 0.0f))) { |
| jlee887 | 0:ebbc3cd3a61e | 117 | |
| jlee887 | 0:ebbc3cd3a61e | 118 | // Normalise accelerometer measurement |
| jlee887 | 0:ebbc3cd3a61e | 119 | recipNorm = invSqrt(ax * ax + ay * ay + az * az); |
| jlee887 | 0:ebbc3cd3a61e | 120 | ax *= recipNorm; |
| jlee887 | 0:ebbc3cd3a61e | 121 | ay *= recipNorm; |
| jlee887 | 0:ebbc3cd3a61e | 122 | az *= recipNorm; |
| jlee887 | 0:ebbc3cd3a61e | 123 | |
| jlee887 | 0:ebbc3cd3a61e | 124 | // Estimated direction of gravity and vector perpendicular to magnetic flux |
| jlee887 | 0:ebbc3cd3a61e | 125 | halfvx = q1 * q3 - q0 * q2; |
| jlee887 | 0:ebbc3cd3a61e | 126 | halfvy = q0 * q1 + q2 * q3; |
| jlee887 | 0:ebbc3cd3a61e | 127 | halfvz = q0 * q0 - 0.5f + q3 * q3; |
| jlee887 | 0:ebbc3cd3a61e | 128 | |
| jlee887 | 0:ebbc3cd3a61e | 129 | // Error is sum of cross product between estimated and measured direction of gravity |
| jlee887 | 0:ebbc3cd3a61e | 130 | halfex = (ay * halfvz - az * halfvy); |
| jlee887 | 0:ebbc3cd3a61e | 131 | halfey = (az * halfvx - ax * halfvz); |
| jlee887 | 0:ebbc3cd3a61e | 132 | halfez = (ax * halfvy - ay * halfvx); |
| jlee887 | 0:ebbc3cd3a61e | 133 | |
| jlee887 | 0:ebbc3cd3a61e | 134 | // Compute and apply integral feedback if enabled |
| jlee887 | 0:ebbc3cd3a61e | 135 | if(twoKi > 0.0f) { |
| jlee887 | 0:ebbc3cd3a61e | 136 | integralFBx += twoKi * halfex * (1.0f / sampleFreq); // integral error scaled by Ki |
| jlee887 | 0:ebbc3cd3a61e | 137 | integralFBy += twoKi * halfey * (1.0f / sampleFreq); |
| jlee887 | 0:ebbc3cd3a61e | 138 | integralFBz += twoKi * halfez * (1.0f / sampleFreq); |
| jlee887 | 0:ebbc3cd3a61e | 139 | gx += integralFBx; // apply integral feedback |
| jlee887 | 0:ebbc3cd3a61e | 140 | gy += integralFBy; |
| jlee887 | 0:ebbc3cd3a61e | 141 | gz += integralFBz; |
| jlee887 | 0:ebbc3cd3a61e | 142 | } |
| jlee887 | 0:ebbc3cd3a61e | 143 | else { |
| jlee887 | 0:ebbc3cd3a61e | 144 | integralFBx = 0.0f; // prevent integral windup |
| jlee887 | 0:ebbc3cd3a61e | 145 | integralFBy = 0.0f; |
| jlee887 | 0:ebbc3cd3a61e | 146 | integralFBz = 0.0f; |
| jlee887 | 0:ebbc3cd3a61e | 147 | } |
| jlee887 | 0:ebbc3cd3a61e | 148 | |
| jlee887 | 0:ebbc3cd3a61e | 149 | // Apply proportional feedback |
| jlee887 | 0:ebbc3cd3a61e | 150 | gx += twoKp * halfex; |
| jlee887 | 0:ebbc3cd3a61e | 151 | gy += twoKp * halfey; |
| jlee887 | 0:ebbc3cd3a61e | 152 | gz += twoKp * halfez; |
| jlee887 | 0:ebbc3cd3a61e | 153 | } |
| jlee887 | 0:ebbc3cd3a61e | 154 | |
| jlee887 | 0:ebbc3cd3a61e | 155 | // Integrate rate of change of quaternion |
| jlee887 | 0:ebbc3cd3a61e | 156 | gx *= (0.5f * (1.0f / sampleFreq)); // pre-multiply common factors |
| jlee887 | 0:ebbc3cd3a61e | 157 | gy *= (0.5f * (1.0f / sampleFreq)); |
| jlee887 | 0:ebbc3cd3a61e | 158 | gz *= (0.5f * (1.0f / sampleFreq)); |
| jlee887 | 0:ebbc3cd3a61e | 159 | qa = q0; |
| jlee887 | 0:ebbc3cd3a61e | 160 | qb = q1; |
| jlee887 | 0:ebbc3cd3a61e | 161 | qc = q2; |
| jlee887 | 0:ebbc3cd3a61e | 162 | q0 += (-qb * gx - qc * gy - q3 * gz); |
| jlee887 | 0:ebbc3cd3a61e | 163 | q1 += (qa * gx + qc * gz - q3 * gy); |
| jlee887 | 0:ebbc3cd3a61e | 164 | q2 += (qa * gy - qb * gz + q3 * gx); |
| jlee887 | 0:ebbc3cd3a61e | 165 | q3 += (qa * gz + qb * gy - qc * gx); |
| jlee887 | 0:ebbc3cd3a61e | 166 | |
| jlee887 | 0:ebbc3cd3a61e | 167 | // Normalise quaternion |
| jlee887 | 0:ebbc3cd3a61e | 168 | recipNorm = invSqrt(q0 * q0 + q1 * q1 + q2 * q2 + q3 * q3); |
| jlee887 | 0:ebbc3cd3a61e | 169 | q0 *= recipNorm; |
| jlee887 | 0:ebbc3cd3a61e | 170 | q1 *= recipNorm; |
| jlee887 | 0:ebbc3cd3a61e | 171 | q2 *= recipNorm; |
| jlee887 | 0:ebbc3cd3a61e | 172 | q3 *= recipNorm; |
| jlee887 | 0:ebbc3cd3a61e | 173 | |
| jlee887 | 0:ebbc3cd3a61e | 174 | } |
| jlee887 | 0:ebbc3cd3a61e | 175 | |
| jlee887 | 0:ebbc3cd3a61e | 176 | /** |
| jlee887 | 0:ebbc3cd3a61e | 177 | * Calculates the quaternion representation based on a 9DOF sensor. |
| jlee887 | 0:ebbc3cd3a61e | 178 | * @param gx, the rotation about the x axis in rad/sec |
| jlee887 | 0:ebbc3cd3a61e | 179 | * @param gy, the rotation about the y axis in rad/sec |
| jlee887 | 0:ebbc3cd3a61e | 180 | * @param gz, the rotation about the z axis in rad/sec |
| jlee887 | 0:ebbc3cd3a61e | 181 | * @param ax, the raw acceleration in the x direction. |
| jlee887 | 0:ebbc3cd3a61e | 182 | * @param ay, the raw acceleration in the y direction. |
| jlee887 | 0:ebbc3cd3a61e | 183 | * @param az, the raw acceleration in the z direction. |
| jlee887 | 0:ebbc3cd3a61e | 184 | * @param mx, the raw magnometer heading in the x direction. |
| jlee887 | 0:ebbc3cd3a61e | 185 | * @param my, the raw magnometer heading in the y direction. |
| jlee887 | 0:ebbc3cd3a61e | 186 | * @param mz, the raw magnometer heading in the z direction. |
| jlee887 | 0:ebbc3cd3a61e | 187 | **/ |
| jlee887 | 0:ebbc3cd3a61e | 188 | void Quaternion::update9DOF(float gx, float gy, float gz, float ax, float ay, float az, float mx, float my, float mz) { |
| jlee887 | 0:ebbc3cd3a61e | 189 | //update the frequency first. |
| jlee887 | 0:ebbc3cd3a61e | 190 | updateSampleFrequency(); |
| jlee887 | 0:ebbc3cd3a61e | 191 | float recipNorm; |
| jlee887 | 0:ebbc3cd3a61e | 192 | float q0q0, q0q1, q0q2, q0q3, q1q1, q1q2, q1q3, q2q2, q2q3, q3q3; |
| jlee887 | 0:ebbc3cd3a61e | 193 | float hx, hy, bx, bz; |
| jlee887 | 0:ebbc3cd3a61e | 194 | float halfvx, halfvy, halfvz, halfwx, halfwy, halfwz; |
| jlee887 | 0:ebbc3cd3a61e | 195 | float halfex, halfey, halfez; |
| jlee887 | 0:ebbc3cd3a61e | 196 | float qa, qb, qc; |
| jlee887 | 0:ebbc3cd3a61e | 197 | |
| jlee887 | 0:ebbc3cd3a61e | 198 | // Use IMU algorithm if magnetometer measurement invalid (avoids NaN in magnetometer normalisation) |
| jlee887 | 0:ebbc3cd3a61e | 199 | if((mx == 0.0f) && (my == 0.0f) && (mz == 0.0f)) { |
| jlee887 | 0:ebbc3cd3a61e | 200 | update6DOF(gx, gy, gz, ax, ay, az); |
| jlee887 | 0:ebbc3cd3a61e | 201 | return; |
| jlee887 | 0:ebbc3cd3a61e | 202 | } |
| jlee887 | 0:ebbc3cd3a61e | 203 | |
| jlee887 | 0:ebbc3cd3a61e | 204 | // Compute feedback only if accelerometer measurement valid (avoids NaN in accelerometer normalisation) |
| jlee887 | 0:ebbc3cd3a61e | 205 | if(!((ax == 0.0f) && (ay == 0.0f) && (az == 0.0f))) { |
| jlee887 | 0:ebbc3cd3a61e | 206 | |
| jlee887 | 0:ebbc3cd3a61e | 207 | // Normalise accelerometer measurement |
| jlee887 | 0:ebbc3cd3a61e | 208 | recipNorm = invSqrt(ax * ax + ay * ay + az * az); |
| jlee887 | 0:ebbc3cd3a61e | 209 | ax *= recipNorm; |
| jlee887 | 0:ebbc3cd3a61e | 210 | ay *= recipNorm; |
| jlee887 | 0:ebbc3cd3a61e | 211 | az *= recipNorm; |
| jlee887 | 0:ebbc3cd3a61e | 212 | |
| jlee887 | 0:ebbc3cd3a61e | 213 | // Normalise magnetometer measurement |
| jlee887 | 0:ebbc3cd3a61e | 214 | recipNorm = invSqrt(mx * mx + my * my + mz * mz); |
| jlee887 | 0:ebbc3cd3a61e | 215 | mx *= recipNorm; |
| jlee887 | 0:ebbc3cd3a61e | 216 | my *= recipNorm; |
| jlee887 | 0:ebbc3cd3a61e | 217 | mz *= recipNorm; |
| jlee887 | 0:ebbc3cd3a61e | 218 | |
| jlee887 | 0:ebbc3cd3a61e | 219 | // Auxiliary variables to avoid repeated arithmetic |
| jlee887 | 0:ebbc3cd3a61e | 220 | q0q0 = q0 * q0; |
| jlee887 | 0:ebbc3cd3a61e | 221 | q0q1 = q0 * q1; |
| jlee887 | 0:ebbc3cd3a61e | 222 | q0q2 = q0 * q2; |
| jlee887 | 0:ebbc3cd3a61e | 223 | q0q3 = q0 * q3; |
| jlee887 | 0:ebbc3cd3a61e | 224 | q1q1 = q1 * q1; |
| jlee887 | 0:ebbc3cd3a61e | 225 | q1q2 = q1 * q2; |
| jlee887 | 0:ebbc3cd3a61e | 226 | q1q3 = q1 * q3; |
| jlee887 | 0:ebbc3cd3a61e | 227 | q2q2 = q2 * q2; |
| jlee887 | 0:ebbc3cd3a61e | 228 | q2q3 = q2 * q3; |
| jlee887 | 0:ebbc3cd3a61e | 229 | q3q3 = q3 * q3; |
| jlee887 | 0:ebbc3cd3a61e | 230 | |
| jlee887 | 0:ebbc3cd3a61e | 231 | // Reference direction of Earth's magnetic field |
| jlee887 | 0:ebbc3cd3a61e | 232 | hx = 2.0f * (mx * (0.5f - q2q2 - q3q3) + my * (q1q2 - q0q3) + mz * (q1q3 + q0q2)); |
| jlee887 | 0:ebbc3cd3a61e | 233 | hy = 2.0f * (mx * (q1q2 + q0q3) + my * (0.5f - q1q1 - q3q3) + mz * (q2q3 - q0q1)); |
| jlee887 | 0:ebbc3cd3a61e | 234 | bx = sqrt(hx * hx + hy * hy); |
| jlee887 | 0:ebbc3cd3a61e | 235 | bz = 2.0f * (mx * (q1q3 - q0q2) + my * (q2q3 + q0q1) + mz * (0.5f - q1q1 - q2q2)); |
| jlee887 | 0:ebbc3cd3a61e | 236 | |
| jlee887 | 0:ebbc3cd3a61e | 237 | // Estimated direction of gravity and magnetic field |
| jlee887 | 0:ebbc3cd3a61e | 238 | halfvx = q1q3 - q0q2; |
| jlee887 | 0:ebbc3cd3a61e | 239 | halfvy = q0q1 + q2q3; |
| jlee887 | 0:ebbc3cd3a61e | 240 | halfvz = q0q0 - 0.5f + q3q3; |
| jlee887 | 0:ebbc3cd3a61e | 241 | halfwx = bx * (0.5f - q2q2 - q3q3) + bz * (q1q3 - q0q2); |
| jlee887 | 0:ebbc3cd3a61e | 242 | halfwy = bx * (q1q2 - q0q3) + bz * (q0q1 + q2q3); |
| jlee887 | 0:ebbc3cd3a61e | 243 | halfwz = bx * (q0q2 + q1q3) + bz * (0.5f - q1q1 - q2q2); |
| jlee887 | 0:ebbc3cd3a61e | 244 | |
| jlee887 | 0:ebbc3cd3a61e | 245 | // Error is sum of cross product between estimated direction and measured direction of field vectors |
| jlee887 | 0:ebbc3cd3a61e | 246 | halfex = (ay * halfvz - az * halfvy) + (my * halfwz - mz * halfwy); |
| jlee887 | 0:ebbc3cd3a61e | 247 | halfey = (az * halfvx - ax * halfvz) + (mz * halfwx - mx * halfwz); |
| jlee887 | 0:ebbc3cd3a61e | 248 | halfez = (ax * halfvy - ay * halfvx) + (mx * halfwy - my * halfwx); |
| jlee887 | 0:ebbc3cd3a61e | 249 | |
| jlee887 | 0:ebbc3cd3a61e | 250 | // Compute and apply integral feedback if enabled |
| jlee887 | 0:ebbc3cd3a61e | 251 | if(twoKi > 0.0f) { |
| jlee887 | 0:ebbc3cd3a61e | 252 | integralFBx += twoKi * halfex * (1.0f / sampleFreq); // integral error scaled by Ki |
| jlee887 | 0:ebbc3cd3a61e | 253 | integralFBy += twoKi * halfey * (1.0f / sampleFreq); |
| jlee887 | 0:ebbc3cd3a61e | 254 | integralFBz += twoKi * halfez * (1.0f / sampleFreq); |
| jlee887 | 0:ebbc3cd3a61e | 255 | gx += integralFBx; // apply integral feedback |
| jlee887 | 0:ebbc3cd3a61e | 256 | gy += integralFBy; |
| jlee887 | 0:ebbc3cd3a61e | 257 | gz += integralFBz; |
| jlee887 | 0:ebbc3cd3a61e | 258 | } |
| jlee887 | 0:ebbc3cd3a61e | 259 | else { |
| jlee887 | 0:ebbc3cd3a61e | 260 | integralFBx = 0.0f; // prevent integral windup |
| jlee887 | 0:ebbc3cd3a61e | 261 | integralFBy = 0.0f; |
| jlee887 | 0:ebbc3cd3a61e | 262 | integralFBz = 0.0f; |
| jlee887 | 0:ebbc3cd3a61e | 263 | } |
| jlee887 | 0:ebbc3cd3a61e | 264 | |
| jlee887 | 0:ebbc3cd3a61e | 265 | // Apply proportional feedback |
| jlee887 | 0:ebbc3cd3a61e | 266 | gx += twoKp * halfex; |
| jlee887 | 0:ebbc3cd3a61e | 267 | gy += twoKp * halfey; |
| jlee887 | 0:ebbc3cd3a61e | 268 | gz += twoKp * halfez; |
| jlee887 | 0:ebbc3cd3a61e | 269 | } |
| jlee887 | 0:ebbc3cd3a61e | 270 | |
| jlee887 | 0:ebbc3cd3a61e | 271 | // Integrate rate of change of quaternion |
| jlee887 | 0:ebbc3cd3a61e | 272 | gx *= (0.5f * (1.0f / sampleFreq)); // pre-multiply common factors |
| jlee887 | 0:ebbc3cd3a61e | 273 | gy *= (0.5f * (1.0f / sampleFreq)); |
| jlee887 | 0:ebbc3cd3a61e | 274 | gz *= (0.5f * (1.0f / sampleFreq)); |
| jlee887 | 0:ebbc3cd3a61e | 275 | qa = q0; |
| jlee887 | 0:ebbc3cd3a61e | 276 | qb = q1; |
| jlee887 | 0:ebbc3cd3a61e | 277 | qc = q2; |
| jlee887 | 0:ebbc3cd3a61e | 278 | q0 += (-qb * gx - qc * gy - q3 * gz); |
| jlee887 | 0:ebbc3cd3a61e | 279 | q1 += (qa * gx + qc * gz - q3 * gy); |
| jlee887 | 0:ebbc3cd3a61e | 280 | q2 += (qa * gy - qb * gz + q3 * gx); |
| jlee887 | 0:ebbc3cd3a61e | 281 | q3 += (qa * gz + qb * gy - qc * gx); |
| jlee887 | 0:ebbc3cd3a61e | 282 | |
| jlee887 | 0:ebbc3cd3a61e | 283 | // Normalise quaternion |
| jlee887 | 0:ebbc3cd3a61e | 284 | recipNorm = invSqrt(q0 * q0 + q1 * q1 + q2 * q2 + q3 * q3); |
| jlee887 | 0:ebbc3cd3a61e | 285 | q0 *= recipNorm; |
| jlee887 | 0:ebbc3cd3a61e | 286 | q1 *= recipNorm; |
| jlee887 | 0:ebbc3cd3a61e | 287 | q2 *= recipNorm; |
| jlee887 | 0:ebbc3cd3a61e | 288 | q3 *= recipNorm; |
| jlee887 | 0:ebbc3cd3a61e | 289 | } |
| jlee887 | 0:ebbc3cd3a61e | 290 | |
| jlee887 | 0:ebbc3cd3a61e | 291 | /** |
| jlee887 | 0:ebbc3cd3a61e | 292 | * Super fast inverted square root. |
| jlee887 | 0:ebbc3cd3a61e | 293 | **/ |
| jlee887 | 0:ebbc3cd3a61e | 294 | float Quaternion::invSqrt(float x) { |
| jlee887 | 0:ebbc3cd3a61e | 295 | float halfx = 0.5f * x; |
| jlee887 | 0:ebbc3cd3a61e | 296 | float y = x; |
| jlee887 | 0:ebbc3cd3a61e | 297 | long i = *(long*)&y; |
| jlee887 | 0:ebbc3cd3a61e | 298 | i = 0x5f3759df - (i>>1); |
| jlee887 | 0:ebbc3cd3a61e | 299 | y = *(float*)&i; |
| jlee887 | 0:ebbc3cd3a61e | 300 | y = y * (1.5f - (halfx * y * y)); |
| jlee887 | 0:ebbc3cd3a61e | 301 | return y; |
| jlee887 | 0:ebbc3cd3a61e | 302 | } |