5 шт./лот MPU6050 MPU-6050A MPU-3050 MPU-6000 MPU-6052C MPU-6500 MPU-6515 QFN24 чипсет | Электронные компоненты и принадлежности | АлиЭкспресс

5 шт./лот MPU6050 MPU-6050A MPU-3050 MPU-6000 MPU-6052C MPU-6500 MPU-6515 QFN24 чипсет | Электронные компоненты и принадлежности | АлиЭкспресс Мультикоптеры

Урок 12. управление сервоприводами с помощью гироскопа mpu6050 gy-521 – описания, примеры, подключение к arduino

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

5 шт./лот MPU6050 MPU-6050A MPU-3050 MPU-6000 MPU-6052C MPU-6500 MPU-6515 QFN24 чипсет | Электронные компоненты и принадлежности | АлиЭкспресс

В этом примере мы научимся управлять двумя серво приводами с помощью акселерометра, когда мы будем отклонять акселерометр Gy-521 (MPU6050) по координате X и Y сервоприводы будут поворачиваться на отклоненный угол.

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

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

Сборка:

1) Подключаем Акселерометр Gy-521

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

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

2) Сервоприводы подключаем следующим образом:

Arduino (uno)Servo 1Servo 2
5VКрасный (Центральный)Красный (Центральный)
GNDЧерный или Коричневый (Левый)Черный или Коричневый (Левый)
Pin8 – для servo 1
Pin9 – для servo 2
Белый или Оранжевый (Правый)Белый или Оранжевый (Правый)

Сервопривод рекомендуется питать от внешнего источника питания, если запитать сервопривод от ардуины, то могут возникнуть помехи и перебои в работе arduino. Организовать это можно с помощью источника питания 9V и комбинированного стабилизатора 5V ,3.3V.

Теперь, когда все подключено, приступим к загрузке скетча.

Скетч:

#include <Wire.h>
#include "Kalman.h"
#include <Servo.h> 
Servo myservoX; 
Servo myservoY; 
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);
myservoX.attach(8);  
myservoY.attach(9);
  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(" ");
 myservoX.write((int)kalAngleX-90);
 myservoY.write((int)kalAngleY-90);
  // 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;
}

Скачать скетч можно по этой ссылке

Видео:

Iic i2c spi mpu6500 mpu-6500 6-осевой гироскоп модуль датчика акселерометра заменить mpu6050 для arduino с контактами gy-6500 | электронные компоненты и принадлежности | алиэкспресс

MPU-6500 модули (трехосевой гироскоп трехосный акселерометр)

Модель модуля: GY-6500

Использование чипа: MPU-6500

Источник питания: 3-5 в (внутренний понижающий регулятор)

Коммуникация: стандартный протокол связи IIC / SPI

-Чип 16 бит AD конвертер, выходные данные 16

Диапазон гироскопа: ± 250 500 1000 2000 °/С

Диапазон ускорения: ± 2 ± 4 ± 8 ± 16 г

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

2,54 мм булавки шаг

Размер модуля 15 мм * 25 мм

5 шт./лот MPU6050 MPU-6050A MPU-3050 MPU-6000 MPU-6052C MPU-6500 MPU-6515 QFN24 чипсет | Электронные компоненты и принадлежности | АлиЭкспресс

5 шт./лот MPU6050 MPU-6050A MPU-3050 MPU-6000 MPU-6052C MPU-6500 MPU-6515 QFN24 чипсет | Электронные компоненты и принадлежности | АлиЭкспресс5 шт./лот MPU6050 MPU-6050A MPU-3050 MPU-6000 MPU-6052C MPU-6500 MPU-6515 QFN24 чипсет | Электронные компоненты и принадлежности | АлиЭкспресс

Если вам нужно больше деталей, пожалуйста, нажмите5 шт./лот MPU6050 MPU-6050A MPU-3050 MPU-6000 MPU-6052C MPU-6500 MPU-6515 QFN24 чипсет | Электронные компоненты и принадлежности | АлиЭкспрессИ отправьте заказ. Если вам нужно больше количества, пожалуйста, свяжитесь с нами. Если вы не согласны с ценой, если некоторые детали не могут узнать в моем магазине, пожалуйста, свяжитесь с нами, у нас все еще есть много частей, которые не опубликованы.

Мы отправимПоследняя версия продукта,Функция сортировки. Он может иметь различную форму или цвет. Если вы не согласны, пожалуйста, не покупайте.

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

