Pull to refresh

Автономная навигация мобильного робота

Reading time22 min
Views10K
Существует огромное количество способов, при помощи которых робот может получать информацию из внешнего мира с целью взаимодействия с ним. Также, в зависимости от задач, поставленных перед ним, различаются и методы обработки этой информации. В данной статье я опишу основные этапы работы, выполненной в рамках школьного проекта, цель которого — систематизировать информацию, посвященную различным методам автономной навигации робота и применить полученные знания в процессе создания робота для соревнований «Кубок РТК».



Введение


На соревнованиях «Кубок РТК» есть блок задач, которые должны быть выполнены без вмешательства оператора. Я считаю, что многие участники несправедливо обходят эти задания стороной, ведь за кажущейся сложностью создания конструкции робота и написания программы, скрываются во многом упрощенные задания из других соревновательных дисциплин, объединенные в одном полигоне. Своим проектом я хочу показать, возможные решения подобных задач, рассмотрев в качестве примера следование по линии.

Для достижения цели проекта были сформулированы следующие промежуточные задачи:

  • Анализ регламента соревнований «Кубок РТК»
  • Анализ существующих алгоритмов автономного ориентирования мобильного робота
  • Создание ПО

Анализ регламента соревнований «Кубок РТК»


На соревнованиях «Кубок РТК» участникам представлен полигон, на котором смоделированы участки различной сложности. Соревнования ставят своей целью стимулировать молодых робототехников на создание устройств, способных работать в экстремальных условиях, преодолевать препятствия, под управлением человека, или же автономно.



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

Туман:



Буераки:



Участок поля – открытая поверхность, на которой расположение все «большие» испытания (подвесной мост) или испытания, которые невозможно выполнить в условиях ограниченного пространства лабиринта. Также, на многие испытания из зоны поля дополнительно нанесена линия, проехав по которой автономно, робот может получить вдвое больше очков, чем преодолев тот же участок при помощи дистанционного управления.

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

Состязания подразделяются на две принципиально отличающиеся друг от друга номинации: «Искатель» и «Экстремал». Это сделано для того, чтобы соревнования проходили между участниками с минимальной разницей в возрасте и опыте разработки робототехнических систем: «Искатель» для младшего уровня, а «Экстремал» — для участников от 14 лет и старше. В номинации «Искатель» оператор может свободно передвигаться по полигону и иметь непосредственный зрительный контакт с машиной, в то время как номинация «Экстремал» предполагает наличие у робота систем видеосвязи и/или компьютерного зрения, так как оператор должен ориентироваться в лабиринте, полагаясь только на встроенные в робота камеру и датчики, находясь при этом за специальной ширмой.

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

  • Движение по линии при помощи датчика освещенности или системы технического зрения на линиях
  • Автономный захват маяка при помощи датчика расстояния или систем технического зрения
  • Движение по сложной траектории (например, подъем/спуск по лестнице) по линии, с помощи компаса, гироскопа, акселерометра, системы технического зрения или комбинированных методов

Также, баллы за преодоление препятствий удваиваются, если робот проходит их автономно.

В рамках данного проекта будет рассмотрено решение первого из заданий — движения по линии. Наиболее распространенными методами, применяемыми при движении по линии являются датчики освещенности и камера. К плюсам датчиков можно отнести простоту создания программы — многие из них оснащены подстроечным резистором, так что, настроив датчик под фоновое освещение, он будет выдавать 0 или 1 в зависимости от того, находится он на линии или нет. По этой же причине датчики освещенности не требовательны к вычислительной мощности используемого контроллера. Также, из-за этого решение задачи при помощи датчиков освещенности является наименее затратным — стоимость самого простого сенсора составляет 35 рублей, а для относительно стабильной езды по линии достаточно трех датчиков (один установлен на линии, а два — по бокам). Однако, один из главных минусов подобных датчиков — ограничения по установке. Идеально, чтобы датчик был установлен ровно по центру, на небольшом расстоянии от пола, иначе он будет давать некорректные значения. Это не является проблемой на специализированных соревнованиях, где робот должен максимально быстро проехать по трассе, но, в условиях соревнований «Кубок РТК», все вышеизложенные недостатки датчиков могут быть критичными — их установка в первую очередь требует наличия на роботе дополнительных механических частей, поднимающих и опускающих сенсоры, а это требует дополнительного места на роботе, отдельного двигателя, перемещающего датчики, а также является местом потенциальной поломки и увеличивает массу робота.



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

