求助有关姿态传感器的问题

事情是这样的,我正在开发惯性导航的航电用于火箭,因为暂时不需要太高精度,所以只用普通的姿态传感器,mpu6050收集数据,但是不知道为什么,平放在桌上时,x轴角速度读数就有0.02rad/s,加速度有10m/s²,虽然看起来误差不大,但是随着时间的推移必然导致很大误差,运动状态产生的误差说不定更大。我假设是线性的修复了一下,但是不理想。我用的是四元数法,不管是我自己写还是去网上找代码,飘得都很厉害,运动缓慢还好,可以接受,但角速度一快精度就下滑得很离谱,我知道四元数法在角速度大的情况下精度低,但应该不至于低到这么离谱。我想问一下mpu6050都这样吗?但示数肉眼发现不对这有点离谱了吧……还是说我被坑了?但是我买了两次都这样……如果mpu6050不能用的话,应该选哪个模块呢?希望做过惯性导航的大佬能推荐下。最好50元以下,精度不必很高,因为我现在火箭算上下降也就滞空100s左右,没必要用精密的陀螺仪,但是别向这样错误明显漂移严重就行了。谢谢各位


来自:航空航天 / 喷气推进特殊话题:提问/求助
15
1
已屏蔽 原因:{{ notice.reason }}已屏蔽
{{notice.noticeContent}}
~~空空如也
可爱的叔丁基锂
1年6个月前 IP:江苏
920952

没有校准吗?这个数据是不正常的。不过建议搭配电子罗盘使用。

引用
评论
加载评论中,请稍候...
200字以内,仅用于支线交流,主线讨论请采用回复功能。
折叠评论
Colin胡
1年6个月前 IP:湖南
920959

电子陀螺仪是要校准的,静置于桌面上,那个读数基本是不变的,减掉就行。


而对于加速度计而言,你可以试一试自由落体状态下是什么样的。自由落体时读数为零。


最后,多看数据手册,网上的英文教程。

引用
评论
加载评论中,请稍候...
200字以内,仅用于支线交流,主线讨论请采用回复功能。
折叠评论
BSP
1年6个月前 IP:湖南
920962

闲鱼有卖拆机的adis16505-2性能好,也不是很贵,但是是拆机的,量程对于火箭应用可能也不够大。

ICM42688p不贵,是消费级的单芯片产品。

或者博世的bmi088,比前两年便宜不少。

引用
评论
加载评论中,请稍候...
200字以内,仅用于支线交流,主线讨论请采用回复功能。
折叠评论
某倒吊的亚雷斯塔作者
1年6个月前 IP:广东
920963
引用Colin胡发表于2楼的内容
电子陀螺仪是要校准的,静置于桌面上,那个读数基本是不变的,减掉就行。而对于加速度计而言,你可以试一试...


是的,角速度一直是-0.02,0.00,0.00,可以x轴直接加上0.02吗?另外重力加速度平放是10左右,调整是减0.2还是乘0.98呢?

引用
评论
加载评论中,请稍候...
200字以内,仅用于支线交流,主线讨论请采用回复功能。
折叠评论
信仰は儚き人間の為に
1年6个月前 IP:广东
920966
引用某倒吊的亚雷斯塔发表于4楼的内容
是的,角速度一直是-0.02,0.00,0.00,可以x轴直接加上0.02吗?另外重力加速度平放是1...

速率陀螺减去零飘,加速度计要椭球矫正。

引用
评论
加载评论中,请稍候...
200字以内,仅用于支线交流,主线讨论请采用回复功能。
折叠评论
某倒吊的亚雷斯塔作者
1年6个月前 IP:广东
920994

谢谢各位,我又发现了个离谱的现象,有个操作是四元数的规范化处理,本来应该是若干次更新后来将四元数整理为规范四元数的,但是不知道为啥加了这一个之后精度反而降低了不少……我用的arduino,什么dmp都没有库,再加上也不需很高精度,干脆校准一下凑合凑合用了吧 sticker

引用
评论
加载评论中,请稍候...
200字以内,仅用于支线交流,主线讨论请采用回复功能。
折叠评论
某倒吊的亚雷斯塔作者
1年5个月前 IP:广东
921480

