Помощь с MPU-6050

Я купил MPU-6050 месяц назад для проекта, я получил протокол hang I2C и зарегистрировал карты MPU. Я научился получать исходные значения акселерометра и гироскопа. Что мне было нужно для моего проекта, так это углы крена и тангажа и ускорение по осям x, y, z. Я попробовал некоторые алгоритмы, как показано ниже:


Сначала я увидел код, который использует только необработанные значения акселерометра для получения крена и тангажа. Вот небольшой фрагмент этого кода:

x=(AcX)/16384;
y=(AcY)/16384;
z=(AcZ)/16384;
roll = (atan2(y, z)+PI)*57.295779513082320876798154814105;
pitch = (atan2(x , z)+PI)*57.295779513082320876798154814105;

Как вы можете видеть, здесь используется принцип угла между двумя векторами, углы были довольно точными. Но проблема с этим кодом заключается в том, что когда датчик движется в определенном направлении без изменения крена или тангажа, они все равно, казалось, изменялись (значения крена и тангажа зависят от значений ускорения).
Далее я увидел об использовании дополнительного фильтра в моем коде:

#include<Wire.h>
const int  MPU_addr=0x68, dt=20;
float AcX,AcY,AcZ,Tmp,GyX,GyY,GyZ;
float xa,ya,za,xg,yg,zg;
float gan_x = 0 ,gan_y = 0,aan_x = 0,aan_y = 0, anx = 0, any = 0, gain  =0.95;
int xao = -521 ,yao = 1073 ,zao = 1724 ,xgo = 94 ,ygo =31 ,zgo = 60;                       //offset values
void setup()
{
  Serial.begin(115200);
  Wire.begin();
  Wire.beginTransmission(MPU_addr);
  Wire.write(0x1A);
  Wire.write(0b00000110);
  Wire.endTransmission(true);
  Wire.beginTransmission(MPU_addr);
  Wire.write(0x1B);
  Wire.write(0b00000000);
  Wire.endTransmission(true);
  Wire.beginTransmission(MPU_addr);
  Wire.write(0x1C);
  Wire.write(0b00000000);
  Wire.endTransmission(true);
  Wire.beginTransmission(MPU_addr);
  Wire.write(0x6B);
  Wire.write(0b00000000);
  Wire.endTransmission(true);
}
void loop()
{ 
  Wire.beginTransmission(MPU_addr);
  Wire.write(0x3B);  // starting with register 0x3B (ACCEL_XOUT_H)
  Wire.endTransmission(false);
  Wire.requestFrom(MPU_addr,14,true);  // request a total of 14 registers
  AcX=Wire.read()<<8|Wire.read();  // 0x3B (ACCEL_XOUT_H) & 0x3C (ACCEL_XOUT_L)    
  AcY=Wire.read()<<8|Wire.read();  // 0x3D (ACCEL_YOUT_H) & 0x3E (ACCEL_YOUT_L)
  AcZ=Wire.read()<<8|Wire.read();  // 0x3F (ACCEL_ZOUT_H) & 0x40 (ACCEL_ZOUT_L)
  Tmp=Wire.read()<<8|Wire.read();  // 0x41 (TEMP_OUT_H) & 0x42 (TEMP_OUT_L)
  GyX=Wire.read()<<8|Wire.read();  // 0x43 (GYRO_XOUT_H) & 0x44 (GYRO_XOUT_L)
  GyY=Wire.read()<<8|Wire.read();  // 0x45 (GYRO_YOUT_H) & 0x46 (GYRO_YOUT_L)
  GyZ=Wire.read()<<8|Wire.read();  // 0x47 (GYRO_ZOUT_H) & 0x48 (GYRO_ZOUT_L)
  xa = (AcX-xao)/16384;                                                    //sensitivity adjusting
  ya = (AcY-yao)/16384;
  za = (AcZ-zao)/16384;
  xg = (GyX-xgo)/131;
  yg = (GyY-ygo)/131;
  zg = (GyZ-zgo)/131;
  gan_x = dt * xg + anx;                                                       // integrating
  gan_y = dt * yg + any;
  aan_x = (atan2(ya,za)+PI)*57.295779513082320876798154814105;
  aan_y = (atan2(xa,za)+PI)*57.295779513082320876798154814105;
  anx = (gain) * gan_x + (1-gain) * aan_x;                                     // filtering
  any = (gain) * gan_y + (1-gain) * aan_y;
  //Serial.print(" | GyX = "); 
  Serial.print(gan_x);
  Serial.print(" ");
  //Serial.print(" | GyY = "); 
  Serial.println(gan_y);
}

Этот код дает хорошие значения гироскопа с точки зрения угловой скорости, но я хочу его в градусах. есть ли какой-нибудь способ изменить эти значения гироскопа на градусы (как в первом случае)?


