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

Знакомство с девятиосевым модулем 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 и выводит в консоль (Монитор последовательного порта)

Смотрите про коптеры:  DJI Phantom 3 Standard инструкция на русском - страница 34

Знакомство с девятиосевым модулем 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”). Она сохраняет максимальные и минимальные значения, а затем на их основе вычисляет среднее.

Смотрите про коптеры:  Как не потерять квадрокоптер: 6 советов как найти улетевший дрон

Калибровка исходных/необработанных данных (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() контроллером)

Оцените статью
Радиокоптер.ру
Добавить комментарий