再次求助一下各位,我使用chatgpt帮助,再加以改动,直接开了dmp,读取四元数,但是仍然有许多问题,我排查了好几天,都没有找到哪里出了问题。希望能得到帮助。

首先,我怀疑问题可能出现再硬件上,我就先说下我的硬件,用的arduino

Nano,atmega328p。

以下是第一份代码,也是chatgpt在我提了一堆问题后修修补补出来的。以下是代码

#include <Wire.h>
#include "MPU6050.h"
#include "I2Cdev.h"
#include "helper_3dmath.h"

MPU6050 mpu;

Quaternion quat;
VectorFloat gravity;
VectorInt16 aaReal;
VectorInt16 aaWorld;
float ypr[3];

uint8_t mpuIntStatus;
bool dmpReady;
uint16_t packetSize;
uint16_t fifoCount;
uint8_t fifoBuffer[128];
unsigned long t;

void setup() {
  Wire.begin();
  Serial.begin(115200);
  mpu.initialize();
  mpu.dmpInitialize();
  mpu.setXGyroOffset(220);
  mpu.setYGyroOffset(76);
  mpu.setZGyroOffset(-85);
  mpu.setZAccelOffset(1788);

  if (!mpu.testConnection()) {
    Serial.println("MPU6050 connection failed");
    while (1) {}
  }
  mpu.setDMPEnabled(true);
  mpuIntStatus = mpu.getIntStatus();
  dmpReady = true;
 
  packetSize = mpu.dmpGetFIFOPacketSize();
}

void loop() {
  if (!dmpReady) return;
 
  while (fifoCount < packetSize) {
    fifoCount = mpu.getFIFOCount();
  }
 
  mpu.getFIFOBytes(fifoBuffer, packetSize);
 
  fifoCount -= packetSize;

  mpu.dmpGetQuaternion(&quat, fifoBuffer);
  mpu.dmpGetEuler(ypr, &quat);
  mpu.dmpGetGravity(&gravity, &quat);
  mpu.dmpGetAccel(&aaReal, fifoBuffer);
  mpu.dmpGetLinearAccelInWorld(&aaWorld, &aaReal, &quat);
  memset(fifoBuffer, 0, packetSize);// 清空fifoBuffer数组
  if (millis()-t>100){
    Serial.print(" q0: ");
    Serial.print(quat.w);
    Serial.print(" q1: ");
    Serial.print(quat.x);
    Serial.print(" q2: ");
    Serial.print(quat.y);
    Serial.print(" q3: ");
    Serial.print(quat.z);
    Serial.print(" yaw: ");
    Serial.print(ypr[0]*180/M_PI);
    Serial.print(" pitch: ");
    Serial.print(ypr[1]*180/M_PI);
    Serial.print(" roll: ");
    Serial.print(ypr[2]*180/M_PI);
    Serial.print(" gx: ");
    Serial.print((float)gravity.x * 9.8);
    Serial.print(" gy: ");
    Serial.print((float)gravity.y * 9.8);
    Serial.print(" gz: ");
    Serial.print((float)gravity.z * 9.8);
    Serial.print(" arx: ");
    Serial.print(aaReal.x);
    Serial.print(" ary: ");
    Serial.print(aaReal.y);
    Serial.print(" arz: ");
    Serial.print(aaReal.z);
    Serial.print(" awx: ");
    Serial.print(aaWorld.x);
    Serial.print(" awy: ");
    Serial.print(aaWorld.y);
    Serial.print(" awz: ");
    Serial.println(aaWorld.z);    
    t = millis();
  }
}

这个代码的校准值都是chatgpt给的,肯定不对,但是先不管它。这个代码有以下几点问题。有时候,他会输出一段时间正常的,然后突然输出错误的数据,然后错误一段时间后输出正确的数据,然后几分钟后就停止输出了,有时候他会输出正常,但是几分钟后就停止输出了。输出正确的时候数据是没问题的,我手算了下四元数符合,就是校准问题数据在飘而已,还可以接受。这个问题我完全找不到原因。还有,他输出的加速度数据换算来只有0.5g往上

微信截图_20230607121345.png

接下来是第二个代码。我改了量程,并且重新校准,毕竟用在火箭上嘛。这个校准是没问题的,单独输出三轴加速度和角速度的时候非常完美。以下是代码