5 шт./лот MPU6050 MPU-6050A MPU-3050 MPU-6000 MPU-6052C MPU-6500 MPU-6515 QFN24 чипсет | Электронные компоненты и принадлежности | АлиЭкспресс

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

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

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

Все заказы отправляются в течение 1-5 дней после подтверждения оплаты. Пожалуйста, подождите с терпением.

Почта Китая почта не быстрая, обычно требуется 15-60 дней. Если срочно. Пожалуйста, выберите DHL/ Fedex/EMS. Мы можем написать низкую стоимость для cutomer, если это необходимо!

Почта Китая обычный маленький пакет плюс просто может отслеживать в нашей стране, но это не влияет на ваш прием.

Если вы хотите получить свою продукцию и не хотите тратить свое время, пожалуйста, выберите Почта Китая зарегистрированной авиапочтой. Если вы выбираете Почта Китая обычный маленький пакет плюс, посылка теряется. Мы докажем вам, что мы отправляем посылка, и мы можем вернуть только 50%. Это ваш выбор,Мы все должны рисковать. Если вы не согласны, пожалуйста, не покупайте.

Почта Китая обычный маленький пакет плюс и Почта Китая зарегистрированная АвиапочтаWww.17track.net/en/

5 шт./лот MPU6050 MPU-6050A MPU-3050 MPU-6000 MPU-6052C MPU-6500 MPU-6515 QFN24 чипсет | Электронные компоненты и принадлежности | АлиЭкспресс

Мы вернем вам деньги, если вы вернете товарыВ течение 15 днейПолучения товаров по любой причине. Однако покупатель должен убедиться, что возвращенные товары находятся в их первоначальном состоянии. Если товары повреждены или утеряны, когда они возвращаются, покупатель будет нести ответственность за такой ущерб или потерю, и Мы не дадим покупателю полный возврат средств. Покупатель должен подать иск на транспортную компанию, чтобы возместить стоимость ущерба или убытков.

При возврате товара Покупатель несет расходы за обратную доставку.5 шт./лот MPU6050 MPU-6050A MPU-3050 MPU-6000 MPU-6052C MPU-6500 MPU-6515 QFN24 чипсет | Электронные компоненты и принадлежности | АлиЭкспресс

Удовлетворение ваших запросов и положительная обратная связь очень важны для нас. Пожалуйста, оставьте положительные отзывы и 5 звезд, если вы удовлетворены нашими товарами и услугами.

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

Ардуино леонардо подключился к mpu6500 с программой go для получения данных

#include <Wire.h>
#include <TimerOne.h>

#define MPU9250_ADDRESS 0x68
#define MAG_ADDRESS 0x0C

#define GYRO_FULL_SCALE_250_DPS 0x00
#define GYRO_FULL_SCALE_500_DPS 0x08
#define GYRO_FULL_SCALE_1000_DPS 0x10
#define GYRO_FULL_SCALE_2000_DPS 0x18

#define ACC_FULL_SCALE_2_G 0x00
#define ACC_FULL_SCALE_4_G 0x08
#define ACC_FULL_SCALE_8_G 0x10
#define ACC_FULL_SCALE_16_G 0x18

// This function read Nbytes bytes from I2C device at address Address.
// Put read bytes starting at register Register in the Data array.
void I2Cread(uint8_t Address, uint8_t Register, uint8_t Nbytes, uint8_t* Data)
{
// Set register address
Wire.beginTransmission(Address);
Wire.write(Register);
Wire.endTransmission();

// Read Nbytes
Wire.requestFrom(Address, Nbytes);
uint8_t index=0;
while (Wire.available())
Data[index ]=Wire.read();
}

// Write a byte (Data) in device (Address) at register (Register)
void I2CwriteByte(uint8_t Address, uint8_t Register, uint8_t Data)
{
// Set register address
Wire.beginTransmission(Address);
Wire.write(Register);
Wire.write(Data);
Wire.endTransmission();
}

// Initial time
long int ti;
volatile bool intFlag=false;

// Calcul the speed
int16_t Speed(int16_t Axyz, int16_t vi, int16_t t){
int16_t vf;
vf = vi (Axyz*t);
return vf;
}

int16_t Position(int16_t Vxyz, int16_t pi, int16_t t){
int16_t pf;
pf = pi (Vxyz*t);
return pf;
}

