Лидар (дальномер) на Arduino и датчике VL6180X
В настоящее время лидары (световые радары, дальномеры оптического диапазона) находят широкое применение в различных областях: сканирование зданий, 3D принтерах, построении объемных карт, беспилотных автомобилях и т.д. Поэтому в данной статье мы рассмотрим создание лидара (дальномера) на основе платы Arduino и датчике света и приближения VL6180X.
Необходимые компоненты
- Плата Arduino Uno (купить на AliExpress).
- Датчик света и приближения VL6180X (SparkFun ToF Range Finder Sensor) (купить на AliExpress) – подходит для коротких расстояний. Для больших расстояний лучше использовать датчик VL53L0X (купить на AliExpress).
- Сервомотор.
Что такое лидар
Лида́р (транслитерация LIDAR англ. Light Detection and Ranging «обнаружение и определение дальности с помощью света») — технология измерения расстояний путем излучения света (лазер) и замера времени возвращения этого отражённого света на ресивер (Википедия).
Сканирующие лидары в системах машинного зрения формируют двумерную или трёхмерную картину окружающего пространства. Лидары особенно ценны в развивающихся сегодня стремительными темпами технологиях построения беспилотных автомобилей благодаря таким своим возможностям как построение карты высот местности, определение расстояний до объектов с высокой точностью, определение скорости движущегося объекта.
Лидар (LIDAR) состоит из передатчика светового потока (обычно это лазер) и приемника отраженного от препятствия света. Зная скорость распространения света и время распространения луча света до препятствия и обратно, можно определить расстояние до препятствия.
Одним из способов измерения времени пролета (time of flight, ToF) светового луча является использование импульсного лазера и непосредственное измерение затраченного времени. Но в данном случае электроника, измеряющее это время, должна уметь работать с пикосекундными интервалами времени, что делает эти устройства весьма дорогими.
Однако есть и другой, более дешевый и простой способ измерения времени пролета светового луча, который основан на измерении фазового сдвига отраженного света. Для подобных измерений используется коллимированный инфракрасный лазер.
Для поверхностей, шероховатость (неравномерность) которых больше длины волны падающего света, возникает рассеянное (диффузное) отражение светового потока. В этом случае компоненты инфракрасного света возвращаются обратно практически параллельно излученному световому потоку. После этого датчик измеряет фазовый сдвиг между излученным и принятыми сигналами. При этом справедливо выражение:
где c – это скорость света, f – модулирующая частота, τ – длина волны.
А расстояние D’, пройденное излученным светом, определяется выражением:
D’ = B + 2A = B + (θ * τ) / 2π
Где A – это измеряемое расстояние, B – расстояние от устройства измерения фазы. В этом случае необходимое расстояние D между излучателем света и препятствием рассчитывается по формуле:
Где θ – измеренный электроникой фазовый сдвиг между переданным и принятым лучами света.
Можно показать, что расстояние обратно пропорционально квадрату амплитуды принятого сигнала. То есть амплитуда принятого сигнала непосредственно влияет на точность датчика.
Датчик VL6180X
VL6180X – это оптический датчик от компании STMicroelectronics. Его также называют датчиком света (освещенности) и приближения. Его внешний вид показан на следующем рисунке.

Датчик VL6180 содержит ИК-излучатель, датчик внешней освещенности и датчик дальности. К микроконтроллерам и другим электронным устройствам он подключается по интерфейсу I2C. Также датчик имеет встроенный стабилизатор напряжения 2,8 В, поэтому он не повредится если подключить к нему напряжение больше 2,8 В (ну в пределах разумного, конечно же). Еще в датчике присутствуют два программируемых вывода GPIO. Распиновка датчика показана на следующем рисунке.

VL6180X в отличие от других подобных датчиков содержит в своем составе точные часы для измерения времени пролета светового потока, что позволяет ему обеспечивать значительно большую точность измерений по сравнению с другими аналогичными датчиками. Это также делает его практически невосприимчивым к шуму.
Датчик VL6180X способен измерять расстояния до 25 см. Поэтому, к примеру, в домашнем использовании он отлично подойдет для измерения фокусного расстояния камеры или фотоаппарата. Если же вам необходим лидар, работающий на большие расстояния, то в данном случае вам вместо датчика VL6180X необходимо использовать датчик Vl53l0x, который также отличается большей точностью измерений и не имеет проблем с линейностью, приводящих к “двойному изображению”.
Схема проекта
Схема лидара (дальномера) на Arduino и датчике VL6180X представлена на следующем рисунке.

Скорость лидара в нашем проекте ограничена частотой 10 Гц в связи с низкой скоростью работой сервомотора. Если вы хотите сконструировать высокоскоростной лидар, то используйте шаговый двигатель или двигатель постоянного тока с обратной связью.
Установка библиотеки для работы с датчиком
Для работы с датчиком в Arduino IDE необходимо установить библиотеку Adafruit_VL6180X.h от компании Adafruit. Процесс установки этой библиотеки показан на следующем рисунке.
Исходный код программы (скетча)
Данный код управляет движениями сервомотора и считывает результаты измерений с датчика VL6180X.
Самодельный лидар на Arduino и VL53L0X TOF-дальномере