#include <Wire.h>
#include "MPU6050.h"
#include "I2Cdev.h"
#include "helper_3dmath.h"

MPU6050 mpu;

Quaternion quat;
VectorFloat gravity;
VectorInt16 aaReal;
VectorInt16 aaWorld;
float ypr[3];

uint8_t mpuIntStatus;
bool dmpReady;
uint16_t packetSize;
uint16_t fifoCount;
uint8_t fifoBuffer[128];
unsigned long t;

void setup() {
  Wire.begin();
  Serial.begin(115200);
  mpu.initialize();
  mpu.dmpInitialize();
  //设置量程
  mpu.setFullScaleAccelRange(MPU6050_ACCEL_FS_16);
  mpu.setFullScaleGyroRange(MPU6050_GYRO_FS_1000);
  //校准,不同传感器在这里改
  mpu.setXGyroOffset(36);
  mpu.setYGyroOffset(-2);
  mpu.setZGyroOffset(-3);
  mpu.setXAccelOffset(-3252);
  mpu.setYAccelOffset(1713);
  mpu.setZAccelOffset(1507);

  if (!mpu.testConnection()) {
    Serial.println("MPU6050 connection failed");
    while (1) {}
  }
  mpu.setDMPEnabled(true);
  mpuIntStatus = mpu.getIntStatus();
  dmpReady = true;
 
  packetSize = mpu.dmpGetFIFOPacketSize();
}

void loop() {
  if (!dmpReady) return;
 
  while (fifoCount < packetSize) {
    fifoCount = mpu.getFIFOCount();
  }
 
  mpu.getFIFOBytes(fifoBuffer, packetSize);
 
  fifoCount -= packetSize;

  mpu.dmpGetQuaternion(&quat, fifoBuffer);
  mpu.dmpGetEuler(ypr, &quat);
  mpu.dmpGetGravity(&gravity, &quat);
  mpu.dmpGetAccel(&aaReal, fifoBuffer);
  mpu.dmpGetLinearAccelInWorld(&aaWorld, &aaReal, &quat);
  memset(fifoBuffer, 0, packetSize);// 清空fifoBuffer数组
  if (millis()-t>100){
    Serial.print(" q0: ");
    Serial.print(quat.w);
    Serial.print(" q1: ");
    Serial.print(quat.x);
    Serial.print(" q2: ");
    Serial.print(quat.y);
    Serial.print(" q3: ");
    Serial.print(quat.z);
    Serial.print(" yaw: ");
    Serial.print(ypr[0]*180/M_PI);
    Serial.print(" pitch: ");
    Serial.print(ypr[1]*180/M_PI);
    Serial.print(" roll: ");
    Serial.print(ypr[2]*180/M_PI);
    Serial.print(" gx: ");
    Serial.print((float)gravity.x * 9.8);
    Serial.print(" gy: ");
    Serial.print((float)gravity.y * 9.8);
    Serial.print(" gz: ");
    Serial.print((float)gravity.z * 9.8);
    Serial.print(" arx: ");
    Serial.print(aaReal.x);
    Serial.print(" ary: ");
    Serial.print(aaReal.y);
    Serial.print(" arz: ");
    Serial.print(aaReal.z);
    Serial.print(" awx: ");
    Serial.print(aaWorld.x);
    Serial.print(" awy: ");
    Serial.print(aaWorld.y);
    Serial.print(" awz: ");
    Serial.println(aaWorld.z);    
    t = millis();
  }
}

但是这一次输出仍有代码一的问题,而且更要命的是,正常输出时数据也是错的,比如转90°它会显示到100多,而且四元数都是有问题的。

然后我又改了一遍

#include <Wire.h>
#include "MPU6050.h"
#include "I2Cdev.h"
#include "helper_3dmath.h"

MPU6050 mpu;

Quaternion quat;
VectorFloat gravity;
VectorInt16 aaReal;
VectorInt16 aaWorld;
float ypr[3];

uint8_t mpuIntStatus;
bool dmpReady;
uint16_t packetSize;
uint16_t fifoCount;
uint8_t fifoBuffer[128];
unsigned long t;