// Initializations
void setup()
{
// Arduino initializations
Wire.begin();
Serial.begin(115200);

// Set accelerometers low pass filter at 5Hz
I2CwriteByte(MPU9250_ADDRESS,29,0×06);
// Set gyroscope low pass filter at 5Hz
I2CwriteByte(MPU9250_ADDRESS,26,0×06);

// Configure gyroscope range
I2CwriteByte(MPU9250_ADDRESS,27,GYRO_FULL_SCALE_1000_DPS);
// Configure accelerometers range
I2CwriteByte(MPU9250_ADDRESS,28,ACC_FULL_SCALE_4_G);
// Set by pass mode for the magnetometers
I2CwriteByte(MPU9250_ADDRESS,0x37,0x02);

Смотрите про коптеры:  Unity 3d Tank Tutorial: Ходовая часть (Урок 2. Гусеничное шасси) / Хабр

// Request continuous magnetometer measurements in 16 bits
I2CwriteByte(MAG_ADDRESS,0x0A,0x16);

pinMode(13, OUTPUT);
Timer1.initialize(10000); // initialize timer1, and set a 1/2 second period
Timer1.attachInterrupt(callback); // attaches callback() as a timer overflow interrupt

// Store initial value
ti=millis();
}

// Counter
long int cpt=0;

void callback()
{
intFlag=true;
digitalWrite(13, digitalRead(13) ^ 1);
}

// Main loop, read and display data
void loop()
{
while (!intFlag);
intFlag=false;

// Display time
Serial.print (millis()-ti,DEC);
Serial.print (“t”);

// _______________
// ::: Counter :::

// Display data counter
Serial.print (cpt ,DEC);
Serial.print (“t”);

// ____________________________________
// ::: accelerometer and gyroscope :::

// Read accelerometer and gyroscope
uint8_t Buf[14];
I2Cread(MPU9250_ADDRESS,0x3B,14,Buf);

// Create 16 bits values from 8 bits data

// Accelerometer
int16_t ax=-(Buf[0]<<8 | Buf[1]);
int16_t ay=-(Buf[2]<<8 | Buf[3]);
int16_t az=Buf[4]<<8 | Buf[5];

// Speed
int16_t vx= 0;
vx = Speed(ax,0,millis()-ti);
int16_t vy= 0;
vy = Speed(ay,0,millis()-ti);
int16_t vz= 0;
vz = Speed(az,0,millis()-ti);

//Position
int16_t px= 0;
px = Position(vx,0,millis()-ti);
int16_t py= 0;
py = Position(vy,0,millis()-ti);
int16_t pz= 0;
pz = Position(vz,0,millis()-ti);

// Gyroscope
int16_t gx=-(Buf[8]<<8 | Buf[9]);
int16_t gy=-(Buf[10]<<8 | Buf[11]);
int16_t gz=Buf[12]<<8 | Buf[13];

// Display values

// Accelerometer
//Serial.print (“ax : “);
Serial.print (ax,DEC);
Serial.print (” “);
//Serial.print (“ay : “);
Serial.print (ay,DEC);
Serial.print (” “);
//Serial.print (“az : “);
Serial.print (az,DEC);
Serial.print (” “);

// Speed
// Serial.print (“vx : “);
Serial.print (vx,DEC);
Serial.print (” “);
//Serial.print (“vy : “);
Serial.print (vy,DEC);
Serial.print (” “);
// Serial.print (“vz : “);
Serial.print (vz,DEC);
Serial.print (” “);

// Postion
//Serial.print (“px : “);
Serial.print (px,DEC);
Serial.print (” “);
// Serial.print (“py : “);
Serial.print (py,DEC);
Serial.print (” “);
// Serial.print (“pz : “);
Serial.print (pz,DEC);
Serial.print (” “);

/*

// Gyroscope
Serial.print (“gx : “);
Serial.print (gx,DEC);
Serial.print (“t”);
Serial.print (“gy : “);
Serial.print (gy,DEC);
Serial.print (“t”);
Serial.print (“gz : “);
Serial.print (gz,DEC);
Serial.print (“t”);
*/

// _____________________
// ::: Magnetometer :::

// Read register Status 1 and wait for the DRDY: Data Ready

uint8_t ST1;
do
{
I2Cread(MAG_ADDRESS,0x02,1,&ST1);
}
while (!(ST1&0x01));

// Read magnetometer data
uint8_t Mag[7];
I2Cread(MAG_ADDRESS,0x03,7,Mag);

// Create 16 bits values from 8 bits data

// Magnetometer
int16_t mx=-(Mag[3]<<8 | Mag[2]);
int16_t my=-(Mag[1]<<8 | Mag[0]);
int16_t mz=-(Mag[5]<<8 | Mag[4]);

/*
// Magnetometer
Serial.print (“mx : “);
Serial.print (mx 200,DEC);
Serial.print (“t”);
Serial.print (“my : “);
Serial.print (my-70,DEC);
Serial.print (“t”);
Serial.print (“mz : “);
Serial.print (mz-700,DEC);
Serial.print (“t”);
*/

// End of line
Serial.println(“”);
delay(1000);

}