В данном вопросе лично для меня ответ очевиден — в номинации «экстремал» обязательно наличие у робота курсовой камеры, при помощи которой будет ориентироваться оператор. Если использовать готовые FPV решения, то общая стоимость «датчиков» может получится даже выше, при этом требуя установки дополнительных устройств. Более того, робот с raspberry pi и камерой имеет больший потенциал для развития автономного движения, так как камера может решать большой спектр задач и использоваться не только в движении по линии, при этом не сильно усложняя конструкцию.

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


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

Системы компьютерного зрения состоит из:

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

Как писалось ранее, существует множество способов идентифицировать интересующие нас объекты. В случае езды по линии, необходимо отделить саму линию от контрастирующего фона (черная линия на белом фоне или белая линия на черном фоне для инверсной линии). Алгоритмы, использующие систему компьютерного зрения можно условно разделить на несколько «шагов» по обработке исходного изображения:

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

Предварительная обработка: перед тем, как методы компьютерного зрения могут быть применены к видеоданным, необходима предварительная обработка, вводимая с целью удовлетворения некоторым условиям, в зависимости от используемого метода. Примерами являются:

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

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

  • Выделение определённого набора интересующих точек по цвету, количеству обособленных пикселей, схожих по какому-то признаку(кривизне фигуры, цвету, яркости и т.д.)
  • Сегментация одного или нескольких участков изображения, которые содержат характерный объект.

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

  • Фильтрация значений по какому-либо критерию
  • Оценка таких параметров, как физические размеры объекта, форма, его расположение в кадре или относительно других характерных объектов
  • Классификация

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

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

Среди рассмотренных мной вариантов я выделил открытую библиотеку компьютерного зрения OpenCV, так как она поддерживает работу на Python, имеет обширную онлайн-документацию. В интернете есть множество статей и инструкций, описывающих все тонкости работы с данной библиотекой. Есть официальный форум от разработчиков, где любой желающий может задать вопрос о ней. Также, эта библиотека реализована на языках C/C++, что гарантирует быстродействие системы, а ее структура поддерживает различные модули, которые могут быть отключены с целью увеличения производительности.

Разработка программного обеспечения


После установки ОС и первичной настройки Raspberry pi но перед тем, как начинать создание программы, необходимо установить все необходимые для пакеты. Большинство этих пакетов, в свою очередь, устанавливаются при помощи менеджера пакетов pip (в случае Python 3 – pip3)

$ sudo apt install python3-pip

Далее устанавливаются такие библиотеки, как:

  • picamera – библиотека для работы с камерой raspberry pi
  • numpy – библиотека для работы с многомерными массивами данных, в качестве которых и представлены изображения

$ sudo pip3 install picamera
$ sudo pip3 install numpy

cmake — Утилита для автоматической сборки программы из исходного кода
cmake-curses-gui — Пакет GUI (графический интерфейс) для cmake

$ sudo apt-get install cmake cmake-curses-gui libgtk2.0-dev
$ sudo apt-get install cmake cmake-curses-gui libgtk2.0-dev

библиотеки для работы с разными форматами изображений и видео и прочее

$ sudo apt-get install libavcodec-dev libavformat-dev libswscale-dev libv4l-dev libx264-dev libxvidcore-dev
$ sudo apt-get install libjpeg-dev libpng12-dev libtiff5-dev libjasper-dev
$ sudo apt-get install gfortran libatlas-base-dev

Для трансляции видеоданных с робота на компьютер будет использован GStreamer – фреймворк, предназначенный для получения, обработки и передачи мультимедиа данных:

$ sudo apt install libgstreamer1.0-0 gstreamer1.0-plugins-base gstreamer1.0-plugins-good gstreamer1.0-plugins-bad gstreamer1.0-plugins-ugly gstreamer1.0-libav gstreamer1.0-doc gstreamer1.0-tools gstreamer1.0-x gstreamer1.0-alsa gstreamer1.0-gl gstreamer1.0-gtk3 gstreamer1.0-qt5 gstreamer1.0-pulseaudio

