Что значит робота разработать

    Вы читаете о роботах и программировании и думаете: «Было бы здорово сделать что-то подобное самому!» Теми, кем эта идея овладевает чуть больше просто мыслей смотрят кто и как делал своего робота. Читают статьи, смотрят видео. На картинках все понятно. В видеороликах тоже обычно показываются уже готовые продукты, а также сжато показываются технологии их изготовления. И вроде бы то же всё понятно: отпилил, прикрутил, припаял, соединил, запрограммировал вон на той программе вот этим кодом.

    Еще более увлечённые, выбрав интересный и, с первого взгляда, простой вариант, переходят к действию и, зачастую копируя, делают своего первого робота. Это волевое и очень значимое решение — главное начать хоть что-то сделать самому! В процессе изготовления оказывается куча технологических заковырок вплоть до того, что оказывается для заказа/покупки какой-то штукенции, надо узнать как она точно называется. А еще — разъёмы не паяются нормально — и как на видео в одно касание всё получается? Процесс создания нередко затягивается, но настойчивый начинающий робототехник так или иначе добивает результат до какого-то осмысленного первого запуска хотя бы «по прямой».



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

    Да, я тоже раньше с интересом читал статьи. И сейчас читаю! Особенно мне нравятся статьи про коптеры: с земли, да в небо! Однако, если быть честным, они во мне вызывали только мысли. Тем более, надо было разбираться в программировании, а последний раз я работал с Си в институте, всё уже начисто забылось. Я знаю, что такое Arduino, но я никогда не видел её «живьём». И так далее. У меня техническое образование, я занимаюсь вопросами ЦОС на FPGA. Технически для меня нет препятствий взять и разобраться, но… по сути я занимаюсь всем этим на работе и мне там всего этого дела, как говорится, «выше крыши». В результате нужен был какой-то хороший стимул, чтобы этим как-то начать заниматься. Стимул появился, когда это стало нужно кому-то ещё: правду говорят, что настоящее созидание — когда отдаешь другим.

    Мой хороший знакомый, который является одним из учредителей молодой (можно сказать свежеиспечённой) компании Endurance, зная характер моей работы, обратился ко мне по ряду технических вопросов. Оказалось, что ребята имеют разработчиков в США и уже представили свой прототип супердешевого (телефон + пластиковая платформа с колесами на ардуине — вроде бы уже дешевле и некуда) робота телеприсутствия. Но они захотели не просто заниматься одним маркетингом и бизнесом в стиле «купил там — продал тут», а захотели более подробно погрузиться в техническую тему и даже планируют разработать свою модель (пусть даже эквивалентную по принципу и функционалу), но уже тут, у нас в России, с целью получения не только готового результата, но и понятной самим технологии его изготовления. Такова предпосылка участия в этом деле вашего покорного слуги (это я на себя намекаю).

    Поначалу я тоже почитал статьи, посмотрел где и что покупают и как делают. Но не было единой картины. Вроде бы, в целом-то, всё понятно, но одновременно и не всё. Непонятно как управлять с компьютера, как видео на компьютер доставить. Тем более, я же не софтовик и не хотелось бы «раскапывать» эту, софтовую, тему с нуля, хотя это было бы интересно конечно; так-то я все свои потребности как-то успешно удовлетворял скриптовым языком AutoIt. В результате поисков я нашел два ключевых ресурса, которые стали для меня ключевыми: интернет-магазин carduino и форум cyber-place. В магазине можно было купить все необходимые комплектующие — всё в одном месте.

    Цены, по сравнению с ebay или ali, конечно, дикие, но главное было не в этом — можно было купить платформу и необходимый обвес к ней, также как и всю мелочевку. И это было удобно забирать/заказывать. На форуме в разделе «Сделай сам»\«Робототехника» и «Сделай сам»\«CyberWrt» можно почерпнуть информацию общего характера, а также как детально организовать связь компьютера с роботом через Wi-Fi. Десятилетний опыт работы по специальности дал о себе знать, и я вместо того, чтобы немедленно начать копировать, что-нибудь скручивая или программируя, взял в руки карандаш и бумагу…

    Что значит «разработать»? Вот понятно, что такое «сделать робота» — берёшь и… делаешь! А «разработать» это как?
    Ниже я попытался описать процесс, именно, разработки, а не создания робота. Создание робота, как конструирование, изначально интуитивно понятно и представляемо. По мере создания прикручивается то — то, то — это. Понакрутил так — и вдруг оказалось, что ту, вторую деталь, надо иначе прикрутить, тогда восьмая деталь встанет как влитая, надо только проводки от батареек передвинуть влево поближе к пятой детали, а, чтобы не болтались, лучше прикрепить к четвёртой, немного их подогнув, так как там рядом будет десятая деталь. Сплошное творчество. А давайте попробуем по взрослому: сначала продумаем, что будем делать, а потом сделаем это :)

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

    Представим себе человека, который никогда не занимался робототехникой. У него есть друг-паяльник, мультиметр и всякий ручной инструмент типа кусачек, отверток и прочего. Представили себя? Отлично! Поехали…




    Первый этап — осмысление

    Что мы хотим сделать? И зачем это нужно?

    Для меня этот вопрос был изначально решён за меня — мне нужно сделать машинку с видеокамерой, которая бы могла подключаться через Wi-Fi или интернет к компьютеру, на компьютере я бы видел картинку с машинки и управлял бы ей, заставляя её двигаться. Вроде бы понятно, но как-то скучно и чего-то не хватает. Удалённое управление — робот что ли какой-то? Робот? Что такое робот? Робот — это какая-то автономная штука, которая выполняет работу самостоятельно. Как это применить для данной задачи? А пусть машинка будет ездить сама! Как таковой постановки задачи нет — ну пусть ездит как захочет. Однако, если она будет ездить как вздумается, то будет сталкиваться с предметами. Это как-то нехорошо, неправильно. Значит, машинка должна уметь видеть впереди себя и объезжать препятствия, предотвращая столкновение. Как это можно сделать? Можно обрабатывать данные с видеокамеры. Э-эээ… как-то непонятно как вообще к этому подступаться. Наверное это непросто. Что у нас ещё есть? Продаются всякие инфракрасные и лазерные измерители расстояния! Можно купить такой и как-то получать расстояние с него. Бред вроде, но что-то в этом есть…

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

    — умеет перемещаться по поверхности по командам оператора транслируя ему видео впереди себя;
    — имеет автономный режим «существования»: умеет самостоятельно двигаться в произвольном направлении;
    — контролирует обстановку впереди себя;
    — в случае обнаружения впереди какого-то объекта не даёт себе с ним столкнуться, игнорируя команды оператора, или меняет направление движения в автономном режиме.

    Кажется определились с целью. Берём паяльник и устраиваем его на столе. Можно в тряпочку укутать — ему помягче будет, поуютнее. Пускай привыкает пока к обстановке.

    Второй этап — изучение реальности

    После того, как определились с базовым функционалом и стало понятно, что нужно сделать, встаёт вопрос: как это сделать, как это будет выглядеть?

    Можно взять молоток в руки и, обведя суровым взглядом свое жилище, безапелляционным тоном заявить окружающим: крышка от стола, колесики от кресел, резинка из трусов, пара китайских палочек, 20 сантиметров ткани от шторы, защёлки из босоножек, ноутбук разбираем на запчасти — всё сдать мне во имя прогресса! Дабы не было ненужных вопросов голосу можно придать оттенки угрозы и одухотворения одновременно. Однако, предлагаю более эффективный метод: для этой цели идеально подходит созданный людьми инструмент под названием «Интернет». Набираем слово «Робот своими руками» в той поисковой системе, которую позволяет использовать ваша религия, и начинаем читать, смотреть, слушать и вникать. Этот этап может длиться гораздо дольше предыдущего, так как придумать что-то сделать гораздо проще, чем придумать, как сделать это что-то. Тем более, мимоходом можно ещё столько интересного узнать!

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

    — трёхколёсная платформа с двумя двигателями: два ведущих колеса, третье — как подпорка;
    — питание от аккумуляторов АА (1,2-1,3В х 4 = примерно 5 вольт — как раз для двигателей подходит);
    — устройством будет управлять микроконтроллер Arduino Nano (управление двигателями — через драйвер двигателей, запитать микроконтроллер можно от 5В);
    — дальность определять будем используя ультразвуковой датчик (питание 5В);
    — перепрошитый маршрутизатор (питание 5В) будет работать как точка доступа, подсоединяясь к которой можно подключиться к вебсерверу и выдавать команды на микроконтроллер, при этом нам будет доступна картинка с вебкамеры, подключенной к маршрутизатору;
    — на устройстве установлены два переключателя: ВКЛ/ОТКЛ питание и ВКЛ/ОТКЛ автономный режим — с этого момента никаких устройств, раз автономно умеет работать — значит робот;
    — робот снабжён светодиодом, который будет загораться при обнаружении препятствия;
    — на роботе будут присутствовать спереди два ярких белых светодиода — фары — почему бы и нет: когда темно, роботу не должно быть страшно, а с ярким светом всегда как-то пободрее, тут главное — назад не оглядываться лишний раз — значит видеокамера будет направлена только вперёд;
    — на роботе будет установлена веб-камера, которая будет подключена к маршрутизатору.

    Этот, представляемый нами внешний вид базируется на тех представлениях, которые мы получили изучая рынок комплектующих для роботов. Здесь нет детального представления робота до винтика — только общие черты, однако, они позволяют представить себе, что получится в результате. В целом, приходим к выводу, что можно всё купить в магазине carduino, а также организовать связь с роботом, используя маршрутизатор и настроив его примерно так, как указано на форуме cyber-place. Пока не разбирались в тонкостях подключения отдельных комплектующих и не разбирались как программировать маршрутизатор. Но поняли, что отдельные комплектующие можно друг к другу подключить (электрически и информационно), а также определились, как минимум, со структурой и внешним видом.

    Компоненты робота
    Платформа с колёсами, двигателями и крепежом:



    Плата для электроники:



    Микроконтроллер Carduino Nano v.7:



    Ультразвуковой датчик HC-SR04:



    Драйвер для управления двигателями:



    Маршрутизатор TP-Link MR3020:



    Хаб (компактный и миниатюрный хаб неизвестной модели):



    Вебкамера (Logitech C270):




    Паяльник лежит на столе и заинтересованно смотрит на картинки выше. Интерес понятен — паять-то это всё ему!

    Третий этап — творчество: общая разработка

    Столько времени потрачено, а результата еще не видно! Результат только у нас в голове в виде образов и мыслей. Может быть стоит как-то материализовать эти мысли? Как будет выглядеть внешний вид робота уже, примерно, понятно. Также определён набор комплектующих. Как это будет соединяться? Как это всё будет общаться между собой? Робот — это же не лампочка — щелкнул выключателем и она загорелась. Тут щелкнул тумблером, питание подалось — а дальше что? И вот тут, уважаемые читатели, разработка начинает ветвиться на три составляющие: конструкторская, электротехническая и программная.

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

    Посидев и порисовав можно прийти к следующему функциональному виду робота:



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

    Что мы видим на картинке?

    Наш робот будет состоять из двух независимых частей: блок связи и блок управления. Блок связи связывается с внешним оператором, принимает от него команды и выдаёт их на блок управления. Блок управления, принимая команды, выполняет их. То есть, мы поделили управление на два уровня — низкоуровневое (управление платформой, взаимодействие с датчиками) и высокоуровневое (взаимодействие с оператором и командное управление низкоуровневой частью). Задача по управлению разбилась на две части, которые можно решать раздельно: сначала разобраться с движущейся платформой в виде микроконтроллера Arduino, затем — со связью в виде перенастроенного маршрутизатора.

    Блок связи.

    1) Элеткротехника. Тут, вроде как, в целом всё ясно: камера даёт видео и звук и подключается через хаб к маршрутизатору. К нему же через хаб подключается и Arduino. Невелика премудрость воткнуть кабель в USB разъем, вероятность сделать это не так, или наоборот, достаточно мала Правда, некоторым и такой вероятности хватает, но это ж не про нас!!! Мы же смотрим, куда вставляем, если не лезет просто посильнее дожмем — должно влезать, это ж USB!

    2) Программирование. Тут никакой разработки нет, так как используется уже готовая прошивка под маршрутизатор и готовые модули для робота. Интерфейсный протокол с блоком управления уже определили за нас — надо тоже только разобраться. Как именно прошивать и что именно там настраивать — разберёмся потом.

    Блок управления.

    1) Электротехника. Вроде бы, тоже, всё понятно. У нас имеется материнская плата, на которой всё распаивается. Надо будет только разобраться, куда подсоединяются датчики к Arduino и при этом нигде не натворить лишней работы, перепутав землю с питанием. С точки зрения подключения, вроде бы, нет особых проблем. Правда, сразу и не сообразишь куда на плате что подключается, но посмотрев работы мастеров, а также, найдя нижеприведённую картинку, всё становится на свои места. Как там и что разведено — потом разберёмся, тем более правило напильника никто не отменял: если чего — доработаем!



    2) Программирование. А вот тут проблема. Как и чего там должно делаться. Вот пришла команда. Чего делать? Берём опять карандашик с бумажкой и рисуем. В результате можно получить следующую функциональную программную схему (только платформы):



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

    Если нажата кнопка автономного режима, то модуль управления должен будет определять нет ли впереди препятствия и двигаться вперёд выдавая соответствующую команду на модуль моторов, а также принимать какие-то действия, если препятствие имеется. Функции непосредственно управления конечными устройствами частично делегированы другим модулям исходя из уровня сложности. Модулю управления, чтобы приказать модулю двигателям двигаться вперед, нужно знать можно ли двигаться вперёд. Всего скорее, просто так, с ультразвукового датчика данные не получишь — да и зачем нам этим заниматься? Мы только командовать умеем — для работы с датчиком имеется модуль сонара! Вот у него и запрашиваем доклад об оперативной обстановке спереди робота — ехать-то вперед можно?

    Паяльник радостно и незаметно выбрался из-под тряпочки, заинтересованно уставившись на розетку: интересно, сколько там вольт — 110 или 220?

    Четвёртый этап — творчество: детальная разработка

    Что и как делать понятно. Понятно из чего. Понятно, как именно, это должно и будет выглядеть функционально. Пора! Пора уже начать что-то делать! Столько всего прочитали, просмотрели, оценили, обдумали, придумали, забраковали, разрисовали. Эх… вон, паяльник невдалеке лежит. На нас смотрит. Недоумевает — казалось бы бери да паяй, чего всякой фигнёй заниматься? Вздыхаем и накрываем паяльник листком бумаги: поспи родной пока, ещё не все готово. Мало понять как будет выглядеть всё функционально, нужно понять как будет выглядеть всё структурно. Нужно максимально детализировать своё понимание.

    Блок связи.

    Практически всё выяснили на прошлом этапе. Всё понятно. На форуме cyber-place ознакомимся с этой темой и понимаем как перепрошить маршрутизатор TP-Link MR3020 и настроить его. Скачиваем прошивку для маршрутизатора. Создаём файлики с описанием куда какой IP вводить, что и где нажимать. Единственная проблема — непонятно какие именно команды будут высылаться на микроконтроллер. Посидев на форуме, почитав и пообщавшись становится всё понятным (больше спасибо всем, кто мне отвечал и пытался помочь). Записываем все команды так же в текстовой файл и помечаем, какая команда за что отвечает. Оказалось всё просто — при нажатии на кнопку, например «вперёд», высылается определенная строка ASCII кода, а при отпускании кнопки выдаётся другая команда — «стоп» (какие именно байты передаются приведено ниже).

    Блок управления.

    На прошлых этапах определили что именно будет подключено к блоку управления. Сердце блока управления — Arduino nano. Что это такое? За что отвечают вон те лампочки и вот эти разъёмы? К каким выводам подключены ножки от сонара? Да и вообще: а с сонаром-то как общаться?

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

    Создаём для себя «хелп» по микроконтроллеру:
    Carduino Nano v.7


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



    И ультразвуковой датчик:
    Сонар HC-SR04




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



    Рисование этой схемы требует хороших мыслительных усилий — ведь задаётся по сути каркас, на который потом будет ложится код. Тут необходимо сделать ремарку. Дело в том, что я занимаюсь разработкой на ПЛИС, поэтому, изначально я и разрисовал структуру так, как делал бы для ПЛИС. Надо понимать, что для СИ-подобного программирования количество сигналов на схеме вообще-то избыточно. Но зато мы получаем некую платформонезависимость — можно всё реализовать на ПЛИС попробовать, тем более есть доступные варианты.

    Давайте прочитаем эту схему слева направо. Интерфейсный модуль по UART получит данные. Если данные являются командой, то он выдаёт управляющий байт CTRL под импульсным стробом SCTRL на модуль управления. Модуль управления, получив строб, понимает, что пришла какая-то команда, читает вход CTRL и выполняет команду. Допустим, пришла команда «вперёд». На выход выдаётся импульсный сигнал Get_Dist и ожидаются данные дальности по входу Dist. В принципе, нам не запрещено постоянно выдавать импульсы Get_Dist, в этом случае значение дальности мы можем просто снимать с входного порта Dist в нужное нам время. Допустим с дальностью всё в порядке — до препятствия еще далеко, можно и прокатиться с ветерком. На модуль управления моторами по шине Mode выдаётся сигнал, говорящий о том, что нужно двигаться вперёд с заданной скорость Speed. При измерении дальности и при движении на соответствующие светодиоды так же выдаётся высокий уровень (логическая единица) для индикации работы и понимания что происходит с системой (полезно будет при отладке).

    Модульная схема есть! В голове порядок! Понятно как и что. Теперь надо только это описать — что каждый модуль должен делать и при каких условиях.

    Интерфейсный модуль:




    Модуль управления:






    Модуль измерения дальности:




    Модуль управления двигателями:




    Из-под края листа за угол стола вывалился хвостик с вилкой от паяльника. Лист немного сполз и открылась его мордочка. Паяльник смотрит с выражением даже не требующим жеста «покрутить палец у виска»: «сам себе ТЗ пишет — чё за ересь… вот „повезло“ мне с владельцем, однако» — грустно подумал он, незаметно покачивая вилкой питания за столом.

    Пятый этап — сотворение

    Разработка почти завершена. Все моменты уяснены до предела. Осталось сотворить чудо — из этой кучи запчастей и небольшой стопки листочков с картинками, табличками, схемами и диаграммами собрать что-то такое, ради чего всё затевалось.

    Хватаем грустный паяльник и включаем в розетку. Он начинает греться и шипеть от удовольствия в те моменты, когда мы очищаем жало о губку. Флюс с припоем водят дружный хоровод на столе. Солнце заглядывает в окно, подсвечивая место точечной пайки SMD резистора на плате. Жизнь хороша, когда паяешь не спеша!




    Самый веселый и зрелищный этап разработки, который никого не оставит равнодушным. Тут уже на глазах получается что-то интересное, всем очевидное. Этот этап контрастирует со всеми предыдущими, так как рисование каких-то картинок с табличками, мало того, что неинтересно наблюдателю так еще и искренне непонятно, как из этого может что-то получиться. А тут… это же так здорово, когда твой первый робот проедет свой первый сантиметр на столе и почтительно остановится от края ровно за 20 сантиметров только потому, что ты ладонью загородил край стола. Умная тварюшка получилась — понимает что дальше ему нельзя!




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





    Строго говоря, по завершении этого этапа должен быть сформирован список, который в совокупе с вышеприведенной документацией обеспечивал бы повторяемость результата. Крупные комплектующие я приводил выше, однако, при формировании заказа я попросил положить также необходимую «рассыпуху»: переключатель питания, разъем для подключения батарейного отсека, колодки разъёмов 2,54мм…




    Помимо этого в работе использовались расходные материалы — провода, припой, флюс, а также инструменты. Если есть какие-то технологические нюансы, на них тоже должно быть акцентировано внимание.



    Программу я писал два раза.
    Первый раз написалось так, как написалось. Активно читал сайт arduino.ru Я и не программу больше писал, а разбирался с синтаксисом, вспоминал Си и постоянно матерился на последовательность выполнения программного кода (я-то привык к параллельности исполнения кода, когда по тактам/циклам работают все вычислительные блоки, а не отрабатывается только одна строка).

    Второй раз я подошел к организации программы жестко структурно. Не всегда это оказалось оправдано, но если задали изначальный стиль, то его нужно придерживаться. Смысл заключается в… повторении в коде структуры программных моделей нарисованных выше. В коде сначала у нас объявляются все переменные и прописываются настроечные константы. Затем следует блок инициализации «void setup». А затем следует основной цикл «void loop», задачей которого является непрерывный опрос модулей. Каждый модуль имеет свои входные и выходные переменные, которые влияют на его работу. В основном цикле происходит управление этими переменными.

    Опрашиваемая кнопка задаёт режимы работы всех модулей. Если по интерфейсу пришла команда, то она транслируется на другие модули управления, если не активирован автономный режим. Сначала у меня было только два модуля — управления мотором и светом. Затем появился модуль формирования звука, который я сделать не успел. Тут как раз и сказалась заложенная структура — просто и понятно добавлять/исключать модули: блок в объявления переменных, строчку в инициализацию, строчку в основной цикл и код самого модуля. Модуль определения дальности и модуль реализующий алгоритм поворотов вызываются из других модулей по необходимости.

    Текст программы
    //////////////////////////////////////////////////////////////////////////////////////////////////////////
    // Robot Platform RASH 1  AlSHex
    // 1.0
    //
    // Create: 23/03/2015
    // Modification: 11/04/2015
    //
    // Description: Программа управления платформой RASH 1
    //
    //////////////////////////////////////////////////////////////////////////////////////////////////////////
    
    //припиновка
    const int LEDtech = 13; //вывод технологического сигнала на светодиод
    const int LEDdist = 3; //вывод сигнала от сонара на светодиод
    const int Sonar_t = 14; //данные на сонар
    const int Sonar_r = 15; //данные от сонара
    const int Mode = 16; //подключение кнопки установки режима - автономный или ручной: =0 - ручной; 1= автономный режим
    const int MB1 = 4; //левый мотор - digital
    const int MB2 = 5; //левый мотор - pwm
    const int MA1 = 6; //правый мотор - pwm
    const int MA2 = 7; //правый мотор - digital
    const int HDL = 17; //вывод сигнала на фары
    const int SPK = 11; //вывод сигнала на динамик
    //const int LGHT = ; //данные от датчика освещенности
    
    //настройка модуля интерфейса
    const long UART_Speed = 9600; //Скорость UART
    
    //настройка модуля сонара
    const int D1 = 20; //коэффициенты функции ШИМ для вывода данных сонара на светодиод
    const int D2 = 110;
    const float A = 2.5;
    const float B = 305;
    
    //настройка модуля управления
    const byte Byte_forward = byte('W'); //команда: вперед
    const byte Byte_back = byte('S'); //команда: назад
    const byte Byte_left = byte('A'); //команда: влево
    const byte Byte_right = byte('D'); //команда: вправо
    const byte Byte_stop = byte('x'); //команда: стоп
    const byte Byte_sound = byte('C'); //команда: звуковой сигнал/мелодия
    const byte Byte_light = byte('V'); //команда: фары
    const int Dist_min = 40; //минимальная дистанция, при которой робот запустит алгоритм объезда препятствий [см]
    const int Cycle_lightoff = 1000; //кол-во циклов повтора программы после которого отключаются включенные фары в автономном режиме
    const int Speed_default = 255; //значение по умолчанию, которое будет использоваться после включения пока не придёт команда, скорость сбрасывается в значение по умолчанию при переходе на автономный режим
    
    //общие настройки
    const int Delay_prog = 10; //задержка выполнения повтора программы, [мс]
    const int M_stop = 0; //константы для модуля мотора (для удобства использования)
    const int M_forward = 1;
    const int M_back = 2;
    const int M_left = 3;
    const int M_right = 4;
    
    //объявление и описание модулей - функций
    
    //========== Interface ==========
    void _UART_Interf(unsigned int RST, unsigned int *Data); //данные от интерфейса: Data[0]=1 - наличие команды; Data[1] - данные команды
    //RST - сброс: 0= нормальная работа; 1= сброс
    //Data - массив данных от интерфейса: Data[0]=1 - наличие команды; Data[1] - данные команды
    //
    //Подсоединение к UART происходит с помощью библиотеки Serial
    
    //========== Motor ==========
    void _Motor(unsigned int RST, unsigned int Mode, unsigned int Speed);
    //RST - сброс: 0= нормальная работа; 1= сброс
    //Mode - режим работы: 0= стоп; 1= движение вперед; 2= движение назад; 3= поворот налево; 4= поворот направо
    //Speed: скорость вращения двигателей - уровень, формируемый ШИМ: 0= нулевой уровень; 255= максимальный уровень
    //
    //MA1, MA2, MB1, MB2 - сигналы припиновки двигателей, MB1 и MA2 - цифровые, MB2 и MA1 - аналоговые (0/255)
    //LEDtech - цифровой сигнал припиновки для технологического светодиода, загорается при выполнении команды и гаснет при получении команды "стоп"
    
    //========== Sonar ==========
    unsigned int _Sonar(unsigned int RST);
    //RST - сброс: 0= нормальная работа; 1= сброс
    //
    //Sonar_t - цифровой сигнал припиновки для сонара Trig
    //Sonar_r - цифровой сигнал припиновки для сонара Echo
    //LEDdist - аналоговый (0/255) сигнал припиновки для светодиода, на который будет выводиться ШИМ-сигнал для визуализации дальности - чем ближе объект, тем светодиод будет гореть ярче
    
    //========== Control Motor ==========
    void _ControlM(unsigned int RST, unsigned int SCTRL, unsigned int DCTRL, unsigned int Mode);
    //RST - сброс: 0= нормальная работа; 1= сброс
    //SCTRL - сигнал актуальности данных DCTRL: 0= неактуальны; 1= актуальны
    //DCTRL - данные (команда) от модуля интерфейса
    //Mode - режим управления: 0= управления по командам; 1= автономный режим
    //
    //Модуль обращается к модулям Sonar, Motor и Rotate
    //LEDtech - цифровой сигнал припиновки для технологического светодиода, загорается на 1 секунду при получении команды установки скорости
    
    //========== Control Light ==========
    void _ControlL(unsigned int RST, unsigned int SCTRL, unsigned int DCTRL, unsigned int Mode);
    //RST - сброс: 0= нормальная работа; 1= сброс
    //SCTRL - сигнал актуальности данных DCTRL: 0= неактуальны; 1= актуальны
    //DCTRL - данные (команда) от модуля интерфейса
    //Mode - режим управления: 0= управления по командам; 1= автономный режим
    //
    //HDL - цифровой сигнал припиновки для управления фарами: 0= фары выключены; 1= фары включены
    
    //========== Control Sound ==========
    void _ControlS(unsigned int RST, unsigned int SCTRL, unsigned int DCTRL, unsigned int Mode);
    //RST - сброс: 0= нормальная работа; 1= сброс
    //SCTRL - сигнал актуальности данных DCTRL: 0= неактуальны; 1= актуальны
    //DCTRL - данные (команда) от модуля интерфейса
    //Mode - режим управления: 0= управления по командам; 1= автономный режим
    //
    //SPK - аналоговый (0/255) сигнал припиновки для вывода ШИМ сигнала
    
    //========== Rotate ========== - алгоритм вращения, модуль выделенный из состава модуля ControlM в самостоятельный
    void _Rotate(unsigned int RST, unsigned int Speed);
    //RST - сброс: 0= нормальная работа; 1= сброс
    //Speed: скорость вращения двигателей - уровень, формируемый ШИМ: 0= нулевой уровень; 255= максимальный уровень
    
    //объявление переменных
    unsigned int CMD[2] = {0,0}; //данные от интерфейса: CMD[0]=1 - наличие команды; CMD[1] - данные команды
    unsigned int CTRL[8] = {0,0,0,0,0,0,0,0}; //массив сигналов управления
    //CTRL[0]: 0= нормальная работа; 1= сброс модулей
    //CTRL[1]: 0= ручной режим; 1= автономный режим
    
    void setup() {
      //припиновка
      pinMode(MB1, OUTPUT); digitalWrite(MB1, LOW);
      pinMode(MB2, OUTPUT); analogWrite(MB2, 0);
      pinMode(MA1, OUTPUT); analogWrite(MA1, 0);
      pinMode(MA2, OUTPUT); digitalWrite(MA2, LOW);
      pinMode(Sonar_t, OUTPUT); digitalWrite(Sonar_t, LOW);
      pinMode(Sonar_r, INPUT); digitalWrite(Sonar_r, LOW);
      pinMode(LEDdist, OUTPUT); analogWrite(LEDdist, 0);  
      pinMode(Mode, INPUT); digitalWrite(Mode, LOW);
      pinMode(LEDtech, OUTPUT); digitalWrite(LEDtech, LOW);
      pinMode(HDL, OUTPUT); digitalWrite(HDL, LOW);
      pinMode(SPK, OUTPUT); analogWrite(SPK, 0);
      
      //начальная инициализация модулей
      Serial.begin(UART_Speed);
      _UART_Interf(1, CMD);
      _UART_Interf(0, CMD);
      _Sonar(1);
      _Sonar(0);
      _Motor(1, 0, 0);
      _Motor(0, 0, 0);
      _Rotate(1, 0);
      _Rotate(0, 0);
      _ControlM(1, 0, 0, 0);
      _ControlM(0, 0, 0, 0);
      _ControlL(1, 0, 0, 0);
      _ControlL(0, 0, 0, 0);
      _ControlS(1, 0, 0, 0);
      _ControlS(0, 0, 0, 0);  
    }
    
    
    
    void loop() {
      //опрос кнопки режима и установка режима работы
      if (digitalRead(Mode) == LOW) {
        if (CTRL[1] == 1) { CTRL[0] = 1; } else { CTRL[0] = 0; } //если было переключении из другого режима, то будет выдан reset на все схемы при проходе программы, на след. проходе reset снимется
        CTRL[1] = 0;
      } else {
        if (CTRL[1] == 0) { CTRL[0] = 1; } else { CTRL[0] = 0; }
        CTRL[1] = 1;
      }
    
      //опрос интерфейса, результат будет доступен в D_Interf
      _UART_Interf(CTRL[0], CMD);
      
      //выполнение схемы управления
      //управление моторами
      _ControlM(CTRL[0], CMD[0], CMD[1], CTRL[1]);
      //управление фарами
      _ControlL(CTRL[0], CMD[0], CMD[1], CTRL[1]);
      //управление звуком
      _ControlS(CTRL[0], CMD[0], CMD[1], CTRL[1]);  
      
      delay(Delay_prog);
    }
    
    
    
    //========== Interface module ==========
    void _UART_Interf(unsigned int RST, unsigned int *Data) { 
    
    unsigned int DUART;
    static unsigned int cnt_byte;
    
      if (RST == 0) {
          if (Serial.available() != 0) {
            DUART = Serial.read();
    
            switch (cnt_byte) { //проверка целостности пакета, если хоть один сбой - сбрасываем приём и начинаем поиск заголовка нового пакета
              case 0:
                if (DUART == byte('t')) { cnt_byte++; } else { cnt_byte = 0; }
                Data[0] = 0; Data[1] = 0;
                break;
              
              case 1:
                if (DUART == byte('x')) { cnt_byte++; } else { cnt_byte = 0; }
                Data[0] = 0; Data[1] = 0;
                break;
                
              case 2:
                if (DUART == byte('_')) { cnt_byte++; } else { cnt_byte = 0; }
                Data[0] = 0; Data[1] = 0;
                break;
                
              case 3:
                if (DUART == byte('c')) { cnt_byte++; } else { cnt_byte = 0; }
                Data[0] = 0; Data[1] = 0;
                break;
        
              case 4:
                if (DUART == byte('o')) { cnt_byte++; } else { cnt_byte = 0; }
                Data[0] = 0; Data[1] = 0;
                break;
        
              case 5:
                if (DUART == byte('m')) { cnt_byte++; } else { cnt_byte = 0; }
                Data[0] = 0; Data[1] = 0;
                break;
        
              case 6:
                if (DUART == byte('=')) { cnt_byte++; } else { cnt_byte = 0; }
                Data[0] = 0; Data[1] = 0;
                break;
                
              case 7: //если дошли до конца, то пакет верен и можно выдать команду
                cnt_byte = 0;
                Data[0] = 1; Data[1] = DUART;
                break;
            }
         } else {
           Data[0] = 0; Data[1] = 0;       
         }
      } else {
        cnt_byte = 0;
        Data[0] = 0; Data[1] = 0;
      }
    }
    
    
    
    //========== Sonar module ==========
    unsigned int _Sonar(unsigned int RST) {
    
    unsigned int Duration;
      
      if (RST == 0) {
        digitalWrite(Sonar_t, HIGH); //инициализация работы УЗ датчика
        delayMicroseconds(10);
        digitalWrite(Sonar_t, LOW);
        Duration = pulseIn(Sonar_r, HIGH); //приём данных с УЗ датчика (ширина импульса в мс)
        
        //вывод дальности на светодиод
        if (Duration/58 > D1 && Duration/58 < D2) {
          analogWrite(LEDdist,int((-A*float(Duration/58)+B)));
        } else {
          if (Duration/58 < D1) {
            analogWrite(LEDdist, HIGH);
          } else {
            analogWrite(LEDdist, LOW);
          }
        }
        
        return Duration/58; //выдача расстояния в сантиметрах
      } else {
        digitalWrite(LEDdist, LOW);
        return 0;
      }
    }
    
    
    
    //========== Control Motor module ==========
    void _ControlM(unsigned int RST, unsigned int SCTRL, unsigned int DCTRL, unsigned int Mode) {
      
    unsigned int Dist;
    static unsigned int Speed;
    static unsigned long Time_forward;  
      
      if (RST == 0) {
        
        if (Mode == 0) { //"ручной" режим работы по командам      
          if (SCTRL == 1) { //если получили команду
            switch (byte(DCTRL)) { //расшифровываем команду
              case Byte_forward:
                Dist = _Sonar(0);
                if (Dist > D1) { _Motor(0, M_forward, Speed); } //если можно ехать вперед - едем
                break;
            
              case Byte_back:
                _Motor(0, M_back, Speed);
                break;
    
              case Byte_left:
                _Motor(0, M_left, Speed);
                break;
    
              case Byte_right:
                _Motor(0, M_right, Speed);
                break;
    
              case Byte_stop:
                _Motor(0, M_stop, Speed);
                break;
               
              default:
                break;
            }
        
            if (DCTRL > 47 && DCTRL < 58) { //получение скорости
            Speed = (DCTRL-47)*25+5;
              digitalWrite(LEDtech, HIGH);
              delay(1000);
              digitalWrite(LEDtech, LOW);
            }
          }
        }
        
        if (Mode == 1) { //автономный режим
          Speed = Speed_default;
          Dist = _Sonar(0);
          
          if (Dist > Dist_min) {
            if (millis()-Time_forward < 21000) {
              _Motor(0, M_forward, Speed);
            } else {  //если 20 сек движется только прямо, то нужно сдать назад, т.к. наверное застряли в небольшой комнате.
              _Motor(0, M_stop, Speed);
              delay(300);
              _Motor(0, M_back, Speed);
              delay(600);
              _Motor(0, M_stop, Speed);
              delay(300);
              _Rotate(0, Speed);
              _Motor(0, M_stop, Speed);
              delay(300);
              
              Time_forward = millis()-1; //-1 для гарантии, что millis()-Time_forward будет точно всегда положительное число
            }      
          } else {
            _Motor(0, M_stop, Speed);
            delay(300);
            _Rotate(0, Speed);
            delay(300);
            
            Time_forward = millis()-1;
          }
        }
      } else {
        Dist = 0;
        Speed = Speed_default;
        Time_forward = 0;
    
        _Sonar(1);
        _Motor(0, 0, 0);
        _Rotate(1, 0);
        
        digitalWrite(LEDtech, LOW);
      }
    }
    
    
    
    //========== Control Light ==========
    void _ControlL(unsigned int RST, unsigned int SCTRL, unsigned int DCTRL, unsigned int Mode) {
      
    static unsigned int Light; // 0= фары выключены; 1= фары включены
      
      if (RST == 0) {
        
        if (Mode == 0) { //"ручной" режим работы по командам
          if (SCTRL == 1) { //если получили команду
            switch (byte(DCTRL)) { //расшифровываем команду
              case Byte_light:
                if (Light == 0) {
                  Light = 1;
                  digitalWrite(HDL, HIGH);
                } else {
                  Light = 0;
                  digitalWrite(HDL, LOW);
                }
                break;
    
              default:
                break;
            }
          }
        }
        
        if (Mode == 1) { //автономный режим
          //block operations
        }
      } else {
        Light = 0;
        digitalWrite(HDL, LOW);
      }
    }
    
    
    //========== Control Sound ==========
    void _ControlS(unsigned int RST, unsigned int SCTRL, unsigned int DCTRL, unsigned int Mode) {
        
      if (RST == 0) {
        
        if (Mode == 0) { //"ручной" режим работы по командам
          if (SCTRL == 1) { //если получили команду
            switch (byte(DCTRL)) { //расшифровываем команду
              case Byte_sound:
                //block operations
                break;
      
              default:
                break;
            }
          }
        }
    
        if (Mode == 1) { //автономный режим
          //block operations
        }
      } else {
        analogWrite(SPK, 0);
      }
    }
    
    
    
    //========== Motor module ==========
    void _Motor(unsigned int RST, unsigned int Mode, unsigned int Speed) {
      if (RST == 0) {
        switch (Mode) { //расшифровка команды управления и выполнение действия
          case 0: //stop
            digitalWrite(LEDtech, LOW);
            
            digitalWrite(MB1, LOW);
            analogWrite(MB2, 0);
            
            analogWrite(MA1, 0);
            digitalWrite(MA2, LOW);
            
            break;
        		
          case 1: //forward
            digitalWrite(LEDtech, HIGH);
            
            digitalWrite(MB1, HIGH);
            analogWrite(MB2, 255-Speed);
            
            analogWrite(MA1, Speed);
            digitalWrite(MA2, LOW);
      
            break;
        
          case 2: //back
            digitalWrite(LEDtech, HIGH);
            
            digitalWrite(MB1, LOW);
            analogWrite(MB2, Speed);
            
            analogWrite(MA1, 255-Speed);
            digitalWrite(MA2, HIGH);
            
            break;
        
          case 3: //left
            digitalWrite(LEDtech, HIGH);
            
            digitalWrite(MB1, LOW);
            analogWrite(MB2, Speed);
            
            analogWrite(MA1, Speed);
            digitalWrite(MA2, LOW);
            
            break;
          
          case 4: //right
            digitalWrite(LEDtech, HIGH);
            
            digitalWrite(MB1, HIGH);
            analogWrite(MB2, 255-Speed);
          
            analogWrite(MA1, 255-Speed);
            digitalWrite(MA2, HIGH);
            break;
            		
          default:
            break;
        }
      } else {
        digitalWrite(LEDtech, LOW);
        
        digitalWrite(MB1, LOW);
        analogWrite(MB2, 0);
        
        analogWrite(MA1, 0);
        digitalWrite(MA2, LOW);
      }
    }
    
    
    
    //========== Rotate ========== - алгоритм поворота
    void _Rotate(unsigned int RST, unsigned int Speed) {
      
    unsigned int Dist;
    static unsigned int Num;
    unsigned int cnt;
    unsigned long Now_time;
      
      if (RST == 0) {
        do {
          if (Num%2 == 0) { //определение четности
            if (Num >= 0 && Num < 128) {
              _Motor(0, M_right, Speed);
              delay(100);
              _Motor(0, M_stop, Speed);
              delay(100);
            }
            if (Num >= 128 && Num < 255) {
              _Motor(0, M_right, Speed);
              delay(200);
              _Motor(0, M_stop, Speed);
              delay(100);        
            }
          } else {
             if (Num >= 0 && Num < 128) {
              _Motor(0, M_left, Speed);
              delay(250);
              _Motor(0, M_stop, Speed);
              delay(100);
            }
            if (Num >= 128 && Num < 255) {
              _Motor(0, M_left, Speed);
              delay(150);
              _Motor(0, M_stop, Speed);
              delay(100);        
            }
          }
          cnt++;
    
          Dist = _Sonar(0);
        
        } while (Dist < Dist_min && cnt <= 3); //выходим если дальность разрешенная или если 3 раза провернулись (на след. цикле программы зайдем сюда опять и будем снова крутиться может быть по-другому из-за другого Num)
     
        cnt = 0;
        
        Now_time = millis(); //чтение времени
        while (Now_time > 255) { //ограничение времени
    		  Now_time -= 255;
        }
        Num += Now_time; //получение нового случайного числа: старое+время
        while (Num > 255) { //ограничение нового числа при необходимости
          Num -= 255;
        }
        
      } else {
        Dist = 0;
        Num = 0;
        cnt = 0;
        Now_time = 0;
        _Motor(1, 0, 0);
        _Sonar(1);
      }
    }
    



    Шестой этап — осмысление творения

    Что мы хотели сделать?
    Давайте посмотрим:



    В целом, вроде бы ничего. Ездит. Останавливается. Крутится. Но… не все так хорошо. Робот довольно бодро ездит без «второго этажа», но при установке второго явно «скучнеет». Кроме того, он постоянно забирает влево намекая, что одному в жизни очень плохо вообще-то, и раз уж сотворил, то неплохо было бы озаботиться этим самым… разнообразием видов.

    Подведя итог, можно честно признаться, что развиваться есть куда.

    1) Робот всегда забирает влево при прямолинейном движении. Чем сильнее разряд источника питания, тем отклонение сильнее. Очевидно, это происходит из-за разброса параметров двигателей, возможно — из-за разной работы каналов драйвера двигателей. Для исключения подобного поведения робота необходимо подключать оптические энкодеры и, получая информацию от них, корректировать алгоритм модуля управления двигателями при движении вперед или назад. Кроме того, для источника питания необходимо иметь интегральный стабилизатор напряжения, например, можно перевести питание на пару Li-ION элементов с использованием понижающего преобразователя напряжения.

    2) Робот довольно часто не видит впереди стоящий объект. Это связано с узким углом обзора ультразвукового измерителя расстояния. Очевидно, что точность и надежность определения расстояния до объектов можно повысить, установив два-три УЗ датчика. Для определения расстояния датчики опрашиваются последовательно с небольшими паузами для исключения влияния переотражений УЗ сигналов друг на друга.

    3) Пару раз на прошивке версии 1.0 наблюдалась ситуация зависания алгоритма поворота – робот постоянно крутился и не двигался вперёд, даже если впереди не было препятствия. Если робота принудительно удерживать в свободном направлении, то через некоторое время он начинал нормально работать. Для исключения таких ситуаций необходимо расширить критерии самодиагностики поведения робота с его принудительной переинициализацией в случае возникновения проблем. Для исключения зависания МК необходимо использовать WatchDog.

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

    5) При создании прошивки для МК специфику вносит последовательное выполнение программы. Для того, чтобы отработать команду «вперед», нужно последовательно опросить модули интерфейса, определения дальности, управления и затем вызвать модуль движения. При таком опросе модуль определения дальности вносит задержку до полусотни миллисекунд, тормозя принятие решения по пришедшей команде. В случае чрезмерного разрастания функционала или появления дополнительных модулей дальности задержка может стать неприемлемой. В ПЛИС такой проблемы нет – все блоки в каждый момент времени работают параллельно, т.е. допустимо в один момент времени принимать команду, обрабатывать дальность и выводить её на светодиод, а также выполнять кучу других операций. В МК же параллельное выполнение даже не связанных функций друг с другом невозможно, либо надо постоянно использовать прерывания по внешним событиям, что не всегда удобно. Однако, разработка прошивки для ПЛИС – это, существенно, более трудоёмкая работа, требующая определённой квалификации, т.к. написать код на Си может практически любой программист, а для написания кода на VHDL/Verilog требуется понимание сути работы логики микросхемы.

    6) При удалённом управлении картинка с бортовой камеры слишком сильно изменяется, затрудняя ориентирование в пространстве. Для снижения этого эффекта необходимо применять сглаживание и/или уменьшение окна отображения.

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

    8) При ручном управлении на максимальной скорости при движении вперёд, в случае необходимости остановки и быстром движении назад, робот мог резко наклоняться вперёд, при этом задняя точка опоры отрывалась от земли.

    И как нам теперь быть?

    1) Реализация выдачи звукового сигнала – разработка генератора звуковых сигналов с определённой частотой.
    2) Выравнивание скорости колёс при прямолинейном движении, используя оптические датчики и оптические энкодеры для колёс.
    3) Реализация автоматического включения/отключения фар, используя датчик освещенности.
    4) Повышение надёжности определения расстояний до впереди стоящих объектов путём увеличения УЗ датчиков до трёх.
    5) Организация питания на базе двух Li-Ion аккумуляторов типоразмера 18650 и интегрального стабилизатора напряжения.
    6) Разобрать и смазать редукторы двигателей силиконовой смазкой для устранения скрипов.
    7) Оценить увеличение питающего напряжения до 6,0-7,5В. При этом учесть, что придётся пересчитывать и перепаивать резисторы для светодиодов, чтобы не допустить превышения максимального выходного тока вывода МК.
    8) При резкой смене состояния (начало движения или остановка) реализовать плавное изменение скорости движения, функция изменения может быть линейной или экспоненциальной.

    Седьмой этап — развитие

    go to «Второй этап»




    Ну, вот, мы добрались и до финала всей истории.

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

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

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

    Удачи в роботостроении!
    Не бойтесь начинать.
    Поделиться публикацией

    Комментарии 10

      +4
      Сам сейчас тоже начинаю заниматься arduino и тп.
      Прочитал статью по диагонали. Больше интересовал код.
      Посмотрел код и хочется сразу дать пару советов.

      Лучше уйти от конструкции delay().
      Особенно учитывая что даже функция замера расстояния в теории может длиться до 400мс, то данная конструкция приводит к тому что мы можем реально проводить замеры и другие функции довольно большое время.
      Тот же датчик измерения расстояния можно повесить на прерывание.
      Я не смотрел как реализованы функции управления моторами в arduino, но если опять же мы хотим на время поворота не стоять в цикле и ждать пока он произойдет а делать полезную работу, то лучше аналогично перейти на прерывания. В интернете есть статьи как это реализовать.

      А так, со стороны кажется что все просто. Поставил датчик и он тебе показывает измерения. И вроде как конструктор, но реальный мир накладывает свои ограничения. Может кто-нибудь подскажет УЗ датчик расстояния с лучшими характеристиками диаграммы направленности чем HC-SR04. Желательно с хорошим соотношением цена/качество.
        +2
        Именно ультразвуковой? С УЗ надо еще поразбираться, т.к. если в доме есть, например, кот или собака — как они будут на это реагировать? Если несущая 40кГц — не будет ли это плохо для животных?
        Я прицеливался еще к инфракрасному датчику Sharp (например, GP2Y0A21YK). Если заказывать на али/ебей, то цена вполне нормальная будет. Да и с него гораздо проще данные снимать — просто оцифровываем уровень напряжения с него и всё.
          0
          ИК под мои задачи не подходит.
          Необходимо измерять уровень воды в баке.
          Поэтому УЗ.
            0
            Да и с него гораздо проще данные снимать — просто оцифровываем уровень напряжения с него и всё.

            Не все так просто, нужно стабилизировать питание датчика, а иначе показания этого датчика будут плясать вместе с питанием.

          +2
          5) При создании прошивки для МК специфику вносит последовательное выполнение программы...

          Для «распараллеливания» задач нужно смотреть в сторону RTOS и, в частности, сюда можно заглянуть.
            0
            А если поставить датчики магнитного поля а на колёсах закрепить магниты, вероятно получится получить обратную связь от управляемых объектов достаточную для прямого хода?
              0
              Оптические энкодеры имеют больше разрешение, да и диски энкодеров расчитаные для этих моторов, уже идут в комплекте с платформой
              image
                0
                Спасибо за картинку и информацию :-) Видимо магнитные 3D сенсоры более подходят для размеров настоящих автомобилей.
                Так же можно на них делать виртуальные ручки управления для AR.
                0
                Да, колесо энкодера уже идёт в комплекте. Ось из редуктора торчит с двух сторон. С одной стороны одевается колесо, с другой — как раз аккуратно колесо энкодера. Как это ни удивительно, но почему-то оптических датчиков в магазине не было. Поэтому надо заказывать нечто типа этого и как-то творчески это дело монтировать)
                  0
                  В «Чип и дипе» можно купить. Называется TCST2103
                  И напильником не придется пользоваться :)

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

              Самое читаемое