Общие сведения.

Основная микросхема модуль GY-521 чип MPU-6050 , который содержит в себе акселерометр и гироскоп, а так же датчик температуры. Обработка данных осуществляется с помощью 16-битное АЦП (Аналого-цифровой преобразователь) на каждый канал, поэтому он обрабатывает значение x, y и z одновременно.

Встроенный датчик температуры предназначен для измерения температуры и имеет диапазон измерений от -40 ° С до 85 ° С.Для взаимодействия с Arduino используется шина I2C и датчик MPU-6050 всегда выступает в качестве подчиненного устройства. Но кроме обычной шины I2C, есть собственный контроллер I2C, в котором MPU-6050 ведомый, выводы SDAи XDA и с помощью это шины можно управлять например магнитометром и передавать данные на Arduino.

Датчика MPU-6050 работает от напряжение ~2.4 — 3.5 В и чтобы стабилизировать питание, на модуле GY-521 добавили стабилизатор напряжения на 3.3 В с малым падением напряжении, поэтому модуль можно подключить к напряжению 5 В и 3.3 В.

Назначение выводов:►  VCC – « » питание модуля 3.3 В до 5 В ►  GND – «-» питание модуля►  SCL – линия синхронизации для протокола I2C►  SDA – линия передачи данных протокола I2C►  XDA – линия передачи данных протокола при работе в режиме мастера►  XCL – линия синхронизации для протокола I2C при работе в режиме мастера►  AD0 – если вывод лог «0» адрес I2C будет 0x68, если вывод лог «1» адрес I2C будет 0x69►  INT – прерывание

Подключение gy-521 (mpu 6050) к arduino

В новой информационной статье хотим поговорить с вами о том, как подключить GY-521 (MPU 6050), какие задачи имеет данный модуль, где применяется и т.д. Речь идет о двойном устройстве на базе одноименной микросхемы (с совмещенными опциями трех-осевого гироскопа, термометра и акселерометра).

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

В продолжение темы рассмотрим основные технические параметры датчика:

  • напряжение питания: 3,5 – 6V;
  • рабочий ток: 500 мкА;
  • тип шины: I2C;
  • диапазон измерений акселерометра: ± 2, 4, 8, 16 г;
  • разрядность АЦП: 16.

Распиновка выводов показана на скриншоте:
5 шт./лот MPU6050 MPU-6050A MPU-3050 MPU-6000 MPU-6052C MPU-6500 MPU-6515 QFN24 чипсет | Электронные компоненты и принадлежности | АлиЭкспрессРеализовываем несложный проект подключения всех аппаратных компонентов через интерфейс I2C. Для этого нам понадобится следующее «железо»:

  1. микроконтроллер Ардуино Uno;
  2. 3-осевой гироскоп-акселерометр GY-521;
  3. соединительные провода (набор);
  4. макетная плата (прототипирования);
  5. USB-кабель для подключения к компьютеру.

Принципиальная схема сборки:
5 шт./лот MPU6050 MPU-6050A MPU-3050 MPU-6000 MPU-6052C MPU-6500 MPU-6515 QFN24 чипсет | Электронные компоненты и принадлежности | АлиЭкспрессА так она выглядит на макете:
5 шт./лот MPU6050 MPU-6050A MPU-3050 MPU-6000 MPU-6052C MPU-6500 MPU-6515 QFN24 чипсет | Электронные компоненты и принадлежности | АлиЭкспресс
Чтобы наладить соединение (получить необходимые значения) и просто проверить правильность подключения, пропишем скетч:

#include "I2Cdev.h"
#include "MPU6050.h"

#define T_OUT 20

MPU6050 accel;

unsigned long int t_next;

void setup() {
    Serial.begin(9600);
    accel.initialize();
    Serial.println(accel.testConnection() ? "MPU6050 connection successful" : "MPU6050 connection failed");
}

