Помощь с 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.
-Заранее благодарю вас
@Teja Allani, 👍2
Обсуждение1 ответ
Лучший ответ:
У меня наконец-то получилось это сделать после одной бессонной ночи
#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
- Линейное ускорение от MPU 6050
- Снять гравитацию с акселерометра MPU-6050
- Использование MPU-6050 без I2C
- Изменение скорости передачи данных не работает
- Arduino Mega и ошибочные значения гироскопа
- Стабилизация данных с четырех гироскопов/акселерометра
- MPU-6050 с Arduino – вскрытие
- Трудности получения значений угла от MPU6050.
* Этот код дает хорошие значения гироскопа в терминах угловой скорости, но я хочу, чтобы они были в градусах. есть ли какой-нибудь способ изменить эти значения гироскопа на градусы (как в первом случае)? * - И в чем измеряется * угловая скорость *?, @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