void setup() {
  Wire.begin();
  Serial.begin(115200);
  mpu.initialize();
  mpu.dmpInitialize();
  //设置量程
  mpu.setFullScaleAccelRange(MPU6050_ACCEL_FS_16);
  //校准,不同传感器在这里改
  mpu.setXAccelOffset(-3252);
  mpu.setYAccelOffset(1713);
  mpu.setZAccelOffset(1507);
  /*mpu.setXGyroOffset(220);
  mpu.setYGyroOffset(76);
  mpu.setZGyroOffset(-85);
  mpu.setZAccelOffset(1788);*/

  if (!mpu.testConnection()) {
    Serial.println("MPU6050 connection failed");
    while (1) {}
  }
  mpu.setDMPEnabled(true);
  mpuIntStatus = mpu.getIntStatus();
  dmpReady = true;
 
  packetSize = mpu.dmpGetFIFOPacketSize();
}

void loop() {
  if (!dmpReady) return;
 
  while (fifoCount < packetSize) {
    fifoCount = mpu.getFIFOCount();
  }
 
  mpu.getFIFOBytes(fifoBuffer, packetSize);
 
  fifoCount -= packetSize;

  mpu.dmpGetQuaternion(&quat, fifoBuffer);
  mpu.dmpGetEuler(ypr, &quat);
  mpu.dmpGetGravity(&gravity, &quat);
  mpu.dmpGetAccel(&aaReal, fifoBuffer);
  mpu.dmpGetLinearAccelInWorld(&aaWorld, &aaReal, &quat);
  memset(fifoBuffer, 0, packetSize);// 清空fifoBuffer数组
  if (millis()-t>100){
    Serial.print(" q0: ");
    Serial.print(quat.w);
    Serial.print(" q1: ");
    Serial.print(quat.x);
    Serial.print(" q2: ");
    Serial.print(quat.y);
    Serial.print(" q3: ");
    Serial.print(quat.z);
    Serial.print(" yaw: ");
    Serial.print(ypr[0]*180/M_PI);
    Serial.print(" pitch: ");
    Serial.print(ypr[1]*180/M_PI);
    Serial.print(" roll: ");
    Serial.print(ypr[2]*180/M_PI);
    Serial.print(" gx: ");
    Serial.print((float)gravity.x * 9.8);
    Serial.print(" gy: ");
    Serial.print((float)gravity.y * 9.8);
    Serial.print(" gz: ");
    Serial.print((float)gravity.z * 9.8);
    Serial.print(" arx: ");
    Serial.print(aaReal.x);
    Serial.print(" ary: ");
    Serial.print(aaReal.y);
    Serial.print(" arz: ");
    Serial.print(aaReal.z);
    Serial.print(" awx: ");
    Serial.print(aaWorld.x);
    Serial.print(" awy: ");
    Serial.print(aaWorld.y);
    Serial.print(" awz: ");
    Serial.println(aaWorld.z);    
    t = millis();
  }
}

也就是说,只把加速度量程调到16g,角速度还是250°/s。现在就只有代码一的问题了。看样子导致像代码二那样完全没法输出正确的原因还是和角速度有关。

但是,我无法继续排查问题,我真的找不到问题出在哪里。我感觉问题很底层,甚至在硬件上,但这只是感觉。我没有学过单片机,我打算学stm32,但是最近真的很忙没时间,9月才能开始学。希望大家能给点帮助,帮忙看看问题出在哪里。谢谢各位了。

引用
评论
加载评论中,请稍候...
200字以内,仅用于支线交流,主线讨论请采用回复功能。
折叠评论
warmonkey
1年5个月前 IP:广东
921523

max21100是一个高性能imu,只有存货可以买,数量还比较多。

可以不用dmp,用梯度下降法求解姿态。

陀螺仪校准方法是静止得到零位。加速度计校准方法是在所有方向缓慢转动,然后用椭球去拟合(加速度的矢量和应该是不变的)。

引用
评论
加载评论中,请稍候...
200字以内,仅用于支线交流,主线讨论请采用回复功能。
折叠评论
信仰は儚き人間の為に
1年5个月前 修改于 1年5个月前 IP:广东
921527

现阶段ChatGPT 适合在你懂原理的情况下加速开发用。IMU 的校准值并非常量,应定期校准,速率陀螺的零偏可以每次上电时消去。你买个开源飞控玩玩就明白了。