В этом проекте мы сделаем свой лидар. По характеристикам он конечно немного не дотягивает до коммерчески выпускаемых лидаров, но вполне подойдёт для небольшого простого робота, чтобы он мог обнаруживать и избегать препятствия на своём пути или более-менее точно строить карту помещения.
Я объясню вам, что такое лидар, принципы его работы и один из вариантов, как его можно сделать на Arduino и времяпролётном (TOF) инфракрасном датчике расстояния.
Корпус лидара можно сделать по-разному, но для данного проекта разработал я выбрал один из самых простых и удобных вариантов его изготовления — напечатал на 3D-принтере. Корпус сделан так, чтобы верхняя часть, в которой установлен дальномер, мог вращаться на 360 градусов без остановки. Чтобы верхняя часть не болталась и могла легко вращаться, в основании большого размера установлен подшипник.
Лидар (LIDAR, Light Detection and Ranging — «обнаружение и определение дальности с помощью света») — технология получения и обработки информации об удалённых объектах с помощью активных оптических систем, использующих явления поглощения и рассеяния света в оптически прозрачных средах. Лидар как прибор представляет собой, как минимум, активный дальномер оптического диапазона. Есть несколько типов лидаров, один из них – это сканирующие лидары, с помощью которых формируют двумерную или трёхмерную картину окружающего пространства. В данном случае с помощью нашего лидара можно получать информацию о расстояниях в двумерном пространстве, т.е. на плоскости.
VL53L0X – это миниатюрный высокоточный лазерный дальномер, обеспечивающий измерение расстояния до 2 м. Для измерения расстояния используется метод измерения времени (TOF, Time-of-Flight). TOF-дальномеры ещё называют время пролётными дальномерами. В датчике установлен миниатюрный лазер и фоточувствительная матрица диодов. Лазер короткими импульсами испускает свет, который отражаясь от препятствий попадает обратно и регистрируется на фоточувствительной матрице. Датчик измеряет время, за которое свет прошёл путь от излучателя до препятствия и обратно до датчика и на основе этого вычисляет расстояние до препятствия.

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

Вращая датчик, постоянно измеряется расстояние. После каждого измерения в последовательный порт отправляются данные о текущем расстоянии и угле, на который в данный момент был повёрнут датчик.
Существует несколько способов, как определять текущий угол поворота.
Очень часто используется следующий метод. Используя коллекторный мотор, маленький магнит и датчик холла, можно раскрутить мотор до нужной скорости и затем по датчику холла определять момент, когда при вращении магнит окажется возле датчика холла.
В момент, когда магнит приблизился к датчику холла, в программе переменной «угол» присваивается значение 0. Мотор постоянно вращает датчик, периодически производятся измерения расстояний до препятствий и текущий угол поворота датчика.
Текущий угол поворота вычислить не сложно. В программе можно получить информацию о том, сколько тактов прошло с момента включения микроконтроллера. Если, к примеру, Arduino работает на частоте 16Мгц, в секунду таких тактов будет 16 миллионов, т.е. каждый такт это 1/16 000 000 секунды. Этого более чем достаточно для точного измерения времени при реализации такого лидара. Можно использовать и не системный таймер-счётчик, считающий такты микроконтроллера, принцип будет такой же.
После каждого измерения расстояния вычисляется, сколько тактов прошло с последнего измерения (прошло = текущее – предыдущее). Скорость вращения постоянна и известна (за сколько тактов таймер-счётчика делается один оборот), время, прошедшее с последнего измерения тоже. На основе этих данных и вычисляется текущий угол поворота.
Вместо датчика холла можно использовать магнитный или абсолютный оптический энкодер и при каждом измерении расстояния, ещё получать информацию о текущем угле поворота.
В данном лидаре используется другой способ. Он очень прост в реализации и в некоторых случаях очень удобен. Для его реализации нужен шаговый мотор, маленький магнит и датчик холла.
Шаговый мотор в отличие от коллекторного вращается не постоянно, а проворачивается на небольшой фиксированный угол (который ещё называют «шаг», от сюда и название «шаговый мотор»), после чего вал мотора удерживается в текущем положении до тех пор, пока драйверу мотора не поступит команда опять провернуть вал в нужную сторону.
Магнит и датчик холла используется аналогично первому методу – когда магнит приблизился к датчику холла, в программе переменной «угол» присваивается значение 0. После каждой команды провернуть вал в ту или иную сторону, значение некоторой переменной изменяется в большую или в меньшую сторону (в зависимости от направления поворота), на некоторое значение.
Можно увеличивать или уменьшать значение переменной на единицу после каждого шага или делая несколько шагов, изменять значение переменной на соответствующее число. В таком случае в переменной будет храниться количество шагов.
Можно сразу считать не количество шагов, а вычислять угол, на который провернулся вал шагового мотора. Если дальномер вращается на прямую от мотора, а через редуктор, при вычислении угла делается поправка на придаточное число редуктора. В данном случае мотор подключен не на прямую и сразу вычисляется угол поворота в градусах.
В loop цикле программы сначала делается два шага, производится измерение расстояния и затем к переменной «угол» прибавляется 2.4 (градуса). 2.4 градуса на два шага это с шага используемого шагового мотора и придаточного числа редуктора.
Удобство использования шагового мотора заключается в том, что можно проворачивать датчик на нужный угол, делая один или несколько шагов в нужную сторону и только когда датчик смотрит в нужном направлении, уже и делать измерения расстояния.
После того, как мотор провернёт вал, он будет оставаться в этом положении столько, сколько нужно и в коде можно сколь угодно долго делать что-то до следующей команды драйверу мотора провернуть вал. В случае использования коллекторного мотора, он ждать не будет и продолжит вращать датчик.
Используя шаговый мотор можно сканировать препятствия также, как и в случае с коллекторным мотором, постоянно вращая на 360 градусов датчик и делая измерения.
Если нам нужно измерять расстояния только в определённом направлении, допустим в направлении 0° или 45°, мы может провернуть датчик на нужный угол и затем производить измерения расстояния в этом направлении столь угодно долго.
Если нужно сканировать некоторый сектор, допустим в пределах 200-250°, можно не постоянно вращать на 360градусов, а поворачивать датчик сначала влево, затем вправо в нужных нам пределах.

Используемые компоненты

