[Перевод] Электронный циркуль Тима

4_8ubfwu1gknip2wlyieog14d9s.png


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

Недавно мне пришла в голову идея собрать небольшой инструмент — электронный циркуль — чтобы показать базовые принципы работы датчика эффекта Холла. И в процессе съёмки ролика, демонстрирующего циркуль в действии, я решил также пояснить, как нахожу полюса используемых в этом циркуле магнитов.

Печать всех необходимых деталей я делал на 3D-принтере. В принципе, можно взять и другие материалы, но тут очень важна точность.

Основой проекта выступает датчик Холла 49Е и два магнита. Вычислениями при этом занимается Arduino Nano.

А поскольку AtMega328p оснащён всего лишь 10-битным АЦП, и в вычислениях используется синус, то придётся допустить некоторые погрешности. Результаты я буду выводить на двухстрочный дисплей 1602 с 16 символами на строку.

Если всё пойдёт хорошо, то я планирую реализовать и другие подобные проекты на датчиках Холла, а именно 2D и 3D-инструменты.

▍ Материалы


  • линейный датчик Холла 49E;
  • два неодимовых магнита, ширина 5 мм, высота 2 мм (Север/Юг определяется по оси высоты);
  • инструмент для определения полюсов магнитов. Я использовал приложение на телефоне;
  • Arduino Nano;
  • дисплей 1602 с интерфейсом I2C. (модуль PCF8575);
  • кабель;
  • клей, я взял УФ-смолу;
  • 3D-принтер для печати ножек циркуля.


Фото компонентов


Ссылки на STL-файлы для печати:

▍ Шаг 1: датчик Холла 49E


Датчики Холла выпускают многие компании, хотя большой разницы между ними нет. Ниже я приложил спецификацию, которую нашёл на Honeywell.

Лично я использовал самый дешёвый, какой смог найти в интернете, модель 49E, и пластиковые детали подогнал конкретно под него.

_auke5vtqu_kesxw2yruhgflf0i.png

-vwigaa32kdefelo-bmfgu8gsog.png

У этого датчика всего три вывода:

  • пин 1 = питание (от +2.7 до +6.5В).
  • пин 2 = земля (GND).
  • пин 3 = аналоговый вывод (обычно на 1 вольт меньше входного напряжения)


Вывод линейный, мВ/Гс.

В спецификации указано направление, в котором датчик определяет силу магнитного поля [N S]. Если двигать магнит ближе-дальше от датчика, то показания будут отражать магнитную силу в указанном направлении.


▍ Шаг 2: магниты


Для изменения силы магнитного поля вокруг датчика я использую два неодимовых магнита. Направление Север/Юг соответствует верху/низу магнитов, то есть ориентировано по оси высоты.

Разберём, как создаётся поле одним магнитом.

gfvmu98pr4qttwubtnsdckkfum4.png

Поле сконцентрировано в его центре, где магнитные линии идут с Юга на Север параллельно. Выходя за края магнита, эти линии изгибаются и в итоге замыкаются с другой стороны — на противоположный полюс.

_i8gjrxsq7lbz6x-zekjnjnok3g.png

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

Датчики Холла очень чувствительны, поэтому их неточное расположение приведёт к ошибочным показаниям. Нам же нужно сократить возможное количество потенциальных ошибок. Жаль, нельзя разместить датчик внутри самого магнита — было бы идеально. Я же с целью уменьшить вероятность ошибок решил использовать два магнита.

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

▍ Шаг 3: вычисление угла


Магниты мы будем вращать вокруг датчика.

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

Для лучшего понимания я сделал чертёж.

2tdemdh_opbvcu2k2ncoo5ndqe4.png

  • Фиолетовый кружок — это путь, по которому магниты движутся вокруг центра, то есть датчика.
  • синие линии представляют углы/позиции магнитов по мере их вращения.
  • жёлтыми линиями обозначена линейные силы, которые датчик будет регистрировать с Юга на Север.
  • зелёной линией показан график вычисления синусов.
  • красная — это зона точности.


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

