- Урок 11. подключение гироскопа gy-521 mpu-6050 к arduio. – описания, примеры, подключение к arduino
- Github – codeninjadev/mpu-9265arduino: arduino sketch for the mpu-92565 gyro
- Автоматическая калибровка азимута (yaw) rtpt с помощью p-контроллера
- Автоматическая калибровка магнитометра
- Калибровка исходных/необработанных данных (raw data)
- Комплектующие
- Назначение связки гироскоп и акселерометр
- Наладка
- Необходимые компоненты
- Обработка данных от датчика mpu9250
- Общие сведения о датчике mpu9250
- Подключение к arduino
- Получение “реальных” значений
- Постоянная калибровка для определенного места
- Программы
- Работа с arduino и mpu6050 | alexgyver
- Сборка
- Схема проекта
- Считывание данных с датчика mpu9250 в плату arduino mega 2560
- Тестирование
- Тестовая установка
- Усреднение данных
- Фильтрация данных
- Характеристики:
Урок 11. подключение гироскопа gy-521 mpu-6050 к arduio. – описания, примеры, подключение к arduino
Модуль Gy-521 выполнен на базе микросхемы MPU6050, это 3-осевой гироскоп и акселерометр. Данную модель можно использовать для определения положения в пространстве.
В данном уроке нам понадобится:
Для реализации проекта нам необходимо установить библиотеки:
В данном уроке рассмотрим библиотеку, которая позволяет преобразовать показания координат X и Y.
Подключение модуля производится следующим образом.
Gy-521 (mpu6050) | Arduino (Uno) |
---|---|
VCC | 3.3 V |
GND | GND |
SCL | A5 |
SDA | A4 |
Для питания модуля необходимо использовать строго 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 и выводит в консоль (Монитор последовательного порта)
Когда 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”). Она сохраняет максимальные и минимальные значения, а затем на их основе вычисляет среднее.
Serial.println(“Mag Calibration: Wave device in a figure eight until done!”);
sample_count = 128;
for(ii = 0; ii < sample_count; ii ) {
readMagData(mag_temp); // Read the mag data
for (int jj = 0; jj < 3; jj ) {
if(mag_temp[jj] > mag_max[jj]) mag_max[jj] = mag_temp[jj];
if(mag_temp[jj] < mag_min[jj]) mag_min[jj] = mag_temp[jj];
}
delay(135); // at 8 Hz ODR, new mag data is available every 125 ms
}
// Get hard iron correction
mag_bias[0] = (mag_max[0] mag_min[0])/2; // get average x mag bias in counts
mag_bias[1] = (mag_max[1] mag_min[1])/2; // get average y mag bias in counts
mag_bias[2] = (mag_max[2] mag_min[2])/2; // get average z mag bias in counts
dest1[0] = (float) mag_bias[0]*mRes*magCalibration[0]; // save mag biases in G for main program
dest1[1] = (float) mag_bias[1]*mRes*magCalibration[1];
dest1[2] = (float) mag_bias[2]*mRes*magCalibration[2];
// Get soft iron correction estimate
mag_scale[0] = (mag_max[0] – mag_min[0])/2; // get average x axis max chord length in counts
mag_scale[1] = (mag_max[1] – mag_min[1])/2; // get average y axis max chord length in counts
mag_scale[2] = (mag_max[2] – mag_min[2])/2; // get average z axis max chord length in counts
float avg_rad = mag_scale[0] mag_scale[1] mag_scale[2];
avg_rad /= 3.0;
dest2[0] = avg_rad/((float)mag_scale[0]);
dest2[1] = avg_rad/((float)mag_scale[1]);
dest2[2] = avg_rad/((float)mag_scale[2]);
Serial.println(“Mag Calibration done!”);
}
voidmagcalMPU9250(float*dest1,float*dest2){ uint16_tii=0,sample_count=0; int32_tmag_bias[3]={0,0,0},mag_scale[3]={0,0,0}; int16_tmag_max[3]={0x8000,0x8000,0x8000},mag_min[3]={0x7FFF,0x7FFF,0x7FFF},mag_temp[3]={0,0,0}; Serial.println(“MagCalibration:Wavedeviceinafigureeightuntildone!”); sample_count=128; for(ii=0;ii<sample_count;ii ){ readMagData(mag_temp); // Read the mag data for(intjj=0;jj<3;jj ){ if(mag_temp[jj]>mag_max[jj])mag_max[jj]=mag_temp[jj]; if(mag_temp[jj]<mag_min[jj])mag_min[jj]=mag_temp[jj]; } delay(135); // at 8 Hz ODR, new mag data is available every 125 ms } // Get hard iron correction mag_bias[0] =(mag_max[0] mag_min[0])/2; // get average x mag bias in counts mag_bias[1] =(mag_max[1] mag_min[1])/2; // get average y mag bias in counts mag_bias[2] =(mag_max[2] mag_min[2])/2; // get average z mag bias in counts dest1[0]=(float)mag_bias[0]*mRes*magCalibration[0]; // save mag biases in G for main program dest1[1]=(float)mag_bias[1]*mRes*magCalibration[1]; dest1[2]=(float)mag_bias[2]*mRes*magCalibration[2]; // Get soft iron correction estimate mag_scale[0] =(mag_max[0]–mag_min[0])/2; // get average x axis max chord length in counts mag_scale[1] =(mag_max[1]–mag_min[1])/2; // get average y axis max chord length in counts mag_scale[2] =(mag_max[2]–mag_min[2])/2; // get average z axis max chord length in counts floatavg_rad=mag_scale[0] mag_scale[1] mag_scale[2]; avg_rad/=3.0; dest2[0]=avg_rad/((float)mag_scale[0]); dest2[1]=avg_rad/((float)mag_scale[1]); dest2[2]=avg_rad/((float)mag_scale[2]); Serial.println(“MagCalibrationdone!”); } |
Калибровка исходных/необработанных данных (raw data)
Получаемые с датчика MPU9250 данные должны быть откалиброваны в соответствии с оборудованием пользователя, в котором будет использоваться датчик. Калибровка магнитометра требуется для компенсации магнитного склонения (Magnetic Declination). Корректировочное значение в данном случае сильно зависит от местоположения датчика. Необходимо откалибровать две переменные: yaw и magbias.
Ниже приведенный фрагмент кода реализует калибровку переменной yaw для магнитного склонения, характерного для местности: Potheri, Chennai, India.
Данные о магнитном склонении в конкретной местности можно получить, к примеру, с помощью следующих сайтов:
В следующем фрагменте кода реализована калибровка переменной magbias, которая взята из функции magcalMPU9250(float * dest1, float * dest2) (из библиотеки, которую мы используем для работы с датчиком MPU9250).
Комплектующие
Создаётся данный датчик или МК, в зависимости от того, что вы собрались приобретать, из компонентов ATmega328.

Так, в нём имеются:
- 14 штук различных пинов и цифровых выходов, половина из которых являются ШИМ-выходами.
- Специальные кварцевые резонаторы до 16 МГц мощностью.
- Встроенный вход под usb-кабель, который позволит вам сэкономить не только время, но и деньги, которые вы могли бы потратить на покупку адаптера.
- Контакты и распиновка для стандартного питания с нулем, фазой и заземлением.
- Контакты для сброса до заводских настроек, при которых весь машинный код и данные будут стёрты. Это полезно в том случае, если вы напортачили с программой и модуль превратился в бесполезную груду железа, и просто как экономия времени, если необходимо сменить прошивку.
- ICSP контакт, который необходим для того, чтобы запрограммировать машинный код, который будет находиться внутри системы.
Все эти компоненты и составляют Arduino гироскоп, позволяя ему выполнять свои базовые функции. Но как же запрограммировать систему, если вы до этого не имели опыта работа с данными МК?
Назначение связки гироскоп и акселерометр
Для начала давайте разберёмся, зачем Arduino mpu 6050 (Gy-521) вообще нужен и что собой представляет гироскоп-акселерометр в целом. Такой датчик все мы видели в смартфонах, и там он выполняет следующие функции:
- Позволяет замерять шаги. Акселерометр способен отслеживать резкие движения устройства, а в зависимости от его настройки и чувствительности, считать некоторые из них за шаг.
- Измеряет поворот экрана. Здесь уже оба устройства работают в паре. Ведь когда вы поворачиваете смартфон набок, картинка должна изменить свою ориентацию для пользователя, и лишь с помощью гироскопа удаётся определить угол наклона, под которым ПО это должно будет сделать.
- Компас, карты и навигация. Акселерометр с гироскопом позволяют определить ориентацию устройства в пространстве, что необходимо в различных приложениях для мобильной навигации.
Вот и выходит, что данный датчик подойдёт для тех проектов, в которых вам необходимо измерить ориентацию или движения прибора в пространстве, без точных данных о его местоположении. Это может быть, как самодельная линейка со встроенным уровнем, чтобы пользователь мог определить, насколько ровно стоит та или иная мебель, так и устройство для кровати, встроенной в стену, включающее свет, когда она выдвигается.
Но применить модуль можно и с большей выдумкой, например, для измерения количества оборотов в секунду и регуляции мощности охладительной системы или автоматизации различных процессов.
Всё зависит исключительно от вашей выдумки и конкретного проекта.
Чаще всего гироскоп для Ардуино применяется в системах автоматизации под так называемые «смартхаусы» (умные дома – прим. ред.), являясь своеобразным переключателем. Передавая определённые данные в МК, который затем отправляет их по блютуз-модулю к другому устройству, он может управлять всей техникой в доме.
Ещё один простой способ применения – использование вместо датчика движения на дверях, для включения света и кондиционирования, когда вы возвращаетесь домой.
Наладка
Далее наступает самый ответственный этап – отладка программного кода. Здесь вам необходимо подключить питание к прибору, а сам прибор – к компьютеру, чтобы следить за строками в консоли. Прогоните несколько базовых функций и посмотрите, не будет ли ошибок или багов. Если они возникают, то воспользуйтесь любым удобным методом дебагинга.
Самый простой вариант – использовать для ввода переменные, которые вычисляются рандомным образом, и смотреть, как код будет вести себя в различных ситуациях.
Необходимые компоненты
- Плата Arduino Mega 2560 (купить на AliExpress) (можно также использовать и другие типы плат Arduino).
- Датчик 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 такая:
… а значит у нас уже не 8 лишних штырьков, а целых двенадцать!
Получение “реальных” значений
Наконец, на завершающем этапе обработки данных, мы получим значения параметров yaw, pitch и roll из кватернионов.
yaw =atan2(2.0f*(q[1]*q[2] q[0]*q[3]),q[0]*q[0] q[1]*q[1]–q[2]*q[2]–q[3]*q[3]); pitch=–asin(2.0f*(q[1]*q[3]–q[0]*q[2])); roll =atan2(2.0f*(q[0]*q[1] q[2]*q[3]),q[0]*q[0]–q[1]*q[1]–q[2]*q[2] q[3]*q[3]); pitch*=180.0f/PI; yaw *=180.0f/PI; yaw =1.34;/* Declination at Potheri, Chennail ,India Model Used: IGRF12 Help Latitude: 12.823640° N Longitude: 80.043518° E Date Declination 2023-04-09 1.34° W changing by 0.06° E per year ( ve for west )*/ roll *=180.0f/PI; Serial.print(“Yaw,Pitch,Roll:“); Serial.print(yaw 180,2); Serial.print(“,“); Serial.print(pitch,2); Serial.print(“,“); Serial.println(roll,2); |
Постоянная калибровка для определенного места
Если вы не хотите каждый раз при включении устройства производить автоматическую калибровку магнитометра, то вы должны записать средние значения переменной magbias[] после их вычислений с помощью следующего фрагмента кода:
Значения 470, 120, 125 фиксированы для местоположения автора проекта, поэтому после выполнения данной процедуры нет необходимости вызова функции void magcalMPU9250(float * dest1, float * dest2), поэтому вы можете закомментировать или удалить ее из программы. Также не забудьте закомментарить в программе ее вызов как сделано в следующем примере кода:
Программы
Без программы модуль будет не более чем грудой железа, которая не выполнит ни одной функции. Базовые библиотеки для взаимодействия с другими МК можно найти на официальном сайте или в интернете, но, помимо них, вам потребуется вспомогательный код. С его помощью можно настроить взаимодействие между акселерометром и тем же блютуз модулем, без которого, в большинстве проектов, он станет бесполезен.
Мы воспользуемся готовой библиотекой для Arduino MPU 6050, которую написал Джефф Роуберг.
Работа с 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();
Сборка
Здесь всё зависит от используемого вами интерфейса, например, для I2C от Ардуино пригодятся контакты: A4, A5, которые являются SDA и SCL входами соответственно.
Для нормального функционирования всей этой системы необходимо будет использовать wire библиотеку в коде.
Gy-521 (mpu6050) | Arduino (Uno) |
---|---|
VCC | 3.3 V |
GND | GND |
SCL | A5 |
SDA | A4 |
Также будьте готовы к тому, что распиновка может оказаться не самой удачной, поэтому не стоит делать корпус для устройства впритык, пока вы не подключите и не увидите реальные размеры вашего проекта.
Питание модуля строго 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,
Более приятный глазу пример описан
Тестовая установка
, о котором
, был пересмотрен, урезан и сокращён (кстати, на этом фото заметно отличие в качестве металлизации отверстий обозреваемой платы и дешевой платы Ардуино):
если отсоединить
тентакли
шлейф от креплений, то откроются два ряда контактов, у которых нужно «поотламывать половинки», — в результате получится разъем, удобно вставляющийся в отверстия макетной платы. Для пущей надёжности, нужно проклеить корпус, т.к. держаться на одних контактах конструкция не будет.
всё ещё может напугать, но на самом деле бояться нечего:
спаял с обратной стороны контакты, прозвонил их тестером — и можно подключать. Лично моя практика показывает, что лучше потратить пару секунд на предварительную проверку, чем ткнуть «не туда» и спалить девайс… Я так спалил Orange Pi PC =)
Товар предоставлен для написания обзора магазином. Обзор опубликован в соответствии с п.18 Правил сайта.
Усреднение данных
Поскольку данные на выходе датчика изменяются очень быстро, мы будем производить их выборку в течение определенного промежутка времени (50 ms) и вычислять их среднее значение.
Фильтрация данных
Поскольку исходные данные (raw data) содержат достаточно большое количество шума, мы будем использовать различные фильтры (Madgwick/Mahony/Kalman) чтобы преобразовать их в кватернионы (Quaternions).
voidMadgwickQuaternionUpdate(floatax,floatay,floataz,floatgx,floatgy,floatgz,floatmx,floatmy,floatmz) { floatq1=q[0],q2=q[1],q3=q[2],q4=q[3]; // short name local variable for readability floatnorm; floathx,hy,_2bx,_2bz; floats1,s2,s3,s4; floatqDot1,qDot2,qDot3,qDot4; // Auxiliary variables to avoid repeated arithmetic float_2q1mx; float_2q1my; float_2q1mz; float_2q2mx; float_4bx; float_4bz; float_2q1=2.0f*q1; float_2q2=2.0f*q2; float_2q3=2.0f*q3; float_2q4=2.0f*q4; float_2q1q3=2.0f*q1*q3; float_2q3q4=2.0f*q3*q4; floatq1q1=q1*q1; floatq1q2=q1*q2; floatq1q3=q1*q3; floatq1q4=q1*q4; floatq2q2=q2*q2; floatq2q3=q2*q3; floatq2q4=q2*q4; floatq3q3=q3*q3; floatq3q4=q3*q4; floatq4q4=q4*q4; // Normalise accelerometer measurement norm=sqrt(ax*ax ay*ay az*az); if(norm==0.0f)return;// handle NaN norm=1.0f/norm; ax*=norm; ay*=norm; az*=norm; // Normalise magnetometer measurement norm=sqrt(mx*mx my*my mz*mz); if(norm==0.0f)return;// handle NaN norm=1.0f/norm; mx*=norm; my*=norm; mz*=norm; // Reference direction of Earth’s magnetic field _2q1mx=2.0f*q1*mx; _2q1my=2.0f*q1*my; _2q1mz=2.0f*q1*mz; _2q2mx=2.0f*q2*mx; hx=mx*q1q1–_2q1my*q4 _2q1mz*q3 mx*q2q2 _2q2*my*q3 _2q2*mz*q4–mx*q3q3–mx*q4q4; hy=_2q1mx*q4 my*q1q1–_2q1mz*q2 _2q2mx*q3–my*q2q2 my*q3q3 _2q3*mz*q4–my*q4q4; _2bx=sqrt(hx*hx hy*hy); _2bz=–_2q1mx*q3 _2q1my*q2 mz*q1q1 _2q2mx*q4–mz*q2q2 _2q3*my*q4–mz*q3q3 mz*q4q4; _4bx=2.0f*_2bx; _4bz=2.0f*_2bz; // Gradient decent algorithm corrective step s1=–_2q3*(2.0f*q2q4–_2q1q3–ax) _2q2*(2.0f*q1q2 _2q3q4–ay)–_2bz*q3*(_2bx*(0.5f–q3q3–q4q4) _2bz*(q2q4–q1q3)–mx) (–_2bx*q4 _2bz*q2)*(_2bx*(q2q3–q1q4) _2bz*(q1q2 q3q4)–my) _2bx*q3*(_2bx*(q1q3 q2q4) _2bz*(0.5f–q2q2–q3q3)–mz); s2=_2q4*(2.0f*q2q4–_2q1q3–ax) _2q1*(2.0f*q1q2 _2q3q4–ay)–4.0f*q2*(1.0f–2.0f*q2q2–2.0f*q3q3–az) _2bz*q4*(_2bx*(0.5f–q3q3–q4q4) _2bz*(q2q4–q1q3)–mx) (_2bx*q3 _2bz*q1)*(_2bx*(q2q3–q1q4) _2bz*(q1q2 q3q4)–my) (_2bx*q4–_4bz*q2)*(_2bx*(q1q3 q2q4) _2bz*(0.5f–q2q2–q3q3)–mz); s3=–_2q1*(2.0f*q2q4–_2q1q3–ax) _2q4*(2.0f*q1q2 _2q3q4–ay)–4.0f*q3*(1.0f–2.0f*q2q2–2.0f*q3q3–az) (–_4bx*q3–_2bz*q1)*(_2bx*(0.5f–q3q3–q4q4) _2bz*(q2q4–q1q3)–mx) (_2bx*q2 _2bz*q4)*(_2bx*(q2q3–q1q4) _2bz*(q1q2 q3q4)–my) (_2bx*q1–_4bz*q3)*(_2bx*(q1q3 q2q4) _2bz*(0.5f–q2q2–q3q3)–mz); s4=_2q2*(2.0f*q2q4–_2q1q3–ax) _2q3*(2.0f*q1q2 _2q3q4–ay) (–_4bx*q4 _2bz*q2)*(_2bx*(0.5f–q3q3–q4q4) _2bz*(q2q4–q1q3)–mx) (–_2bx*q1 _2bz*q3)*(_2bx*(q2q3–q1q4) _2bz*(q1q2 q3q4)–my) _2bx*q2*(_2bx*(q1q3 q2q4) _2bz*(0.5f–q2q2–q3q3)–mz); norm=sqrt(s1*s1 s2*s2 s3*s3 s4*s4); // normalise step magnitude norm=1.0f/norm; s1*=norm; s2*=norm; s3*=norm; s4*=norm; // Compute rate of change of quaternion qDot1=0.5f*(–q2*gx–q3*gy–q4*gz)–beta*s1; qDot2=0.5f*(q1*gx q3*gz–q4*gy)–beta*s2; qDot3=0.5f*(q1*gy–q2*gz q4*gx)–beta*s3; qDot4=0.5f*(q1*gz q2*gy–q3*gx)–beta*s4; // Integrate to yield quaternion q1 =qDot1*deltat; q2 =qDot2*deltat; q3 =qDot3*deltat; q4 =qDot4*deltat; norm=sqrt(q1*q1 q2*q2 q3*q3 q4*q4); // normalise quaternion norm=1.0f/norm; q[0]=q1*norm; q[1]=q2*norm; q[2]=q3*norm; q[3]=q4*norm; } // Similar to Madgwick scheme but uses proportional and integral filtering on the error between estimated reference vectors and // measured ones. voidMahonyQuaternionUpdate(floatax,floatay,floataz,floatgx,floatgy,floatgz,floatmx,floatmy,floatmz) { floatq1=q[0],q2=q[1],q3=q[2],q4=q[3]; // short name local variable for readability floatnorm; floathx,hy,bx,bz; floatvx,vy,vz,wx,wy,wz; floatex,ey,ez; floatpa,pb,pc; // Auxiliary variables to avoid repeated arithmetic floatq1q1=q1*q1; floatq1q2=q1*q2; floatq1q3=q1*q3; floatq1q4=q1*q4; floatq2q2=q2*q2; floatq2q3=q2*q3; floatq2q4=q2*q4; floatq3q3=q3*q3; floatq3q4=q3*q4; floatq4q4=q4*q4; // Normalise accelerometer measurement norm=sqrt(ax*ax ay*ay az*az); if(norm==0.0f)return;// handle NaN norm=1.0f/norm; // use reciprocal for division ax*=norm; ay*=norm; az*=norm; // Normalise magnetometer measurement norm=sqrt(mx*mx my*my mz*mz); if(norm==0.0f)return;// handle NaN norm=1.0f/norm; // use reciprocal for division mx*=norm; my*=norm; mz*=norm; // Reference direction of Earth’s magnetic field hx=2.0f*mx*(0.5f–q3q3–q4q4) 2.0f*my*(q2q3–q1q4) 2.0f*mz*(q2q4 q1q3); hy=2.0f*mx*(q2q3 q1q4) 2.0f*my*(0.5f–q2q2–q4q4) 2.0f*mz*(q3q4–q1q2); bx=sqrt((hx*hx) (hy*hy)); bz=2.0f*mx*(q2q4–q1q3) 2.0f*my*(q3q4 q1q2) 2.0f*mz*(0.5f–q2q2–q3q3); // Estimated direction of gravity and magnetic field vx=2.0f*(q2q4–q1q3); vy=2.0f*(q1q2 q3q4); vz=q1q1–q2q2–q3q3 q4q4; wx=2.0f*bx*(0.5f–q3q3–q4q4) 2.0f*bz*(q2q4–q1q3); wy=2.0f*bx*(q2q3–q1q4) 2.0f*bz*(q1q2 q3q4); wz=2.0f*bx*(q1q3 q2q4) 2.0f*bz*(0.5f–q2q2–q3q3); // Error is cross product between estimated direction and measured direction of gravity ex=(ay*vz–az*vy) (my*wz–mz*wy); ey=(az*vx–ax*vz) (mz*wx–mx*wz); ez=(ax*vy–ay*vx) (mx*wy–my*wx); if(Ki>0.0f) { eInt[0] =ex; // accumulate integral error eInt[1] =ey; eInt[2] =ez; } else { eInt[0]=0.0f; // prevent integral wind up eInt[1]=0.0f; eInt[2]=0.0f; } // Apply feedback terms gx=gx Kp*ex Ki*eInt[0]; gy=gy Kp*ey Ki*eInt[1]; gz=gz Kp*ez Ki*eInt[2]; // Integrate rate of change of quaternion pa=q2; pb=q3; pc=q4; q1=q1 (–q2*gx–q3*gy–q4*gz)*(0.5f*deltat); q2=pa (q1*gx pb*gz–pc*gy)*(0.5f*deltat); q3=pb (q1*gy–pa*gz pc*gx)*(0.5f*deltat); q4=pc (q1*gz pa*gy–pb*gx)*(0.5f*deltat); // Normalise quaternion norm=sqrt(q1*q1 q2*q2 q3*q3 q4*q4); norm=1.0f/norm; q[0]=q1*norm; q[1]=q2*norm; q[2]=q3*norm; q[3]=q4*norm; } |
Характеристики:
— 16-битный АЦП,
— напряжение питания 3-5В,
— поддержка протокола «IIC» (может, I2C ?),
— диапазон ускорений: ± 2 ± 4 ± 8 ± 16g,
— диапазон «гиро»: ± 250 500 1000 2000 ° / s,
— покрытие иммерсионным золотом вместо лужения,
— ток при работе последнего примера составил 5.3 мА и 1.2 мА когда устройство не успело стартовать (питание на модуль было подано после выполнения setup() контроллером)