OpenEdv-开源电子网

 找回密码
 立即注册
正点原子全套STM32/Linux/FPGA开发资料,上千讲STM32视频教程免费下载...
查看: 7838|回复: 5

求助mpu6050自启重置水平面的问题

[复制链接]

1

主题

2

帖子

0

精华

新手入门

积分
9
金钱
9
注册时间
2021-8-5
在线时间
2 小时
发表于 2021-8-5 15:47:08 | 显示全部楼层 |阅读模式
1金钱
用的uno接受mpu6050的数据,再由串口把x y两个方向的角度数值给matlab处理。使用过程中,我发现一个阻拦在实际应用中的问题:假如测智能小车的底盘倾角,放在水平面上时,多次接受数据发现:x和y方向的数据不能很好的提供水平时的基准值。如图中,有时候x方向是负角度,有时候是正的,这就很不合理,至于水平面上没有触碰却有这个结果。那对于以后应用时就很难办了,我无法确定某一次测量基准时得到的数据是合理的,是能为之后底盘倾角改变测量倾角时提供参考的,请问有什么办法能解决吗。 1.PNG
这是arduino代码(Arduino教程:MPU6050的数据获取、分析与处理 - 知乎 (zhihu.com)

  1. // 本代码版权归Devymex所有,以GNU GENERAL PUBLIC LICENSE V3.0发布
  2. // http://www.gnu.org/licenses/gpl-3.0.en.html
  3. // 相关文档参见作者于知乎专栏发表的原创文章:
  4. // http://zhuanlan.zhihu.com/devymex/20082486

  5. //连线方法
  6. //MPU-UNO
  7. //VCC-VCC
  8. //GND-GND
  9. //SCL-A5
  10. //SDA-A4
  11. //INT-2 (Optional)

  12. #include <Kalman.h>
  13. #include <Wire.h>
  14. #include <Math.h>

  15. float fRad2Deg = 57.295779513f; //将弧度转为角度的乘数
  16. const int MPU = 0x68; //MPU-6050的I2C地址
  17. const int nValCnt = 7; //一次读取寄存器的数量

  18. const int nCalibTimes = 1000; //校准时读数的次数
  19. int calibData[nValCnt]; //校准数据

  20. unsigned long nLastTime = 0; //上一次读数的时间
  21. float fLastRoll = 0.0f; //上一次滤波得到的Roll角
  22. float fLastPitch = 0.0f; //上一次滤波得到的Pitch角
  23. Kalman kalmanRoll; //Roll角滤波器
  24. Kalman kalmanPitch; //Pitch角滤波器

  25. void setup() {
  26.   Serial.begin(9600); //初始化串口,指定波特率
  27.   Wire.begin(); //初始化Wire库
  28.   WriteMPUReg(0x6B, 0); //启动MPU6050设备

  29.   Calibration(); //执行校准
  30.   nLastTime = micros(); //记录当前时间
  31. }

  32. void loop() {
  33.   int readouts[nValCnt];
  34.   ReadAccGyr(readouts); //读出测量值
  35.   
  36.   float realVals[7];
  37.   Rectify(readouts, realVals); //根据校准的偏移量进行纠正

  38.   //计算加速度向量的模长,均以g为单位
  39.   float fNorm = sqrt(realVals[0] * realVals[0] + realVals[1] * realVals[1] + realVals[2] * realVals[2]);
  40.   float fRoll = GetRoll(realVals, fNorm); //计算Roll角
  41.   if (realVals[1] > 0) {
  42.     fRoll = -fRoll;
  43.   }
  44.   float fPitch = GetPitch(realVals, fNorm); //计算Pitch角
  45.   if (realVals[0] < 0) {
  46.     fPitch = -fPitch;
  47.   }

  48.   //计算两次测量的时间间隔dt,以秒为单位
  49.   unsigned long nCurTime = micros();
  50.   float dt = (double)(nCurTime - nLastTime) / 1000000.0;
  51.   //对Roll角和Pitch角进行卡尔曼滤波
  52.   float fNewRoll = kalmanRoll.getAngle(fRoll, realVals[4], dt);
  53.   float fNewPitch = kalmanPitch.getAngle(fPitch, realVals[5], dt);
  54.   //跟据滤波值计算角度速
  55.   float fRollRate = (fNewRoll - fLastRoll) / dt;
  56.   float fPitchRate = (fNewPitch - fLastPitch) / dt;

  57. //更新Roll角和Pitch角
  58.   fLastRoll = fNewRoll;
  59.   fLastPitch = fNewPitch;
  60.   //更新本次测的时间
  61.   nLastTime = nCurTime;

  62.   //向串口打印输出Roll角和Pitch角,运行时在Arduino的串口监视器中查看
  63.   Serial.print("Roll:");
  64.   Serial.print(fNewRoll); Serial.print('(');
  65.   Serial.print(fRollRate); Serial.print("),\tPitch:");
  66.   Serial.print(fNewPitch); Serial.print('(');
  67.   Serial.print(fPitchRate); Serial.print(")\n");
  68.   delay(10);
  69. }

  70. //向MPU6050写入一个字节的数据
  71. //指定寄存器地址与一个字节的值
  72. void WriteMPUReg(int nReg, unsigned char nVal) {
  73.   Wire.beginTransmission(MPU);
  74.   Wire.write(nReg);
  75.   Wire.write(nVal);
  76.   Wire.endTransmission(true);
  77. }

  78. //从MPU6050读出一个字节的数据
  79. //指定寄存器地址,返回读出的值
  80. unsigned char ReadMPUReg(int nReg) {
  81.   Wire.beginTransmission(MPU);
  82.   Wire.write(nReg);
  83.   Wire.requestFrom(MPU, 1, true);
  84.   Wire.endTransmission(true);
  85.   return Wire.read();
  86. }

  87. //从MPU6050读出加速度计三个分量、温度和三个角速度计
  88. //保存在指定的数组中
  89. void ReadAccGyr(int *pVals) {
  90.   Wire.beginTransmission(MPU);
  91.   Wire.write(0x3B);
  92.   Wire.requestFrom(MPU, nValCnt * 2, true);
  93.   Wire.endTransmission(true);
  94.   for (long i = 0; i < nValCnt; ++i) {
  95.     pVals[i] = Wire.read() << 8 | Wire.read();
  96.   }
  97. }

  98. //对大量读数进行统计,校准平均偏移量
  99. void Calibration()
  100. {
  101.   float valSums[7] = {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0};
  102.   //先求和
  103.   for (int i = 0; i < nCalibTimes; ++i) {
  104.     int mpuVals[nValCnt];
  105.     ReadAccGyr(mpuVals);
  106.     for (int j = 0; j < nValCnt; ++j) {
  107.       valSums[j] += mpuVals[j];
  108.     }
  109.   }
  110.   //再求平均
  111.   for (int i = 0; i < nValCnt; ++i) {
  112.     calibData[i] = int(valSums[i] / nCalibTimes);
  113.   }
  114.   calibData[2] += 16384; //设芯片Z轴竖直向下,设定静态工作点。
  115. }

  116. //算得Roll角。算法见文档。
  117. float GetRoll(float *pRealVals, float fNorm) {
  118.   float fNormXZ = sqrt(pRealVals[0] * pRealVals[0] + pRealVals[2] * pRealVals[2]);
  119.   float fCos = fNormXZ / fNorm;
  120.   return acos(fCos) * fRad2Deg;
  121. }

  122. //算得Pitch角。算法见文档。
  123. float GetPitch(float *pRealVals, float fNorm) {
  124.   float fNormYZ = sqrt(pRealVals[1] * pRealVals[1] + pRealVals[2] * pRealVals[2]);
  125.   float fCos = fNormYZ / fNorm;
  126.   return acos(fCos) * fRad2Deg;
  127. }

  128. //对读数进行纠正,消除偏移,并转换为物理量。公式见文档。
  129. void Rectify(int *pReadout, float *pRealVals) {
  130.   for (int i = 0; i < 3; ++i) {
  131.     pRealVals[i] = (float)(pReadout[i] - calibData[i]) / 16384.0f;
  132.   }
  133.   pRealVals[3] = pReadout[3] / 340.0f + 36.53;
  134.   for (int i = 4; i < 7; ++i) {
  135.     pRealVals[i] = (float)(pReadout[i] - calibData[i]) / 131.0f;
  136.   }
  137. }
复制代码
代码里是启动模块重置基准面还是打开串口时重置?这是一个困惑的地方。
如果是打开串口时重置,解决起来很容易,matlab写的app只需不关闭串口一直获取数据即可
如果是启动时重置,这就不知道怎么解决了

放的大
的撒在水平

面上时,多次接受数据发现:x和y方向的数据不能很好的提供水平时的基准值。如图中,有时候x方向是负角度,有时候是正的,这就很不合理,至于水平面上没有触碰却有这个结果。那对于以后应用时就很难办了,我无法确定某一次测量基准时得到的数据是合理的,是能为之后底盘倾角改变测量倾角时提供参考的,请问有什么办法能解决吗。



正点原子逻辑分析仪DL16劲爆上市
回复

使用道具 举报

558

主题

11万

帖子

34

精华

管理员

Rank: 12Rank: 12Rank: 12

积分
164897
金钱
164897
注册时间
2010-12-1
在线时间
2100 小时
发表于 2021-8-6 02:43:19 | 显示全部楼层
回复

使用道具 举报

1

主题

2

帖子

0

精华

新手入门

积分
9
金钱
9
注册时间
2021-8-5
在线时间
2 小时
 楼主| 发表于 2021-8-6 09:35:57 | 显示全部楼层
如果想实现始终以地面为基准,像手机水平仪那样,那改进思路应该是怎样的呢
回复

使用道具 举报

13

主题

643

帖子

0

精华

金牌会员

Rank: 6Rank: 6

积分
2432
金钱
2432
注册时间
2019-12-28
在线时间
527 小时
发表于 2021-8-6 10:28:38 | 显示全部楼层
帮顶  
回复

使用道具 举报

1

主题

3

帖子

0

精华

新手上路

积分
34
金钱
34
注册时间
2021-6-19
在线时间
8 小时
发表于 2021-8-12 17:37:50 | 显示全部楼层
同问,置于水平面上发生晃动之后,再次回到原来的位置上就会发生航偏角出现数值正/负方向的变化
回复

使用道具 举报

1

主题

5

帖子

0

精华

新手入门

积分
14
金钱
14
注册时间
2021-11-16
在线时间
5 小时
发表于 2021-11-25 18:21:20 | 显示全部楼层
seraluwa 发表于 2021-8-6 09:35
如果想实现始终以地面为基准,像手机水平仪那样,那改进思路应该是怎样的呢

楼主实现了吗,怎么才能设定某个姿态为基准,而不是初始化时的姿态为基准
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则



关闭

原子哥极力推荐上一条 /2 下一条

正点原子公众号

QQ|手机版|OpenEdv-开源电子网 ( 粤ICP备12000418号-1 )

GMT+8, 2024-6-10 21:56

Powered by OpenEdv-开源电子网

© 2001-2030 OpenEdv-开源电子网

快速回复 返回顶部 返回列表