В Arduino Nano используется 10-битный АЦП. У этого микроконтроллера тип double аналогичен float, который имеет всего 6–7 десятичных знаков. И поскольку у нас происходит вращение, а не удаление магнитов от датчика, выводом будет результат вычисления синуса.

При этом в нашем случае также неизбежны участки, в которых 10-битный АЦП не позволит произвести точные расчёты.

Углы, которые можно вычислить точно, показаны синими линиями с кружочками на конце. Проблемные же углы обозначены линиями с крестиками.

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

Для охвата этих проблемных зон можно задействовать второй датчик, расположив его под 90° относительно первого. Это позволит использовать только точные области каждого.

В плане математики здесь простая тригонометрия, и я покажу это, когда дело дойдёт до кода.

▍ Шаг 4: сборка циркуля


riuxhecgczgis75cmi8ckvbze7s.png

Я уже сказал, что датчик очень чувствительный, и размещать компоненты необходимо очень чётко. В этом смысле мне помог 3D-принтер, который гарантирует точную печать деталей.

При установке минимальная погрешность в выравнивании допустима, но после фиксации гулять элементы уже не должны. Дополнительно некоторые огрехи в их расположении можно будет компенсировать в коде.

▍ Шаг 5: установка датчика


_sstsw4bdsntnklfgbe2o3m0gio.png

Датчик нужно вклеить в отведённую для него ножку циркуля (Sensor Arm). В этой детали есть специальное отверстие для определения его правильного положения.

▍ Шаг 6: Ориентация магнитов


Очень важно правильно выставить магниты. Я для этой процедуры использовал приложение на телефоне. Подойдёт такое решение только для устройств с магнитометром. Хотя обычно, если у телефона есть GPS, то и магнитометр тоже должен быть.

sr8nmsf8zjtnplhbjaczyo1se_c.png

Что касается приложения, то из множества вариантов я предпочёл трёхмерный, а именно 3D Compass and Magnetometer. На изображении показано типичное 3D-приложение, в котором стрелка указывает в направлении севера.

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

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

▍ Шаг 7: установка магнитов во вторую ножку циркуля


Здесь придётся немного повозиться.

zotiwgtqpspbrvr_oimhwsnupl8.png
Прежде чем соединять ножки, убедитесь, что магниты могут перемещаться внутри канавки той, что с датчиком — ходить они в ней должны плотно.

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

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

▍ Шаг 8: подключение кабеля


Следующим этапом нужно припаять к датчику провода для подключения Arduino.
Мой Arduino установлен на макетной плате, и для соединения с ним я на конце провода использовал разъём DuPont «мама».

rur8yn7mfsxxcm2fj0bdth6fuge.png
Уточнить правильность соединения контактов можно в шаге 1.

▍ Шаг 9: ЖК-дисплей


Для отображения показаний датчика я использую двухстрочный ЖК-дисплей с 16 символами на строку. Кроме того, я использовал переходной модуль PCF8575, так что для подключения мне нужны только выводы I2C. К Arduino же я подцепил всё это 4-жильным кабелем с разъёмами типа «мама» (правильно ли я всё понял и выразил?…).

lixfo0pc3hxm-uzvkfn6bvkbbmk.png

0km7sv0kvzxtswx0ex7xnyl-nge.png

▍ Шаг 10: подключение к Arduino


Схема подключения к Arduino Nano в программе Fritzing:

mteejki7omjtffvji0px9bojqik.png

pf7jc9wzhqc0rcsr7rxzbimxskc.png

Файлы для скачивания:


▍ Шаг 11: код


tg4y4tjs7blyto8xo1sfc7mp45m.png

Если вы уже работали с Arduino Nano, то наверняка также посещали профильный ресурс Arduino.cc для изучения различных нюансов.