Следующим шагом будет установка самой библиотеки openCV из источников, ее конфигурация и сборка. Для этого создается рабочая папка opencv.

$ mkdir opencv
$ cd opencv

Для того, чтобы скачать последние версии библиотеки использован wget –консольная программа для загрузки файлов из сети. На момент создания программы последняя стабильная версия openCV – 4.1.0, поэтому загружаем и распаковываем исходники:

$ wget https://github.com/opencv/opencv/archive/4.1.0.zip -O opencv_source.zip
$ unzip opencv_source.zip
$ wget https://github.com/opencv/opencv_contrib/archive/4.1.0.zip -O opencv_contrib.zip
$ unzip opencv_contrib.zip

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

$ rm opencv_source.zip
$ rm opencv_contrib.zip

Создается директория для сборки и конфигурации.

$ cd /home/pi/opencv/opencv-4.1.0
$ mkdir build
$ cd build

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

cmake -D CMAKE_BUILD_TYPE=RELEASE -D CMAKE_INSTALL_PREFIX=/usr/local -D INSTALL_PYTHON_EXAMPLES=ON -D INSTALL_C_EXAMPLES=OFF -D BUILD_opencv_python2=OFF -D WITH_GSTREAMER=ON -D BUILD_EXAMPLES=ON -DENABLE_VFPV3=ON -DENABLE_NEON=ON -DCPU_BASELINE=NEON ..

После настройки конфигурации утилита выведет все параметры. Далее необходимо произвести компиляцию библиотеки. Для этого используется консольная команда make –jN, где N – количество ядер, которое будет задействовано в процессе компиляции. Для raspberry pi 3 число ядер – 4, но узнать это число точно можно прописав в консоли команду nproc.

$ make –j4

Из-за ограниченности ресурсов raspberry, компиляция может идти достаточно долго. В некоторых случаях малина может даже зависнуть, но если потом зайти в папку сборки и заново прописать make, работа продолжится. Если такое произошло, стоит уменьшить количество задействованных ядер, однако, у меня компиляция прошла без проблем. Также, на этом этапе стоит задуматься об активном охлаждении raspberry, ведь даже вместе с ним температура процессора достигала порядка 75 градусов.

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

$ sudo make install
$ sudo ldconfig

Проверяем правильность установки, прописав в интерактивном режиме python следующие команды:

import cv2
print(cv2.getBuildInformation())

Свидетельством корректной установки будет являться следующий вывод программы



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

Создание схемы распределения видеоданных

Перед тем, как приступить к написанию программного кода необходимо разработать схемы, согласно которым будет функционировать алгоритм. В рассматриваемом случае разработки ПО робота, создаваемого для участия в соревнованиях «Кубок РТК» в номинации «Экстремал», вся программа будет разделена на две части: робот и «пульт», в роли которого будет выступать компьютер с установленной ОС Linux. Одной из важнейших задач здесь является создание примерной схемы того, как видеоданные будут передаваться между различными частями алгоритма. В качестве канала связи между двумя устройствами будет использован Wi-Fi. Пакеты данных, обеспечивающие управление роботом и данные обратной связи будут передаваться от одного устройства к другому при помощи протокола UDP, реализованного в с использованием библиотеки socket. Видеоданные, из-за ограничения по объему UDP пакета будут передаваться при помощи GStreamer. Для удобства отладки будет реализовано два видеопотока:

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

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



Дистанционное управление роботом будет осуществляться за счет работы двух параллельных потоков на роботе и на компьютере:

  • «пульт» в цикле опрашивает все доступные устройства ввода и формирует управляющий пакет данных, состоящий из самих данных, а также контрольной суммы (на момент внесения финальных правок в статью я отказался от создания контрольных сумм с целью уменьшения задержки, но в исходниках, которые я выложил в конце этот участок кода оставлен) – некоторого значения, рассчитанного по набору данных путем работы некоторого алгоритма, используемого для определения целостности данных при их передаче
  • Робот — ожидает подхода данных от компьютера. Распаковывает данные, заново вычисляет контрольную сумму и сравнивает ее с отправленной и рассчитанной на стороне компьютера. Если контрольные суммы совпали, данные передаются основной программе.

Перед разбором алгоритма детектирования линии, предлагаю ознакомиться с особенностями конструкции робота:

