Знакомство с девятиосевым модулем MPU 9250.

Урок 11. подключение гироскопа gy-521 mpu-6050 к arduio. – описания, примеры, подключение к arduino

Модуль Gy-521 выполнен на базе микросхемы MPU6050, это 3-осевой гироскоп и акселерометр. Данную модель можно использовать для определения положения в пространстве.

Знакомство с девятиосевым модулем MPU 9250.

В данном уроке нам понадобится:

Для реализации проекта нам необходимо установить библиотеки:

В данном уроке рассмотрим библиотеку, которая позволяет преобразовать показания координат X и Y.

Подключение модуля производится следующим образом.

Gy-521 (mpu6050)Arduino (Uno)
VCC 3.3 V
GNDGND
SCLA5
SDAA4

Для питания модуля необходимо использовать строго 3.3V! Для этого можно использовать преобразователь напряжения на 3.3V.

Пришло время записать следующий скетч в нашу Arduino:

#include <Wire.h>
#include "Kalman.h"
Kalman kalmanX;
Kalman kalmanY;
uint8_t IMUAddress = 0x68;
/* IMU Data */
int16_t accX;
int16_t accY;
int16_t accZ;
int16_t tempRaw;
int16_t gyroX;
int16_t gyroY;
int16_t gyroZ;
double accXangle; // Angle calculate using the accelerometer
double accYangle;
double temp;
double gyroXangle = 180; // Angle calculate using the gyro
double gyroYangle = 180;
double compAngleX = 180; // Calculate the angle using a Kalman filter
double compAngleY = 180;
double kalAngleX; // Calculate the angle using a Kalman filter
double kalAngleY;
uint32_t timer;
void setup() { Wire.begin(); Serial.begin(9600); i2cWrite(0x6B,0x00); // Disable sleep mode kalmanX.setAngle(180); // Set starting angle kalmanY.setAngle(180); timer = micros();
}
void loop() { /* Update all the values */ uint8_t* data = i2cRead(0x3B,14); accX = ((data[0] << 8) | data[1]); accY = ((data[2] << 8) | data[3]); accZ = ((data[4] << 8) | data[5]); tempRaw = ((data[6] << 8) | data[7]); gyroX = ((data[8] << 8) | data[9]); gyroY = ((data[10] << 8) | data[11]); gyroZ = ((data[12] << 8) | data[13]); /* Calculate the angls based on the different sensors and algorithm */ accYangle = (atan2(accX,accZ) PI)*RAD_TO_DEG; accXangle = (atan2(accY,accZ) PI)*RAD_TO_DEG; double gyroXrate = (double)gyroX/131.0; double gyroYrate = -((double)gyroY/131.0); gyroXangle = kalmanX.getRate()*((double)(micros()-timer)/1000000); // Calculate gyro angle using the unbiased rate gyroYangle = kalmanY.getRate()*((double)(micros()-timer)/1000000); kalAngleX = kalmanX.getAngle(accXangle, gyroXrate, (double)(micros()-timer)/1000000); // Calculate the angle using a Kalman filter kalAngleY = kalmanY.getAngle(accYangle, gyroYrate, (double)(micros()-timer)/1000000); timer = micros();
Serial.println(); Serial.print("X:"); Serial.print(kalAngleX,0); Serial.print(" "); Serial.print("Y:"); Serial.print(kalAngleY,0); Serial.println(" "); // The accelerometer's maximum samples rate is 1kHz
}
void i2cWrite(uint8_t registerAddress, uint8_t data){ Wire.beginTransmission(IMUAddress); Wire.write(registerAddress); Wire.write(data); Wire.endTransmission(); // Send stop
}
uint8_t* i2cRead(uint8_t registerAddress, uint8_t nbytes) { uint8_t data[nbytes]; Wire.beginTransmission(IMUAddress); Wire.write(registerAddress); Wire.endTransmission(false); // Don't release the bus Wire.requestFrom(IMUAddress, nbytes); // Send a repeated start and then release the bus after reading for(uint8_t i = 0; i < nbytes; i ) data [i]= Wire.read(); return data;
}

Скачать скетч этого урока

Данный пример пересчитывает координату X и Y и выводит в консоль (Монитор последовательного порта)

Знакомство с девятиосевым модулем MPU 9250.

Когда X и Y равны 180, значит гироскоп находится в горизонтальной плоскости.

Github – codeninjadev/mpu-9265arduino: arduino sketch for the mpu-92565 gyro

Arduino Sketch for the MPU-92565 Gyro

