Eyeduino - техническое зрение на Ардуино

Тема в разделе "Глядите, что я сделал", создана пользователем Eyeduino, 22 июн 2014.

  1. Eyeduino

    Eyeduino Нерд

    Проект Eyeduino: компьютерное зрение на Ардуино: это возможно!
    [​IMG]
    О том, чтобы попытаться загружать в Ардуино картинки с низким разрешением, мы задумывались ещё некоторое время назад: не видно было принципиальных ограничений, не позволяющих загнать в контроллер 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, что наш проект был отобран среди десяти финалистов и нам готовы прислать робота, чтобы мы стартовали работы. Оказалось, что быстро прислать робота — проблема, пришлось его покупать уже в России. Ладно, это — не самая сложная проблема, которую пришлось решать, а продавец даже любезно сделал скидку ради такого случая!
    [​IMG]
    Ввязавшись в проект, пришлось форсировать получение картинки. Ну не может же быть, что совсем ничего нельзя сделать! Чтобы иметь больше времени на обработку строчки и не прерываться следующей строчкой (я думал, что причина могла быть в этом), мы добавили ещё счётчик: теперь прерывание вызывалось не на каждой строчке, а на каждой десятой. Кроме того, счётчик (похоже, ещё советского дизайна схема К155ИЕ1) имел ещё полезную особенность: двойной вход, связанный логическим отношением «И». Т.е. сигнал с этой микросхемы можно было спокойно включать/отключать, подав на один из входов «1» или «0».
    Кроме того, пришлось подумать над минимизацией времени считывания сигнала с компаратора. Приблизительно измерив время выполнения стандартной функции Ардуино digitalRead(), я понял, что она не годится: она выполняется порядка 6мкс, в то время. как нам нужно меньше 2мкс (стандартная строчка идёт чуть более 60мкс, за это время мы надеемся снять 32 точки). Пришлось лезть в реализацию и откидывать то, что можно вычислить и сделать один раз, зная номер PIN, на который заводится выход с компаратора. Отказался даже от цикла, чтобы не тратить время на вычитывание счётчика цикла в регистр, инкремент и снова сохранение в память (подозреваю, в такие команды должен был откомпилироваться цикл for(pixelCount=0;pixelCount<PIXELS_PER_ROW;pixelCount++){...}). Поскольку программной памяти в контроллере на порядок больше, чем оперативной, ради скорости Copy-Paste использовать не стыдно.
    В итоге, во время очередного ночного штурма я увидел какое-то связное пятно. Это был кусок тёмной наждачной бумаги на светлом фоне, лежащий на столе!
    Ардуино глазами робота
    [​IMG]
    Как было получено
    [​IMG]
    Поднастроив задержки при помощи NOP'ов и покрутив потенциометр, устанавливающий уровень второго входа компаратора (практически, яркость), я получил вполне приемлемую картинку.
    Утром пришлось придумывать логотип, чтобы именно его снимок на камеру разместить первым на станице проекта.
     
  2. Eyeduino

    Eyeduino Нерд

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

    Пришла пора портировать прототип на целевую платформу — робота. Для всяких проявлений народного творчества типа нашего на плате предусмотрены площадки для паяния. Внимание! Прежде, чем начать паять, разберитесь, что и куда там подходит, в смысле земли и питания (п'отом заработанный вывод Алексея). Оказалось также, что для того, чтобы подключить схему к прерываниям контроллера, придётся отказаться от чего-то важного: последовательного интерфейса или I2C. Поскольку последовательный интерфейс используется для связи с нижней (моторной) платой и, возможно, с компьютером, жертвой был выбран интерфейс I2C. Жалко, конечно, компас и другие интересные вещи, но проект важнее! Скольких трудов потом это нам стоило, но, возможно, по-другому это не делается вообще.
    Итак, Алексей распаивет схему, слегка модифицируем программу под новые порты — работает! Причём стабильно, контакт надёжнее, чем на breadboard, а новый потенциометр — вообще красавчик: позволяет тонкую настройку и красиво смотрится на плате.
    [​IMG]
    Ну, хватит только глазеть, пора начинать ходить, поуправляем моторной платой. И тут неожиданно начался кошмар, который вспоминаю с содроганием. Попытка вставить чтение изображения в программу, в которой отправляются команды на моторную плату, приводила к очень печальной вещи: робот уходил в себя, ничего не делал и, самое главное, терял способность взаимодействовать с компьютером! Загрузить что-нибудь даже безобидное уже было невозможно. Проект «Фобос» своими руками!
    Так происходит иногда с людьми, но, как правило время и новые впечатления лечат, люди снова становятся готовыми к общению. Тут же случай был похож на фатальный: это же просто микроконтроллер, который я умудрился запрограммировать так, что ему стало плевать на попытки его перепрограммировать по-хорошему. О вариантах более жёсткой заливки программы пока думать не хотелось…
    Ну, в общем, всё оказалось не так плохо, поиск по FAQ свёл интеллектуальную задачу к физическому упражнению: если Вы потеряли Arduino Robot (или другую Ардуину на 32u4) с экрана IDE Arduino и списка USB-устройств, но физически держите в руках, поможет кнопка Reset и многократные попытки загрузить программу. Примерно после 3-4 попыток плата снова возвращается под контроль.
    Радость от победы над Фобосом омрачало одно обстоятельство: время шло (а его на проект вообще отпущено невероятно мало) а робот не ехал. Люди! Не позволяйте себе бездумно расточать время, «чтобы не было мучительно больно за бесцельно прожитые годы». Мучительно больно! Лучше сказать невозможно, наверное.
    Итак, в первоначальных планах у меня было с ездой по прерывистой линии выступить на открытых соревнованиях Политехнического музея 16.02.2014 http://www.railab.ru/season-2014/129-vi-vserossijskij-otkrytyj-robototekhnicheskij-turnir.html . К вечеру 15.02.2014 я уверенно освоил Reset, но и только: одновременно считывать видео и ездить робот отказывался. Причина крылась где-то в классе RobotControl: как только я его включал в код, случался Фобос. Класс использовал внутри себя I2C устройства, и, видимо, при инициализации или при дальнейшей жизни пытался поговорить с ними по I2C; поскольку я вешал на соответствующие порты прерывания, похоже было что контроллер уходил в вечную обработку прерываний. Это гипотеза, возможно всё или сложней или, наоборот, банальней.
    С этими печальными открытиями я попытался уснуть, утешая себя тем, что завтра всё равно будет с чем поехать на соревнования, но сон, как говорится, не шёл. Шли идеи, в конце концов я продолжил изыскания: решил попытаться написать класс NoI2CRobotControl или просто накопипастить функционал взаимодействия с моторной платой прямо в основной код. Начал с первого, но получилось только второе и к половине 6-го утра у меня был ездящий робот с обработкой видеоизображений. То есть езда и обработка были в одной программе, но обработка мало влияла на езду. Настройки я надеялся дотянуть на соревнованиях, тем более полигон у меня есть только приблизительно похожий на целевой.
    [​IMG]
    На соревнованиях — не пошло. Во-первых, пришлось очередной раз убедиться, что чисто пропорциональный регулятор (а именно такой я попытался использовать — вдруг пронесёт) плохо работает для инерционных систем, робот раскачивается и сходит. Во-вторых, к середине дня ушла картинка (как потом выяснили — из-за плохого контакта ушло напряжение на компараторе). Несколько утешили награды в других номинациях и лекция по ROS.
    Понадобилось несколько дней (ну не совсем дней, глубоких вечеров) на то, чтобы доделать PD, разобраться с матчастью — и он поехал! И по сплошной линии, и по прерывистой. Причём алгоритм — примитивен, управляющий параметр — эксцентриситет, характеризующий смещение относительно центра — считается как усреднённый центр масс чёрных точек по линиям, с откинутыми краями. «Когда б вы знали, из какого сора растут стихи, не ведая стыда» (А.Ахматова) — цитирую по памяти, прошу простить, если неточно. В общем, робот поехал, поснимали видео для клипа (вспомнили и стали лихорадочно набирать материалы).
    Пора было заняться вторым упражнением — охотой на таракана.
    [​IMG]
    Собственно алгоритм оказался простым. Точнее, я понял, что сложный (с идентификацией отдельных пятен) сделать не успею, поэтому сделал сильное допущение, что снимать мы будем единственное пятно, и это — таракан. Дальше ищем центр масс пятна (точнее, чёрных точек, мы же сделали сильное допущение) и пытаемся следовать за ним (используя регуляторы в радиальном и тангенциальном направлениях), после чего сигнал на моторы представлял собой суперпозицию этих регуляторов.
    Реализация оказалась вполне работоспособной, только камеру мы ещё приподняли: поле зрения оказалось узким, «таракан» быстро покидал его, регулятор зачастую не успевал отрабатывать, а при повышении коэффициентов обратной связи зачастую проскакивал целевое состояние. Дело в том, что мощность моторов весьма нелинейно зависит от задаваемого уровняя сигнала, и при малых уровнях моторы только напрягаются, но не крутят (печальное состояние, надеюсь, дорогой читатель, нам с Вами не придётся попадать в него, по крайней мере, надолго: можно перегореть!).
    Итак, преследовать чёрное пятно мы более-менее научились. Точнее, чёрное пятно среднего размера. Был поставлен фильтр, позволяющий мелкие пятна игнорировать, а от крупных — уезжать. Мало ли какой таракан попадётся, если он занимает больше половины поля зрения. может лучше уехать;)
    Следующая часть упражнения — активировать сервомашинку, которая будет хлопать «таракана» — оказалась совсем несложной и для меня, и для Алексея. Для Алексея — потому что у него в руках всё работает (когда не горит); для меня — ну, просто подключить серву к роботу реально просто. И выглядело весьма забавно!
    В свободное время на Робофесте мы отсняли «охоту на таракана»: уже подходило время монтировать ролик про проект, а материалов было весьма негусто.
    Вы никогда не монтировали ролик про свой проект, причём имея очень приблизительное представление и про то, как это делается технически, и про законы жанра, и про другие законы (имею в виду копирайт законодательство: на какую музыку положить ролик и не получить потом иски)? А я монтировал! Уже. И более того, с удовольствием пересматриваю результат. Ну, возможно, это новый вид графомании, для которого ещё предстоит придумать (или уже придумали) какое-то мудрёное название.
    Музыкой со мной поделился коллега Николай Сырцев, который играет в группе Disen Gage. Поскольку материалов было немного (как мне казалось), выбрали одну из самых коротких и динамичных композиций и постарались нарастить на неё сюжет. Какой был креатив! И «Прибытие поезда» братьев Люмьер глазами робота! И прибытие робота его же глазами! Точнее, глазом с разрешением 32х32 точки, выводимых символами '#' и ' ' (пробел) в Serial. И получилось! Впрочем, про графоманию я уже писал.

    Ссылка на ролик, выложенный организаторами в YouTube

     
    Tomasina нравится это.
  3. Eyeduino

    Eyeduino Нерд

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

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

    Страница RobotChallenge с условиями соревнований http://www.robotchallenge.org/competition/
    Страница проекта в FaceBook https://www.facebook.com/eyeduino
    Материалы проекта на GitHub https://github.com/EduardPetrenko/Eyeduino