про робота
В качестве базы для робота был выбран готовый набор из Китая.

Вот — очень похожий на купленный мной кит набор. Состоит он из трех массивных (3мм в толщину) деталей из гнутого алюминия с нанесенным анодированным покрытием. В комплекте с ним шли коллекторные двигатели с редуктором с одной стороны и датчиками холла с другой, которые впоследствии были заменены на бесколлекторные. С каждой стороны робота по 6 катков, один из них напрямую присоединен к валу мотора. Остальные установлены на два маленьких подшипника с фланцем и прижаты винтом. Пластмассовые гусеницы абсолютно не имеют сцепления. Эту проблему у себя я решил промазав их сверху термоклеем. После обкатки робота на ардуино, было решено делать что-то более серьезное. Для этого в качестве «мозга» робота выбрана rasberry pi 3 b — самая продвинутая на то время модель.

Она, а также шилд для контроля за устройствами, управляющимися посредством ШИМ, помещены в корпус, смоделированный в Solidworks и напечатанный из petg пластика. Корпус оснащен активным охлаждением, что позволило снизить температуру raspberry на порядок в сравнении с обычным пассивным радиатором.

Для обеспечения связи с роботом установлены две промышленные точки доступа ubiquiti bullet M5 hp. Так как круглый корпус (который также требует охлаждения) закрепить на роботе не представлялось возможным, он заменен на напечатанный вариант с добавленной системой активного охлаждения. И на робота, и на «пульт» установлена вот такая антенна.


Манипулятор: в качестве основы для «клешни» выбор пал на следующую модель из thingiverse. Понравилась она своей компактностью, надежной конструкцией, а также фланцем, задуманным под установку на поворотную платформу, которую я с самого начала планировал сделать на своем роботе.


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





из-за ограничения в виде потенциометра (он поворачивается всего где-то на 200 градусов) и платы внутри сервопривода, которая вращала клешню не в полном диапазоне, получилось добиться поворота в 90 градусов в одну сторону и приблизительно 70 в другую (можно увидеть на прикрепленной выше гифке), чего хватает для выполнения всех заданий для проверки возможностей манипулятора, представленных на полигоне соревнований «кубок РТК». Также, на конец манипулятора добавлен инфракрасный лазерный датчик расстояния на базе чипа VL53L0X для создания в дальнейшем алгоритма автоматического захвата маяков, или же других точных работ манипулятором.


Сама «рука» манипулятора собрана из соединительных элементов, шедших в комплекте с сервоприводами, которые я использовал (rds3115). Из их особенностей — крепления, предназначенные для использования в робототехнике, небольшая скорость, но большой создаваемый крутящий момент, вкупе с относительно невысокой стоимостью.


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


Из-за того, что центр тяжести у рамы слишком завышен, также пришлось добавить сзади робота дополнительные два колеса без привода, на которые робот может дополнительно опираться. Для большей мобильности эти колеса могут подниматься от земли перпендикулярно ей. В качестве курсовой камеры использован штатный модуль от raspberry, установленный на поворотной платформе под робота, под защитой толстого переднего щитка. Для использования камеры в режиме автономной езды, она может поворачиваться практически перпендикулярно полу.

Сейчас на роботе есть вторая камера, подключающаяся по USB. Расположена она на манипуляторе и предназначена для более точной работы с ним, но также планируется при помощи нее расширить возможности робота по автономной работе, задействовав ее для захвата маяков.


А так выглядит переход робота в режим автономной езды

Создание алгоритма детектирования линии методами библиотеки OpenCV


I.Получение данных

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

Самый простой способ получения кадров с камеры c с целью последующей обработки — использование библиотеки picamera. Перед началом работы нужно разрешить доступ к камере через raspi-config --> interfacing options camera --> выбрать yes.

sudo raspi-config

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

from picamera.array import PiRGBArray
from picamera import PiCamera
import cv2
#импортируем все нужные библиотеки
camera = PiCamera()
camera.resolution = (640, 480) 
camera.framerate = 30
cap = PiRGBArray(camera, size=(640, 480))

for frame in camera.capture_continuous(cap , format="bgr", use_video_port=True):
	new_frame = frame.array
	cap.truncate(0)
	if False: #здесь должно быть какое-нибудь условие для выхода
		break

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