void loop() {
    long int t = millis();
    if( t_next < t ){
        int16_t ax_raw, ay_raw, az_raw, gx_raw, gy_raw, gz_raw;

        t_next = t   T_OUT;
        accel.getMotion6(&ax_raw, &ay_raw, &az_raw, &gx_raw, &gy_raw, &gz_raw);
 
        Serial.println(ay_raw); // вывод в порт проекции ускорения на ось Y
    }
}

В нем мы использовали 2 специализированных софта – библиотеки MPU6050 и I2Cdev. Их следует скачать и инсталлировать в среду программирования IDE (как обычно, разархивируем файлы в директорию libraries).
http://git.robotclass.ru/download/Arduino/MPU6050.zip
http://git.robotclass.ru/download/Arduino/I2Cdev.zip

Управление стандартное – через монитор последовательного порта (Serial Monitor ). Открываем при помощи сочетания клавиш Ctrl Shift M или через меню Инструменты. В нем “побегут” нужные нам показания.

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

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

Пример использования

В качестве примера рассмотрим проект по созданию пульта на MPU6050 для удаленного управления движущейся платформой.

Нам потребуются следующие компоненты. Для пульта управления:


Для движущейся платформы:

Схема соединения элементов пульта управления показана на рис. 6.

Рисунок 6.


Схема соединений для компонентов для движущейся платформы показана на рис. 7.

Рисунок 7.

Приступим к написанию скетчей. Передатчик отправляет 3 значения – начальный байт отправки B11111111 и 2 значения наклона датчика – по оси x и по оси y.

Содержимое скетча показано в листинге 4.

Листинг 4

#include "I2Cdev.h"
#include "MPU6050.h"
#include <RCSwitch.h>

#define TO_DEG 57.2957f
#define TIME_OUT 20

MPU6050 accgyro;
RCSwitch mySwitch = RCSwitch();

float anglex;
float angley;
long int t1;

void setup() {
    Serial.begin(9600);
    // инициализация датчика 
    accgyro.initialize(); 
    mySwitch.enableTransmit(2);
}

void loop() {
    long int t = millis();
    if( t1 < t ){
        int16_t ax, ay, az, gx, gy, gz;
        float accy,gyrox;

        t1 = t   TIME_OUT;
        accgyro.getMotion6(&ax, &ay, &az, &gx, &gy, &gz);
 
        // преобразование в единицы гравитации при настройках 1G = 4096
        accx = ax/4096.0;
        accy = ax/4096.0;
        // границы от -1G до  1G  
        accx = clamp(accx, -1.0, 1.0);
        accy = clamp(accy, -1.0, 1.0);

        // получить значение в градусах
        if( accy >= 0){
            anglex = 90 - TO_DEG*acos(accy);
        } else {
            anglex = TO_DEG*acos(-accy) - 90;
        }
        if( accx >= 0){
            angley = 90 - TO_DEG*acos(accx);
        } else {
            angley = TO_DEG*acos(-accx) - 90;
        }
        // отправка данных
        mySwitch.send(B11111111, 8);
        delay(50);
        mySwitch.send((byte)anglex, 8);
        delay(50);
        mySwitch.send((byte)angley, 8);
        delay(100); 
    }
}

Плата Arduino на движущейся платформе должна получать данные и преобразовывать их в команды установки скорости для двух моторов.

Содержимое скетча показано в листинге 5.

Листинг 5

// подключение библитеки
#include <RCSwitch.h>
// создание объекта
RCSwitch mySwitch = RCSwitch();

int motor=0;

void setup() {
  pinMode(10,OUTPUT);
  pinMode(9,OUTPUT);
  pinMode(8,OUTPUT);
  pinMode(5,OUTPUT);
  pinMode(7,OUTPUT);
  pinMode(6,OUTPUT);
  // запуск приемника
  mySwitch.enableReceive(0);
}

void loop() {
    if( mySwitch.available() ){
        // получить данные
        int value = mySwitch.getReceivedValue();
        
        if( value == B11111111 ) {// начало передачи
            motor=1;
        }
        else {
            if(motor==1) {
                go(10,9,8,value);
            }
            else if(motor==2) {
                go(5,7,6,value);
            }
            motor  ;
        }
        mySwitch.resetAvailable();
    }
}
// запуск двигателей
void go(int pina,int pin1,int pin2,int val) {
   analogWrite(pina,map(abs(val),0,90,0,255));
   if(val<=0) {
      digitalWrite(pin1,0);
      digitalWrite(pin2,1);
   }
   else {
      digitalWrite(pin1,1);
      digitalWrite(pin2,0);
   }
}

