4180 final project
Dependencies: LSM9DS0 USBDevice mbed
Revision 0:ebbc3cd3a61e, committed 2015-12-05
- Comitter:
- jlee887
- Date:
- Sat Dec 05 18:39:33 2015 +0000
- Commit message:
- d
Changed in this revision
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/LSM9DS0.lib Sat Dec 05 18:39:33 2015 +0000 @@ -0,0 +1,1 @@ +http://developer.mbed.org/users/aswild/code/LSM9DS0/#5556e6fb99f5
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Quaternion/Quaternion.cpp Sat Dec 05 18:39:33 2015 +0000 @@ -0,0 +1,302 @@ + +#include "Quaternion.h" +#include "mbed.h" +#define M_PI 3.14159265 + +Timer t; +/** +* Default constructor. +**/ +Quaternion::Quaternion() { + q0 = 1.0f; + q1 = 0.0f; + q2 = 0.0f; + q3 = 0.0f; + twoKp = twoKpDef; + twoKi = twoKiDef; + sampleFreq = 0.0f; + lastUpdate = 0L; + now = 0L; + integralFBx = 0.0f; + integralFBy = 0.0f; + integralFBz = 0.0f; + t.start(); +} + +/** +* Updates the sample frequency based on the elapsed time. +**/ +void Quaternion::updateSampleFrequency() { + now = t.read(); + sampleFreq = 1.0 / ((now - lastUpdate)); + lastUpdate = now; +} +/** +* Returns the quaternion representation of the orientation. +**/ +void Quaternion::getQ(float * q) { + q[0] = q0; + q[1] = q1; + q[2] = q2; + q[3] = q3; +} + +/** +* Gets the linear acceleration by estimating gravity and then subtracting it. All accelerations +* need to be scaled to 1 g. So if at 1 g your accelerometer reads 245, divide it by 245 before passing it +* to this function. +* @param *linearAccel, pointer to float array for linear accelerations, +* @param ax, the scaled acceleration in the x direction. +* @param ay, the scaled acceleration in the y direction. +* @param az, the scaled acceleration in the z direction. +**/ +void Quaternion::getLinearAcceleration(float * linearAccel, float ax, float ay, float az) { + + float gravity[3]; + getGravity(gravity); + + + + float xwog = ax - gravity[0]; + float ywog = ay - gravity[1]; + float zwog = az - gravity[2]; + + linearAccel[0] = xwog * 9.8; + linearAccel[1] = ywog * 9.8; + linearAccel[2] = zwog * 9.8; +} + +/** +* Returns the euler angles psi, theta and phi. +**/ +void Quaternion::getEulerAngles(float * angles) { + angles[0] = atan2(2 * q1 * q2- 2 * q0 * q3, 2 * q0*q0 + 2 * q1 * q1 - 1) * 180/M_PI; // psi + angles[1] = -asin(2 * q1 * q3 + 2 * q0 * q2) * 180/M_PI; // theta + angles[2] = atan2(2 * q2 * q3 - 2 * q0 * q1, 2 * q0 * q0 + 2 * q3 * q3 - 1) * 180/M_PI; // phi +} + +/** +* Returns the yaw pitch and roll of the device. +**/ +void Quaternion::getYawPitchRoll(double * ypr) { + + ypr[0] = atan2(double(2*q1*q2 + 2*q0*q3), double(q0*q0 + q1*q1 - q2*q2 - q3*q3)) * 180/M_PI; //yaw + ypr[1] = -asin(2*q0*q2 - 2*q1*q3) * 180/M_PI; // pitch + ypr[2] = -atan2(2*q2*q3 + 2*q0*q1, -q0*q0 + q1*q1 + q2*q2 - q3*q3) * 180/M_PI; //roll + +} +/** +* Gets an estimation of gravity based on quaternion orientation representation. +**/ +void Quaternion::getGravity(float * gravity) { + float q[4]; + getQ(q); + gravity[0] = 2 * (q[1] * q[3] - q[0] *q[2]); + gravity[1] = 2 * (q[0] * q[1] + q[2] * q[3]); + gravity[2] = q[0] * q[0] - q[1] * q[1] - q[2] * q[2] + q[3] * q[3]; +} + +/** +* Calculates the quaternion representation based on a 6DOF sensor. +* @param gx, the rotation about the x axis in rad/sec +* @param gy, the rotation about the y axis in rad/sec +* @param gz, the rotation about the z axis in rad/sec +* @param ax, the raw acceleration in the x direction. +* @param ay, the raw acceleration in the y direction. +* @param az, the raw acceleration in the z direction. +**/ +void Quaternion::update6DOF(float gx, float gy, float gz, float ax, float ay, float az) { + updateSampleFrequency(); + float recipNorm; + float halfvx, halfvy, halfvz; + float halfex, halfey, halfez; + float qa, qb, qc; + + // Compute feedback only if accelerometer measurement valid (avoids NaN in accelerometer normalisation) + if(!((ax == 0.0f) && (ay == 0.0f) && (az == 0.0f))) { + + // Normalise accelerometer measurement + recipNorm = invSqrt(ax * ax + ay * ay + az * az); + ax *= recipNorm; + ay *= recipNorm; + az *= recipNorm; + + // Estimated direction of gravity and vector perpendicular to magnetic flux + halfvx = q1 * q3 - q0 * q2; + halfvy = q0 * q1 + q2 * q3; + halfvz = q0 * q0 - 0.5f + q3 * q3; + + // Error is sum of cross product between estimated and measured direction of gravity + halfex = (ay * halfvz - az * halfvy); + halfey = (az * halfvx - ax * halfvz); + halfez = (ax * halfvy - ay * halfvx); + + // Compute and apply integral feedback if enabled + if(twoKi > 0.0f) { + integralFBx += twoKi * halfex * (1.0f / sampleFreq); // integral error scaled by Ki + integralFBy += twoKi * halfey * (1.0f / sampleFreq); + integralFBz += twoKi * halfez * (1.0f / sampleFreq); + gx += integralFBx; // apply integral feedback + gy += integralFBy; + gz += integralFBz; + } + else { + integralFBx = 0.0f; // prevent integral windup + integralFBy = 0.0f; + integralFBz = 0.0f; + } + + // Apply proportional feedback + gx += twoKp * halfex; + gy += twoKp * halfey; + gz += twoKp * halfez; + } + + // Integrate rate of change of quaternion + gx *= (0.5f * (1.0f / sampleFreq)); // pre-multiply common factors + gy *= (0.5f * (1.0f / sampleFreq)); + gz *= (0.5f * (1.0f / sampleFreq)); + qa = q0; + qb = q1; + qc = q2; + q0 += (-qb * gx - qc * gy - q3 * gz); + q1 += (qa * gx + qc * gz - q3 * gy); + q2 += (qa * gy - qb * gz + q3 * gx); + q3 += (qa * gz + qb * gy - qc * gx); + + // Normalise quaternion + recipNorm = invSqrt(q0 * q0 + q1 * q1 + q2 * q2 + q3 * q3); + q0 *= recipNorm; + q1 *= recipNorm; + q2 *= recipNorm; + q3 *= recipNorm; + +} + +/** +* Calculates the quaternion representation based on a 9DOF sensor. +* @param gx, the rotation about the x axis in rad/sec +* @param gy, the rotation about the y axis in rad/sec +* @param gz, the rotation about the z axis in rad/sec +* @param ax, the raw acceleration in the x direction. +* @param ay, the raw acceleration in the y direction. +* @param az, the raw acceleration in the z direction. +* @param mx, the raw magnometer heading in the x direction. +* @param my, the raw magnometer heading in the y direction. +* @param mz, the raw magnometer heading in the z direction. +**/ +void Quaternion::update9DOF(float gx, float gy, float gz, float ax, float ay, float az, float mx, float my, float mz) { + //update the frequency first. + updateSampleFrequency(); + float recipNorm; + float q0q0, q0q1, q0q2, q0q3, q1q1, q1q2, q1q3, q2q2, q2q3, q3q3; + float hx, hy, bx, bz; + float halfvx, halfvy, halfvz, halfwx, halfwy, halfwz; + float halfex, halfey, halfez; + float qa, qb, qc; + + // Use IMU algorithm if magnetometer measurement invalid (avoids NaN in magnetometer normalisation) + if((mx == 0.0f) && (my == 0.0f) && (mz == 0.0f)) { + update6DOF(gx, gy, gz, ax, ay, az); + return; + } + + // Compute feedback only if accelerometer measurement valid (avoids NaN in accelerometer normalisation) + if(!((ax == 0.0f) && (ay == 0.0f) && (az == 0.0f))) { + + // Normalise accelerometer measurement + recipNorm = invSqrt(ax * ax + ay * ay + az * az); + ax *= recipNorm; + ay *= recipNorm; + az *= recipNorm; + + // Normalise magnetometer measurement + recipNorm = invSqrt(mx * mx + my * my + mz * mz); + mx *= recipNorm; + my *= recipNorm; + mz *= recipNorm; + + // Auxiliary variables to avoid repeated arithmetic + q0q0 = q0 * q0; + q0q1 = q0 * q1; + q0q2 = q0 * q2; + q0q3 = q0 * q3; + q1q1 = q1 * q1; + q1q2 = q1 * q2; + q1q3 = q1 * q3; + q2q2 = q2 * q2; + q2q3 = q2 * q3; + q3q3 = q3 * q3; + + // Reference direction of Earth's magnetic field + hx = 2.0f * (mx * (0.5f - q2q2 - q3q3) + my * (q1q2 - q0q3) + mz * (q1q3 + q0q2)); + hy = 2.0f * (mx * (q1q2 + q0q3) + my * (0.5f - q1q1 - q3q3) + mz * (q2q3 - q0q1)); + bx = sqrt(hx * hx + hy * hy); + bz = 2.0f * (mx * (q1q3 - q0q2) + my * (q2q3 + q0q1) + mz * (0.5f - q1q1 - q2q2)); + + // Estimated direction of gravity and magnetic field + halfvx = q1q3 - q0q2; + halfvy = q0q1 + q2q3; + halfvz = q0q0 - 0.5f + q3q3; + halfwx = bx * (0.5f - q2q2 - q3q3) + bz * (q1q3 - q0q2); + halfwy = bx * (q1q2 - q0q3) + bz * (q0q1 + q2q3); + halfwz = bx * (q0q2 + q1q3) + bz * (0.5f - q1q1 - q2q2); + + // Error is sum of cross product between estimated direction and measured direction of field vectors + halfex = (ay * halfvz - az * halfvy) + (my * halfwz - mz * halfwy); + halfey = (az * halfvx - ax * halfvz) + (mz * halfwx - mx * halfwz); + halfez = (ax * halfvy - ay * halfvx) + (mx * halfwy - my * halfwx); + + // Compute and apply integral feedback if enabled + if(twoKi > 0.0f) { + integralFBx += twoKi * halfex * (1.0f / sampleFreq); // integral error scaled by Ki + integralFBy += twoKi * halfey * (1.0f / sampleFreq); + integralFBz += twoKi * halfez * (1.0f / sampleFreq); + gx += integralFBx; // apply integral feedback + gy += integralFBy; + gz += integralFBz; + } + else { + integralFBx = 0.0f; // prevent integral windup + integralFBy = 0.0f; + integralFBz = 0.0f; + } + + // Apply proportional feedback + gx += twoKp * halfex; + gy += twoKp * halfey; + gz += twoKp * halfez; + } + + // Integrate rate of change of quaternion + gx *= (0.5f * (1.0f / sampleFreq)); // pre-multiply common factors + gy *= (0.5f * (1.0f / sampleFreq)); + gz *= (0.5f * (1.0f / sampleFreq)); + qa = q0; + qb = q1; + qc = q2; + q0 += (-qb * gx - qc * gy - q3 * gz); + q1 += (qa * gx + qc * gz - q3 * gy); + q2 += (qa * gy - qb * gz + q3 * gx); + q3 += (qa * gz + qb * gy - qc * gx); + + // Normalise quaternion + recipNorm = invSqrt(q0 * q0 + q1 * q1 + q2 * q2 + q3 * q3); + q0 *= recipNorm; + q1 *= recipNorm; + q2 *= recipNorm; + q3 *= recipNorm; +} + +/** +* Super fast inverted square root. +**/ +float Quaternion::invSqrt(float x) { + float halfx = 0.5f * x; + float y = x; + long i = *(long*)&y; + i = 0x5f3759df - (i>>1); + y = *(float*)&i; + y = y * (1.5f - (halfx * y * y)); + return y; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Quaternion/Quaternion.h Sat Dec 05 18:39:33 2015 +0000 @@ -0,0 +1,32 @@ +#ifndef Quaternion_h +#define Quaternion_h +#include "mbed.h" + + +#define twoKpDef (2.0f * 0.5f) // 2 * proportional gain +#define twoKiDef (2.0f * 0.0f) // 2 * integral gain + +class Quaternion { + + public: + Quaternion(); + void update6DOF(float gx, float gy, float gz, float ax, float ay, float az); + void update9DOF(float gx, float gy, float gz, float ax, float ay, float az, float mx, float my, float mz); + void getQ(float * q); + void getEulerAngles(float * angles); + void getYawPitchRoll(double * ypr); + void getGravity(float * gravity); + void getLinearAcceleration(float * linearAccel, float ax, float ay, float az); + + private: + float invSqrt(float x); + volatile float q0, q1, q2, q3; + volatile float twoKp; // 2 * proportional gain (Kp) + volatile float twoKi; // 2 * integral gain (Ki) + volatile float integralFBx, integralFBy, integralFBz; + unsigned long lastUpdate, now; + float sampleFreq; + void updateSampleFrequency(); +}; + +#endif \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/USBDevice.lib Sat Dec 05 18:39:33 2015 +0000 @@ -0,0 +1,1 @@ +https://developer.mbed.org/users/jlee887/code/USBDevice/#d29ac157bf32
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/main.cpp Sat Dec 05 18:39:33 2015 +0000 @@ -0,0 +1,125 @@ +#include "mbed.h" +#include "Quaternion.h" +#include "LSM9DS0.h" +#include "USBKeyboard.h" + +Serial pc(USBTX, USBRX); +//IMU +// SDO_XM and SDO_G are pulled up, so our addresses are: +#define LSM9DS0_XM_ADDR 0x1D // Would be 0x1E if SDO_XM is LOW +#define LSM9DS0_G_ADDR 0x6B // Would be 0x6A if SDO_G is LOW +LSM9DS0 imu(p9, p10, LSM9DS0_G_ADDR, LSM9DS0_XM_ADDR); +Quaternion q; +#define M_PI 3.14159265 + +float accLin[3]; // linear accelerations +double ypr[3]; //yaw pitch roll + +USBKeyboard keyboard; +USBKeyboard keyboard2; + +DigitalIn left(p15); +DigitalIn right(p16); + +int main() { + + //IMU + uint16_t status = imu.begin(); + double test; + //Make sure communication is working + pc.printf("LSM9DS0 WHO_AM_I's returned: 0x%X\n", status); + pc.printf("Should be 0x49D4\n\n"); + float xjump = 9; // threshold for jump + float yjump = 1.15; // g threshold + float zright = .4; // move right z acc threshold + float zleft = -.2; // move left z acc threshold + float rollRight = -120; // be less than this to move right + float rollLeft = -80; // be greater than this to move left + + q = Quaternion(); + while(1) { + imu.readAccel(); + imu.readGyro(); + imu.readMag(); + //t2.start(); + //pc.printf("ax= %f, ay = %f, az = %f \n", imu.ax,imu.ay,imu.az); + //pc.printf("gx= %f, gy = %f, gz = %f \n", imu.gx,imu.gy,imu.gz); + + q.update9DOF(imu.gx*M_PI/180, imu.gy*M_PI/180, imu.gz*M_PI/180, imu.ax, imu.ay, imu.az, imu.mx, imu.my, imu.mz); + q.getLinearAcceleration(accLin, imu.ax,imu.ay,imu.az); + q.getYawPitchRoll(ypr); + + //pc.printf("xl= %f, yl= %f, zl= %f \n \n",accLin[0],accLin[1],accLin[2]); + //pc.printf("y= %f, p= %f, r= %f \n \n",ypr[0],ypr[1],ypr[2]); + /* + pc.printf("p = %f ", ypr[1]); + pc.printf("r = %f ",ypr[2]); + pc.printf("a = %f ",accLin[1]); + pc. printf("ax = %f ", accLin[0]); + pc.printf("imuax = %f ", imu.ax); + pc.printf("imuay = %f ", imu.ay); + pc.printf("imuaz = %f \n", imu.az); + */ + pc.printf("%f,%f \n",ypr[2],imu.az); + + + /* + + if (ypr[2] > rollLeft | left) + { + keyboard.keyCode(LEFT_ARROW); + + } + else if (ypr[2] < rollRight) + { + keyboard.keyCode(RIGHT_ARROW); + } + else + { + keyboard.keyCodeOff(LEFT_ARROW); + } + */ + + if (imu.az > zright) + {keyboard.keyCode(RIGHT_ARROW);} + else if (imu.az < zleft) + {keyboard.keyCode(LEFT_ARROW);} + else + {keyboard.keyCodeOff(LEFT_ARROW);} + if(imu.ay > yjump) + { + for(int i =0; i < 100; i++) + {keyboard.keyCode(UP_ARROW);} + keyboard.keyCodeOff(UP_ARROW); + } + + + /* + if (ypr[2] < rollLeft | left) + { + keyboard.keyCodeOff(LEFT_ARROW); + } + + if (ypr[2] < rollRight | right) + { + keyboard2.keyCode(RIGHT_ARROW); + } + if (ypr[2] > rollRight | right) + { + keyboard2.keyCodeOff(RIGHT_ARROW); + + } */ +/* + if (accLin[1] > yjump) + { + keyboard.keyCode(UP_ARROW); + pc.printf("here3"); + } + */ + //keyboard.keyCode(RIGHT_ARROW); + + //wait(1.0); + + } +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mbed.bld Sat Dec 05 18:39:33 2015 +0000 @@ -0,0 +1,1 @@ +http://mbed.org/users/mbed_official/code/mbed/builds/165afa46840b \ No newline at end of file