Автоматическая калибровка азимута (yaw) rtpt с помощью p-контроллера

Сначала нам необходимо преобразовать значение параметра yaw из диапазона (-180 to 180) в диапазон (0 to 360) с помощью следующей простой формулы:

yaw = yaw 180;

Затем нам необходимо найти ошибку в параметре yaw используя простой пропорциональный регулятор (Proportional controller), затем прибавить значение этой ошибки к значению параметра yaw. После этого можно будет работать с этим новым значением параметра yaw.

Автоматическая калибровка магнитометра

Это одна из самых простых, но в то же время важных участков кода программы. Функция magcalMPU9250(float * dest1, float * dest2) производит калибровку магнитометра в то время когда вы перемещаете его по фигуре восьмерки (цифры “8”). Она сохраняет максимальные и минимальные значения, а затем на их основе вычисляет среднее.

Калибровка исходных/необработанных данных (raw data)

Получаемые с датчика MPU9250 данные должны быть откалиброваны в соответствии с оборудованием пользователя, в котором будет использоваться датчик. Калибровка магнитометра требуется для компенсации магнитного склонения (Magnetic Declination). Корректировочное значение в данном случае сильно зависит от местоположения датчика. Необходимо откалибровать две переменные: yaw и magbias.

Ниже приведенный фрагмент кода реализует калибровку переменной yaw для магнитного склонения, характерного для местности: Potheri, Chennai, India.

Данные о магнитном склонении в конкретной местности можно получить, к примеру, с помощью следующих сайтов:

В следующем фрагменте кода реализована калибровка переменной magbias, которая взята из функции magcalMPU9250(float * dest1, float * dest2) (из библиотеки, которую мы используем для работы с датчиком MPU9250).

Комплектующие

Создаётся данный датчик или МК, в зависимости от того, что вы собрались приобретать, из компонентов ATmega328.

Распиновка модуля Arduino MPU 6050
Распиновка модуля Arduino MPU 6050

Так, в нём имеются:

  1. 14 штук различных пинов и цифровых выходов, половина из которых являются ШИМ-выходами.
  2. Специальные кварцевые резонаторы до 16 МГц мощностью.
  3. Встроенный вход под usb-кабель, который позволит вам сэкономить не только время, но и деньги, которые вы могли бы потратить на покупку адаптера.
  4. Контакты и распиновка для стандартного питания с нулем, фазой и заземлением.
  5. Контакты для сброса до заводских настроек, при которых весь машинный код и данные будут стёрты. Это полезно в том случае, если вы напортачили с программой и модуль превратился в бесполезную груду железа, и просто как экономия времени, если необходимо сменить прошивку.
  6. ICSP контакт, который необходим для того, чтобы запрограммировать машинный код, который будет находиться внутри системы.

Все эти компоненты и составляют Arduino гироскоп, позволяя ему выполнять свои базовые функции. Но как же запрограммировать систему, если вы до этого не имели опыта работа с данными МК?

Назначение связки гироскоп и акселерометр

Для начала давайте разберёмся, зачем Arduino mpu 6050 (Gy-521) вообще нужен и что собой представляет гироскоп-акселерометр в целом. Такой датчик все мы видели в смартфонах, и там он выполняет следующие функции:

  1. Позволяет замерять шаги. Акселерометр способен отслеживать резкие движения устройства, а в зависимости от его настройки и чувствительности, считать некоторые из них за шаг.
  2. Измеряет поворот экрана. Здесь уже оба устройства работают в паре. Ведь когда вы поворачиваете смартфон набок, картинка должна изменить свою ориентацию для пользователя, и лишь с помощью гироскопа удаётся определить угол наклона, под которым ПО это должно будет сделать.
  3. Компас, карты и навигация. Акселерометр с гироскопом позволяют определить ориентацию устройства в пространстве, что необходимо в различных приложениях для мобильной навигации.

Вот и выходит, что данный датчик подойдёт для тех проектов, в которых вам необходимо измерить ориентацию или движения прибора в пространстве, без точных данных о его местоположении. Это может быть, как самодельная линейка со встроенным уровнем, чтобы пользователь мог определить, насколько ровно стоит та или иная мебель, так и устройство для кровати, встроенной в стену, включающее свет, когда она выдвигается.

Но применить модуль можно и с большей выдумкой, например, для измерения количества оборотов в секунду и регуляции мощности охладительной системы или автоматизации различных процессов.

Всё зависит исключительно от вашей выдумки и конкретного проекта.

