Mpu6050 arduino code
Here’s the Arduino code for reading the data from the MPU6050 sensor. Below the code you can find a detail description of it.
#include<Wire.h>constint MPU = 0x68; float AccX, AccY, AccZ;
float GyroX, GyroY, GyroZ;
float accAngleX, accAngleY, gyroAngleX, gyroAngleY, gyroAngleZ;
float roll, pitch, yaw;
float AccErrorX, AccErrorY, GyroErrorX, GyroErrorY, GyroErrorZ;
float elapsedTime, currentTime, previousTime;
int c = 0;
voidsetup(){
Serial.begin(19200);
Wire.begin(); Wire.beginTransmission(MPU); Wire.write(0x6B); Wire.write(0x00); Wire.endTransmission(true);
calculate_IMU_error();
delay(20);
}
voidloop(){
Wire.beginTransmission(MPU);
Wire.write(0x3B); Wire.endTransmission(false);
Wire.requestFrom(MPU, 6, true);
AccX = (Wire.read() << 8 | Wire.read()) / 16384.0;
AccY = (Wire.read() << 8 | Wire.read()) / 16384.0;
AccZ = (Wire.read() << 8 | Wire.read()) / 16384.0;
accAngleX = (atan(AccY / sqrt(pow(AccX, 2) pow(AccZ, 2))) * 180 / PI) - 0.58;
accAngleY = (atan(-1 * AccX / sqrt(pow(AccY, 2) pow(AccZ, 2))) * 180 / PI) 1.58;
previousTime = currentTime;
currentTime = millis();
elapsedTime = (currentTime - previousTime) / 1000; Wire.beginTransmission(MPU);
Wire.write(0x43); Wire.endTransmission(false);
Wire.requestFrom(MPU, 6, true);
GyroX = (Wire.read() << 8 | Wire.read()) / 131.0;
GyroY = (Wire.read() << 8 | Wire.read()) / 131.0;
GyroZ = (Wire.read() << 8 | Wire.read()) / 131.0;
GyroX = GyroX 0.56;
GyroY = GyroY - 2;
GyroZ = GyroZ 0.79;
gyroAngleX = gyroAngleX GyroX * elapsedTime;
gyroAngleY = gyroAngleY GyroY * elapsedTime;
yaw = yaw GyroZ * elapsedTime;
roll = 0.96 * gyroAngleX 0.04 * accAngleX;
pitch = 0.96 * gyroAngleY 0.04 * accAngleY;
Serial.print(roll);
Serial.print("/");
Serial.print(pitch);
Serial.print("/");
Serial.println(yaw);
}
voidcalculate_IMU_error(){
while (c < 200) {
Wire.beginTransmission(MPU);
Wire.write(0x3B);
Wire.endTransmission(false);
Wire.requestFrom(MPU, 6, true);
AccX = (Wire.read() << 8 | Wire.read()) / 16384.0 ;
AccY = (Wire.read() << 8 | Wire.read()) / 16384.0 ;
AccZ = (Wire.read() << 8 | Wire.read()) / 16384.0 ;
AccErrorX = AccErrorX ((atan((AccY) / sqrt(pow((AccX), 2) pow((AccZ), 2))) * 180 / PI));
AccErrorY = AccErrorY ((atan(-1 * (AccX) / sqrt(pow((AccY), 2) pow((AccZ), 2))) * 180 / PI));
c ;
}
AccErrorX = AccErrorX / 200;
AccErrorY = AccErrorY / 200;
c = 0;
while (c < 200) {
Wire.beginTransmission(MPU);
Wire.write(0x43);
Wire.endTransmission(false);
Wire.requestFrom(MPU, 6, true);
GyroX = Wire.read() << 8 | Wire.read();
GyroY = Wire.read() << 8 | Wire.read();
GyroZ = Wire.read() << 8 | Wire.read();
GyroErrorX = GyroErrorX (GyroX / 131.0);
GyroErrorY = GyroErrorY (GyroY / 131.0);
GyroErrorZ = GyroErrorZ (GyroZ / 131.0);
c ;
}
GyroErrorX = GyroErrorX / 200;
GyroErrorY = GyroErrorY / 200;
GyroErrorZ = GyroErrorZ / 200;
Serial.print("AccErrorX: ");
Serial.println(AccErrorX);
Serial.print("AccErrorY: ");
Serial.println(AccErrorY);
Serial.print("GyroErrorX: ");
Serial.println(GyroErrorX);
Serial.print("GyroErrorY: ");
Serial.println(GyroErrorY);
Serial.print("GyroErrorZ: ");
Serial.println(GyroErrorZ);
}
Code language:Arduino(arduino)
Code Description: So first we need to include the Wire.h library which is used for the I2C communication and define some variables needed storing the data.
In the setup section, we need initialize the wire library and reset the sensor through the power management register. In order to do that we need to take a look at the datasheet of the sensor from where we can see the register address.
Also, if we want, we can select the Full-Scale Range for the accelerometer and the gyroscope using their configuration registers. For this example, we will use the default – 2g range for the accelerometer and 250 degrees/s range for the gyroscope, so I will leave this part of the code commented.
Wire.beginTransmission(MPU);
Wire.write(0x1C); Wire.write(0x10); Wire.endTransmission(true);
Wire.beginTransmission(MPU);
Wire.write(0x1B); Wire.write(0x10); Wire.endTransmission(true);
*/
Code language:Arduino(arduino)
In the loop section we start by reading the accelerometer data. The data for each axis is stored in two bytes or registers and we can see the addresses of these registers from the datasheet of the sensor.
In order to read them all, we start with the first register, and using the requiestFrom() function we request to read all 6 registers for the X, Y and Z axes. Then we read the data from each register, and because the outputs are twos complement, we combine them appropriately to get the correct values.
Wire.beginTransmission(MPU);
Wire.write(0x3B); Wire.endTransmission(false);
Wire.requestFrom(MPU, 6, true);
AccX = (Wire.read() << 8 | Wire.read()) / 16384.0;
AccY = (Wire.read() << 8 | Wire.read()) / 16384.0;
AccZ = (Wire.read() << 8 | Wire.read()) / 16384.0;
Code language:Arduino(arduino)
In order to get output values from -1g to 1g, suitable for calculating the angles, we divide the output with the previously selected sensitivity.
Finally, using these two formulas, we calculate the roll and pitch angles from the accelerometer data.
accAngleX = (atan(AccY / sqrt(pow(AccX, 2) pow(AccZ, 2))) * 180 / PI) - 0.58;
accAngleY = (atan(-1 * AccX / sqrt(pow(AccY, 2) pow(AccZ, 2))) * 180 / PI) 1.58;
Code language:Arduino(arduino)
Next, using the same method we get the gyroscope data.
We read the six gyroscope registers, combine their data appropriately and divide it by the previously selected sensitivity in order to get the output in degrees per second.
previousTime = currentTime;
currentTime = millis();
elapsedTime = (currentTime - previousTime) / 1000; Wire.beginTransmission(MPU);
Wire.write(0x43); Wire.endTransmission(false);
Wire.requestFrom(MPU, 6, true);
GyroX = (Wire.read() << 8 | Wire.read()) / 131.0;
GyroY = (Wire.read() << 8 | Wire.read()) / 131.0;
GyroZ = (Wire.read() << 8 | Wire.read()) / 131.0;
Code language:Arduino(arduino)
Here you can notice that I correct the output values with some small calculated error values, which I will explain how we get them in a minute. So as the outputs are in degrees per second, now we need to multiply them with the time to get just degrees. The time value is captured before each reading iteration using the millis() function.
GyroX = GyroX 0.56;
GyroY = GyroY - 2;
GyroZ = GyroZ 0.79;
gyroAngleX = gyroAngleX GyroX * elapsedTime;
gyroAngleY = gyroAngleY GyroY * elapsedTime;
yaw = yaw GyroZ * elapsedTime;
Code language:Arduino(arduino)
Finally, we fuse the accelerometer and the gyroscope data using a complementary filter. Here, we take 96% of the gyroscope data because it is very accurate and doesn’t suffer from external forces. The down side of the gyroscope is that it drifts, or it introduces error in the output as the time goes on.
roll = 0.96 * gyroAngleX 0.04 * accAngleX;
pitch = 0.96 * gyroAngleY 0.04 * accAngleY;
Code language:Arduino(arduino)
However, as we cannot calculate the Yaw from the accelerometer data, we cannot implement the complementary filter on it.
Before we take a look at the results, let me quickly explain how to get the error correction values. For calculate these errors we can call the calculate_IMU_error() custom function while the sensor is in flat still position. Here we do 200 readings for all outputs, we sum them and divide them by 200.
voidcalculate_IMU_error(){
while (c < 200) {
Wire.beginTransmission(MPU);
Wire.write(0x3B);
Wire.endTransmission(false);
Wire.requestFrom(MPU, 6, true);
AccX = (Wire.read() << 8 | Wire.read()) / 16384.0 ;
AccY = (Wire.read() << 8 | Wire.read()) / 16384.0 ;
AccZ = (Wire.read() << 8 | Wire.read()) / 16384.0 ;
AccErrorX = AccErrorX ((atan((AccY) / sqrt(pow((AccX), 2) pow((AccZ), 2))) * 180 / PI));
AccErrorY = AccErrorY ((atan(-1 * (AccX) / sqrt(pow((AccY), 2) pow((AccZ), 2))) * 180 / PI));
c ;
}
AccErrorX = AccErrorX / 200;
AccErrorY = AccErrorY / 200;
c = 0;
while (c < 200) {
Wire.beginTransmission(MPU);
Wire.write(0x43);
Wire.endTransmission(false);
Wire.requestFrom(MPU, 6, true);
GyroX = Wire.read() << 8 | Wire.read();
GyroY = Wire.read() << 8 | Wire.read();
GyroZ = Wire.read() << 8 | Wire.read();
GyroErrorX = GyroErrorX (GyroX / 131.0);
GyroErrorY = GyroErrorY (GyroY / 131.0);
GyroErrorZ = GyroErrorZ (GyroZ / 131.0);
c ;
}
GyroErrorX = GyroErrorX / 200;
GyroErrorY = GyroErrorY / 200;
GyroErrorZ = GyroErrorZ / 200;
Serial.print("AccErrorX: ");
Serial.println(AccErrorX);
Serial.print("AccErrorY: ");
Serial.println(AccErrorY);
Serial.print("GyroErrorX: ");
Serial.println(GyroErrorX);
Serial.print("GyroErrorY: ");
Serial.println(GyroErrorY);
Serial.print("GyroErrorZ: ");
Serial.println(GyroErrorZ);
}
Code language:Arduino(arduino)
Работа с arduino и mpu6050 | alexgyver
Для достижения максимальной точности измерений нужно откалибровать акселерометр и гироскоп. Калибровка акселерометра позволяет выставить “ноль” для вектора силы тяжести, а калибровка гироскопа уменьшает его “дрифт”, то есть статическое отклонение в режиме покоя. Идеально откалиброванный и лежащий горизонтально датчик должен показывать ускорение ~16384 по оси Z и нули по всем остальным осям ускорения и угловой скорости. Но это фантастика =)
Максимально правильно использовать калибровку в проекте нужно так: калибровка по запросу (кнопка, меню, и т.д.), затем запись калибровочных значений в EEPROM. При запуске – чтение и настройка оффсетов. Да, можно замерить значения по всем 6 осям в покое, сохранить их в переменные, а затем вычитать из свежих прочитанных в процессе работы. Такой способ работает для каких-то базовых операций с датчиком (определение перегрузок, тряски, наличия вращения, и т.д.). Для использования MPU6050 с целью максимально точного определения углов поворота платы такой вариант к сожалению не подходит: калибровать нужно
рекурсивно
.
Рассмотрим несколько примеров калибровки, первый – из библиотеки. Калибрует долго, но максимально точно.
При малых вибрациях и движениях датчика в процессе калибровки (даже от громкого звука) калибровка может не закончиться
. Второй вариант – мой упрощённый алгоритм калибровки, калибрует быстро, без возможности зависнуть при тряске, но даёт менее точный результат. Я делюсь примерами, в свой проект их нужно будет переносить вручную и аккуратно =)
Также в библиотеке есть готовые функции для калибровки акселя и гиро, они принимают количество итераций калибровки (до 15):
mpu.CalibrateAccel(6); mpu.CalibrateGyro(6);
Функции блокирующие, выполняются и автоматически выставляют оффсеты, т.е. датчик сразу калибруется. Для чтения оффсетов (например для записи в EEPROM) можно воспользоваться тем же способом, что и раньше:
mpu.getXAccelOffset(); mpu.getYAccelOffset(); mpu.getZAccelOffset(); mpu.getXGyroOffset(); mpu.getYGyroOffset(); mpu.getZGyroOffset();
Связь
Радиоуправление (RC)
Управление посредством радиосвязи обычно включает в себя RC передатчик/RC transmitter (в беспилотном хобби — радиоаппаратура управления/пульт) и RC приёмник (RC receiver). Для взаимодействия с БПЛА пользователю потребуется как минимум четырёх (и более) канальный RC передатчик. По умолчанию первые четыре канала связаны с:
Все остальные имеющиеся каналы могут быть задействованы для таких действий как:
- Арминг (Arming или Arm)/Дизарминг (Disarming или Disarm) — постановка/снятие с охраны моторов.
- Управление подвесом (панорамирование вверх/вниз, вращение по часовой стрелке/против часовой стрелки, зуммирование)
- Смена режимов полёта (ACRO/ANGLE и т.д.)
- Активировать/Задействовать полезную нагрузку (парашют, зуммер или другое устройство)
- Любое другое применение
Большинство пользователей (пилотов БПЛА) предпочитают именно ручное управление, это ещё раз доказывает, что пилотирование при помощи аппаратуры управления по прежнему является выбором номер один. Сам по себе RC приёмник просто передаёт поступающие от RC передатчика значения, а значит не может управлять беспилотником. RC приёмник должен быть подключен к контроллеру полёта, который в свою очередь должен быть запрограммирован для приёма RC сигналов. На рынке очень мало полётных контроллеров, которые принимают входящие радиокоманды от приёмника на прямую, а большинство ПК даже обеспечивают питание приёмника от одного из контактных выводов. Дополнительные соображения при выборе пульта дистанционного управления включают в себя:
- Не все RC передатчики могут обеспечить полный диапазон RC сигналов от 500мс до 2500мс; некоторые искусственно ограничивают этот диапазон, так как большинство используемых RC предназначены для радиоуправляемых автомобилей, самолётов и вертолётов.
- Дальность/Макс. воздушный радиус действия (измеряется в футах или метрах) RC-системы — практически никогда не предоставляются производителями, поскольку на этот параметр влияют множество факторов, таких как помехи, температура, влажность, заряд батареи и другие.
- Некоторые RC-системы имеют приёмник, который также имеет встроенный передатчик для передачи данных от датчика (например, GPS-координат), которые в последствии будут отображаться на ЖК-дисплее RC передатчика.
Bluetooth
Bluetooth и более поздние продукты BLE (Bluetooth Low Energy) изначально предназначались для передачи данных между устройствами без заморочек сопряжения или согласования частот. Некоторые имеющиеся на рынке контроллеры полёта могут отправлять и получать данные по беспроводной связи через соединение Bluetooth, что упрощает поиск неисправностей в полевых условиях.
Wi-Fi
Управление по Wi-Fi обычно достигается посредством Wi-Fi роутера, компьютера (в том числе ноутбук, десктоп, планшет) или смартфон. Wi-Fi в состоянии справится как с передачей данных, так и с передачей видеопотока, но одновременно с этим эту технологию сложнее настроить/реализовать. Как и для всех Wi-Fi устройств, расстояние удаления ограничено Wi-Fi передатчиком.
Радиочастота (RF или РЧ)
Радиочастотное (РЧ) управление в этом контексте относится к беспроводной передаче данных с компьютера или микроконтроллера на летательный аппарат с использованием РЧ передатчика/Приёмника (или двухполосного приёмопередатчика). Использование обычного радиочастотного блока, подключенного к компьютеру, позволяет осуществлять двухполосную связь на большие расстояния с высокой «плотностью» данных (обычно в последовательном формате).
Смартфон
Хоть это и не тип связи, самого вопроса, как управлять дроном используя смартфон, достаточно, чтобы уделить ему отдельный раздел. Современные смартфоны это по сути мощные компьютеры, которые по случайному совпадению могут также совершать телефонные звонки. Почти все смартфоны имеют встроенный модуль Bluetooth, а также модуль WiFi, каждый из которых используется для управления дроном и/или получения данных и/или видео.
Инфракрасное излучение (Infrared (IR))
Инфракрасная связь (то что можно найти в каждом телевизионном пульте дистанционного управления) редко используется для управления дронами, так как даже в обычных комнатах (не говоря уже об открытом пространстве) присутствует так много инфракрасных помех, что они не очень надёжны. Несмотря на то, что технологию можно использовать для управления БПЛА, не может быть предложена как основной вариант.
Сенсоры
С точки зрения аппаратного обеспечения, контроллер полёта по сути является обычным программируемым микроконтроллером, только со специальными датчиками на борту. Как минимум, контроллер полёта будет включать в себя 3-осевой гироскоп, но без автовыравнивания. Не все контроллеры полёта оснащаются указанными ниже сенсорами, но они также могут включать их комбинацию:
- Акселерометр: Как следует из названия, акселерометры измеряют линейное ускорение по трем осям (назовём их: X, Y и Z). Обычно измеряется в «G (на рус. Же)». Стандартное (нормальное) значение, составляет g = 9.80665 м/с². Для определения положения, выход акселерометра может быть интегрирован дважды, правда из-за потерь на выходе объект может быть подвержен дрейфу. Самой значимой характеристикой трёхосевых акселерометров является то, что они регистрируют гравитацию, и как таковые, могут знать, в каком направлении «спуск». Это играет главную роль в обеспечении стабильности многороторного БЛА. Акселерометр должен быть установлен на контроллере полёта так, чтобы линейные оси совпадали с основными осями беспилотника.
- Гироскоп: Гироскоп измеряет скорость изменения углов по трём угловым осям (назовём их: альфа, бета и гамма). Обычно измеряется в градусах в секунду. Обратите внимание, что гироскоп не измеряет абсолютные углы напрямую, но вы можете выполнить итерацию, чтобы получить угол, который, как и у акселерометра, способствует дрейфу. Выход реального гироскопа имеет тенденцию быть аналоговым или I2C, но в большинстве случаев вам не нужно беспокоиться об этом, так как все поступающие данные обрабатываются кодом контроллера полёта. Гироскоп должен быть установлен так, чтобы его оси вращения совпадали с осями БПЛА.
- Инерционный измерительный блок (IMU): IMU — по сути, это небольшая плата, которая содержит как акселерометр, так и гироскоп (обычно многоосевые). Большинство из них включают трёхосевой акселерометр и трёхосевой гироскоп, другие могут включать дополнительные сенсоры, например трёхосевой магнитометр, обеспечивающий в общей сложности 9 осей измерения.
- Компас/Магнитометр: Электронный магнитный компас способный определять магнитное поле Земли и использовать эти данные для определения направления компаса беспилотника (относительно северного магнитного полюса). Этот сенсор почти всегда присутствует, если система имеет GPS вход и доступно от одной до трех осей.
- Давление/Барометр: Так как атмосферное давление изменяется по мере удаления от уровня моря, можно использовать сенсор давления, чтобы получить довольно точные показания высоты БПЛА. Для расчёта максимально точной высоты, большинство контроллеров полёта получают данные одновременно от сенсора давления и спутниковой системы навигации (GPS). При сборке обратите внимание, что предпочтительнее, чтобы отверстие в корпусе барометра было накрыто куском поролона, это уменьшить отрицательное влияние ветра на чип.
- Расстояние: Датчики расстояния все чаще используются на беспилотниках, поскольку GPS-координаты и датчики давления не могут рассказать вам, насколько далеко вы находитесь от земли (холма, горы или здания), либо столкнётесь ли вы с объектом или нет. Датчик расстояния, обращенный вниз, может быть основан на ультразвуковой, лазерной или лидарной технологии (ИК-сенсоры могут испытывать проблемы в работе при солнечном свете). Датчики расстояния редко входят в стандартный комплект полётного контроллера.