Список деталей:
1 х Arduino NANO
1 х Модуль с датчиком VL53L0X
1 х Повышающий преобразователь
1 х Шаговый мотор
1 х Драйвер шагового мотора на микросхеме A4988
1 х Скользящие контакты (slip ring) на 6 проводов. Диаметр 12.5мм, длина 15мм или меньше
1 х 6710ZZ подшипник
1 х Датчик Холла 49E
1 х Макетная плата
1 х 100 мкФ электролитический конденсатор
1 х 5 мм х 1 мм неодимовый магнит
Так же понадобиться резистор на 10К, немного M3 винтов, гаек, вставных гаек (резьбовые вставки, insert nuts), «пасик» и напечатанный на 3D-принтере корпус, припой и паяльник.
3D-файлы для печати можно скачать по следующей ссылке. Для печати использовался PLA пластик. Вместо стального подшипника можно использовать напечатанный (стальные шарики продаются пакетами, к примеру, как запасные для линейных направляющих). Модель для печати можно начертить самостоятельно или попробовать поискать на сайте Thingiverse, Cults и т.д., к примеру введя в поиск «parametric bearing». Вместо подшипника можно распечатать просто пластиковое кольцо, например, из PLA, PETG, нейлона или пэт. Если используемый пластик будет недостаточно скользить или переживаете, что со временем из-за трения протрёт, сверху и снизу можно наклеить липкую ленту или кольцо из какого-нибудь материала (плёнки для ламинации, плёнки для лазерной печати, упаковки для фломастеров, тонкостенной коробочки и т.д.).
Повышающий стабилизатор нужен для шагового мотора. Такие моторы обычно потребляют максимум сотни миллиампер. Стабилизатор желательно взять с запасом, допустим на 1А или больше. Выходное напряжение питания у стабилизатора, 12В или более. Выбирая стабилизатор, так же учитывайте, что они не должен быть слишком большой, иначе его нельзя будет поместить внутри корпуса.
Схема



Схема проста, часть деталей запаивается на макетной плате.
Через скользящие контакты от Arduino к датчику VL53L03X подключаются контакты +5В, земля и две линии интерфейса I2C (SCL и SDA). К датчику Холла подключается земля, +5В и вывод D8 от Arduino. Также между +5В и D8 устанавливается резистор на 10кОм.
На повышающего преобразователя подаётся 5В. На плате построечным резистором устанавливается выходное напряжение примерно 12В. Эти 12В с выхода стабилизатора подключаются к выводам «питание мотора» модуля драйвера моторов. Так же по линии 12В устанавливается электролитический конденсатор, который нужен что бы драйвер мотора работал без сбоев (на плате его может не быть совсем, а даже когда запаян керамический или танталовый конденсатор, его ёмкости обычно недостаточно). 5В подключается к выводам «питание логики» драйвера моторов. Не перепутайте, где выводы «питание логики» и «питание мотора», иначе драйвер может выйти из строя.
Платы бывают разные, иногда плюс питания для мотора обозначается, допустим надписью VMOT, а плюс питания логики обозначаться как VCC. Выводов GND на плате может быть один или несколько, в данном случае это не имеет значения.
Выводы STEP, DIR и EN от драйвера моторов подключаются к Arduino, а выводы RESET и SLEEP соединяются между собой.
На платах с драйвером моторов A4988 обычно запаян подстроечный резистор, им настраивается ограничение по току для шагового мотора. Ограничение тока для шаговых моторов очень желательно настроить. Если ограничить ток слишком сильно, мотор под нагрузкой или не сможет вообще проворачивать вал или будут пропуски шагов. Если задать слишком большой ток и источник питания способен будет его обеспечить, мотор будет греться или его обмотки просто сгорят. Так же это абсолютно ненужная нагрузка на источник питания. При питании от аккумулятора это приведёт к тому, что он будет быстрей разряжаться. При питании от USB (блока питания, порт компьютера или ноутбука и т.д.) в лучшем случае будет просто лишняя нагрузка, в худшем, когда блок питания или порт не рассчитан на такой ток и нет защиты, это даже может привести к выходу из строя блока питания или порта.
Вот и все. В качестве источника питания используется USB-кабель, подключенный к Arduino NANO.
Печать корпуса и сборка

Загрузите файлы STL, по ссылке выше (после списка деталей) и распечатайте детали корпуса. Настройте слайсер, в зависимости от особенностей вашего принтера и типа пластика, которым будете печатать. В данном случае детали напечатаны PLA пластиком, основные настройки были следующими — 2 периметра, высота слоя 0.3 мм, заполнение 20%. Когда детали корпуса будут готовы, можно приступить к сборке.
Возьмите скользящие контакты и поместите его в верхнюю часть корпуса. Убедитесь, что вращающаяся часть кольца находится на верхней стороне корпуса, чтобы она вращалась одновременно с диском. Теперь установите шаговый мотор, который фиксируется к корпусу двумя 3M винтами и гайкам. Крышка готова:

Теперь установим большой подшипник в верхней части корпуса:

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



Теперь пропускаем провода от скользящих контактов через отверстие вращающегося диска:

После чего берём датчик и припаиваем к нему 4 провода (+5V, GND, SCL и SDA) от скользящих контактов:

С помощью двух болтов М3 закрепляем модуль дальномера на корпусе вращающегося диска:

Если у вас модуль с другим расстоянием между крепёжными отверстиями, модуль можно закрепить только одним болтом. Если крепёжных отверстий совсем нет, модуль можно приклеить (двустороння липкая лента, термоклеем с помощью клеевого пистолета и т.д.).
Когда датчик будет закреплён, вращающийся диск надевается на подшипник:

На вращающуюся крышку приклеивается неодимовый магнит, а в верхнюю крышку вставляется датчик холла:

Магнит служит для того, чтобы на него на него срабатывал датчик Холла и в этот момент в коде происходит установка переменной «угол» в некоторое значение. Если магнит по размерам позволяет наклеить его по центру под датчиком, это будет самый лучший вариант, т.к. при срабатывании переменной «угол» нужно будет присвоить значение 0. Если нет, магнит можно наклеить возле датчика. Тогда переменной «угол» нужно будет присвоить не 0, а соответствующее значение (на какой угол относительно магнита повёрнут датчик). Если магнит находится с противоположной стороны, нужно присвоить 180. Если угол составляет 20 градусам (на фото выше угол немного больше):

Тогда переменной «угол» нужно присвоить 20 и т.д.
На макетную плату по схеме, приведенной ранее, запаиваем конденсатор, драйвер мотора, 10K резистор, датчик Холла, провода от Arduino и стабилизатора питания:


Всё припаяно, теперь закрепляем (двусторонней липкой лентой, клеем, термоклеем и т.д.) Arduino Nano внутри корпуса и наш лидар почти готов:

Осталось вплавить в нижнюю крышку корпуса три вставные гайки, затем прикрутить крышку корпуса, надеть на шкив пасик и можно переходить к программированию и экспериментам.
Скетч для Arduino
Откройте Arduino IDE и скопируйте в него скетч. Если при загрузке скетча в плату появится сообщение, что отсутствует библиотека «Adafruit_VL53L0X.h»:

Её нужно будет установить. Для этого в меню «Инструменты» выберете пункт «Управлять библиотеками. » и в появившемся окне найдите «Adafruit_VL53L0X» (что бы сократить список, можно ввести в поле поиска «VL53»:

Для VL53L0X кроме библиотеки от Adafruit есть и от Pololu. Подключается она аналогичным образом, но код нужно немного изменить:
В коде для обоих библиотек, в самом начале цикла loop мы проверяем, не сработал ли датчик Холла (в прерывании для этого переменной loop_starts устанавливается значение true) и, если сработал, устанавливаем переменной angle значение 0. Если у вас магнит находится не центру под датчиком, присвойте этой переменной соответствующее число.
Далее в коде отправляем команду драйверу мотора провернуть вал на один шаг, небольшая пауза с помощью delayMicroseconds, затем опять проворачиваем вал на один шаг и опять пауза.
После этого вызываем функцию измерения расстояния и проверяем, считал ли датчик корректные данные. При получении корректных данных от датчика, полученные данные о расстоянии присваиваются переменной r. Затем происходит проверка, выходит ли измеренное расстояние за установленные границы (переменная maxdist) и, если выходит, переменной r присваивается значение maxdist.
В конце цикла в последовательный порт выводятся полученные данные и к текущему значению переменной angle прибавляется значение переменной angle_step (в данном коде это 2.4, т.е. 2.4 градуса). Если у вас шаговый мотор с другим шагом, будете использовать режим микрошага, измените размеры шкивов или будете делать не два, а один, три или ещё сколько-то шагов, не забудьте пересчитать на какой угол будет вращаться датчик и присвоить переменной angle_step соответствующее значение.
Скетч для компьютера
Для визуализации используется приложение Processing, скачать его можно по следующей ссылке. Запустив среду Processing можно переключить языка интерфейса на русский. Для этого в меню нажмите «File», затем » Preferences…» и в открывшемся окне можно будет переключить язык:


Если при запуске появится сообщение, что не найдено Android SDK:

можно проигнорировать это сообщение и просто закрыть окно, нажав на крестик. Только при когда отобразиться окно программы, нужно будет переключить среду Processing из режима работы «Android» в режим «Java». Для этого слева вверху нажмите на «Android»:

И в появившемся меню выберете пункт «Java»:
После чего среда автоматически перезапустится:

Создайте новый скетч (файл -> создать) и скопируйте в него скетч:
Одно из отличий Processing от Arduino IDE заключается в том, как выбирается последовательный порт. В Processing порт выбирается не в меню, а в коде по индексу массива. При запуске скетча, внизу окна выводится список портов:

Найдите в списке порт, к которому подключена Arduino и в строчке:
myPort = new Serial(this, Serial.list()[0], 115200);
при необходимости поменяйте индекс. Если к примеру текущий порт «COM4», замените в коде ноль на единицу:
Самодельный лидар на Arduino и VL53L0X TOF-дальномере
Применяемая в составе лазерных дальномеров микроэлектроника позволяет не только выполнять прямые замеры. Многие устройства подобного типа обладают некоторыми дополнительными функции, к которым можно отнести:
- Функция непрерывного измерения. При работе в обычном режиме дальномер при нажатии кнопки на пульте фиксирует результат и выводит его на монитор. Но, довольно часто, возникает необходимость в проведении постоянного измерения расстояния, например, от стены до будущей перегородки. Для этого прибор переводят в режим непрерывного измерения. В таком режиме работы, устройство с некоторой частотой самостоятельно выполняет замер и показывает их результаты на монитор. Измерение проходит в реальном режиме времени.
- Определение наибольшего и наименьшего расстояния. Эта функция полезна при определении диагонали в комнате. Дело в том, что выполнить ее замер не так и просто при направлении лазерного луча можно промахнуться и в результате будут получены неточные результаты. После установки на приборе минимального расстояния, он будет фиксировать только те замеры, которые больше установленной.
Схема проекта
Схема лидара (дальномера) на Arduino и датчике VL6180X представлена на следующем рисунке.

Скорость лидара в нашем проекте ограничена частотой 10 Гц в связи с низкой скоростью работой сервомотора. Если вы хотите сконструировать высокоскоростной лидар, то используйте шаговый двигатель или двигатель постоянного тока с обратной связью.
Правила пользования
При работе с такими устройствами необходимо соблюдать определенные правила. Так, категорически недопустимо направлять лазерный луч в сторону человека. Его попадание в глаза может привести к непоправимым последствиям, вплоть до потери зрения.
Проведение измерений при ярком солнце может быть затруднено из-за сложностей с видимостью лазерного маркера. В таком случае необходимо использовать специальные очки, через которые сразу будет его видно.

Лазерная съемка на местности
Во время выполнения измерения на улице, особенно на большие расстояния, необходимо применять пластину, которую называют визир.
Датчик VL6180X
VL6180X – это оптический датчик от компании STMicroelectronics. Его также называют датчиком света (освещенности) и приближения. Его внешний вид показан на следующем рисунке.
Датчик VL6180 содержит ИК-излучатель, датчик внешней освещенности и датчик дальности. К микроконтроллерам и другим электронным устройствам он подключается по интерфейсу I2C. Также датчик имеет встроенный стабилизатор напряжения 2,8 В, поэтому он не повредится если подключить к нему напряжение больше 2,8 В (ну в пределах разумного, конечно же). Еще в датчике присутствуют два программируемых вывода GPIO. Распиновка датчика показана на следующем рисунке.
VL6180X в отличие от других подобных датчиков содержит в своем составе точные часы для измерения времени пролета светового потока, что позволяет ему обеспечивать значительно большую точность измерений по сравнению с другими аналогичными датчиками. Это также делает его практически невосприимчивым к шуму.
Датчик VL6180X способен измерять расстояния до 25 см. Поэтому, к примеру, в домашнем использовании он отлично подойдет для измерения фокусного расстояния камеры или фотоаппарата. Если же вам необходим лидар, работающий на большие расстояния, то в данном случае вам вместо датчика VL6180X необходимо использовать датчик Vl53l0x, который также отличается большей точностью измерений и не имеет проблем с линейностью, приводящих к “двойному изображению”.
Особенности
При работе с лазерным дальномером целесообразно учитывать некоторые особенности работы с этим устройством.
Дальномеры имеют возможность выполнять измерения на разных расстояниях и с определенной погрешностью. Так, предельное расстояние может лежать в диапазоне от 60 до 200 метров, при погрешности в 5 см. Эти данные указываются в паспорте на изделие. Большая часть моделей дальномеров работает в пределах от – 10 до + 50 градусов.
При эксплуатации прибора на улице, необходимо помнить о том, что не последнюю роль играют погодные условия. Эффективность работы может быть снижена как в плохую, так и в солнечную погоду.

При выполнении замеров необходимо устранить препятствия, которые могут возникнуть между прибором и объектом, это, может быть, листва, стекло и пр.
Практика использования лазерных приборов измерения привела к появлению определенных правил работы. Например, результат измерений будет искажен, если луч будет направлен на поверхность с высокой отражающей поверхностью (зеркало, фольга). Результат будет не совсем верный, если луч будет направлен на объект с низкой отражательной способностью (толь).
Для получения предельно точных результатов используют специальное приспособление ,обладающее отражательной поверхностью.
Во время эксплуатации необходимо постоянно следить за состоянием аккумуляторов или батареек. Слабые источники тока также отрицательно влияют результаты измерений.
При проведении измерений целесообразно использовать штатив. В таком случае точность замера будет повышена.
Содержание
В продаже, есть большое количество дешевых датчиков – дальномеров, в их числе ультразвуковые и инфракрасные. Все эти устройства работают хорошо, но из-за значительного веса, не подходят для летающих роботов. Миниатюрный робот вертолет, например, может нести около 100 г полезной нагрузки. Это даёт возможность использовать, для поиска препятствий и предотвращения столкновений с ними, машинное зрение, используя веб-камеры (или другие миниатюрные, беспроводные камеры с подключением к компьютеру через USB). А еще лучше, установить две камеры, что обеспечит роботу, стерео зрение, таким образом, благодаря информации о глубине изображения, улучшится обход препятствий. Недостатком этой идеи является сравнительно большой вес камеры.
Самодельный сканирующий лазерный дальномер
В этой статье я расскажу о том, как я делал самодельный лазерный сканирующий дальномер, использующий триангуляционный принцип измерения расстояния, и об опыте его использования на роботе.
Зачем нужен сканирующий дальномер?
На сегодняшний день в робототехнике не так уж и много методов навигации внутри помещений. Определение положения робота в пространстве с использованием лазерного сканера — один из них. Важное достоинство этого метода — он не требует установки в помещении каких-либо маяков. В отличие от систем, использующих распознавание изображения с камер, обработка данных с дальномера не так ресурсоемка. Но есть и недостаток — сложность, и соответственно, цена дальномера.
Традиционно в робототехнике используются лазерные сканеры, использующие фазовый или времяпролетный принцип для измерения расстояния до объектов. Реализация этих принципов требует довольно сложной схемотехники и дорогих деталей, хотя и характеристики при этом получаются приличные — используя эти принципы, можно добиться высокой скорости сканирования и большой дальности измерения расстояния.
Но для домашних экспериментов в робототехнике такие сканеры мало подходят — цена на них начинаются от 1000$.
На помощь приходят дальномеры, использующие триангуляционный принцип измерения расстояния. Дальномер такого типа впервые появился в роботах-пылесосах Neato:
Довольно быстро любители расшифровали протокол этого дальномера, и начали использовать его в своих проектах. Сами дальномеры в качестве запчастей появились на ebay в небольших количествах по цене около 100$. Через несколько лет китайская компания смогла выпустить сканирующий дальномер RPLIDAR, который поставлялся как полноценный прибор, а не запчасть. Только цена этих дальномеров оказалась достаточно высокой — 400$.
Самодельный дальномер

Как только я узнал о дальномерах Neato, мне захотелось собрать самому аналогичный. В конце концов, мне это удалось, и процесс сборки я описал на Робофоруме.
Первая версия дальномера:
Позже я сделал еще одну версию дальномера, более пригодную для использования на реальном роботе, но и ее качество работы не полностью устроило меня. Настало время третьей версии дальномера, и именно она будет описана далее.
Устройство сканирующего триангуляционного лазерного дальномера
Принцип измерения расстояния до объекта основан на измерении угла между лазерным лучом, попадающим на объект, и объективом дальномера. Зная расстояние лазер-объектив (h) и измеренный угол, можно вычислить расстояние до объекта — чем меньше угол, тем больше расстояние.
Принцип хорошо иллюстрирует картинка из статьи:
Таким образом, ключевые оптические компоненты такого дальномера — лазер, объектив и фотоприемная линейка.
Так как дальномер сканирующий, то все эти детали, а так же управляющая электроника устанавливаются на вращающейся головке.
Тут может возникнуть вопрос — зачем нужно вращать оптику и электронику, ведь можно установить вращающееся зеркало? Проблема в том, что точность дальномера зависит от расстояния между объективом и лазером (базового расстояния), так что оно должно быть достаточно большим. Соответственно, для кругового сканирования понадобится зеркало диаметром, большим базового расстояния. Дальномер с таким зеркалом получается достаточно громоздким.
Сканирующая головка дальномера при помощи подшипника закрепляется на неподвижном основании. На нем же закрепляется двигатель, вращающий головку. Также в состав дальномера должен входить энкодер, предназначенный для получения информации о положении головки.
Как видно, дальномеры Neato, RPLIDAR и мои самодельные сделаны именно по этой схеме.
Самое сложное в самодельном дальномере — изготовление механической части. Именно ее работа вызывала у меня больше всего нареканий в ранних версиях дальномера. Сложность заключается в изготовлении сканирующей головки, которая должна быть прочно закреплена на подшипнике, вращаться без биений и при этом не нее нужно каким-то образом передавать электрические сигналы.
Во второй версии дальномера первые две проблемы я решил, использовав части старого HDD — сам диск использовался как основание сканирующей головки, а двигатель, на котором он закреплен, уже содержал качественные подшипники. В то же время, при этом возникла третья проблема — электрические линии можно было провести только через небольшое отверстие в оси двигателя. Мне удалось сделать самодельный щеточный узел на 3 линии, закрепленный в этом отверстии, но получившаяся конструкция получилась шумной и ненадежной. При этом возникла еще одна проблема — линии, чтобы пробросить сигнал энкодера, не было, и датчик энкодера в такой конструкции должен быть установлен на головке, а диск энкодера с метками — на неподвижном основании. Диск энкодера получился не жестким, и это часто вызывало проблемы.
Фотография второй версии дальномера:
Еще один недостаток получившегося дальномера — низкая скорость сканирования и сильное падение точности на расстояниях больше 3м.
Именно эти недостатки я решил устранить в третьей версии дальномера.
Электроника
В принципе, электронная часть триангуляционного дальномера достаточно проста и содержит всего два ключевых компонента -светочувствительную линейку и микроконтроллер. Если с выбором контроллера проблем нет, то с линейкой все значительно сложнее. Светочувствительная линейка, используемая в подобном дальномере, должна одновременно иметь достаточно высокую световую чувствительность, позволять считывать сигнал с высокой скоростью и иметь маленькие габариты. Различные CCD линейки, применяемые в бытовых сканерах, обычно довольно длинные. Линейки, используемые в сканерах штрихкодов — тоже не самые короткие и быстрые.
В первой и второй версии дальномера я использовал линейки TSL1401 и ее аналог iC-LF1401. Эти линейки хорошо подходят по размеру, они дешевые, но содержат всего 128 пикселей. Для точного измерения расстояния до 3 метров этого мало, и спасает только возможность субпискельного анализа изображения.
В третьей версии дальномера я решил использовать линейку ELIS-1024:
Однако купить ее оказалось непросто. У основных поставщиков электроники этих линеек просто нет.
Первая линейка, которую я смог купить на Taobao, оказалась нерабочей. Второю я купил на Aliexpress (за 18$), она оказалась рабочей. Обе линейки выглядели паянными — обе имели облуженные контакты и, судя по маркировке, были изготовлены в 2007 году. Причем даже на фотографиях у большинства китайских продавцов линейки именно такие. Похоже, что действительно новую линейку ELIS-1024 можно купить только напрямую у производителя.
Светочувствительная линейка ELIS-1024, как следует из названия, содержит 1024 пикселя. Она имеет аналоговый выход, и достаточно просто управляется.
Еще более хорошими характеристиками обладает линейка DLIS-2K. При сходных размерах, она содержит 2048 пикселей и имеет цифровой выход. Насколько мне известно, именно она используется в дальномере Neato, и возможно, в RPLIDAR. Однако, найти ее в свободной продаже очень сложно, даже в китайских магазинах она появляется не часто и дорого стоит — более 50$.
Так как я решил использовать линейку с аналоговым выходом сигнала, то микроконтроллер дальномера должен содержать достаточно быстрый АЦП. Поэтому я решил использовать серию контроллеров — STM32F303, которые, при относительно небольшой стоимости, имеют несколько быстрых АЦП, способных работать одновременно.
В результате у меня получилась такая схема:
Сигнал с линейки (вывод 10) имеет достаточно высокий уровень постоянной составляющей, и ее приходится отфильтровывать при помощи разделительного конденсатора.
Далее сигнал нужно усилить — для этого используется операционный усилитель AD8061. Далеко расположенные объекты дают достаточно слабый сигнал, так что пришлось установить коэффициент усиления равным 100.
Как оказалось в результате экспериментов, даже при отсутствии сигнала, на выходе выбранного ОУ по какой-то причине постоянно присутствует напряжение около 1.5В, что мешает обработке результатов и ухудшает точность измерения амплитуды сигнала. Для того, чтобы избавится от этого смещения, мне пришлось подать дополнительное напряжение на инвертирующий вход ОУ.
Плату разводил двухстороннюю, сделать такую плату в домашних условиях качественно довольно сложно, так что заказал изготовление плат в Китае (пришлось заказать сразу 10 штук):
В этом дальномере я использовал дешевый объектив с резьбой M12, имеющий фокусное расстояние 16мм. Объектив закреплен на печатной плате при помощи готового держателя объектива (такие используются в различных камерах).
Лазер в данном дальномере — инфракрасный (780 нм) лазерный модуль, мощностью 3.5 мВт.
Изначально я предполагал, что излучение лазера нужно будет модулировать, но позже оказалось, что с используемой линейкой в этом нет смысла, и поэтому сейчас лазер включен постоянно.
Для проверки работоспособности электроники была собрана вот такая конструкция, имитирующая сканирующую головку дальномера:
Уже в таком виде можно было проверить, какую точность измерения расстояния позволяет обеспечить дальномер.
Для анализа сигнала, формируемого линейкой, были написаны тестовые программы для микроконтроллера и ПК.
Пример вида сигнала с линейки (объект на расстоянии 3 м).
Изначально схема была не совсем такая, как приведена выше. В ходе экспериментов мне пришлось частично переделать изначальную схему, так что, как видно из фотографий, некоторые детали пришлось установить навесным монтажом.
Механическая часть
После того, как электроника была отлажена, настало время изготовить механическую часть.
В этот раз я не стал связываться с механикой из HDD, и решил изготовить механические детали из жидкого пластика, заливаемого в силиконовую форму. Эта технология подробно описана в Интернете, в том числе и на Гиктаймс.
Уже после того, как я изготовил детали, стало понятно, что изготовить детали на 3D принтере было бы проще, они могли выйти тверже, и возможно, можно было бы сделать одну деталь вместо двух. Доступа к 3D принтеру у меня нет, так что пришлось бы заказывать изготовление детали в какой-либо компании.
Фото одной из деталей сканирующей головки дальномера:
Эта деталь является основой головки. Она состоит из втулки, на которую позже надевается подшипник, и диска. Диск предназначен для крепления второй детали башни, кроме того, на него снизу наклеивается диск энкодера.
Втулка и диск содержат сквозное отверстие, в которое вставляется покупной щеточный узел на 6 линий — его видно на фотографии. Именно те провода, что видны на фотографии, могут вращаться относительно корпуса этого узла. Для повышения стабильности работы для передачи сигналов GND и UART TX используется 2 пары линий щеток. Оставшиеся 2 линии используются для передачи напряжения питания и сигнала энкодера.
Силиконовая форма для отливки этой детали:
Вторая деталь сканирующей головки была изготовлена тем же способом. Она предназначена для крепления печатной платы и лазера к диску. К сожалению, фотографий изготовления этой детали у меня не сохранилось, так что ее можно увидеть только в составе дальномера.
Для крепления сканирующей головки к основанию дальномера используется шариковый подшипник. Я использовал дешевый китайский подшипник 6806ZZ. Честно говоря, качество подшипника мне не понравилось — ось его внутренней втулки могла отклонятся относительно оси внешней на небольшой угол, из-за чего головка дальномера тоже немного наклоняется. Крепление подшипника к детали с диском и основанию будет показано ниже.
Основание я сделал из прозрачного оргстекла толщиной 5 мм. К основанию крепится подшипник, датчик энкодера, двигатель дальномера и маленькая печатная плата. Само основание устанавливается на любую подходящую поверхность при помощи стоек.
Вот так выглядит основание дальномера снизу:
Печатная плата содержит регулируемый линейный стабилизатор напряжения для питания двигателя, и площадки для подключения проводов узла щеток. Сюда же подводится питание дальномера.
Как и в других дальномерах, двигатель вращает сканирующую головку при помощи пассика. Для того, чтобы он не сваливался с втулки, на ней имеется специальное углубление.
Как видно из фотографии, подшипник закреплен в основании при помощи трех винтов. На сканирующей головке подшипник удерживается за счет выступа на втулке и прижимается к ней другими винтами, одновременно удерживающими щеточный узел.
Энкодер состоит из бумажного диска с напечатанными рисками и оптопары с фототранзистором, работающей на отражение. Оптопара закреплена при помощи стойки на основании так, что плоскость диска оказывается рядом с ней:
Сигнал от оптопары через щетки передается на вход компаратора микроконтроллера. В качестве источника опорного напряжения для компаратора выступает ЦАП микроконтроллера.
Для того, чтобы дальномер мог определить положение нулевого угла, на диск энкодера нанесена длинная риска, отмечающая нулевое положение головки (она видна справа на фотографии выше).
Вот так выглядит собранный дальномер:
Вид сверху:
Разъем сзади дальномера используется для прошивки микроконтроллера.
Для балансировки сканирующей головки на нее спереди устанавливается крупная гайка — она практически полностью устраняет вибрацию при вращении головки.
Собранный дальномер нужно отюстировать — установить лазер в такое положение, чтобы отраженный от объектов свет попадал на фотоприемную линейку. Обе пластмассовые детали содержат соосные отверстия, располагающиеся под пазом лазера. В отверстия вворачиваются регулировочные винты, упирающиеся в корпус лазера. Поворачивая эти винты, можно изменять наклон лазера.
Наблюдая в программе на компьютере форму и амплитуду принятого сигнала и изменяя наклон лазера, нужно добиться максимальной амплитуды сигнала.
Также триангуляционные дальномеры требуют проведения калибровки, о чем я писал ранее:
В данном случае калибровка представляла собой серию измерений расстояний до различных объектов самодельным дальномером и лазерной рулеткой, после чего по полученным парам измерений выполняется регрессионный анализ и составляется математическое выражение.
Получившийся дальномер имеет существенный недостаток — из-за отсутствия модуляции излучения лазера он некорректно работает при любой сильной засветке. Обычное комнатное освещение (даже при использовании мощной люстры) не влияет на работу дальномера, но вот расстояние до поверхностей, прямо освещенных Солнцем, дальномер измеряет неправильно. Для решения этой проблемы в состав дальномера нужно включить интерференционный светофильтр, пропускающий световое излучение только определенной длины волны — в данном случае 780 нм.
Эволюция самодельных дальномеров:
Габаритные размеры получившегося дальномера:
Размер основания: 88×110 мм.
Общая высота дальномера: 65 мм (может быть уменьшена до 55 при уменьшении высоты стоек).
Диаметр сканирующей головки: 80 мм (как у mini-CD диска).
Как и у любого другого триангуляционного дальномера, точность измерения расстояния этого дальномера резко падает с ростом расстояния.
При измерениях расстояния до объекта с коэффициентом отражения около 0.7 у меня получились примерно такие точностные характеристики:
| Расстояние | Разброс |
|---|---|
| 1 м | <1 см |
| 2 м | 2 см |
| 5 м | 7 см |
Стоимость изготовления дальномера:
| DIY, $ | Опт., $ | |
|---|---|---|
| Основание | ||
| Пластина основания | 1,00 | 0,50 |
| Двигатель | 0,00 | 1,00 |
| Подшипник | 1,50 | 1,00 |
| Щеточный узел | 7,50 | 5,00 |
| Крепежные детали | 0,00 | 2,00 |
| Сканирующая головка | ||
| Контроллер STM32F303CBT6 | 5,00 | 4,00 |
| Фотоприемная линейка | 18,00 | 12,00 |
| Остальная электроника | 4,00 | 3,00 |
| Плата | 1,50 | 0,50 |
| Объектив | 2,00 | 1,50 |
| Держатель объектива | 1,00 | 0,50 |
| Лазер | 1,00 | 0,80 |
| Пластиковые детали | 3,00 | 2,00 |
| Крепежные детали | 0,00 | 1,00 |
| Сборка | 0,00 | 20,00 |
| Итого: | 45,50 | 54,80 |
В первой колонке — во сколько дальномер обошелся мне, во второй — сколько он мог бы стоить при промышленном изготовлении (оценка очень приблизительная).
Программная часть дальномера
Перед написанием программы нужно рассчитать тактовую частоту, на которой будет работать фотоприемная линейка.
В старых версиях дальномера частота сканирования была ограничена 3 Гц, в новом дальномере я решил сделать ее выше — 6Гц (это учитывалось при выборе линейки). Дальномер делает 360 измерений на один оборот, так что при указанной скорости он должен быть способен производить 2160 измерений в секунду, то есть одно измерение должно занимать менее 460 мкс. Каждое измерение состоит из двух этапов — экспозиция (накопление света линейкой) и считывание данных с линейки. Чем быстрее будет произведено считывание сигнала, тем длиннее может быть время экспозиции, а значит, и тем больше будет амплитуда сигнала. При тактовой частоте линейки 8 МГц время считывания 1024 пикселей будет составлять 128 мкс, при 6 МГц — 170 мкс.
При тактовой частоте микроконтроллера серии STM32F303 в 72 МГц максимальная частота выборок АЦП — 6 MSPS (при разрядности преобразования 10 бит). Так как я хотел проверить работу дальномера при тактовой частоте линейки 8 МГц, я решил использовать режим работы АЦП, в котором два АЦП работают одновременно (Dual ADC mode — Interleaved mode). В этом режиме по сигналу от внешнего источника начала запускается ADC1, а затем, через настраиваемое время, ADC2:
Как видно из диаграммы, суммарная частота выборок АЦП в два раза выше, чем частота триггера (в данном случае это сигнал от таймера TIM1).
При этом TIM1 также должен формировать сигнал тактовой частоты для фотоприемной линейки, синхронный с выборками АЦП.
Чтобы получить с одного таймера два сигнала с частотами, различающимися в два раза, можно переключить один из каналов таймера в режим TIM_OCMode_Toggle, а второй канал должен формировать обычный ШИМ сигнал.
Структурная схема программы дальномера: 
Ключевой частью программы является именно захват данных с линейки и управление ей. Как видно из схемы, этот процесс идет на аппаратном уровне, за счет совместной работы TIM1, ADC1/2 и DMA. Для того, чтобы время экспозиции линейки было постоянным, используется таймер TIM17, работающий в режиме Single Pulse.
Таймер TIM3 генерирует прерывания при срабатывании компаратора, соединенного с энкодером. За счет этого рассчитывается период вращения сканирующей головки дальномера и ее положение. По полученному периоду вращения рассчитывается период таймера TIM16 таким образом, чтобы он формировал прерывания при повороте головки на 1 градус. Именно эти прерывания служат для запуска экспозиции линейки.
После того, как DMA передаст все 1024 значения, захваченные ADC, в память контроллера, программа начинает анализ эти данных: сначала производится поиск положения максимума сигнала с точностью до пикселя, затем, при помощи алгоритма поиска центра тяжести — с более высокой точностью (0.1 пикселя). Полученное значение сохраняется в массив результатов. После того, как сканирующая головка сделает полный оборот, в момент прохождения нуля этот массив предаются в модуль UART при помощи еще одного канала DMA.
Использование дальномера
Качество работы этого дальномера, как предыдущих, проверялось при помощи самописной программы. Ниже пример изображения, формируемого этой программой в результате работы дальномера:
Однако дальномер делался не для того, чтобы просто лежать на столе — он был установлен на старый пылесос Roomba 400 вместо дальномера второй версии:
Также на роботе установлен компьютер Orange Pi PC, предназначенный для управления роботом и связи с ним.
Как оказалось, из-за большой просадки напряжения на линейном источнике питания двигателя дальномера, для работы на скорости 6 об/сек дальномеру требуется питающее напряжение 6В. Поэтому Orange Pi и дальномер питаются от отдельных DC-DC преобразователей.
Для управления роботом и анализа данных от дальномера я использую ROS.
Данные от дальномера обрабатываются специальным ROS-драйвером (основанном на драйвере дальномера Neato), который получает по UART данные от дальномера, пересчитывает их в расстояния до объектов (используя данные калибровки) и публикует их в стандартном формате ROS.
Вот так выглядит полученная информация в rviz (программа для визуализации данных ROS), робот установлен на полу:
Длина стороны клетки — 1 метр.
После того, как данные попали в ROS, их можно обрабатывать, используя уже готовые пакеты программ. Для того, чтобы построить карту квартиры, я использовал hector_slam. Для справки: SLAM — метод одновременного построения карты местности и определения положения робота на ней.
Пример получившейся карты квартиры (форма несколько необычна, потому что дальномер «видит» мебель, а не стены, и не все комнаты показаны):
ROS позволяет объединять несколько программ («узлов» в терминологии ROS), работающих на разных компьютерах, в единую систему. Благодаря этому, на Orange Pi можно запускать только ROS-драйверы Roomba и дальномера, а анализ данных и управление роботом вести с другого компьютера. При этом эксперименты показали, что hector_slam нормально работает и на Orange Pi, приемлемо загружая процессор, так что вполне реально организовать полностью автономную работу робота.
Система SLAM благодаря данным от дальномера позволяет роботу определять свое положение в пространстве. Используя данные о положении робота и построенную карту, можно организовать навигационную систему, позволяющую «направить» робота в указанную точку на карте. ROS содержит в себе пакет программ для решения этой задачи, но, к сожалению, я так и не смог заставить его качественно работать.