Чаще всего гироскоп для Ардуино применяется в системах автоматизации под так называемые «смартхаусы» (умные дома – прим. ред.), являясь своеобразным переключателем. Передавая определённые данные в МК, который затем отправляет их по блютуз-модулю к другому устройству, он может управлять всей техникой в доме.

Ещё один простой способ применения – использование вместо датчика движения на дверях, для включения света и кондиционирования, когда вы возвращаетесь домой.

Наладка

Далее наступает самый ответственный этап – отладка программного кода. Здесь вам необходимо подключить питание к прибору, а сам прибор – к компьютеру, чтобы следить за строками в консоли. Прогоните несколько базовых функций и посмотрите, не будет ли ошибок или багов. Если они возникают, то воспользуйтесь любым удобным методом дебагинга.

Самый простой вариант – использовать для ввода переменные, которые вычисляются рандомным образом, и смотреть, как код будет вести себя в различных ситуациях.

Необходимые компоненты

  1. Плата Arduino Mega 2560 (купить на AliExpress) (можно также использовать и другие типы плат Arduino).
  2. Датчик MPU9250 (купить на AliExpress).

Обработка данных от датчика mpu9250

MPU9250 имеет 16-битный регистр для каждого из 3-х датчиков, входящих в его состав. Они временно хранят данные от датчика прежде чем произойдет их передача по интерфейсу I2C.

Общие сведения о датчике mpu9250

Датчик MPU9250 реализует функции 3-осевого гироскопа, 3-осевого акселерометра и 3-осевого магнитометра, то есть он представляет собой 9-ти осевой IMU сенсор (Inertial Measurement Unit – инерционное измерительное устройство). Его внешний вид с распиновкой контактов показан на следующем рисунке.

Назначение контактов датчика MPU9250:

Обозначение контактаНазначение контакта
VCCВнешнее питание 3.3 В
GNDОбщий
SCLЛиния тактовых импульсов I2C и SPI
SDAЛиния данных для I2C или SPI
EDAЛиния данных при подключении внешних датчиков по шине I2C
ECLЛиния тактов при подключении внешних датчиков по шине I2C
AD0Для выставления адреса I2C в режиме I2C. В режиме SPI это линия данных от датчика
INTЛиния прерываний. Срабатывание настраивается при конфигурировании датчика MPU-9250
NCSВ режиме SPI – линия выбора ведомого (chip select). В режиме I2C не соединяется ни с чем
FSYNCЗависит от конфигурации

MPU9250 является самым миниатюрным в мире девятиосевым датчиком. Это говорит о высокой производительности его микросхемы. Состоит корпус датчика из двух мельчайших кристаллов, один из которых отвечает за гироскоп и акселерометр, а другой за магнитометр.

Рассмотрим более подробно работу составных частей датчика.

Гироскоп – это сенсор, реагирующий на изменение углов ориентации в пространстве.

Акселерометр сравнивает проекцию ускорения объекта с гравитационным ускорением и способен замерять линейную скорость объекта, а вкупе с гироскопом – и положение в пространстве.

Магнитометр представляет собой устройство для измерения интенсивности ближайшего магнитного поля, действующего на объект.

Датчик MPU9250 позволяет измерять такие параметры как угол yaw (рыскание), угол pitch (тангаж) и угол roll (крен). Графическое изображение этих параметров представлено на следующем рисунке.

Подключение к arduino

Для интерфейса

I2C

у Ардуино имеются контакты A4 (SDA) и A5 (SCL), да-а, это те, которые расположены чёрти-где (на одной плате у меня они были справа от контроллера, на другой с левого края). В коде нужно использовать библиотеку Wire, прочитать о ней можно

. Минимальная схема во Fritzing такая:

Знакомство с девятиосевым модулем MPU 9250.

… а значит у нас уже не 8 лишних штырьков, а целых двенадцать!

Получение “реальных” значений

Наконец, на завершающем этапе обработки данных, мы получим значения параметров yaw, pitch и roll из кватернионов.

Постоянная калибровка для определенного места

Если вы не хотите каждый раз при включении устройства производить автоматическую калибровку магнитометра, то вы должны записать средние значения переменной magbias[] после их вычислений с помощью следующего фрагмента кода:

Значения 470, 120, 125 фиксированы для местоположения автора проекта, поэтому после выполнения данной процедуры нет необходимости вызова функции void magcalMPU9250(float * dest1, float * dest2), поэтому вы можете закомментировать или удалить ее из программы. Также не забудьте закомментарить в программе ее вызов как сделано в следующем примере кода:

Программы