Рисунок 8. Пульт

Рисунок 9. Движущаяся платформа

Проект под кодовым названием: «бульболёт». часть 1. погружение в mpu6050 (или нет)

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

С начала я изучил основы, а именно – посмотрел что там придумали китайцы, а придумали они “полётные контроллеры”, в основу которых входит микроконтроллер (в основном STM32), гироскоп, барометр и т.д. В принципе, подумал я, всё выглядит довольно просто, значит, можно повторить.

Смотрите про коптеры:  Как выбрать квадроцикл правильно | Рейтинг лучших моделей 2022 года

Итак, мой путь начался с выбора начинки нашего “полётника”. Я взял за основу микроконтроллер STM32F103C8T6, расположенный на распаянной плате (blue pil). В периферию: микросхему MPU6050 (3 осевой гироскоп и акселерометр) разведенную на плате под кодовым названием (GY-521), BMP280 (датчик давления), HMC5883L (3-осевой цифровой компас) распаянный на плате (модуль GY-273). Для передачи и приёма я использую MRF49XA (трансивер). В последствии всё будет выпаяно и припаяно по месту назначению, а пока ограничимся макетной платой.

И так начнём, для работы с камнем я буду использовать STM32CubeMX (библиотека HAL), а для редактирования прослойки будем юзать STM32CubeIDE. Почему именно эти проги, во-первых, они официальные с поддержкой STM, во-вторых, имеют привлекательный и понятный интерфейс, а как же большое обилие примеров для изучения. Для дебагинга я использую USART, но в иделае надо бы юзать ST LINK (поэтому не экономим и берём вместе с blue pil-ом).

Приступим-с! Открываем STM32CubeMX и выбираем наш МК. Начинаем настраивать его, а именно, для начала включим внешнее тактирование (ведь чем больше частота – тем быстрее работает МК).

На плате blue pil кварц уже припаян к нужным ножкам, его надо только подключить.
На плате blue pil кварц уже припаян к нужным ножкам, его надо только подключить.

Переходи во вкладку Clock Configuration, вписываем значение нашего кварца, в моём случае это 8. После ищем клеточку с надписью HCLK и вписываем 72 (72 MHz – максимальная частота при кварце на 8 MHz). Программа сама подстроит оставшиеся настройки.

Достаточно удобно.
Достаточно удобно.

Так как я буду использовать интерфейс I2C для общения, то нам надо его включить. Как вы видите, я использую Fast Mode, но в принципе его можно и не использовать. (влияет только на скорость общения)

клацк  - клацк
клацк – клацк

Идём дальше и забегая чуть – чуть вперёд сразу скажу, что стандартное общение по I2C довольно сильно замедляет основную программу. Поэтому для решения этой проблемы у нас может быть два пути решения либо исправлять это с помощью прерываний:

Либо с помощью DMA(Direct Memory Access) – прямой доступ к памяти. Прикол этой штуки в том, чтобы распараллелить программу и облегчить жизнь CPU, то есть DMA общается по I2C, а основной процесс обрабатывает полученные данные.

И как вы поняли, я выбрал 2 способ. И так приступим к настройке. Так как я буду только считывать данные, то будем юзать RX. Все настройке на картинке.

5 шт./лот MPU6050 MPU-6050A MPU-3050 MPU-6000 MPU-6052C MPU-6500 MPU-6515 QFN24 чипсет | Электронные компоненты и принадлежности | АлиЭкспресс

Так же не забываем включить прерывания, чтобы знать, когда DMA сделает своё дело

клацк - клацк
клацк – клацк

Включаю USART для дебага.

Не обязательно
Не обязательно

И так пока остановимся, ведь мне надо пояснить некоторые детали, о которых я должен был ещё сказать в начале статьи, а именно то, что сейчас мы подготавливаем наш МК для работы с MPU6050. Поэтому мы и используем I2С, однако это не все плюшки микросхемы, прошу вас обратить внимание на вывод INT. Он нужен для того, чтобы подавать сигналы мастеру, к примеру, о готовности данных. Удобно! Я тоже так думаю, поэтому нам надо настроить наш МК, чтобы он захватывал эти сигналы, для этого настроим таймер.

not клацк - клацк
not клацк – клацк

Активируем TIM1, ибо он самый мясистый.

