基于MPU9250的IMU库,可以测量pitch,roll,yaw,compass
Diff: imu.cpp
- Revision:
- 0:35bba382318b
- Child:
- 1:7cf70724bdb0
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/imu.cpp Thu Jun 21 07:00:40 2018 +0000 @@ -0,0 +1,403 @@ +#include "imu.h" + +I2C i2c(I2C_SDA,I2C_SCL); + +/** + * @brief invSqrt + * @param + * @retval + */ +float IMU::invSqrt(float x) +{ + float halfx = 0.5f * x; + float y = x; + + long i = *(long*)&y; //get bits for floating value + i = 0x5f3759df - (i >> 1); //gives initial guss you + y = *(float*)&i; //convert bits back to float + y = y * (1.5f - (halfx * y * y)); //newtop step, repeating increases accuracy + + return y; +} + +/** + * @brief initializes IMU + * @param None + * @retval None + */ +void IMU::IMU_Init() +{ + MPU9250_Init(); + //BMP180_Init(); + + q0 = 1.0f; + q1 = 0.0f; + q2 = 0.0f; + q3 = 0.0f; +} + +/** + * @brief Updata attitude and heading + * @param ax: accelerometer X + * @param ay: accelerometer Y + * @param az: accelerometer Z + * @param gx: gyroscopes X + * @param gy: gyroscopes Y + * @param gz: gyroscopes Z + * @param mx: magnetometer X + * @param my: magnetometer Y + * @param mz: magnetometer Z + * @retval None + */ +void IMU::IMU_AHRSupdate(float gx, float gy, float gz, float ax, float ay, float az, float mx, float my, float mz) +{ + float norm; + float hx, hy, hz, bx, bz; + float vx, vy, vz, wx, wy, wz; + float exInt = 0.0, eyInt = 0.0, ezInt = 0.0; + float ex, ey, ez, halfT = 0.024f; + + float q0q0 = q0 * q0; + float q0q1 = q0 * q1; + float q0q2 = q0 * q2; + float q0q3 = q0 * q3; + float q1q1 = q1 * q1; + float q1q2 = q1 * q2; + float q1q3 = q1 * q3; + float q2q2 = q2 * q2; + float q2q3 = q2 * q3; + float q3q3 = q3 * q3; + + norm = invSqrt(ax * ax + ay * ay + az * az); + ax = ax * norm; + ay = ay * norm; + az = az * norm; + + norm = invSqrt(mx * mx + my * my + mz * mz); + mx = mx * norm; + my = my * norm; + mz = mz * norm; + + // compute reference direction of flux + hx = 2 * mx * (0.5f - q2q2 - q3q3) + 2 * my * (q1q2 - q0q3) + 2 * mz * (q1q3 + q0q2); + hy = 2 * mx * (q1q2 + q0q3) + 2 * my * (0.5f - q1q1 - q3q3) + 2 * mz * (q2q3 - q0q1); + hz = 2 * mx * (q1q3 - q0q2) + 2 * my * (q2q3 + q0q1) + 2 * mz * (0.5f - q1q1 - q2q2); + bx = sqrt((hx * hx) + (hy * hy)); + bz = hz; + + // estimated direction of gravity and flux (v and w) + vx = 2 * (q1q3 - q0q2); + vy = 2 * (q0q1 + q2q3); + vz = q0q0 - q1q1 - q2q2 + q3q3; + wx = 2 * bx * (0.5 - q2q2 - q3q3) + 2 * bz * (q1q3 - q0q2); + wy = 2 * bx * (q1q2 - q0q3) + 2 * bz * (q0q1 + q2q3); + wz = 2 * bx * (q0q2 + q1q3) + 2 * bz * (0.5 - q1q1 - q2q2); + + // error is sum of cross product between reference direction of fields and direction measured by sensors + ex = (ay * vz - az * vy) + (my * wz - mz * wy); + ey = (az * vx - ax * vz) + (mz * wx - mx * wz); + ez = (ax * vy - ay * vx) + (mx * wy - my * wx); + + if(ex != 0.0f && ey != 0.0f && ez != 0.0f) + { + exInt = exInt + ex * Ki * halfT; + eyInt = eyInt + ey * Ki * halfT; + ezInt = ezInt + ez * Ki * halfT; + + gx = gx + Kp * ex + exInt; + gy = gy + Kp * ey + eyInt; + gz = gz + Kp * ez + ezInt; + } + + q0 = q0 + (-q1 * gx - q2 * gy - q3 * gz) * halfT; + q1 = q1 + (q0 * gx + q2 * gz - q3 * gy) * halfT; + q2 = q2 + (q0 * gy - q1 * gz + q3 * gx) * halfT; + q3 = q3 + (q0 * gz + q1 * gy - q2 * gx) * halfT; + + norm = invSqrt(q0 * q0 + q1 * q1 + q2 * q2 + q3 * q3); + q0 = q0 * norm; + q1 = q1 * norm; + q2 = q2 * norm; + q3 = q3 * norm; +} + +/** + * @brief Get quaters + * @param None + * @retval None + */ +void IMU::IMU_GetQuater(void) +{ + float MotionVal[9]; + + MPU9250_READ_ACCEL(); + MPU9250_READ_GYRO(); + MPU9250_READ_MAG(); + + MotionVal[0]=gyro[0]/32.8; + MotionVal[1]=gyro[1]/32.8; + MotionVal[2]=gyro[2]/32.8; + MotionVal[3]=accel[0]; + MotionVal[4]=accel[1]; + MotionVal[5]=accel[2]; + MotionVal[6]=magn[0]; + MotionVal[7]=magn[1]; + MotionVal[8]=magn[2]; + + IMU_AHRSupdate((float)MotionVal[0] * 0.0175, (float)MotionVal[1] * 0.0175, (float)MotionVal[2] * 0.0175, + (float)MotionVal[3], (float)MotionVal[4], (float)MotionVal[5], (float)MotionVal[6], (float)MotionVal[7], MotionVal[8]); +} + +/** + * @brief Get Yaw Pitch Roll + * @param None + * @retval None + */ +void IMU::IMU_GetYawPitchRoll(float *Angles) +{ + + int x_tmp,y_tmp; + + IMU_GetQuater(); + + x_tmp = magn[0]; + y_tmp = magn[1]; + if(x_tmp>0x7fff)x_tmp-=0xffff; + if(y_tmp>0x7fff)y_tmp-=0xffff; + + Angles[1] = asin(-2 * q1 * q3 + 2 * q0* q2)* 57.3; // pitch + Angles[2] = atan2(2 * q2 * q3 + 2 * q0 * q1, -2 * q1 * q1 - 2 * q2* q2 + 1)* 57.3; // roll + Angles[0] = atan2(-2 * q1 * q2 - 2 * q0 * q3, 2 * q2 * q2 + 2 * q3 * q3 - 1) * 57.3; //Yaw + Angles[3] = atan2((double)(y_tmp) , (double)(x_tmp)) * (180 / 3.14159265) + 180; // Compass angle in degrees + + +} + +/************************************************************/ +//下面的代码是MPU9250的各种操作 +/************************************************************/ +void IMU::I2C_WriteOneByte(uint8_t DevAddr, uint8_t RegAddr, uint8_t Data) +{ + char data_write[2]; + + data_write[0] = RegAddr; + data_write[1] = Data; + + i2c.write(DevAddr,data_write,2,0); +} + +uint8_t IMU::I2C_ReadOneByte(uint8_t DevAddr, uint8_t RegAddr) +{ + char data_write[2]; + char data_read[2]; + char data; + + data_write[0] = RegAddr; + + i2c.write(DevAddr,data_write,1,1); + i2c.read(DevAddr,data_read,1,0); + + data = data_read[0]; + + return data; +} +/** + * @brief Initializes MPU9250 + * @param None + * @retval None + */ +void IMU::MPU9250_Init() +{ + I2C_WriteOneByte(GYRO_ADDRESS,PWR_MGMT_1, 0x00); + I2C_WriteOneByte(GYRO_ADDRESS,SMPLRT_DIV, 0x07); + I2C_WriteOneByte(GYRO_ADDRESS,CONFIG, 0x06); + I2C_WriteOneByte(GYRO_ADDRESS,GYRO_CONFIG, 0x10); + I2C_WriteOneByte(GYRO_ADDRESS,ACCEL_CONFIG, 0x01); + + Thread::wait(10); + + if(MPU9250_Check()) + { + printf("\r\n MPU9255 Ready!\n"); + } + else + { + printf("\r\n MPU9255 Erro!\n"); + } + + MPU9250_InitGyrOffset(); +} + +/** + * @brief Digital filter + * @param *pIndex: + * @param *pAvgBuffer: + * @param InVal: + * @param pOutVal: + * + * @retval None + * + */ +void IMU::MPU9250_CalAvgValue(uint8_t *pIndex, int16_t *pAvgBuffer, int16_t InVal, int32_t *pOutVal) +{ + uint8_t i; + + *(pAvgBuffer + ((*pIndex) ++)) = InVal; + *pIndex &= 0x07; + + *pOutVal = 0; + for(i = 0; i < 8; i ++) + { + *pOutVal += *(pAvgBuffer + i); + } + *pOutVal >>= 3; +} + +/** + * @brief Get accelerometer datas + * @param None + * @retval None + */ +void IMU::MPU9250_READ_ACCEL(void) +{ + uint8_t i; + int16_t InBuffer[3] = {0}; + static int32_t OutBuffer[3] = {0}; + static MPU9250_AvgTypeDef MPU9250_Filter[3]; + + BUF[0]=I2C_ReadOneByte(ACCEL_ADDRESS,ACCEL_XOUT_L); + BUF[1]=I2C_ReadOneByte(ACCEL_ADDRESS,ACCEL_XOUT_H); + InBuffer[0]= (BUF[1]<<8)|BUF[0]; + + BUF[2]=I2C_ReadOneByte(ACCEL_ADDRESS,ACCEL_YOUT_L); + BUF[3]=I2C_ReadOneByte(ACCEL_ADDRESS,ACCEL_YOUT_H); + InBuffer[1]= (BUF[3]<<8)|BUF[2]; + + BUF[4]=I2C_ReadOneByte(ACCEL_ADDRESS,ACCEL_ZOUT_L); + BUF[5]=I2C_ReadOneByte(ACCEL_ADDRESS,ACCEL_ZOUT_H); + InBuffer[2]= (BUF[5]<<8)|BUF[4]; + + for(i = 0; i < 3; i ++) + { + MPU9250_CalAvgValue(&MPU9250_Filter[i].Index, MPU9250_Filter[i].AvgBuffer, InBuffer[i], OutBuffer + i); + } + accel[0] = *(OutBuffer + 0); + accel[1] = *(OutBuffer + 1); + accel[2] = *(OutBuffer + 2); +} + +/** + * @brief Get gyroscopes datas + * @param None + * @retval None + */ +void IMU::MPU9250_READ_GYRO(void) +{ + uint8_t i; + int16_t InBuffer[3] = {0}; + static int32_t OutBuffer[3] = {0}; + static MPU9250_AvgTypeDef MPU9250_Filter[3]; + + BUF[0]=I2C_ReadOneByte(GYRO_ADDRESS,GYRO_XOUT_L); + BUF[1]=I2C_ReadOneByte(GYRO_ADDRESS,GYRO_XOUT_H); + InBuffer[0]= (BUF[1]<<8)|BUF[0]; + + BUF[2]=I2C_ReadOneByte(GYRO_ADDRESS,GYRO_YOUT_L); + BUF[3]=I2C_ReadOneByte(GYRO_ADDRESS,GYRO_YOUT_H); + InBuffer[1] = (BUF[3]<<8)|BUF[2]; + + BUF[4]=I2C_ReadOneByte(GYRO_ADDRESS,GYRO_ZOUT_L); + BUF[5]=I2C_ReadOneByte(GYRO_ADDRESS,GYRO_ZOUT_H); + InBuffer[2] = (BUF[5]<<8)|BUF[4]; + + for(i = 0; i < 3; i ++) + { + MPU9250_CalAvgValue(&MPU9250_Filter[i].Index, MPU9250_Filter[i].AvgBuffer, InBuffer[i], OutBuffer + i); + } + gyro[0] = *(OutBuffer + 0) - MPU9250_Offset.X; + gyro[1] = *(OutBuffer + 1) - MPU9250_Offset.Y; + gyro[2] = *(OutBuffer + 2) - MPU9250_Offset.Z; +} + +/** + * @brief Get compass datas + * @param None + * @retval None + */ +void IMU::MPU9250_READ_MAG(void) +{ + uint8_t i; + int16_t InBuffer[3] = {0}; + static int32_t OutBuffer[3] = {0}; + static MPU9250_AvgTypeDef MPU9250_Filter[3]; + + I2C_WriteOneByte(GYRO_ADDRESS,0x37,0x02);//turn on Bypass Mode + Thread::wait(10); + I2C_WriteOneByte(MAG_ADDRESS,0x0A,0x01); + Thread::wait(10); + BUF[0]=I2C_ReadOneByte (MAG_ADDRESS,MAG_XOUT_L); + BUF[1]=I2C_ReadOneByte (MAG_ADDRESS,MAG_XOUT_H); + InBuffer[1] =(BUF[1]<<8)|BUF[0]; + + BUF[2]=I2C_ReadOneByte(MAG_ADDRESS,MAG_YOUT_L); + BUF[3]=I2C_ReadOneByte(MAG_ADDRESS,MAG_YOUT_H); + InBuffer[0] = (BUF[3]<<8)|BUF[2]; + + BUF[4]=I2C_ReadOneByte(MAG_ADDRESS,MAG_ZOUT_L); + BUF[5]=I2C_ReadOneByte(MAG_ADDRESS,MAG_ZOUT_H); + InBuffer[2] = (BUF[5]<<8)|BUF[4]; + InBuffer[2] = -InBuffer[2]; + + for(i = 0; i < 3; i ++) + { + MPU9250_CalAvgValue(&MPU9250_Filter[i].Index, MPU9250_Filter[i].AvgBuffer, InBuffer[i], OutBuffer + i); + } + magn[0] = *(OutBuffer + 0)-MPU9250_Magn_Offset.X_Off_Err; + magn[1] = *(OutBuffer + 1)-MPU9250_Magn_Offset.Y_Off_Err; + magn[2] = *(OutBuffer + 2)-MPU9250_Magn_Offset.Z_Off_Err; +} + +/** + * @brief Check MPU9250,ensure communication succeed + * @param None + * @retval true: communicate succeed + * false: communicate fualt + */ +bool IMU::MPU9250_Check(void) +{ + if(WHO_AM_I_VAL == I2C_ReadOneByte(DEFAULT_ADDRESS, WHO_AM_I)) + { + return true; + } + else + { + return false; + } +} + +/** + * @brief Initializes gyroscopes offset + * @param None + * @retval None + */ +void IMU::MPU9250_InitGyrOffset(void) +{ + uint8_t i; + int32_t TempGx = 0, TempGy = 0, TempGz = 0; + + for(i = 0; i < 32; i ++) + { + MPU9250_READ_GYRO(); + + TempGx += gyro[0]; + TempGy += gyro[1]; + TempGz += gyro[2]; + + Thread::wait(1); + } + + MPU9250_Offset.X = TempGx >> 5; + MPU9250_Offset.Y = TempGy >> 5; + MPU9250_Offset.Z = TempGz >> 5; + +} \ No newline at end of file