Без программы модуль будет не более чем грудой железа, которая не выполнит ни одной функции. Базовые библиотеки для взаимодействия с другими МК можно найти на официальном сайте или в интернете, но, помимо них, вам потребуется вспомогательный код. С его помощью можно настроить взаимодействие между акселерометром и тем же блютуз модулем, без которого, в большинстве проектов, он станет бесполезен.

Мы воспользуемся готовой библиотекой для Arduino MPU 6050, которую написал Джефф Роуберг.

Работа с arduino и mpu6050 | alexgyver

Для достижения максимальной точности измерений нужно откалибровать акселерометр и гироскоп. Калибровка акселерометра позволяет выставить “ноль” для вектора силы тяжести, а калибровка гироскопа уменьшает его “дрифт”, то есть статическое отклонение в режиме покоя. Идеально откалиброванный и лежащий горизонтально датчик должен показывать ускорение ~16384 по оси Z и нули по всем остальным осям ускорения и угловой скорости. Но это фантастика =)
Максимально правильно использовать калибровку в проекте нужно так: калибровка по запросу (кнопка, меню, и т.д.), затем запись калибровочных значений в EEPROM. При запуске – чтение и настройка оффсетов. Да, можно замерить значения по всем 6 осям в покое, сохранить их в переменные, а затем вычитать из свежих прочитанных в процессе работы. Такой способ работает для каких-то базовых операций с датчиком (определение перегрузок, тряски, наличия вращения, и т.д.). Для использования MPU6050 с целью максимально точного определения углов поворота платы такой вариант к сожалению не подходит: калибровать нужно

рекурсивно

.
Рассмотрим несколько примеров калибровки, первый – из библиотеки. Калибрует долго, но максимально точно. 

При малых вибрациях и движениях датчика в процессе калибровки (даже от громкого звука) калибровка может не закончиться

. Второй вариант – мой упрощённый алгоритм калибровки, калибрует быстро, без возможности зависнуть при тряске, но даёт менее точный результат. Я делюсь примерами, в свой проект их нужно будет переносить вручную и аккуратно =)

Калибровка из примера библиотеки