Я видел библиотеку Джеффа Роуберга и, похоже, не могу понять весь код. Может кто-нибудь, пожалуйста, предоставить какие-либо справочные материалы, объясняющие функции, присутствующие в i2cdevlib.h (например: mpu.dmpInitialize(), mpu.setDMPEnabled() и т.д.) это было бы очень полезно.


Я также хотел бы знать, есть ли какие-либо другие альтернативные IMU (Gyro + accel), отличные от MPU6050, которые имеют более открытый исходный код, чем 6050 (с точки зрения доступа к DMP, что, по-видимому, является основным недостатком MPU) и с которыми проще работать.
Я использую Arduino Nano.

-Заранее благодарю вас

, 👍2

Обсуждение

* Этот код дает хорошие значения гироскопа в терминах угловой скорости, но я хочу, чтобы они были в градусах. есть ли какой-нибудь способ изменить эти значения гироскопа на градусы (как в первом случае)? * - И в чем измеряется * угловая скорость *?, @Eugene Sh.

Угловая скорость = градусы в секунду, мне нужны значения гироскопа в градусах @EugeneSh., @Teja Allani

Все ссылки на библиотеку Джеффа Роуберга находятся в его библиотеке. Его код довольно хорошо прокомментирован. Я использовал raw.ino его примеров для одного из своих проектов, и это сработало отлично., @Len

Просто используйте код Джеффа Роуберга и просмотрите все строки в его коде, возможно, даже откройте .cpp и .h, чтобы лучше понять, что делается с вашим кодом., @Len

Да, я открыл и прочитал библиотеку и cpp. Я постараюсь пройти еще раз :). У меня пока есть работа, спасибо тебе @Len, @Teja Allani

@TejaAllani кроме того, я понимаю, что непонимание библиотеки может расстраивать, но я точно знаю, что она работает действительно хорошо. Иногда этого достаточно, и это также отчасти объясняет, почему вам нужна библиотека: вам не нужно писать и понимать все самостоятельно, потому что библиотека сделает всю тяжелую работу за вас. Удачи вам в вашем проекте., @Len

@Len, я терпеливо прочитал каждую строку кода в файлах .h и .cpp. Наконец-то я получил общее представление о том, как это работает. Проблема, когда я впервые увидел эти файлы, заключалась в моем программировании на C ++. Я не знал, как создается библиотека и что она делает.h должен иметь отношение к файлу .cpp. Я думал, что они были отдельными и не имели никакой связи друг с другом. После нескольких часов поиска в Google, наконец, я смог понять связь между .h и .cpp, что .cpp имеет определения функций, которые были объявлены в файле .h. Теперь я могу с удовлетворением пользоваться этой библиотекой: D. Спасибо вам за помощь., @Teja Allani

эй, Теджа, не могла бы ты поделиться кодом, так как я тоже застрял на этом довольно долго., @Mahshida

@Mahshida, код находится в ответе, @Juraj


1 ответ


Лучший ответ:

3

У меня наконец-то получилось это сделать после одной бессонной ночи

#include<Wire.h>
const int MPU = 0x68;
int t = 0,dt = 1;
int AcX,AcY,AcZ,GyX,GyY,GyZ,tmp;
int AcXo,AcYo,AcZo,GyXo,GyYo,GyZo;
float roll = 0,pitch = 0,rollgy = 0,pitchgy = 0,rollac = 0,pitchac = 0,Ax,Ay,Az,Gx,Gy,Gz,gain = 0.95;
void MPUconfig(int Addr,int data)
{
  Wire.beginTransmission(MPU);
  Wire.write(Addr);
  Wire.write(data);
  Wire.endTransmission();
}
void MPUread()
  {
    Wire.beginTransmission(MPU);
    Wire.write(0x3B);  // starting with register 0x3B (ACCEL_XOUT_H)
    Wire.endTransmission();
    Wire.requestFrom(MPU,14);  // request a total of 14 registers
    AcX=Wire.read()<<8|Wire.read();  // 0x3B (ACCEL_XOUT_H) & 0x3C (ACCEL_XOUT_L)    
    AcY=Wire.read()<<8|Wire.read();  // 0x3D (ACCEL_YOUT_H) & 0x3E (ACCEL_YOUT_L)
    AcZ=Wire.read()<<8|Wire.read();  // 0x3F (ACCEL_ZOUT_H) & 0x40 (ACCEL_ZOUT_L)
    tmp=Wire.read()<<8|Wire.read();  // 0x3B (ACCEL_XOUT_H) & 0x3C (ACCEL_XOUT_L)    
    GyX=Wire.read()<<8|Wire.read();  // 0x3D (ACCEL_YOUT_H) & 0x3E (ACCEL_YOUT_L)
    GyY=Wire.read()<<8|Wire.read();  // 0x3F (ACCEL_ZOUT_H) & 0x40 (ACCEL_ZOUT_L)
    GyZ=Wire.read()<<8|Wire.read();
  }
