[Перевод] Электронный циркуль Тима
Первая из двух статей, посвящённых сборке электронных инструментов на основе датчика Холла. В этой части разбирается простейшая модификация циркуля, включающего в себя один датчик и два магнита. Вторая же часть будет посвящена более сложному инструменту — пантографу (прим. пер.).
Недавно мне пришла в голову идея собрать небольшой инструмент — электронный циркуль — чтобы показать базовые принципы работы датчика эффекта Холла. И в процессе съёмки ролика, демонстрирующего циркуль в действии, я решил также пояснить, как нахожу полюса используемых в этом циркуле магнитов.
Печать всех необходимых деталей я делал на 3D-принтере. В принципе, можно взять и другие материалы, но тут очень важна точность.
Основой проекта выступает датчик Холла 49Е и два магнита. Вычислениями при этом занимается Arduino Nano.
А поскольку AtMega328p оснащён всего лишь 10-битным АЦП, и в вычислениях используется синус, то придётся допустить некоторые погрешности. Результаты я буду выводить на двухстрочный дисплей 1602 с 16 символами на строку.
Если всё пойдёт хорошо, то я планирую реализовать и другие подобные проекты на датчиках Холла, а именно 2D и 3D-инструменты.
▍ Материалы
- линейный датчик Холла 49E;
- два неодимовых магнита, ширина 5 мм, высота 2 мм (Север/Юг определяется по оси высоты);
- инструмент для определения полюсов магнитов. Я использовал приложение на телефоне;
- Arduino Nano;
- дисплей 1602 с интерфейсом I2C. (модуль PCF8575);
- кабель;
- клей, я взял УФ-смолу;
- 3D-принтер для печати ножек циркуля.
Ссылки на STL-файлы для печати:
▍ Шаг 1: датчик Холла 49E
Датчики Холла выпускают многие компании, хотя большой разницы между ними нет. Ниже я приложил спецификацию, которую нашёл на Honeywell.
Лично я использовал самый дешёвый, какой смог найти в интернете, модель 49E, и пластиковые детали подогнал конкретно под него.
У этого датчика всего три вывода:
- пин 1 = питание (от +2.7 до +6.5В).
- пин 2 = земля (GND).
- пин 3 = аналоговый вывод (обычно на 1 вольт меньше входного напряжения)
Вывод линейный, мВ/Гс.
В спецификации указано направление, в котором датчик определяет силу магнитного поля [N S]. Если двигать магнит ближе-дальше от датчика, то показания будут отражать магнитную силу в указанном направлении.
▍ Шаг 2: магниты
Для изменения силы магнитного поля вокруг датчика я использую два неодимовых магнита. Направление Север/Юг соответствует верху/низу магнитов, то есть ориентировано по оси высоты.
Разберём, как создаётся поле одним магнитом.
Поле сконцентрировано в его центре, где магнитные линии идут с Юга на Север параллельно. Выходя за края магнита, эти линии изгибаются и в итоге замыкаются с другой стороны — на противоположный полюс.
Если приблизить датчик к любому полюсу и сдвинуть его к одной из сторон, магнитная сила существенно снизится, поскольку линии магнитного поля замкнуты.
Датчики Холла очень чувствительны, поэтому их неточное расположение приведёт к ошибочным показаниям. Нам же нужно сократить возможное количество потенциальных ошибок. Жаль, нельзя разместить датчик внутри самого магнита — было бы идеально. Я же с целью уменьшить вероятность ошибок решил использовать два магнита.
Если расположить эти магниты недалеко друг от друга, то между ними возникнет однородное поле, близкое к такому, что должно быть внутри магнита. И если расположить в этой области датчик, то при повороте магнитов вокруг него поле должно сохранять тот же угол наклона, что и магниты.
▍ Шаг 3: вычисление угла
Магниты мы будем вращать вокруг датчика.
Вращение и простое отдаление магнитов от датчика — разные вещи. То есть в результате вращения на выводе мы будем получать не линейный результат, а значение синуса угла, на который происходит поворот.
Для лучшего понимания я сделал чертёж.
- Фиолетовый кружок — это путь, по которому магниты движутся вокруг центра, то есть датчика.
- синие линии представляют углы/позиции магнитов по мере их вращения.
- жёлтыми линиями обозначена линейные силы, которые датчик будет регистрировать с Юга на Север.
- зелёной линией показан график вычисления синусов.
- красная — это зона точности.
Поскольку вывод датчика у нас аналоговый, выходит, что точность ограничена количеством десятичных знаков значения, в которое мы можем эти аналоговые показания конвертировать.
В Arduino Nano используется 10-битный АЦП. У этого микроконтроллера тип double
аналогичен float
, который имеет всего 6–7 десятичных знаков. И поскольку у нас происходит вращение, а не удаление магнитов от датчика, выводом будет результат вычисления синуса.
При этом в нашем случае также неизбежны участки, в которых 10-битный АЦП не позволит произвести точные расчёты.
Углы, которые можно вычислить точно, показаны синими линиями с кружочками на конце. Проблемные же углы обозначены линиями с крестиками.
По синусоиде видно, что в крайних положениях вращения в изменении магнитной силы делений градуса получается больше. Для лучшей наглядности я специально указал область точности.
Для охвата этих проблемных зон можно задействовать второй датчик, расположив его под 90° относительно первого. Это позволит использовать только точные области каждого.
В плане математики здесь простая тригонометрия, и я покажу это, когда дело дойдёт до кода.
▍ Шаг 4: сборка циркуля
Я уже сказал, что датчик очень чувствительный, и размещать компоненты необходимо очень чётко. В этом смысле мне помог 3D-принтер, который гарантирует точную печать деталей.
При установке минимальная погрешность в выравнивании допустима, но после фиксации гулять элементы уже не должны. Дополнительно некоторые огрехи в их расположении можно будет компенсировать в коде.
▍ Шаг 5: установка датчика
Датчик нужно вклеить в отведённую для него ножку циркуля (Sensor Arm). В этой детали есть специальное отверстие для определения его правильного положения.
▍ Шаг 6: Ориентация магнитов
Очень важно правильно выставить магниты. Я для этой процедуры использовал приложение на телефоне. Подойдёт такое решение только для устройств с магнитометром. Хотя обычно, если у телефона есть GPS, то и магнитометр тоже должен быть.
Что касается приложения, то из множества вариантов я предпочёл трёхмерный, а именно 3D Compass and Magnetometer. На изображении показано типичное 3D-приложение, в котором стрелка указывает в направлении севера.
Магнитометры в телефонах не всегда расположены по центру. Лучше всего выяснить его точное положение с помощью магнита, так будет проще разобраться с полюсами.
Зная расположение магнитометра, я обычно располагаю телефон в подвешенном положении над столом и кладу под него магнит, чтобы он оказался ровно под магнитометром, и уже тогда определяю полюса.
▍ Шаг 7: установка магнитов во вторую ножку циркуля
Здесь придётся немного повозиться.
Прежде чем соединять ножки, убедитесь, что магниты могут перемещаться внутри канавки той, что с датчиком — ходить они в ней должны плотно.
Магниты необходимо зафиксировать в одном положении, обеспечив правильную направленность их полюсов, и наложить внешнюю сторону второй ножки поверх первой.
После соединения подвижными должны быть только сами ножки, сдвигаясь и раздвигаясь, как это и положено циркулю. Никаких других побочных движений или люфтов быть не должно, иначе возникнут ошибки.
▍ Шаг 8: подключение кабеля
Следующим этапом нужно припаять к датчику провода для подключения Arduino.
Мой Arduino установлен на макетной плате, и для соединения с ним я на конце провода использовал разъём DuPont «мама».
Уточнить правильность соединения контактов можно в шаге 1.
▍ Шаг 9: ЖК-дисплей
Для отображения показаний датчика я использую двухстрочный ЖК-дисплей с 16 символами на строку. Кроме того, я использовал переходной модуль PCF8575, так что для подключения мне нужны только выводы I2C. К Arduino же я подцепил всё это 4-жильным кабелем с разъёмами типа «мама» (правильно ли я всё понял и выразил?…).
▍ Шаг 10: подключение к Arduino
Схема подключения к Arduino Nano в программе Fritzing:
Файлы для скачивания:
▍ Шаг 11: код
Если вы уже работали с 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.
Закройте циркуль и запишите первое число, которое указано над Angle
.
Откройте циркуль и снова запишите первое число, указанное над 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: повышение точности
Выше я продемонстрировал создание простейшего измерительного инструмента и принцип действия датчика Холла.
Модель 49E — это всего лишь один линейный аналоговый датчик, но здесь можно использовать и другие:
- Модель AS5600 представляет собой легко программируемый 12-битный магнитный поворотный датчик положения с аналоговым или PWM-выводом, оснащённый интерфейсом I2C.
- Модель MLX90393 предлагает 16-битный вывод, пропорциональный плотности магнитного потока, регистрируемой вдоль осей X, Y и Z. Можно выбирать между протоколами SPI и I2C.
Более дорогостоящие датчики обеспечат ещё большую точность.