引用
评论
加载评论中,请稍候...
200字以内,仅用于支线交流,主线讨论请采用回复功能。
折叠评论
某倒吊的亚雷斯塔作者
1年5个月前 IP:广东
921562
引用warmonkey发表于8楼的内容
max21100是一个高性能imu,只有存货可以买,数量还比较多。可以不用dmp,用梯度下降法求解姿...

谢谢,我再chatgpt的帮助下解决了大部分问题,但是剩下的那个就是改变量程后的校准,请问校准加速度的具体操作是什么啊,我不认为我的手可以做到缓慢且平稳,我也没有云台之类的。另外我想问一下时怎么用椭球拟合呢?是把输出的三轴加速度值带进解析式里去算出abc吗?然后又怎么做呢?

引用
评论
加载评论中,请稍候...
200字以内,仅用于支线交流,主线讨论请采用回复功能。
折叠评论
某倒吊的亚雷斯塔作者
1年5个月前 IP:广东
921563
引用信仰は儚き人間の為に发表于9楼的内容
现阶段ChatGPT 适合在你懂原理的情况下加速开发用。IMU 的校准值并非常量,应定期校准,速率陀...

谢谢,已经再chatgpt的帮助下解决了大部分问题,但实在没钱去买飞控了 sticker

引用
评论
加载评论中,请稍候...
200字以内,仅用于支线交流,主线讨论请采用回复功能。
折叠评论
某倒吊的亚雷斯塔作者
1年5个月前 IP:广东
921564
引用warmonkey发表于8楼的内容
max21100是一个高性能imu,只有存货可以买,数量还比较多。可以不用dmp,用梯度下降法求解姿...

对了还想请教一下mpu6050(还在用这个,毕竟便宜资源多)如果超过量程dmp会输出什么呢?单独输出三轴加速度和角速度的话,超过量程就会显示最大示数。但是在实验中发现,如果经历较大加速度,角速度一定时间的话,再次静止有可能输出错误,甚至停止输出。虽然最终运用时大概率不会出现这种情况,毕竟到时候用+-16g,+-1000°/s,但经历了的话就感觉有点难处理了。如果有偏差没关系,毕竟超过量程时间肯定不长,不会有太大影响,但是如果出现在空中运行时输出混乱数据或者停机那麻烦就大了。

引用
评论(2)
加载评论中,请稍候...
200字以内,仅用于支线交流,主线讨论请采用回复功能。
折叠评论
warmonkey
1年5个月前 修改于 1年5个月前 IP:广东
921580
引用某倒吊的亚雷斯塔发表于10楼的内容
谢谢,我再chatgpt的帮助下解决了大部分问题,但是剩下的那个就是改变量程后的校准,请问校准加速度...

简易校准可以用六个位置的方法,3d打印一个测试工装,6个面各朝下放置一次。

高级校准可以把模块塞进一个球,并且加配重增加质量,以减小振动加速度。缓慢滚动这个球,加速度矢量和应该不变。


ChatGPT提问:

a=vector(3), scale=vector(3), offset=vector(3)
b=(a-offset).*scale, norm(a)=1, solve offset and scale from b
b is in a csv file, use python curve_fit to find out b

回答:

To use Python's `curve_fit` function to solve for `offset` and `scale` from `b`, 
we first need to define a function that takes `a`, `offset`, and `scale` as inputs 
and returns the value of `b`. We can do this using the NumPy library, which provides 
efficient array operations. Here's an example implementation:

```python
import numpy as np
from scipy.optimize import curve_fit

def func(a, offset, scale):
    return (a - offset) * scale

# Load b from a CSV file
b = np.loadtxt('b.csv', delimiter=',')

# Define initial guesses for offset and scale
offset_guess = np.zeros(3)
scale_guess = np.ones(3)

# Define bounds for offset and scale (optional)
offset_bounds = (-np.inf, np.inf)
scale_bounds = (0, np.inf)
bounds = (offset_bounds, scale_bounds)

# Use curve_fit to solve for offset and scale
popt, pcov = curve_fit(func, a, b, p0=(offset_guess, scale_guess), bounds=bounds)

# Extract the optimized values of offset and scale
offset_opt = popt[0]
scale_opt = popt[1]

# Print the optimized values
print('Optimized offset:', offset_opt)
print('Optimized scale:', scale_opt)
```

