Сегодня речь пойдет от трехосевом магнитометре HMC5883L (модуль GY-273), который при должной обработке можно использовать в качестве компаса.
Подключается датчик по интерфейсу I2C, который на Arduino UNO задействован на портах A4 и A5.
Схема подключения:
Для программирования воспользуемся вот этой библиотекой.
Получение сырых и нормализованных данных:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 |
#include <Wire.h> #include <HMC5883L.h> HMC5883L compass; void setup() { Serial.begin(9600); // инициализация HMC5883L Serial.println("Initialize HMC5883L"); while (!compass.begin()) { Serial.println("HMC5883L не найден, проверьте соединение!"); delay(500); } // Установка диапазона измерения // +/- 0.88 Ga: HMC5883L_RANGE_0_88GA // +/- 1.30 Ga: HMC5883L_RANGE_1_3GA (по умолчанию) // +/- 1.90 Ga: HMC5883L_RANGE_1_9GA // +/- 2.50 Ga: HMC5883L_RANGE_2_5GA // +/- 4.00 Ga: HMC5883L_RANGE_4GA // +/- 4.70 Ga: HMC5883L_RANGE_4_7GA // +/- 5.60 Ga: HMC5883L_RANGE_5_6GA // +/- 8.10 Ga: HMC5883L_RANGE_8_1GA compass.setRange(HMC5883L_RANGE_1_3GA); // Установка режима работы // Спящий режим: HMC5883L_IDLE // Единое измерение: HMC5883L_SINGLE // Непрерывное измерение: HMC5883L_CONTINOUS (по умолчанию) compass.setMeasurementMode(HMC5883L_CONTINOUS); // Установка частоты измерения // 0.75Hz: HMC5883L_DATARATE_0_75HZ // 1.50Hz: HMC5883L_DATARATE_1_5HZ // 3.00Hz: HMC5883L_DATARATE_3HZ // 7.50Hz: HMC5883L_DATARATE_7_50HZ // 15.00Hz: HMC5883L_DATARATE_15HZ (по умолчанию) // 30.00Hz: HMC5883L_DATARATE_30HZ // 75.00Hz: HMC5883L_DATARATE_75HZ compass.setDataRate(HMC5883L_DATARATE_15HZ); // Число усредненных выборок // 1 probka: HMC5883L_SAMPLES_1 (по умолчанию) // 2 probki: HMC5883L_SAMPLES_2 // 4 probki: HMC5883L_SAMPLES_4 // 8 probki: HMC5883L_SAMPLES_8 compass.setSamples(HMC5883L_SAMPLES_1); } void loop() { // Поучение необработанных значений Vector raw = compass.readRaw(); // Поучение нормализованных значений Vector norm = compass.readNormalize(); Serial.print(" Xraw = "); Serial.print(raw.XAxis); Serial.print(" Yraw = "); Serial.print(raw.YAxis); Serial.print(" Zraw = "); Serial.print(raw.ZAxis); Serial.print(" Xnorm = "); Serial.print(norm.XAxis); Serial.print(" Ynorm = "); Serial.print(norm.YAxis); Serial.print(" ZNorm = "); Serial.print(norm.ZAxis); Serial.println(); delay(100); } |
Вектора X и Y позволяют узнать отклонение от направления на север по следующей зависимости:
направление измерения (rad) = atan (vector_y, vector_x);
Кроме того, нужно знать еще и магнитное склонение. Посмотреть его можно здесь http://www.magnetic-declination.com/.
У меня оно 15 градусов 46 минут.
Получим направление:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 |
#include <Wire.h> #include <HMC5883L.h> HMC5883L compass; int previousDegree; void setup() { Serial.begin(9600); Serial.println("Initialize HMC5883L"); while (!compass.begin()) { Serial.println("HMC5883L не найден, проверьте соединение!"); delay(500); } // Установка диапазона измерения compass.setRange(HMC5883L_RANGE_1_3GA); // Установка режима работы compass.setMeasurementMode(HMC5883L_CONTINOUS); // Установка частоты измерения compass.setDataRate(HMC5883L_DATARATE_15HZ); // Число усредненных выборок compass.setSamples(HMC5883L_SAMPLES_4); } void loop() { // Поучение нормализованных значений Vector norm = compass.readNormalize(); // Расчет направления (рад) float heading = atan2(norm.YAxis, norm.XAxis); // Устанавливаем угол наклона для Перми 15'46E (positive) // Формула: (deg + (min / 60.0)) / (180 / M_PI); float declinationAngle = (15.0 + (46.0 / 60.0)) / (180 / M_PI); heading += declinationAngle; if (heading < 0) { heading += 2 * PI; } if (heading > 2 * PI) { heading -= 2 * PI; } // Преобразование радианов в градусы float headingDegrees = heading * 180 / M_PI; Serial.print(" Heading = "); Serial.print(heading); Serial.print(" Degress = "); Serial.print(headingDegrees); Serial.println(); delay(100); delay(100); } |
Нужно иметь в виду, что датчик очень чувствителен к наклону и к магнитным приборам рядом с ним. Примеры взяты отсюда: http://www.jarzebski.pl/arduino/czujniki-i-sensory/3-osiowy-magnetometr-hmc5883l.html
Автор также сообщает, что особенностью данного магнитометра является неравномерное измерение магнитного поля в диапазоне от 1 ° до 180 ° и от 180 ° до 360 °. Для первого отсека наш магнитометр будет генерировать искаженные результаты от 1 ° до 240 ° , а для второго — от 240 ° ÷ 360 ° . Поэтому следует вносить поправки, например, с помощью функции map:
1 2 3 4 5 6 7 8 9 |
float fixedHeadingDegrees; if (headingDegrees >= 1 && headingDegrees < 240) { fixedHeadingDegrees = map(headingDegrees, 0, 239, 0, 179); } else if (headingDegrees >= 240) { fixedHeadingDegrees = map(headingDegrees, 240, 360, 180, 360); } |
Сделаем небольшой проект — компас со светодиодом: когда направление приближается к северу, светодиод горит ярче, когда отдаляется — яркость уменьшается.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 |
#include <Wire.h> #include <HMC5883L.h> HMC5883L compass; int previousDegree; int led = 5; void setup() { Serial.begin(9600); Serial.println("Initialize HMC5883L"); while (!compass.begin()) { Serial.println("HMC5883L не найден, проверьте соединение!"); delay(500); } compass.setRange(HMC5883L_RANGE_1_3GA); compass.setMeasurementMode(HMC5883L_CONTINOUS); compass.setDataRate(HMC5883L_DATARATE_15HZ); compass.setSamples(HMC5883L_SAMPLES_4); } void loop() { Vector norm = compass.readNormalize(); float heading = atan2(norm.YAxis, norm.XAxis); float declinationAngle = (15.0 + (46.0 / 60.0)) / (180 / M_PI); heading += declinationAngle; if (heading < 0) { heading += 2 * PI; } if (heading > 2 * PI) { heading -= 2 * PI; } float headingDegrees = heading * 180 / M_PI; float fixedHeadingDegrees; if (headingDegrees >= 1 && headingDegrees < 240) { fixedHeadingDegrees = map(headingDegrees, 0, 239, 0, 179); } else if (headingDegrees >= 240) { fixedHeadingDegrees = map(headingDegrees, 240, 360, 180, 360); } Serial.print(" Heading = "); Serial.print(heading); Serial.print(" Degress = "); Serial.print(fixedHeadingDegrees); Serial.println(); int dec = abs(fixedHeadingDegrees - 180); // отклонение от севера int bright = map(dec, 0, 180, 0, 255); // преобразуем отклонение в яркость analogWrite(led, bright ); delay(200); } |
Мне понравилась статья.
Как все плохо.
Не сказано о необходимости калибровать программно. Не правильно представлено получения проекции вектора на плоскость (направление на Северный магнитный полюс)
Вектор магнитного поля [X, Y, Z] *[вектор наклона]
Вычисляет проекцию. Вводим поправку склонения. Получаем направление на север