Для тех же, кто использует устройство с архитектурой Arduino впервые, я сначала рекомендую почитать раздел руководства: Arduino IDE 2 Tutorials

Здесь можно скачать Arduino IDE и изучить инструкции, написанные самими создателями этой платформы. Помимо прочего, тут же описывается, как загружать скетч на устройство.

Ниже приведён код, который можно скачать в приложенном файле Tims_Electronic_Deviders.ino.

После скачивания скетча нужно будет поместить его в каталог с тем же именем, но без .ino.

Код
/*
    Tims_Electronic_Deviders.ino
    By Tim Jackson.1960

    Creadits:
        Arduino.
        LiquidCrystal_I2C based on work by DFRobot.

    This is code for: Tim's Electronic Deviders.
    I am using 3D Printed Deviders with a 49E Linear Hall Effect Sensor and two Magnets.
    I am using an Arduino NANO to calculate the postion of the deviders from the values recived from the 49E Linear Hall Effect Sensor.
    The sensor is linear: 3 mV/GS, but to get the angle a Sine of the value is calculated.

    S=O/H C=A/H T=O/A
    Degrees to Radians = degrees * (PI / 180)
    Radians to Degrees = radians * 180.0 / pi

*/
#include 
#include 

#define Hall_49R_Pin                    A1        //    Определение вывода датчика Холла
#define CAL_0                            377        //    Показания датчика, когда угол равен 0
#define CAL_180                            690        //    Показания датчика, когда угол равен 180
#define CAL_RANGE (CAL_180 - CAL_0)                //    Максимальная величина CAL - минимальная величина CAL
#define CAL_RAD ((double)CAL_RANGE / 2)            //    Используется в значении синуса H для получения угла.
#define LEG_LENGTH 99                            //    Длина ножек циркуля.
#define MAG_BIOS                        -3.11    //    Установить  на нуль, а затем изменить на значение, которое будет корректировать показания в районе 60 градусов.
double Angle = 0;                                //    Переменная для значения Angle.
double Length = 0;                                //    Переменная для значения Length.
int SensorValue = CAL_0 + 1;                    //    Переменная для значения Sensor.

LiquidCrystal_I2C lcd(0x27, 16, 2);        //20 to    27    //    Установка адреса 2-строчного ЖК-дисплея на 0x27.


void setup()
{
    Serial.begin(115200);            //    Запуск Serial.
    pinMode(Hall_49R_Pin, INPUT);    //    Определение датчика в качестве Input.

    lcd.init();                        //    Запуск дисплея.
    lcd.backlight();                //    Включение подсветки.
    lcd.setCursor(0, 0);            //    Установка курсора на начало строки 0 (верхняя строка).
    lcd.print(" Angle:");            //    Отображение метки Angle на верхней строке.
    lcd.setCursor(0, 1);            //    Установка курсора на начало строки 1 (нижняя строка).
    lcd.print("Length:");            //    Отображение метки Length на нижней строке.

}
void loop() {

    SensorValue = analogRead(Hall_49R_Pin);    //    Считывание значения с датчика.

    Serial.println(SensorValue);            //    Отправка значения на Serial.
    CalcAngle();                            //    Вычисление угла.
    CalcLength();                            //    Вычисление длины.
    Serial.println();                        //    Отправка символа новой строки на Serial для разделения значений.

    delay(200);                                //    Ожидание выполнения.
}
/*
    Вычисление Angle на основе значения, полученного от датчика.

        S=O/H
        Radians to Degrees = radians * 180.0 / pi

        O = SensorValue - CAL_0 - CAL_RAD
        H = CAL_RAD
        Angle in radians = asin(O / H)
        Angle in degrees = Angle in radians * 180.0 / pi;

*/
void CalcAngle() {

    double O = (double)SensorValue - CAL_0 - CAL_RAD;
    double H = (double)O / CAL_RAD;
    Angle = 90.0 + (asin(H) * 180.0 / PI);

    Serial.print("Angle ");
    Serial.println(Angle + MAG_BIOS, 4);

    lcd.setCursor(7, 0);
    lcd.print("  ");
    lcd.setCursor(Xpos(Angle + MAG_BIOS), 0);
    lcd.print(Angle + MAG_BIOS, 3);
    lcd.print("  ");

}
/*
    Вычисление Length на основе градусов, вычисленных датчиком.

        C=A/H
        A=C*H
        Degrees to Radians = degrees * (PI / 180)

        C = (180 - Angle in degrees) / 2
        H = LEG_LENGTH
        A = C * H
        Length = A * 2

*/
void CalcLength() {

    double _angle = (180.0 - Angle) / 2;
    double _rad = _angle * (PI / 180);
    double C = cos(_rad);
    double H = LEG_LENGTH;
    Length = C * H * 2;

    Serial.print("Length ");
    Serial.println(Length + MAG_BIOS, 4);

    lcd.setCursor(7, 1);
    lcd.print("  ");
    lcd.setCursor(Xpos(Length + MAG_BIOS), 1);
    lcd.print(Length + MAG_BIOS, 3);
    lcd.print("  ");

}
/*
    Функция для вычисления положения значения, отображаемого на дисплее.

        Проверить, указано ли значение в сотнях, десятках или единицах, чтобы соблюсти выравнивание чисел.
.
*/
byte Xpos(byte number) {
    byte val = 8;
    if (number < 100) { val = 9; }
    if (number < 10) { val = 10; }
    return val;
}