// положи датчик горизонтально, надписями вверх
// ДАЖЕ НЕ ДЫШИ НА НЕГО
// отправь любой символ в сериал, чтобы начать калибровку
// жди результатов
// --------------------- НАСТРОЙКИ ----------------------
const int buffersize = 70; // количество итераций калибровки
const int acel_deadzone = 10; // точность калибровки акселерометра (по умолчанию 8)
const int gyro_deadzone = 6; // точность калибровки гироскопа (по умолчанию 2)
// --------------------- НАСТРОЙКИ ----------------------
#include "I2Cdev.h"
#include "MPU6050.h"
#include "Wire.h"
MPU6050 mpu(0x68);
int16_t ax, ay, az, gx, gy, gz;
int mean_ax, mean_ay, mean_az, mean_gx, mean_gy, mean_gz, state = 0;
int ax_offset, ay_offset, az_offset, gx_offset, gy_offset, gz_offset;
/////////////////////////////////// SETUP ////////////////////////////////////
void setup() { Wire.begin(); Serial.begin(9600); mpu.initialize(); // ждём очистки сериал while (Serial.available() && Serial.read()); // чистим while (!Serial.available()) { Serial.println(F("Send any character to start sketch.n")); delay(1500); } while (Serial.available() && Serial.read()); // чистим ещё на всякий Serial.println("nMPU6050 Calibration Sketch"); delay(2000); Serial.println("nYour MPU6050 should be placed in horizontal position, with package letters facing up"); delay(3000); // проверка соединения Serial.println(mpu.testConnection() ? "MPU6050 connection successful" : "MPU6050 connection failed"); delay(1000); // сбросить оффсеты mpu.setXAccelOffset(0); mpu.setYAccelOffset(0); mpu.setZAccelOffset(0); mpu.setXGyroOffset(0); mpu.setYGyroOffset(0); mpu.setZGyroOffset(0);
}
/////////////////////////////////// LOOP ////////////////////////////////////
void loop() { if (state == 0) { Serial.println("nReading sensors for first time..."); meansensors(); state ; delay(1000); } if (state == 1) { Serial.println("nCalculating offsets..."); calibration(); state ; delay(1000); } if (state == 2) { meansensors(); Serial.println("nFINISHED!"); Serial.print("nSensor readings with offsets:t"); Serial.print(mean_ax); Serial.print("t"); Serial.print(mean_ay); Serial.print("t"); Serial.print(mean_az); Serial.print("t"); Serial.print(mean_gx); Serial.print("t"); Serial.print(mean_gy); Serial.print("t"); Serial.println(mean_gz); Serial.print("Your offsets:t"); Serial.print(ax_offset); Serial.print(", "); Serial.print(ay_offset); Serial.print(", "); Serial.print(az_offset); Serial.print(", "); Serial.print(gx_offset); Serial.print(", "); Serial.print(gy_offset); Serial.print(", "); Serial.println(gz_offset); Serial.println("nData is printed as: acelX acelY acelZ giroX giroY giroZ"); Serial.println("Check that your sensor readings are close to 0 0 16384 0 0 0"); Serial.println("If calibration was succesful write down your offsets so you can set them in your projects using something similar to mpu.setXAccelOffset(youroffset)"); while (1); }
}
/////////////////////////////////// FUNCTIONS ////////////////////////////////////
void meansensors() { long i = 0, buff_ax = 0, buff_ay = 0, buff_az = 0, buff_gx = 0, buff_gy = 0, buff_gz = 0; while (i < (buffersize 101)) { // read raw accel/gyro measurements from device mpu.getMotion6(&ax, &ay, &az, &gx, &gy, &gz); if (i > 100 && i <= (buffersize 100)) { //First 100 measures are discarded buff_ax = buff_ax ax; buff_ay = buff_ay ay; buff_az = buff_az az; buff_gx = buff_gx gx; buff_gy = buff_gy gy; buff_gz = buff_gz gz; } if (i == (buffersize 100)) { mean_ax = buff_ax / buffersize; mean_ay = buff_ay / buffersize; mean_az = buff_az / buffersize; mean_gx = buff_gx / buffersize; mean_gy = buff_gy / buffersize; mean_gz = buff_gz / buffersize; } i ; delay(2); }
}
void calibration() { ax_offset = -mean_ax / 8; ay_offset = -mean_ay / 8; az_offset = (16384 - mean_az) / 8; gx_offset = -mean_gx / 4; gy_offset = -mean_gy / 4; gz_offset = -mean_gz / 4; while (1) { int ready = 0; mpu.setXAccelOffset(ax_offset); mpu.setYAccelOffset(ay_offset); mpu.setZAccelOffset(az_offset); mpu.setXGyroOffset(gx_offset); mpu.setYGyroOffset(gy_offset); mpu.setZGyroOffset(gz_offset); meansensors(); Serial.println("..."); if (abs(mean_ax) <= acel_deadzone) ready ; else ax_offset = ax_offset - mean_ax / acel_deadzone; if (abs(mean_ay) <= acel_deadzone) ready ; else ay_offset = ay_offset - mean_ay / acel_deadzone; if (abs(16384 - mean_az) <= acel_deadzone) ready ; else az_offset = az_offset (16384 - mean_az) / acel_deadzone; if (abs(mean_gx) <= gyro_deadzone) ready ; else gx_offset = gx_offset - mean_gx / (gyro_deadzone 1); if (abs(mean_gy) <= gyro_deadzone) ready ; else gy_offset = gy_offset - mean_gy / (gyro_deadzone 1); if (abs(mean_gz) <= gyro_deadzone) ready ; else gz_offset = gz_offset - mean_gz / (gyro_deadzone 1); if (ready == 6) break; }
}

Мой вариант калибровки

#include "Wire.h"
#include "I2Cdev.h"
#include "MPU6050.h"
MPU6050 mpu;
#define BUFFER_SIZE 100
int16_t ax, ay, az;
int16_t gx, gy, gz;
void setup() { Wire.begin(); Serial.begin(9600); mpu.initialize(); mpu.setXAccelOffset(0); mpu.setYAccelOffset(0); mpu.setZAccelOffset(0); mpu.setXGyroOffset(0); mpu.setYGyroOffset(0); mpu.setZGyroOffset(0); Serial.println(F("Send any character to start sketch")); delay(100); while (1) { //входим в бесконечный цикл if (Serial.available() > 0) { //если нажата любая кнопка Serial.read(); //прочитать (чтобы не висел в буфере) break; //выйти из цикла } } delay(1000);
}
void loop() { // выводим начальные значения mpu.getMotion6(&ax, &ay, &az, &gx, &gy, &gz); Serial.print(ax); Serial.print(" "); Serial.print(ay); Serial.print(" "); Serial.print(az); Serial.print(" "); Serial.print(gx); Serial.print(" "); Serial.print(gy); Serial.print(" "); Serial.println(gz); calibration(); // выводим значения после калибровки mpu.getMotion6(&ax, &ay, &az, &gx, &gy, &gz); Serial.print(ax); Serial.print(" "); Serial.print(ay); Serial.print(" "); Serial.print(az); Serial.print(" "); Serial.print(gx); Serial.print(" "); Serial.print(gy); Serial.print(" "); Serial.println(gz); delay(20); while (1);
}
// ======= ФУНКЦИЯ КАЛИБРОВКИ =======
void calibration() { long offsets[6]; long offsetsOld[6]; int16_t mpuGet[6]; // используем стандартную точность mpu.setFullScaleAccelRange(MPU6050_ACCEL_FS_2); mpu.setFullScaleGyroRange(MPU6050_GYRO_FS_250); // обнуляем оффсеты mpu.setXAccelOffset(0); mpu.setYAccelOffset(0); mpu.setZAccelOffset(0); mpu.setXGyroOffset(0); mpu.setYGyroOffset(0); mpu.setZGyroOffset(0); delay(10); Serial.println("Calibration start. It will take about 5 seconds"); for (byte n = 0; n < 10; n ) { // 10 итераций калибровки for (byte j = 0; j < 6; j ) { // обнуляем калибровочный массив offsets[j] = 0; } for (byte i = 0; i < 100 BUFFER_SIZE; i ) { // делаем BUFFER_SIZE измерений для усреднения mpu.getMotion6(&mpuGet[0], &mpuGet[1], &mpuGet[2], &mpuGet[3], &mpuGet[4], &mpuGet[5]); if (i >= 99) { // пропускаем первые 99 измерений for (byte j = 0; j < 6; j ) { offsets[j] = (long)mpuGet[j]; // записываем в калибровочный массив } } } for (byte i = 0; i < 6; i ) { offsets[i] = offsetsOld[i] - ((long)offsets[i] / BUFFER_SIZE); // учитываем предыдущую калибровку if (i == 2) offsets[i] = 16384; // если ось Z, калибруем в 16384 offsetsOld[i] = offsets[i]; } // ставим новые оффсеты mpu.setXAccelOffset(offsets[0] / 8); mpu.setYAccelOffset(offsets[1] / 8); mpu.setZAccelOffset(offsets[2] / 8); mpu.setXGyroOffset(offsets[3] / 4); mpu.setYGyroOffset(offsets[4] / 4); mpu.setZGyroOffset(offsets[5] / 4); delay(2); } /* // выводим в порт Serial.println("Calibration end. Your offsets:"); Serial.println("accX accY accZ gyrX gyrY gyrZ"); Serial.print(mpu.getXAccelOffset()); Serial.print(", "); Serial.print(mpu.getYAccelOffset()); Serial.print(", "); Serial.print(mpu.getZAccelOffset()); Serial.print(", "); Serial.print(mpu.getXGyroOffset()); Serial.print(", "); Serial.print(mpu.getYGyroOffset()); Serial.print(", "); Serial.print(mpu.getZGyroOffset()); Serial.println(" "); Serial.println(" "); */
}

Мой вариант калибровки запись в EEPROM

// калибровка и запись в EEPROM
// при запуске настраиваем оффсеты
// послать символ в сериал - начать повторную калибровку
#include "Wire.h"
#include "I2Cdev.h"
#include "MPU6050.h"
MPU6050 mpu;
#define BUFFER_SIZE 100
#define START_BYTE 1010
int16_t ax, ay, az;
int16_t gx, gy, gz;
void setup() { Wire.begin(); Serial.begin(9600); mpu.initialize(); //----------------- ВСПОМИНАЕМ ОФФСЕТЫ ------------------------ int offsets[6]; EEPROM.get(START_BYTE, offsets); // ставим оффсеты из памяти mpu.setXAccelOffset(offsets[0]); mpu.setYAccelOffset(offsets[1]); mpu.setZAccelOffset(offsets[2]); mpu.setXGyroOffset(offsets[3]); mpu.setYGyroOffset(offsets[4]); mpu.setZGyroOffset(offsets[5]); //----------------- ВСПОМИНАЕМ ОФФСЕТЫ ------------------------ Serial.println(F("Send any character to start sketch")); delay(100); while (1) { //входим в бесконечный цикл if (Serial.available() > 0) { // если нажата любая кнопка Serial.read(); // прочитать (чтобы не висел в буфере) break; // выйти из цикла } } delay(1000);
}
void loop() { calibration(); Serial.println("Current offsets:"); Serial.println("accX accY accZ gyrX gyrY gyrZ"); Serial.print(mpu.getXAccelOffset()); Serial.print(", "); Serial.print(mpu.getYAccelOffset()); Serial.print(", "); Serial.print(mpu.getZAccelOffset()); Serial.print(", "); Serial.print(mpu.getXGyroOffset()); Serial.print(", "); Serial.print(mpu.getYGyroOffset()); Serial.print(", "); Serial.print(mpu.getZGyroOffset()); Serial.println(" "); Serial.println(" "); // выводим значения после калибровки Serial.println("Readings:"); mpu.getMotion6(&ax, &ay, &az, &gx, &gy, &gz); Serial.print(ax); Serial.print(" "); Serial.print(ay); Serial.print(" "); Serial.print(az); Serial.print(" "); Serial.print(gx); Serial.print(" "); Serial.print(gy); Serial.print(" "); Serial.println(gz); delay(20); while (1);
}
// ======= ФУНКЦИЯ КАЛИБРОВКИ И ЗАПИСИ В ЕЕПРОМ =======
void calibration() { long offsets[6]; long offsetsOld[6]; int16_t mpuGet[6]; // используем стандартную точность mpu.setFullScaleAccelRange(MPU6050_ACCEL_FS_2); mpu.setFullScaleGyroRange(MPU6050_GYRO_FS_250); // обнуляем оффсеты mpu.setXAccelOffset(0); mpu.setYAccelOffset(0); mpu.setZAccelOffset(0); mpu.setXGyroOffset(0); mpu.setYGyroOffset(0); mpu.setZGyroOffset(0); delay(5); for (byte n = 0; n < 10; n ) { // 10 итераций калибровки for (byte j = 0; j < 6; j ) { // обнуляем калибровочный массив offsets[j] = 0; } for (byte i = 0; i < 100 BUFFER_SIZE; i ) { // делаем BUFFER_SIZE измерений для усреднения mpu.getMotion6(&mpuGet[0], &mpuGet[1], &mpuGet[2], &mpuGet[3], &mpuGet[4], &mpuGet[5]); if (i >= 99) { // пропускаем первые 99 измерений for (byte j = 0; j < 6; j ) { offsets[j] = (long)mpuGet[j]; // записываем в калибровочный массив } } } for (byte i = 0; i < 6; i ) { offsets[i] = offsetsOld[i] - ((long)offsets[i] / BUFFER_SIZE); // учитываем предыдущую калибровку if (i == 2) offsets[i] = 16384; // если ось Z, калибруем в 16384 offsetsOld[i] = offsets[i]; } // ставим новые оффсеты mpu.setXAccelOffset(offsets[0] / 8); mpu.setYAccelOffset(offsets[1] / 8); mpu.setZAccelOffset(offsets[2] / 8); mpu.setXGyroOffset(offsets[3] / 4); mpu.setYGyroOffset(offsets[4] / 4); mpu.setZGyroOffset(offsets[5] / 4); delay(2); } // пересчитываем для хранения for (byte i = 0; i < 6; i ) { if (i < 3) offsets[i] /= 8; else offsets[i] /= 4; } // запись в память EEPROM.put(START_BYTE, offsets);
}

Также в библиотеке есть готовые функции для калибровки акселя и гиро, они принимают количество итераций калибровки (до 15):

mpu.CalibrateAccel(6);
mpu.CalibrateGyro(6);

Функции блокирующие, выполняются и автоматически выставляют оффсеты, т.е. датчик сразу калибруется. Для чтения оффсетов (например для записи в EEPROM) можно воспользоваться тем же способом, что и раньше:

mpu.getXAccelOffset();
mpu.getYAccelOffset();
mpu.getZAccelOffset();
mpu.getXGyroOffset();
mpu.getYGyroOffset();
mpu.getZGyroOffset();

Сборка

Здесь всё зависит от используемого вами интерфейса, например, для I2C от Ардуино пригодятся контакты: A4, A5, которые являются SDA и SCL входами соответственно.

Для нормального функционирования всей этой системы необходимо будет использовать wire библиотеку в коде.

Gy-521 (mpu6050)Arduino (Uno)
VCC 3.3 V
GNDGND
SCLA5
SDAA4

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

Питание модуля строго 3.3В!

Схема проекта

Схема подключения датчика MPU9250 к плате Arduino представлена на следующем рисунке.

Подключать датчик MPU9250 к плате Arduino можно по интерфейсу I2C или интерфейсу SPI. В нашем случае мы использовали подключение по интерфейсу I2C (5V, Gnd, SCL, SDA). При использовании интерфейса SPI необходимо будет задействовать 5 контактов на плате модуля (5V, Gnd, SCL, SDA, CS, SDO).

Питать модуль можно как от напряжения от 5 В, поскольку на плате датчика имеется линейный стабилизатор для данного типа питания, так и от напряжения 3,3 В.

Считывание данных с датчика mpu9250 в плату arduino mega 2560

Для работы с датчиком MPU9250 существует достаточно много библиотек, но мы будем использовать одну из самых популярных среди них: MPU-9250 Arduino Library by Kriswiner.

Когда вы сохраните библиотеку в ваш каталог Arduino, можно приступать к работе с ней. Откройте пример MPU9250BasicAHRS.ino.

Также выполните следующие соединения в схеме:

MPU9250 Breakout ——— Arduino• VIN ———————- 5V• SDA ———————– SDA (Pin 20)• SCL ———————– SCL (Pin 21)• GND ———————- GND

Помните о том, что соединительные провода в нашем случае не должны быть очень длинными потому что протокол I2C плохо работает на больших расстояниях.

Тестирование

Термометр тестировать проще всего: залил скетч

, открыл монитор порта, выставил скорость на 9600, —

Вследствие нагревания феном для волос, значение

Tmp =

взлетело до 80. Далее покрутили в пространстве платкой — другие показания тоже изменяются, но это не наглядно.

Данные, выводимые вторым скетчем

InvenSense MPU-6050

June 2023

WHO_AM_I: 68, error = 0

PWR_MGMT_1: 40, error = 0

MPU-6050
Read accel, temp and gyro, error = 0
accel x,y,z: 12180, 9468, -9168
temperature: 22.153 degrees Celsius
gyro x,y,z: -462, -5303, -490,

MPU-6050
Read accel, temp and gyro, error = 0
accel x,y,z: 13204, 8928, -7420
temperature: 22.482 degrees Celsius
gyro x,y,z: 282, -2023, -956,

MPU-6050
Read accel, temp and gyro, error = 0
accel x,y,z: -1276, 7932, -16232
temperature: 22.435 degrees Celsius
gyro x,y,z: -1168, 1159, 1258,

MPU-6050
Read accel, temp and gyro, error = 0
accel x,y,z: 6216, 10604, -12796
temperature: 22.576 degrees Celsius
gyro x,y,z: -2161, 4363, 2176,


Более приятный глазу пример описан

Тестовая установка

, о котором

, был пересмотрен, урезан и сокращён (кстати, на этом фото заметно отличие в качестве металлизации отверстий обозреваемой платы и дешевой платы Ардуино):

Знакомство с девятиосевым модулем MPU 9250.

если отсоединить

тентакли

шлейф от креплений, то откроются два ряда контактов, у которых нужно «поотламывать половинки», — в результате получится разъем, удобно вставляющийся в отверстия макетной платы. Для пущей надёжности, нужно проклеить корпус, т.к. держаться на одних контактах конструкция не будет.

Знакомство с девятиосевым модулем MPU 9250.

всё ещё может напугать, но на самом деле бояться нечего:

Знакомство с девятиосевым модулем MPU 9250.

спаял с обратной стороны контакты, прозвонил их тестером — и можно подключать. Лично моя практика показывает, что лучше потратить пару секунд на предварительную проверку, чем ткнуть «не туда» и спалить девайс… Я так спалил Orange Pi PC =)

Товар предоставлен для написания обзора магазином. Обзор опубликован в соответствии с п.18 Правил сайта.

Усреднение данных

Поскольку данные на выходе датчика изменяются очень быстро, мы будем производить их выборку в течение определенного промежутка времени (50 ms) и вычислять их среднее значение.

Фильтрация данных

Поскольку исходные данные (raw data) содержат достаточно большое количество шума, мы будем использовать различные фильтры (Madgwick/Mahony/Kalman) чтобы преобразовать их в кватернионы (Quaternions).

Характеристики:

— 16-битный АЦП,

— напряжение питания 3-5В,

— поддержка протокола «IIC» (может, I2C ?),

— диапазон ускорений: ± 2 ± 4 ± 8 ± 16g,

— диапазон «гиро»: ± 250 500 1000 2000 ° / s,

— покрытие иммерсионным золотом вместо лужения,

— ток при работе последнего примера составил 5.3 мА и 1.2 мА когда устройство не успело стартовать (питание на модуль было подано после выполнения setup() контроллером)

Смотрите про коптеры:  DH разблокировал NFZ и высоту для дронов DJI ! - Страница 2 - Вопросы - Однодневки - Dji-Club
Оцените статью
Радиокоптер.ру
Добавить комментарий