Проект Eyeduino: компьютерное зрение на Ардуино: это возможно!


Проект Eyeduino: компьютерное зрение на Ардуино: это возможно!
О том, чтобы попытаться загружать в Ардуино картинки с низким разрешением, мы задумывались ещё некоторое время назад: не видно было принципиальных ограничений, не позволяющих загнать в контроллер Atmega линейки, используемой в Ардуино, картинки порядка 32х32 (назовём их RI — Rough Images, «грубые изображения»), даже если расточительно тратить байт на хранение пиксела. Более того! Мне попадалась статья, где коллега практически без периферии, на АЦП контроллера получал картинку 64х64, да ещё с уровнями серого! Правда, там картинка не сохранялась в память, а гналась в последовательный порт через второй контроллер, по-моему. Я для себя определил некоторое концептуальное ограничение, из-за которого тот способ не подходил нам, а именно: попытаться вписаться в платформу Arduino как в плане железа, так и использования стандартных функций среды разработки.
Есть потенциально ещё вариант — оптическая мышь. Некоторые микросхемы, по крайней мере, с PS/2 интерфейсом, вроде бы поддерживают передачу точек картинки, которую видит камера мыши, но по 1 пикселу и по крайне медленному интерфейсу.
Наконец, третий вариант (или третий и четвёртый, как считать). Это использование Raspberry Pi или чего-то на Android + IOIO или Arduino. Тут барьер оказался больше психологическим: не доводилось раньше работать с Линуксами, поэтому с ходу программировать оказалось проблематичным (в отличие от Arduino). Хотя, думаю, что этот опыт будет полезен и мне, и сыну Алексею, и в следующем сезоне мы постараемся что-нибудь выкатить на «Малине». Правда, трудно представить себе, что это будет столь же простая и безотказная вещь, как Arduino.
Работа с RI — довольно «вкусная» возможность в спортивной робототехнике, например, для классической задачи следования по линии. Правда, только в качестве вспомогательного средства — из-за низкого разрешения и редкого чтения (большинство камер дадут 25 fps, а, скажем, в нашем «Стремительном» цикл чтения с линейки и расчёта скорости происходит раз в 4мс, т.е. на порядок чаще). Зато сколько даст более раннее знание о предстоящих поворотах! В большинстве конструкций для этого используют вынесенную вперёд линейку, а это сразу ухудшает механику (увеличивает момент инерции робота).
Итак, идея была — обрабатывать аналоговый сигнал с камеры. Поскольку памяти картинка должна занимать мало и считываться быстро, наиболее очевидный подход — черно-белая картинка, снимаемая цифровым входом, сигнал на который подаётся с выхода компаратора.
Дальше нужно было как-то решить проблему с сигналами строчной и кадровой синхронизации. Понятно, что их можно (и нужно) выделять из самого сигнала, и если строчный импульс довольно просто можно снять также при помощи компаратора, то задача вычисления начала кадра казалась мне монстром, который съест всё процессорное время и не оставит возможности решать содержательные задачи. Возможно, это не так.
Не помню уже, в каком чудесном поиске я нашёл класс микросхем «video sync separator». И недорогой представитель этого семейства LM1881 должен был прекрасно решать наши проблемы: получая на вход видеосигнал, он выдаёт цифровые сигналы и новой строки, и нового кадра, и чётного/нечётного кадра, и цветовой «вспышки» (color burst). Можно было собирать прототип и писать какую-то программу. Что мы и сделали. Прототип на breadbord, Arduino Uno — вперёд!
Суть программы следующая: есть переменная, которая отображает состояние чтения картинки (запрошено/в процессе/выполнено). Есть прерывания по новому кадру и по началу линии. По новому кадру (если считывание запрошено) сбрасываются счётчики текущей строки и линии и состояние переводится в «в процессе». По прерыванию линии проверяем номер линии (нам нужна каждая десятая) и, если подходит, считываем 32 раза выход компаратора. Набрав 32 строки, переводим состояние в «выполнено». В основном цикле программы периодически запрашиваем считывание, ждём «выполнено», потом выводим картинку в текстовом виде (белое — пробел, чёрное ‘#’) в последовательный порт. Экспериментировали с этим хозяйством мы в новогодние каникулы, да так ничего толком не добились: вместо картинки шла какая-то каша.
Второе дыхание тема получила в конкурсе RobotChallenge «Hack the Arduino Robot». Идея была оформлена в виде короткого сочинения и отправлена организаторам.
Дальше были выступления на Geek Picnic: по мини-сумо — провальное, по микро-сумо — победное. С этим результатом ехать на RobotChallenge особого смысла не было.
И вот пришло письмо от организаторов RobotChallenge, что наш проект был отобран среди десяти финалистов и нам готовы прислать робота, чтобы мы стартовали работы. Оказалось, что быстро прислать робота — проблема, пришлось его покупать уже в России. Ладно, это — не самая сложная проблема, которую пришлось решать, а продавец (ПакПак)!

Ввязавшись в проект, пришлось форсировать получение картинки. Ну не может же быть, что совсем ничего нельзя сделать! Чтобы иметь больше времени на обработку строчки и не прерываться следующей строчкой (я думал, что причина могла быть в этом), мы добавили ещё счётчик: теперь прерывание вызывалось не на каждой строчке, а на каждой десятой. Кроме того, счётчик (похоже, ещё советского дизайна схема К155ИЕ1) имел ещё полезную особенность: двойной вход, связанный логическим отношением «И». Т.е. сигнал с этой микросхемы можно было спокойно включать/отключать, подав на один из входов «1» или «0».
Кроме того, пришлось подумать над минимизацией времени считывания сигнала с компаратора. Приблизительно измерив время выполнения стандартной функции Ардуино digitalRead(), я понял, что она не годится: она выполняется порядка 6мкс, в то время. как нам нужно меньше 2мкс (стандартная строчка идёт чуть более 60мкс, за это время мы надеемся снять 32 точки). Пришлось лезть в реализацию и откидывать то, что можно вычислить и сделать один раз, зная номер PIN, на который заводится выход с компаратора. Отказался даже от цикла, чтобы не тратить время на вычитывание счётчика цикла в регистр, инкремент и снова сохранение в память (подозреваю, в такие команды должен был откомпилироваться цикл for(pixelCount=0;pixelCountАрдуино глазами робота

Как было получено

Поднастроив задержки при помощи NOP’ов и покрутив потенциометр, устанавливающий уровень второго входа компаратора (практически, яркость), я получил вполне приемлемую картинку.
Утром пришлось придумывать логотип, чтобы именно его снимок на камеру разместить первым на станице проекта.
Пришла пора портировать прототип на целевую платформу — робота. Для всяких проявлений народного творчества типа нашего на плате предусмотрены площадки для паяния. Внимание! Прежде, чем начать паять, разберитесь, что и куда там подходит, в смысле земли и питания (п’отом заработанный вывод Алексея). Оказалось также, что для того, чтобы подключить схему к прерываниям контроллера, придётся отказаться от чего-то важного: последовательного интерфейса или I2C. Поскольку последовательный интерфейс используется для связи с нижней (моторной) платой и, возможно, с компьютером, жертвой был выбран интерфейс I2C. Жалко, конечно, компас и другие интересные вещи, но проект важнее! Скольких трудов потом это нам стоило, но, возможно, по-другому это не делается вообще.
Итак, Алексей распаивет схему, слегка модифицируем программу под новые порты — работает! Причём стабильно, контакт надёжнее, чем на breadboard, а новый потенциометр — вообще красавчик: позволяет тонкую настройку и красиво смотрится на плате.

Ну, хватит только глазеть, пора начинать ходить, поуправляем моторной платой. И тут неожиданно начался кошмар, который вспоминаю с содроганием. Попытка вставить чтение изображения в программу, в которой отправляются команды на моторную плату, приводила к очень печальной вещи: робот уходил в себя, ничего не делал и, самое главное, терял способность взаимодействовать с компьютером! Загрузить что-нибудь даже безобидное уже было невозможно. Проект «Фобос» своими руками!
Так происходит иногда с людьми, но, как правило время и новые впечатления лечат, люди снова становятся готовыми к общению. Тут же случай был похож на фатальный: это же просто микроконтроллер, который я умудрился запрограммировать так, что ему стало плевать на попытки его перепрограммировать по-хорошему. О вариантах более жёсткой заливки программы пока думать не хотелось…
Ну, в общем, всё оказалось не так плохо, поиск по FAQ свёл интеллектуальную задачу к физическому упражнению: если Вы потеряли Arduino Robot (или другую Ардуину на 32u4) с экрана IDE Arduino и списка USB-устройств, но физически держите в руках, поможет кнопка Reset и многократные попытки загрузить программу. Примерно после 3-4 попыток плата снова возвращается под контроль.
Радость от победы над Фобосом омрачало одно обстоятельство: время шло (а его на проект вообще отпущено невероятно мало) а робот не ехал. Люди! Не позволяйте себе бездумно расточать время, «чтобы не было мучительно больно за бесцельно прожитые годы». Мучительно больно! Лучше сказать невозможно, наверное.
Итак, в первоначальных планах у меня было с ездой по прерывистой линии выступить на открытых соревнованиях Политехнического музея 16.02.2014 . К вечеру 15.02.2014 я уверенно освоил Reset, но и только: одновременно считывать видео и ездить робот отказывался. Причина крылась где-то в классе RobotControl: как только я его включал в код, случался Фобос. Класс использовал внутри себя I2C устройства, и, видимо, при инициализации или при дальнейшей жизни пытался поговорить с ними по I2C; поскольку я вешал на соответствующие порты прерывания, похоже было что контроллер уходил в вечную обработку прерываний. Это гипотеза, возможно всё или сложней или, наоборот, банальней.
С этими печальными открытиями я попытался уснуть, утешая себя тем, что завтра всё равно будет с чем поехать на соревнования, но сон, как говорится, не шёл. Шли идеи, в конце концов я продолжил изыскания: решил попытаться написать класс NoI2CRobotControl или просто накопипастить функционал взаимодействия с моторной платой прямо в основной код. Начал с первого, но получилось только второе и к половине 6-го утра у меня был ездящий робот с обработкой видеоизображений. То есть езда и обработка были в одной программе, но обработка мало влияла на езду. Настройки я надеялся дотянуть на соревнованиях, тем более полигон у меня есть только приблизительно похожий на целевой.
На соревнованиях — не пошло. Во-первых, пришлось очередной раз убедиться, что чисто пропорциональный регулятор (а именно такой я попытался использовать — вдруг пронесёт) плохо работает для инерционных систем, робот раскачивается и сходит. Во-вторых, к середине дня ушла картинка (как потом выяснили — из-за плохого контакта ушло напряжение на компараторе). Несколько утешили награды в других номинациях и лекция по ROS.
Понадобилось несколько дней (ну не совсем дней, глубоких вечеров) на то, чтобы доделать PD, разобраться с матчастью — и он поехал! И по сплошной линии, и по прерывистой. Причём алгоритм — примитивен, управляющий параметр — эксцентриситет, характеризующий смещение относительно центра — считается как усреднённый центр масс чёрных точек по линиям, с откинутыми краями. «Когда б вы знали, из какого сора растут стихи, не ведая стыда» (А.Ахматова) — цитирую по памяти, прошу простить, если неточно. В общем, робот поехал, поснимали видео для клипа (вспомнили и стали лихорадочно набирать материалы).
Пора было заняться вторым упражнением — охотой на таракана.
Собственно алгоритм оказался простым. Точнее, я понял, что сложный (с идентификацией отдельных пятен) сделать не успею, поэтому сделал сильное допущение, что снимать мы будем единственное пятно, и это — таракан. Дальше ищем центр масс пятна (точнее, чёрных точек, мы же сделали сильное допущение) и пытаемся следовать за ним (используя регуляторы в радиальном и тангенциальном направлениях), после чего сигнал на моторы представлял собой суперпозицию этих регуляторов.
Реализация оказалась вполне работоспособной, только камеру мы ещё приподняли: поле зрения оказалось узким, «таракан» быстро покидал его, регулятор зачастую не успевал отрабатывать, а при повышении коэффициентов обратной связи зачастую проскакивал целевое состояние. Дело в том, что мощность моторов весьма нелинейно зависит от задаваемого уровняя сигнала, и при малых уровнях моторы только напрягаются, но не крутят (печальное состояние, надеюсь, дорогой читатель, нам с Вами не придётся попадать в него, по крайней мере, надолго: можно перегореть!).
Итак, преследовать чёрное пятно мы более-менее научились. Точнее, чёрное пятно среднего размера. Был поставлен фильтр, позволяющий мелкие пятна игнорировать, а от крупных — уезжать. Мало ли какой таракан попадётся, если он занимает больше половины поля зрения. может лучше уехать;)
Следующая часть упражнения — активировать сервомашинку, которая будет хлопать «таракана» — оказалась совсем несложной и для меня, и для Алексея. Для Алексея — потому что у него в руках всё работает (когда не горит); для меня — ну, просто подключить серву к роботу реально просто. И выглядело весьма забавно!
В свободное время на Робофесте мы отсняли «охоту на таракана»: уже подходило время монтировать ролик про проект, а материалов было весьма негусто.
Вы никогда не монтировали ролик про свой проект, причём имея очень приблизительное представление и про то, как это делается технически, и про законы жанра, и про другие законы (имею в виду копирайт законодательство: на какую музыку положить ролик и не получить потом иски)? А я монтировал! Уже. И более того, с удовольствием пересматриваю результат. Ну, возможно, это новый вид графомании, для которого ещё предстоит придумать (или уже придумали) какое-то мудрёное название.
Музыкой со мной поделился коллега Николай Сырцев, который играет в группе Disen Gage. Поскольку материалов было немного (как мне казалось), выбрали одну из самых коротких и динамичных композиций и постарались нарастить на неё сюжет. Какой был креатив! И «Прибытие поезда» братьев Люмьер глазами робота! И прибытие робота его же глазами! Точнее, глазом с разрешением 32х32 точки, выводимых символами ‘#’ и ‘ ‘ (пробел) в Serial. И получилось! Впрочем, про графоманию я уже писал.

Ссылка на ролик, выложенный организаторами в YouTube
Дальше — мы выложили ролик организаторам, они выложили его для голосования, и тут начался драйв. То есть его и раньше было достаточно (внимательный читатель наверняка заметил), но тут драйв пошёл иного свойства, как у болельщика на стадионе: очень переживаешь и ничего почти сделать не можешь, разве что инфаркт нажить. Помогли друзья, коллеги, Ассоциация Спортивной Робототехники — было сделано всё, чтобы набрать likes. С помощью какой магии итальянской команде Robopet удалось нас обойти — не знаю, но в итоге в призёры они не попали.
Параллельно пришлось решать вопросы загранпаспорта, австрийской визы, билетов, гостиницы, готовить роботов к другим соревнованиям… Сейчас это всё позади, и это (то, что уже всё позади) для меня до сих пор — повод для радости.
Ну а в Вене снова пришлось поработать: презентовать проект посетителям RobotChallenge. Каким успехом пользовалась охота на таракана! Многих посетителей, правда, больше интересовал вибротаракан, чем робот, который за ним гонялся. А сколько радости детям! Да и нам приходилось общаться: конкуренты не дремали! Британская команда активно агитировала, французская тоже. Техника в итоге не выдержала. Под конец первого дня (субботы) движения хлопушки стали вялыми, а робот стал вдруг срываться с места и пытаться уехать куда-то. Сейчас-то такое поведение кажется вполне объяснимым: стал заклинивать сервомоторчик, при этом он потреблял достаточно короткозамкнутый ток, напряжение в бортовой сети просаживалось. Видимо, падение было достаточно глубоким, чтобы переставал нормально работать компаратор в цепи захвата видеосигнала. Вместо чёткого пятна — таракана — на вход алгоритма шёл какой-то мрак. Неудивительно, что робот «пугался» большого пятна и панически пытался куда-то уехать, причём со стола.
Сил, чтобы со всем этим разобраться, в субботу у нас уже не осталось, всё, что успели сделать в воскресенье утром — поменять камеру (похоже было, что с ней тоже что-то не то). Комиссия пришла с нами пообщаться, к сожалению, только в воскресенье.
Не знаю уж, поэтому ли нам досталось не первое место или ещё почему: критерии не объявлялись, а правила менялись по ходу, например, куда-то делась номинация community award, которая должна была присутствовать наравне с наградой по оценке жюри. Ну да ладно, первый блин комом, а второе место — тоже очень ничего, волнительно (и во время процедуры награждения, и потом — так и хочется, чтоб все, все знали…) Вот они, медные трубы. Ну а потом — немного погуляли по почти майской Вене — и домой.
Так закончился проект Eyeduino, давший кому что: роботу — примитивное зрение, нам — какие-то навыки и массу адреналина, иностранным коллегам — очередной повод поудивляться на русских.

А Вы, дорогой читатель, поверили, что закончился? Ха! Такие идеи так просто не заканчиваются! Вот что я скажу напоследок: такая область, как компьютерное зрение, нуждается в простой, дешёвой и доступной для понимания и обучения платформе. Eyeduino может стать для CV (computer vision) тем, чем стало Arduino для хобби-робототехники а Basic — для программирования. Да, для этого нужно сделать очень много: разработать shield (есть идеи на несколько вариантов), хорошо бы разработать plugin для Arduino IDE, который можно было бы запускать вместо Serial Monitor, куда получать картинку через Serial, нарабатывать, нарабатывать алгоритмы и методику обучения… Дорогу осилит идущий. Можете рассматривать это как Eyeduino+ manifesto! Но это — уже совсем другая история…

Страница RobotChallenge с условиями соревнований
Страница проекта в FaceBook
Материалы проекта на GitHub


0 комментариев на «“Проект Eyeduino: компьютерное зрение на Ардуино: это возможно!”»

  1. нереально круто!
    скажите а где можно более подробно почитать о проекте, так чтобы повторить ваш опыт у себя, что нужно, какие детали, скетчи и тд 🙂
    у меня такой проект robocraft.ru/blog/3279.html сейчас думаю над реализацией комп зрения. вопрос стоит в том как бы проще это сделать, с минимумом программист, ибо я не программист.
    а задача стоит тоже ездить по линии или дороге, обходить препятствия, находить промаркированные цели и хватать их клешней. или стрелять по ним с будущей пушки или нынешнего водомета…

Добавить комментарий

Arduino

Что такое Arduino?
Зачем мне Arduino?
Начало работы с Arduino
Для начинающих ардуинщиков
Радиодетали (точка входа для начинающих ардуинщиков)
Первые шаги с Arduino

Разделы

  1. Преимуществ нет, за исключением читабельности: тип bool обычно имеет размер 1 байт, как и uint8_t. Думаю, компилятор в обоих случаях…

  2. Добрый день! Я недавно начал изучать программирование под STM32 и ваши уроки просто бесценны! Хотел узнать зачем использовать переменную типа…

3D-печать AI Android Arduino Bluetooth CraftDuino DIY IDE iRobot Kinect LEGO OpenCV Open Source Python Raspberry Pi RoboCraft ROS swarm ИК автоматизация андроид балансировать бионика версия видео военный датчик дрон интерфейс камера кибервесна манипулятор машинное обучение наше нейронная сеть подводный пылесос работа распознавание робот робототехника светодиод сервомашинка собака управление ходить шаг за шагом шаговый двигатель шилд юмор

OpenCV
Робототехника
Будущее за бионическими роботами?
Нейронная сеть - введение