5 шт./лот MPU6050 MPU-6050A MPU-3050 MPU-6000 MPU-6052C MPU-6500 MPU-6515 QFN24 чипсет | Электронные компоненты и принадлежности | АлиЭкспресс

Теперь пробежимся по пунктам, и первым идёт режим управления таймером Master/Slave.

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

А таймер, получающий сигнал от другого таймера, является подчинённым — режим Slave.

Следовательно, Slave Mode – этот пункт, указывающий, что должен делать таймер, находясь в подчинённом режиме, в нашем случае, таймер будет подчинён сам себе, то есть, совершая захват сигнала, он будет генерировать тригер, и на основании этого сигнала, обнуляет счётчик (Reset Mode — означает, что при поступлении сигнала таймер должен обнулить счётчик) – это нам и нужно. Есть также и другие функции, если кому нужно:

Trigger Source — а этот пункт указывает что будет служить триггерным сигналом для таймера в моём случае TI1FP1, а так смотрим на картинку.

5 шт./лот MPU6050 MPU-6050A MPU-3050 MPU-6000 MPU-6052C MPU-6500 MPU-6515 QFN24 чипсет | Электронные компоненты и принадлежности | АлиЭкспресс

Идём дальше, и тут стоит заметить, что у каждого таймера есть четыре независимых канала, которые могут подключаться к физическим пинам микроконтроллера, а могут и не подключаться, работая как внутренние входы/выходы. Поэтому при настройке двух каналов (direct и indirect) активировался только один вход (РА8). Зачем мы это сделали? Чтобы первый канал ловил передний фронт, а второй — задний, тем самым мы и измерим длину импульса.

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

клацк - клацк
клацк – клацк

Осталось дело за малым, смотрим на картинку. Тут особо менять ничего не надо, но вы проверьте чтобы всё сходилось, а я лишь остановлюсь на одном пункте: Prescaler – предделитель частоты таймера (частоты поступающей с шины APB2). И тут важно отметить! Поскольку счётчик начинает отсчёт с нуля, то предделитель должен быть на 1 меньше. В нашем случае 72мГц / 71 = 1000000 тиков в секунду.

важно
важно

И так с настройкой камня покончено, перейдем к самой микросхеме (MPU6050). И первым делом почитаем даташит и карту регистров. Пойдём по порядку, сначала у нас идёт SELF_TEST, мы его оперативно скипаем ибо мы и сами сможем вычислить среднее значение погрешности. Далее у нас идёт несколько пунктов (SMPLRT_DIV, CONFIG, GYRO_CONFIG, ACCEL_CONFIG), которые нам понадобится для правильной работы датчика. Реализуем это в программе для этого создадим функцию инициализации. В ней мы с помощью стандартной функции общения по I2C (HAL_I2C_Mem_Write()) будем устанавливать начальные параметры работы.

void InitMPU6050(void) {     

        uint8_t data;
        data = 0;
        HAL_I2C_Mem_Write(&hi2c1, MPU6050_Address, PWR_MGMT_1_REG, 1, &data, 1, time);

        data = 0x07;
        HAL_I2C_Mem_Write(&hi2c1, MPU6050_Address, SMPLRT_DIV_REG, 1, &data, 1, time);
 
        data = 0x18;
        HAL_I2C_Mem_Write(&hi2c1, MPU6050_Address, ACCEL_CONFIG_REG, 1, &data, 1, time);

        data = 0x18;
        HAL_I2C_Mem_Write(&hi2c1, MPU6050_Address, GYRO_CONFIG_REG, 1, &data, 1, time);

        data = 0x1;
        HAL_I2C_Mem_Write(&hi2c1, MPU6050_Address, INT_ENABLE_REG, 1, &data, 1, time);

}

Теперь научимся захватывать наши импульсы, с помощью колбека (он срабатывает при прерывании от DMA) HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim){} после мы проверяем тот ли таймер захватил сигнал, если тот то действуем.

uint32_t zntime;

/* USER CODE BEGIN 2 */
  HAL_TIM_IC_Start_DMA(&htim1, TIM_CHANNEL_2, (uint32_t*)&zntime, 1);//включаем таймер     
/* USER CODE END 2 */

/* USER CODE BEGIN 4 */
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim) 
{
        if(htim->Instance == TIM1)
        {
           HAL_I2C_Mem_Read_DMA(&hi2c1, MPU6050_Address, ACCEL_XOUT_H_REG, 1, data, 14);
        }
}
/* USER CODE END 4 */

