zoukankan      html  css  js  c++  java
  • Arduino-Mega2560 多种方式 -结算 MPU6050 姿态

    #include <Wire.h>
    #include <math.h>
    
    #define sampleFreq  33.30f      // sample frequency in Hz
    #define twoKpDef  (2.0f * 0.5f) // 2 * proportional gain
    #define twoKiDef  (2.0f * 0.0f) // 2 * integral gain
    #define betaDef    0.1f
    
    
    //---------------------------------------------------------------------------------------------------
    // Variable definitions
    
    volatile float twoKp = twoKpDef;                      // 2 * proportional gain (Kp)
    volatile float twoKi = twoKiDef;                      // 2 * integral gain (Ki)
    volatile float beta = betaDef;    
    volatile float q0 = 1.0f, q1 = 0.0f, q2 = 0.0f, q3 = 0.0f;          // quaternion of sensor frame relative to auxiliary frame
    volatile float integralFBx = 0.0f,  integralFBy = 0.0f, integralFBz = 0.0f; // integral error terms scaled by Ki
    
    float phi;
    float theta;
    float psi;
    extern volatile float q0, q1, q2, q3;
    unsigned long time_ms = 0;
    
    void setup() {
      Serial.begin(9600);
    
      Wire.begin();
      Wire.beginTransmission(0x68);
      Wire.write(0x6B);
      Wire.write(0);
      Wire.endTransmission(true);
    }
    
    void loop() {
      int x,y,z = 0;
      int t = 0;
      int a,b,c = 0;
      Wire.beginTransmission(0x68);
      Wire.write(0x3B);
      Wire.requestFrom(0x68, 14, true);
      Wire.endTransmission(true);
      x = Wire.read() << 8 | Wire.read();
      y = Wire.read() << 8 | Wire.read();
      z = Wire.read() << 8 | Wire.read();
      t = Wire.read() << 8 | Wire.read();
      a = Wire.read() << 8 | Wire.read();
      b = Wire.read() << 8 | Wire.read();
      c = Wire.read() << 8 | Wire.read();
      double ax,ay,az;
      double gx,gy,gz;
      ax = x/16384.00f;
      ay = y/16384.00f;
      az = z/16384.00f;
      gx = a*3.1415926f/131.00f/180.00f;
      gy = b*3.1415926f/131.00f/180.00f;
      gz = c*3.1415926f/131.00f/180.00f;
      MadgwickAHRSupdateIMU(gx, gy, gz, ax, ay, az);
      time_ms = millis();
    //  Serial.print(time_ms);
      Serial.print(" ");
      Serial.print(q0);
      Serial.print(" ");
      Serial.print(q1);
      Serial.print(" ");
      Serial.print(q2);
      Serial.print(" ");
      Serial.print(q3);
      Serial.println(" ");
    //  Serial.print(psi);
    //  Serial.print(" ");
    //  Serial.print(theta);
    //  Serial.print(" ");
    //  Serial.print(phi);
    //  Serial.println(" ");
    
    
    //  Serial.print(ax);
    //  Serial.print(" ");
    //  Serial.print(ay);
    //  Serial.print(" ");
    //  Serial.print(az);
    //  Serial.println(" ");
    //  Serial.print(gx);
    //  Serial.print(" ");
    //  Serial.print(gy);
    //  Serial.print(" ");
    //  Serial.print(gz);
    //  Serial.println(" ");
    }
    
    
    void MahonyAHRSupdateIMU(float gx, float gy, float gz, float ax, float ay, float az) {
      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;
    
      phi = atan(2*(q1*q2+q0*q3)/(-2*q2*q2+1-2*q3*q3));
      theta = asin(2*(q1*q3-q0*q2));
      psi = atan(2*(q1*q2+q0*q3)/(-2*q1*q1+1-2*q2*q2));
    }
    
    void MadgwickAHRSupdateIMU(float gx, float gy, float gz, float ax, float ay, float az) {
      float recipNorm;
      float s0, s1, s2, s3;
      float qDot1, qDot2, qDot3, qDot4;
      float _2q0, _2q1, _2q2, _2q3, _4q0, _4q1, _4q2 ,_8q1, _8q2, q0q0, q1q1, q2q2, q3q3;
    
      // Rate of change of quaternion from gyroscope
      qDot1 = 0.5f * (-q1 * gx - q2 * gy - q3 * gz);
      qDot2 = 0.5f * (q0 * gx + q2 * gz - q3 * gy);
      qDot3 = 0.5f * (q0 * gy - q1 * gz + q3 * gx);
      qDot4 = 0.5f * (q0 * gz + q1 * gy - q2 * gx);
    
      // 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;   
    
        // Auxiliary variables to avoid repeated arithmetic
        _2q0 = 2.0f * q0;
        _2q1 = 2.0f * q1;
        _2q2 = 2.0f * q2;
        _2q3 = 2.0f * q3;
        _4q0 = 4.0f * q0;
        _4q1 = 4.0f * q1;
        _4q2 = 4.0f * q2;
        _8q1 = 8.0f * q1;
        _8q2 = 8.0f * q2;
        q0q0 = q0 * q0;
        q1q1 = q1 * q1;
        q2q2 = q2 * q2;
        q3q3 = q3 * q3;
    
        // Gradient decent algorithm corrective step
        s0 = _4q0 * q2q2 + _2q2 * ax + _4q0 * q1q1 - _2q1 * ay;
        s1 = _4q1 * q3q3 - _2q3 * ax + 4.0f * q0q0 * q1 - _2q0 * ay - _4q1 + _8q1 * q1q1 + _8q1 * q2q2 + _4q1 * az;
        s2 = 4.0f * q0q0 * q2 + _2q0 * ax + _4q2 * q3q3 - _2q3 * ay - _4q2 + _8q2 * q1q1 + _8q2 * q2q2 + _4q2 * az;
        s3 = 4.0f * q1q1 * q3 - _2q1 * ax + 4.0f * q2q2 * q3 - _2q2 * ay;
        recipNorm = invSqrt(s0 * s0 + s1 * s1 + s2 * s2 + s3 * s3); // normalise step magnitude
        s0 *= recipNorm;
        s1 *= recipNorm;
        s2 *= recipNorm;
        s3 *= recipNorm;
    
        // Apply feedback step
        qDot1 -= beta * s0;
        qDot2 -= beta * s1;
        qDot3 -= beta * s2;
        qDot4 -= beta * s3;
      }
    
      // Integrate rate of change of quaternion to yield quaternion
      q0 += qDot1 * (1.0f / sampleFreq);
      q1 += qDot2 * (1.0f / sampleFreq);
      q2 += qDot3 * (1.0f / sampleFreq);
      q3 += qDot4 * (1.0f / sampleFreq);
    
      // Normalise quaternion
      recipNorm = invSqrt(q0 * q0 + q1 * q1 + q2 * q2 + q3 * q3);
      q0 *= recipNorm;
      q1 *= recipNorm;
      q2 *= recipNorm;
      q3 *= recipNorm;
    }
    
    
    float 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;
    }
    View Code

    上面的代码是Mahony及Madgwick的姿态解算方式。

    #include <Kalman.h>
    #include <Wire.h>
    #include <Math.h>
    
    float fRad2Deg = 57.295779513f; //将弧度转为角度的乘数
    const int MPU = 0x68; //MPU-6050的I2C地址
    const int nValCnt = 7; //一次读取寄存器的数量
    
    const int nCalibTimes = 1000; //校准时读数的次数
    int calibData[nValCnt]; //校准数据
    
    unsigned long nLastTime = 0; //上一次读数的时间
    float fLastRoll = 0.0f; //上一次滤波得到的Roll角
    float fLastPitch = 0.0f; //上一次滤波得到的Pitch角
    Kalman kalmanRoll; //Roll角滤波器
    Kalman kalmanPitch; //Pitch角滤波器
    
    void setup() {
      Serial.begin(9600); //初始化串口,指定波特率
      Wire.begin(); //初始化Wire库
      WriteMPUReg(0x6B, 0); //启动MPU6050设备
    
      Calibration(); //执行校准
      nLastTime = micros(); //记录当前时间
    }
    
    void loop() {
      int readouts[nValCnt];
      ReadAccGyr(readouts); //读出测量值
      
      float realVals[7];
      Rectify(readouts, realVals); //根据校准的偏移量进行纠正
    
      //计算加速度向量的模长,均以g为单位
      float fNorm = sqrt(realVals[0] * realVals[0] + realVals[1] * realVals[1] + realVals[2] * realVals[2]);
      float fRoll = GetRoll(realVals, fNorm); //计算Roll角
      if (realVals[1] > 0) {
        fRoll = -fRoll;
      }
      float fPitch = GetPitch(realVals, fNorm); //计算Pitch角
      if (realVals[0] < 0) {
        fPitch = -fPitch;
      }
    
      //计算两次测量的时间间隔dt,以秒为单位
      unsigned long nCurTime = micros();
      float dt = (double)(nCurTime - nLastTime) / 1000000.0;
      //对Roll角和Pitch角进行卡尔曼滤波
      float fNewRoll = kalmanRoll.getAngle(fRoll, realVals[4], dt);
      float fNewPitch = kalmanPitch.getAngle(fPitch, realVals[5], dt);
      //跟据滤波值计算角度速
      float fRollRate = (fNewRoll - fLastRoll) / dt;
      float fPitchRate = (fNewPitch - fLastPitch) / dt;
     
     //更新Roll角和Pitch角
      fLastRoll = fNewRoll;
      fLastPitch = fNewPitch;
      //更新本次测的时间
      nLastTime = nCurTime;
    
      //向串口打印输出Roll角和Pitch角,运行时在Arduino的串口监视器中查看
      Serial.print("Roll:");
      Serial.print(fNewRoll); Serial.print('(');
      Serial.print(fRollRate); Serial.print("),	Pitch:");
      Serial.print(fNewPitch); Serial.print('(');
      Serial.print(fPitchRate); Serial.print(")
    ");
      delay(10);
    }
    
    //向MPU6050写入一个字节的数据
    //指定寄存器地址与一个字节的值
    void WriteMPUReg(int nReg, unsigned char nVal) {
      Wire.beginTransmission(MPU);
      Wire.write(nReg);
      Wire.write(nVal);
      Wire.endTransmission(true);
    }
    
    //从MPU6050读出一个字节的数据
    //指定寄存器地址,返回读出的值
    unsigned char ReadMPUReg(int nReg) {
      Wire.beginTransmission(MPU);
      Wire.write(nReg);
      Wire.requestFrom(MPU, 1, true);
      Wire.endTransmission(true);
      return Wire.read();
    }
    
    //从MPU6050读出加速度计三个分量、温度和三个角速度计
    //保存在指定的数组中
    void ReadAccGyr(int *pVals) {
      Wire.beginTransmission(MPU);
      Wire.write(0x3B);
      Wire.requestFrom(MPU, nValCnt * 2, true);
      Wire.endTransmission(true);
      for (long i = 0; i < nValCnt; ++i) {
        pVals[i] = Wire.read() << 8 | Wire.read();
      }
    }
    
    //对大量读数进行统计,校准平均偏移量
    void Calibration()
    {
      float valSums[7] = {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0};
      //先求和
      for (int i = 0; i < nCalibTimes; ++i) {
        int mpuVals[nValCnt];
        ReadAccGyr(mpuVals);
        for (int j = 0; j < nValCnt; ++j) {
          valSums[j] += mpuVals[j];
        }
      }
      //再求平均
      for (int i = 0; i < nValCnt; ++i) {
        calibData[i] = int(valSums[i] / nCalibTimes);
      }
      calibData[2] += 16384; //设芯片Z轴竖直向下,设定静态工作点。
    }
    
    //算得Roll角。算法见文档。
    float GetRoll(float *pRealVals, float fNorm) {
      float fNormXZ = sqrt(pRealVals[0] * pRealVals[0] + pRealVals[2] * pRealVals[2]);
      float fCos = fNormXZ / fNorm;
      return acos(fCos) * fRad2Deg;
    }
    
    //算得Pitch角。算法见文档。
    float GetPitch(float *pRealVals, float fNorm) {
      float fNormYZ = sqrt(pRealVals[1] * pRealVals[1] + pRealVals[2] * pRealVals[2]);
      float fCos = fNormYZ / fNorm;
      return acos(fCos) * fRad2Deg;
    }
    
    //对读数进行纠正,消除偏移,并转换为物理量。公式见文档。
    void Rectify(int *pReadout, float *pRealVals) {
      for (int i = 0; i < 3; ++i) {
        pRealVals[i] = (float)(pReadout[i] - calibData[i]) / 16384.0f;
      }
      pRealVals[3] = pReadout[3] / 340.0f + 36.53;
      for (int i = 4; i < 7; ++i) {
        pRealVals[i] = (float)(pReadout[i] - calibData[i]) / 131.0f;
      }
    }
    View Code

    上面的代码是通过卡尔曼滤波获得传感器姿态。

     

  • 相关阅读:
    Flask第31课——include标签
    flask第30篇——宏macro和import标签
    HTML第三课——css盒子
    HTML第二课——css【2】
    HTML第二课——css
    HTML第一课——基础知识普及【2】
    转一篇数据库面试题
    自创建数字证书,安装到浏览器
    【转】Base64算法详解
    【转】二维码生成原理
  • 原文地址:https://www.cnblogs.com/TIANHUAHUA/p/8260464.html
Copyright © 2011-2022 走看看