void offset()
{
  MPUconfig(0x1A,0b00000000);
  int ax = 0, ay = 0,az = 0,gx = 0,gy = 0,gz = 0,i;
MPUread();
MPUread();
ax = AcX;
ay = AcY;
az = AcZ;
gx = GyX;
gy = GyY;
gz = GyZ;
for(i=0;i<1100;i++)
{
    MPUread();
    if(i>100)
    {
      ax = (ax+AcX)/2;
      ay = (ay+AcY)/2;
      az = (az+AcZ)/2;
      gx = (gx+GyX)/2;
      gy = (gy+GyY)/2;
      gz = (gz+GyZ)/2;
    } 
    delay(2);
 }
AcXo = ax;
AcYo = ay;
AcZo = 16384-az;
GyXo = gx;
GyYo = gy;
GyZo = gz;
MPUconfig(0x1A,0b00000110);
}
void setup() 
{
  Serial.begin(115200);
  Wire.begin();
  MPUconfig(0x1A,0b00000110);
  MPUconfig(0x1B,0b00000000);
  MPUconfig(0x1C,0b00000000);
  MPUconfig(0x6B,0b00000000);
  offset();
}

void loop()
{
  t = millis();
  MPUread();
  Ax = (float)(AcX-AcXo)/16384;
  Ay = (float)(AcY-AcYo)/16384;
  Az = (float)(AcZ-AcZo)/16384;
  Gx = (float)(GyX-GyXo)/131;
  Gy = (float)(GyY-GyYo)/131;
  Gz = (float)(GyZ-GyZo)/131;
  pitchgy = (Gy * ((float) dt/1000)) + pitch;
  rollgy = (Gx * ((float) dt/1000)) + roll;
  pitchac = atan2(Ax,Az) * (float)(180/PI);
  rollac =  atan2(Ay,Az) * (float) (180/PI);
  roll = gain * rollgy + (1-gain)* rollac;
  pitch = gain * pitchgy + (1-gain) * pitchac; 
  Serial.print(roll);
  Serial.print(" "); Serial.print(pitch);
  Serial.println(" ");
  dt = millis()-t;
}
,

Привет, Тея Аллани, спасибо, что поделилась кодом. Это работает. Однако меня озадачивает одна проблема: в подпрограмме смещения код принимает 2-й MPUread() для инициализации. Есть ли какая-то причина для этого? Я провел несколько тестов, отметив 2-й MPUread, и мои тесты действительно показывают разные результаты., @Jesse

Кроме того, кажется, что код выдает углы, которые ограничены (-45, 45). Где мне нужно изменить для вычисления угла, расширенного до (-90, 90)?, @Jesse

Привет :) @Джесси , 2-й MPUread() поскольку первые пару показаний нестабильны, требуется некоторое время, чтобы MPU выдавал стабильные результаты, по этой же причине я ввел ** i>100 ** в цикл, поэтому 1-е 100 показаний не используются для калибровки., @Teja Allani

и для этих углов используйте atan2() вместо atan(), чтобы изменить пределы углов. Этот код все еще нуждается в некотором улучшении, потому что я обнаружил, что когда крен или тангаж пересекают 90 градусов, они, казалось, влияют друг на друга (я не могу четко объяснить это явление, протестируйте его после замены atan() на atan2() ) возможно, это потому, что значения Ax и Ay становятся отрицательными после пересечения 90 градусов и это внезапное изменение с +ve на -ve и фильтр эффектов. _ Также попробуйте изменить значение усиления и понаблюдайте за результатами _, @Teja Allani

Спасибо за отзыв. Я проверил, что atan() принимает один аргумент, а угол может быть между [-90,90], что наводит меня на мысль о необходимости некоторых улучшений. Во всяком случае, то, что вы получили большую часть этого за одну бессонную ночь, весьма впечатляет. С наилучшими пожеланиями., @Jesse

После тестирования этого в моем проекте я обнаружил, что эта программа полезна только для крена и тангажа < 90 градусов. Поэтому я решил воспользоваться библиотекой Джеффа Роуберга. Просмотрев код, я обнаружил, что это единственный способ получить идеальные значения от датчика., @Teja Allani

Хммм, интересно это знать. Мой тест на вашем исходном коде показал (-45, 45). Вы обновили свой код?, @Jesse

@Джесси Только что обновил его, @Teja Allani

используется atan2, который принимает 2 аргумента. Спасибо. Приятно было поучиться у тебя., @Jesse