Далее будем считывать готовые данные с помощью DMA,для этого нам понадобятся регистры с 3B (ACCEL_XOUT_H_REG) по 48, то есть за раз нам надо считать 14 регистров. Чтение будет выполняться с помощью функцииHAL_I2C_Mem_Read_DMA()и с помощью прерывания по окончанию передачи по I2C void HAL_I2C_MemRxCpltCallback(I2C_HandleTypeDef *hi2c)

/* USER CODE BEGIN 4 */
void HAL_I2C_MemRxCpltCallback(I2C_HandleTypeDef *hi2c)
{
	  MPU6050Read();
}
/* USER CODE END 4 */

Также стоит отметить, что данные будут состоять из двух 8 битных частей, которые надо будет склеить в 16 битное число, а так же для наглядности высчитаем pitch.

pitch - тангаж
pitch – тангаж
typedef struct
{

    int16_t AccelX;
    int16_t AccelY;
    int16_t AccelZ;
    double aX;
    double aY;
    double aZ;

    int16_t GyroX;
    int16_t GyroY;
    int16_t GyroZ;
    double gX;
    double gY;
    double gZ;

    int16_t temp;
    int Temperature;

} MPU6050znach;// можно с помощью переменных или как тут


MPU6050znach z;


void MPU6050Read(void)
{
  ///////////////////////////склейка/////////////////
        z.AccelX = (int16_t)(data[0] << 8 | data[1]);
        z.AccelY = (int16_t)(data[2] << 8 | data[3]);
        z.AccelZ = (int16_t)(data[4] << 8 | data[5]);
        z.temp = (int16_t)(data[6] << 8 | data[7]);
        z.GyroX = (int16_t)(data[8] << 8 | data[9]);
        z.GyroY = (int16_t)(data[10] << 8 | data[11]);
        z.GyroZ = (int16_t)(data[12] << 8 | data[13]);

/////////////////////////////обработка////////////////////
        z.aX = z.AccelX / 2048.0;
        z.aY = z.AccelY / 2048.0;
        z.aZ = z.AccelZ / 2048.0;
        z.Temperature = (int)((int16_t)z.temp / (float)340.0   (float)36.53);
        z.gX = z.GyroX / 131.0;
        z.gY = z.GyroY / 131.0;
        z.gZ = z.GyroZ / 131.0;

 /////////////////////////вычисление////////////////////
        int pitch;
        int pitch_sqrt = sqrt(z.aY * z.aY    z.aZ * z.aZ);
        pitch = atan2(-z.aX, pitch_sqrt) * RAD_TO_DEG;

  /////////////////////////вывод/////////////////
         snprintf(msg, sizeof(msg), "%d", pitch);
           HAL_UART_Transmit(&huart1, (uint8_t*)msg, strlen(msg), HAL_MAX_DELAY);
           HAL_UART_Transmit(&huart1, (uint8_t*)"rn", strlen("rn"), HAL_MAX_DELAY);
           HAL_UART_Transmit(&huart1, (uint8_t*)"rn", strlen("rn"), HAL_MAX_DELAY);
}

И тадам получаем угол отклонения, однако значения достаточно сырые, ибо должно было получится где-то -90 градусов.

5 шт./лот MPU6050 MPU-6050A MPU-3050 MPU-6000 MPU-6052C MPU-6500 MPU-6515 QFN24 чипсет | Электронные компоненты и принадлежности | АлиЭкспресс

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

закройте окно - шумно!
закройте окно – шумно!
5 шт./лот MPU6050 MPU-6050A MPU-3050 MPU-6000 MPU-6052C MPU-6500 MPU-6515 QFN24 чипсет | Электронные компоненты и принадлежности | АлиЭкспресс

Для борьбы с этим недугом есть 2 пути решения использовать фильтр (к примеру Кальмана) или DMP (это небольшая програмка вшитая в MPU6050, которая сглаживает значения). И об этом мы поговорим в следующей статье, а так же подключим HMC5883L (3-осевой цифровой компас). На этом всё, до скорого!

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

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

Смотрите про коптеры:  ‎Drone Simulator on the App Store

рекурсивно

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

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

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

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

// положи датчик горизонтально, надписями вверх
// ДАЖЕ НЕ ДЫШИ НА НЕГО
// отправь любой символ в сериал, чтобы начать калибровку
// жди результатов

// --------------------- НАСТРОЙКИ ----------------------
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();

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

Adblock
detector