In this example, we first load `b` from a CSV file using NumPy's `loadtxt` function. 
We then define a function `func` that takes `a`, `offset`, and `scale` as inputs and 
returns the value of `b`. We use `curve_fit` to optimize the values of `offset` and 
`scale` that minimize the difference between the actual values of `b` and the values of 
`b` predicted by `func`. We provide initial guesses for `offset` and `scale` using 
the `p0` argument, and we set bounds on `offset` and `scale` using the `bounds` argument. 
Finally, we extract the optimized values of `offset` and `scale` from the `popt` array.


引用
评论
加载评论中,请稍候...
200字以内,仅用于支线交流,主线讨论请采用回复功能。
折叠评论
某倒吊的亚雷斯塔作者
1年5个月前 IP:广东
922182
引用warmonkey发表于13楼的内容
简易校准可以用六个位置的方法,3d打印一个测试工装,6个面各朝下放置一次。高级校准可以把模块塞进一个...

十分感谢,因为之前有点忙,没看kc,最近使用这个稍微改了一下的程序大概完成了校准,代码最困难的地方也接近尾声了。我做这个东西的直接目的是为发射准备,在最高点开伞,(我不太相信气压计的数据,伯努利效应难搞),而最根本的目的是想积累技术,造可用于以后更好的火箭的惯导。经过实验,发现确实可以较精确的在最高点开伞,这次发射是够用了。但只能实现一次,因为误差积累的较大(没有,可能是程序还有一点漏洞,我明天会继续改正,再次校准),我想问一下,mpu6050得到的位移数据大概误差有多大呢?是否需要卡尔曼滤波?

引用
评论
加载评论中,请稍候...
200字以内,仅用于支线交流,主线讨论请采用回复功能。
折叠评论
白沫0001
1年1个月前 IP:天津
925897
引用某倒吊的亚雷斯塔发表于14楼的内容
十分感谢,因为之前有点忙,没看kc,最近使用这个稍微改了一下的程序大概完成了校准,代码最困难的地方也...

位移误差我没测算过,加上卡尔曼滤波以后只能说差强人意,建议换好一点的模块

引用
评论
加载评论中,请稍候...
200字以内,仅用于支线交流,主线讨论请采用回复功能。
折叠评论

想参与大家的讨论?现在就 登录 或者 注册

所属专业
所属分类
上级专业
同级专业
某倒吊的亚雷斯塔
进士 机友
文章
8
回复
114
学术分
0
2020/08/28注册,28天10时前活动

火箭爱好者

主体类型:个人
所属领域:无
认证方式:手机号
IP归属地:广东
文件下载
加载中...
{{errorInfo}}
{{downloadWarning}}
你在 {{downloadTime}} 下载过当前文件。
文件名称:{{resource.defaultFile.name}}
下载次数:{{resource.hits}}
上传用户:{{uploader.username}}
所需积分:{{costScores}},{{holdScores}}下载当前附件免费{{description}}
积分不足,去充值
文件已丢失

当前账号的附件下载数量限制如下:
时段 个数
{{f.startingTime}}点 - {{f.endTime}}点 {{f.fileCount}}
视频暂不能访问,请登录试试
仅供内部学术交流或培训使用,请先保存到本地。本内容不代表科创观点,未经原作者同意,请勿转载。
音频暂不能访问,请登录试试
支持的图片格式:jpg, jpeg, png
插入公式
评论控制
加载中...
文号:{{pid}}
投诉或举报
加载中...
{{tip}}
请选择违规类型:
{{reason.type}}

空空如也

加载中...
详情
详情
推送到专栏从专栏移除
设为匿名取消匿名
查看作者
回复
只看作者
加入收藏取消收藏
收藏
取消收藏
折叠回复
置顶取消置顶
评学术分
鼓励
设为精选取消精选
管理提醒
编辑
通过审核
评论控制
退修或删除
历史版本
违规记录
投诉或举报
加入黑名单移除黑名单
查看IP
{{format('YYYY/MM/DD HH:mm:ss', toc)}}