Пример изображения с курсовой камеры робота без обработки:


II. Предварительная обработка

При езде по линии наиболее просто будет отделить область точек, наиболее контрастирующих с фоновым цветом. Такой метод идеально подходит для соревнований «кубок РТК», ведь там используются черная линя на белом фоне (или белая линия на черном фоне для инверсных участков). Для того, чтобы уменьшить количество информации, нуждающейся в обработке, можно применить к нему алгоритм бинаризации, то есть перевести изображение в монохромный формат, где есть только два типа пикселей — темные и светлые. Перед этим картинку следует перевести в градации серого, а также размыть его для того, чтобы отсечь мелкие дефекты и шум, неизбежно появляющийся в процессе работы камеры. Для размытия картинки применяется фильтр Гаусса.

gray = cv2.cvtColor(self._frame, cv2.COLOR_RGB2GRAY)
blur = cv2.GaussianBlur(gray, (ksize, ksize), 0)

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

Пример изображения после перевода в градации серого и размытия:


III. Выделение деталей

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

thresh = cv2.threshold(blur, self._limit, 255, cv2.THRESH_BINARY_INV)[1]

Здесь все пиксели темнее порогового значения (self._limit) заменяются на 0 (черный), светлее – на 255 (белый).

После обработки изображение выглядит следующим образом:


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

IV.Детектирование

На бинаризированном изображении я применил алгоритм поиска границ. Он нужен для того, чтобы определить отдельно стоящие пятна и превратить их в удобный массив из значений координат точек, составляющих границу. В случае opencv, как написано в документации, стандартный алгоритм для поиска контуров использует алгоритм Suzuki85 (я не смог найти упоминаний алгоритма с конкретно таким названием нигде, кроме документации opencv, но предположу, что это — алгоритм Сузуки-Абе).

contours = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)[0]

И вот так выглядит полученный на этом этапе кадр:


V. Высокоуровневая обработка

Найдя все контуры в кадре, выбирается контур с наибольшей площадью и принимается за контур линии. Зная координаты всех точек этого контура, находится координата его центра. Для этого используются так называемые «моменты изображения». Момент — это суммарная характеристика контура, рассчитанная суммированием координат всех пикселей контура. Разделяют несколько видов моментов – вплоть до третьего порядка. Для данной задачи нужны только момент нулевого порядка (m00) – количество всех точек, составляющих контур (периметр контура), момент первого порядка (m10), представляющий собой сумму X координат всех точек, и m01 — сумму Y координат всех точек. Делением суммы координат точек по одной из осей на их количество получается среднее арифметическое — примерная координата центра контура. Далее вычисляется отклонение робота от курса: курсу «прямо» соответствует координата точки центра по X близкая к значению ширины кадра, деленной на два. Если координата центра линии близка к центру кадра, то управляющее воздействие минимально, и, соответственно, робот сохраняет свой текущий курс. Если робот отклоняется одну из сторон, то будет вводится пропорциональное отклонению управляющее воздействие, пока он не вернется обратно.

mainContour = max(contours, key = cv2.contourArea)
M = cv2.moments(mainContour)
if M['m00'] != 0:#не получается деление на ноль (т.е. если получен хотя-бы один контур)
    cx = int(M['m10']/M['m00'])
    cy = int(M['m01']/M['m00'])

Ниже показан схематичный рисунок положения робота относительно линии и кадры, с наложенными на них результатами работы программы: «главный» контур, линии, проходящие через центр контура, а также точка, находящаяся в центре для оценки отклонения. Эти элементы добавляются при помощи следующего кода:

cv2.line(frame, (cx, 0), (cx, self.height), (255, 0, 0), 1)    # рисуем перекрестие на контуре
cv2.line(frame, (0, cy), (self.width, cy), (255, 0, 0), 1)                  
cv2.circle(frame, (self.width//2, self.height//2), 3, (0, 0, 255), -1) #рисуем центральную точку
cv2.drawContours(frame, mainContour, -1, (0, 255, 0), 2, cv2.FILLED) #отображаем контуры на изображении

Для удобства отладки все ранее описанные элементы добавляются на необработанный кадр:





Таким образом, прогнав кадр через алгоритм обработки, мы получили координаты X и Y центра интересующего нас объекта, а также отладочное изображение. Далее схематично показано положение робота относительно линии, а также изображение, прошедшее алгоритм обработки.


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

Самым простым способом преобразовать разницу между смещением центра цветного пятна относительно центра кадра пропорциональный регулятор (Есть еще релейный регулятор, но, в силу особенностей своей работы, он не очень подходит для езды по линии). Принцип действия подобного алгоритма заключается в том, что регулятор вырабатывает управляющее воздействие на объект пропорционально величине ошибки. Помимо пропорционального регулятора также существуют интегральный, где со временем интегральная составляющая «накапливает» ошибку и дифференциальные, принцип действия которых основывается на применении регулирующего воздействия только при достаточном изменении регулируемой величины. На практике данные простейшие П, И, Д регуляторы комбинируются в регуляторы вида ПИ, ПД, ПИД.

Стоит оговорить, что на своем роботе я пытался «завести» ПИД-регулятор, но его использование не дало никаких серьезных преимуществ перед обычным пропорциональным регулятором. Я допускаю что я мог недостаточно качественно настроить регулятор, но также возможно, что его преимущества нет так явно видны в случае с тяжелым роботом, неспособным физически развить большие скорости. В самой последней на момент написания статьи версии программы используется простой пропорциональный регулятор, но с небольшой особенностью, которая позволяла задействовать больше информации от камеры: при формировании величины ошибки учитывалось не только расположение середины пятна по горизонтали, но и по вертикали, что позволяло по-разному реагировать на элементы линии, находящиеся «вдалеке» и непосредственно перед или под роботом (курсовая камера робота обладает просто огромным углом обзора, так что повернув ее всего на 45 градусов вниз, уже можно видеть значительную часть поля, находящегося под роботом).

error= cx / (self.width/2) - 1  
#преобразование координаты (от 0 до ширины кадра) до [-1; 1]
error*= cy / self.height + self.gain #усиление

Чаще всего в условиях соревнований «кубок РТК» участниками применяется так называемая «танковая схема» — один или несколько двигателей управляют одной стороной робота, причем работает она как с гусеницами, так и с колесами. Использование данной схемы позволяет избавится от сложных элементов трансмиссии, увеличивающих шанс поломки (дифференциалы или карданные валы), получить наименьший возможный радиус разворота, что дает преимущество в условиях замкнутого пространства полигона. Данная схема предполагает параллельное управление двумя «сторонами» для движения по сложной траектории. Для этого в программе используются две переменные – мощность правого и левого мотора. Зависит эта мощность от базовой скорости (BASE_SPEED), изменяющийся в диапазоне от 0 до 100. Ошибки (error) – разница между центром кадра и координатой середины линии и коэффициента пропорционального воздействия (self._koof), который калибруется оператором. Его абсолютное значение влияет на то, с какой скоростью робот будет стараться выровнять себя относительно линии. За счет того, что на одном двигателе из базовой скорости вычитается управляющее воздействие, а на другом – прибавляется, осуществляется разворот при отклонении от курса. Направление, в котором будет производится разворот можно корректировать, изменяя знак переменной self._koof. Также, можно заметить что в результате работы следующего участка кода может появится значение мощности, которое больше чем 100, но в моей программе подобные случаи дополнительно обрабатываются потом.

#if lineFound:
leftSpeed = round(self.base_speed + error*self.koof)
rightSpeed = round(self.base_speed - error*self.koof)

Заключение


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

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

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

После реализации всех шагов, направленных на решение задач проекта, можно с уверенностью сказать, что использование алгоритмов компьютерного зрения при всей своей относительной сложности при программировании и отладке, дает наибольший выигрыш на этапе самих соревнований. При небольших габаритах камеры, она обладает огромным потенциалом в плане развития ПО, ведь камера позволяет заменить сразу несколько «традиционных» датчиков, при этом, получая из окружающего мира невообразимо больше информации. Удалось реализовать цель проекта – создать программу, использующую компьютерное зрение для решения задачи автономной навигации робота в условиях соревнований «Кубок РТК», а также описать процесс создания программы и основные этапы при обработке изображения.

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


Посмотреть программный код, а также следить за дальнейшей работой над проектом можно на моем гитхабе или же здесь, если я сделаю продолжение.
Tags:
Hubs:
+11
Comments11

Articles

Change theme settings