▍ Шаг 12: калибровка


Перед калибровкой значение #defined для MAG-BIOS необходимо установить на 0.

#define MAG_BIOS                        -3.11    //    Установить на 0, а затем изменить на значение, корректирующее показания в районе 60 градусов.

Для калибровки циркуля должен быть запущен Serial Monitor.

7exnqnhuos6rhwvr7ajbgewgowa.png

Закройте циркуль и запишите первое число, которое указано над Angle.

4vxinyl6pos1sv5l86r19165ai0.png

Откройте циркуль и снова запишите первое число, указанное над Angle. (я для соблюдения точных 180° произвожу эти манипуляции, положив циркуль на рабочий стол с разметкой).

Число при раскрытом циркуле, должно быть больше того, что отображалось при закрытом. Если получается наоборот — значит полярность магнитов перепутана.

Измените значения #defined для CAL_0 и CAL_180 согласно зафиксированным показаниям.

#define CAL_0                            377        //    Показания датчика, когда угол равен 0
#define CAL_180                            690        //    Показания датчика, когда угол равен 180


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

Измените значение #defined, чтобы компенсировать эту ошибку.

#define MAG_BIOS                        0    //    Установить на нуль, а затем на значение, корректирующее показания в районе 60 градусов


После калибровки показания будут близки к точным, кроме крайних значений от 0 до 15° и от 165 до 180°.

Дело в том, что в этих областях кривая синусоиды изменяется слабо.

▍ Шаг 13: повышение точности


Выше я продемонстрировал создание простейшего измерительного инструмента и принцип действия датчика Холла.

fpxduqwfp2jadz5vtbxqvcpceu0.png

ciybhszoybyymfkqd09h-3odcua.png

Модель 49E — это всего лишь один линейный аналоговый датчик, но здесь можно использовать и другие:

  • Модель AS5600 представляет собой легко программируемый 12-битный магнитный поворотный датчик положения с аналоговым или PWM-выводом, оснащённый интерфейсом I2C.
  • Модель MLX90393 предлагает 16-битный вывод, пропорциональный плотности магнитного потока, регистрируемой вдоль осей X, Y и Z. Можно выбирать между протоколами SPI и I2C.


Более дорогостоящие датчики обеспечат ещё большую точность.

sz7jpfj8i1pa6ocj-eia09dev4q.png

© Habrahabr.ru