пятница, 26 декабря 2008 г.

Роботы-ходуны

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

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

Дак вот, пока я делаю этого ходуна, вообще роботами класса ходунов заинтересовался не на шутку. И там есть, ЕСТЬ, чем интересоваться! Для примера, приведу трех замечательных ходунов:

Под первым номером - робот-паук от Vovan'а с РобоФорума. К самому Vovan'у я отношусь прохладно, однако проект у него прекрасный. Достаточно посмотреть короткое видео:

Быстрый паучок, классный, не правда ли? Так и хочется улыбнуться при его виде:) Для исполнения нужны всего лишь: пара одинаковых моторов, десяток шестерней, материал для корпуса типа пластика, оргстекла, или тонкого железа, инструмент для обработки - распилки, резки, сверления и т.п. данного материала. Батарейки. Проводки. Всякие там болтики-гаечки, уголки... Вроде, и все. Уверен, для такой красоты эти материалы вполне можно попытаться достать. Схемы, фотографии, обсуждение, возможности усовершенствования конструкции, и т.д. - вы найдете в его теме на форуме. Тема была создана еще в ноябре 2007 года, поэтому содержит массу интересного. Да, кстати, в случае, если шестерней у вас не хватает - всегда можно изготовить сколько угодно копий уже имеющихся (а там шестерней я разглядел всего два вида - одна большая, одна маленькая), для этого нужно просто прочитать тему от Grover'а на том же РобоФоруме.

Второй ходун, на который я обратил внимание - тоже шестиног, но более законченный. К тому же, он еще умеет обходить препятствия. Да и в строительстве - чуть ли не проще, нежели чем предыдущий! Принцип передвижения у него, правда, немного другой, ходит он медленнее, и как-то немного неуклюже, "на цыпочках". Что же, всегда можно немного изменить первоначальный проект... :)

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

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

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

среда, 24 декабря 2008 г.

Microsoft Robotics Studio. Sumo-робот

Продолжаю серию статей про Robotics Studio.

В этой статье я расскажу, как сделать собственного Sumo-робота для соревнований симуляций сумо-роботов от Microsoft. Для этого, помимо установленной Robotics Studio (у меня - версия 1.5), необходимо скачать проект сумо-симуляции с сайта Microsoft.

Скачав, распакуйте zip-архив, и запустите файл Sumo Competition for Microsoft Robotics Studio (1.5).exe. В конце быстрой и простой установки откроется html-инструкция по использованию данного продукта.

Инструкцию можете закрывать, я вам итак все расскажу. Сначала нужно запустить командную строку Robotics Studio (эта "функция" доступна через меню Пуск). Затем выполните команды cd bin (переход в каталог [bin]), и makesumoplayer /name:имя-вашего-сумо-робота /forse:true. Я своего назвал просто: MySumo. В имени должны присутствовать только английские буквы и цифры - без пробелов. Вы, наверное, сможете придумать что-нибудь более оригинальное :) Вот что видим в ответ:

Итак, новый проект создан. Путь, где его можно отыскать, указан. Идем туда, открываем файл проекта. В моем случае этот файл называется MySumo.csproj.

Мне потребовалось сконвертировать проект под 2008-ю студию, т.к. создан он под какую-то более древнюю версию. Но это пара кликов мышью, ничего сложного... В итоге видим:

Итак, проект открыт - и теперь начинается самое интересное. Для начала, просто запускаем проект, нажимая F5. Выбираем в качестве соперника стандартную симуляцию (sumoplayer):

Между прочим, у вас наверняка возник вопрос: как сделать своему роботу особенную картинку? Все очень просто: нужна BMP-картинка 64х64 пиксела. Скопировать ее нужно в папку [Resources] внутри проекта, заменив файлик PlayerImage.bmp.

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

  1. Если один робот, оказывается к другому роботу задом, - все, первый почти гарантированно проигрывает.
  2. Иногда робот сам усугубляет свое положение, оказываясь близко к линии в момент атаки. Логично было бы держаться центра ринга как можно дольше.

Возможно, у вас возникнут собственные выводы по этому поводу. Главное - определиться, что из поведения робота необходимо изменить.

Как только понимание сути изменений достигнуто - можно приступать к следующему этапу: разбору кода. Код сгруппирован в несколько групп с помощью директив #region и #endregion. Нас будут интересовать только две группы: Sensor Handlers to be copied, и Timer Handler to be copied.

Как не сложно догадаться, первая группа отвечает за обработку событий от сенсоров, в то время как вторая - это таймер. В первой группе задается ПЕРВАЯ реакция робота. Например: увидел линию? - развернись на 130 градусов. Или: увидел соперника через камеру? - начни сближение.

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

Сложно? Да нет! Это только так кажется. В любом случае, привыкайте, такова стандартная событийная модель программирования.

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

  • _state.SumoMode == SumoMode.Contact - состояние, когда роботы столкнулись друг с другом и находятся в противоборстве. Основная проблема этого состояния состоит в том, кстати, что робот физически не может определить касание сзади - у него там нет датчика касания.
  • _state.SumoMode == SumoMode.AvoidBoundary - состояние, в котором робот старается избежать попадания за границу ринга.
  • _state.SumoMode == SumoMode.Tracking - робот засек противника камерой и едет за ним.
  • _state.SumoMode == SumoMode.Wander - режим свободного поиска противника.

Есть также и некоторые другие состояния робота, не столь критичные. Если вам понадобятся их описания, весь список присутствует в файле MySumoTypes.cs (ИмяВашегоСумоистаTypes.cs), при описании enum'а SumoMode.

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

Начнем с простого: как заставить робота держаться центра ринга? Очень просто. Надо заставить его просто крутиться на месте, не давая при этом двигаться вперед. В таком случае, качество поиска измениться не сильно, зато подставляться наш "боец" будет меньше.

Очевидно, что нам необходимо изменить действия робота в режиме свободного поиска. Поискав "SumoMode.Wander" в коде (Ctrl-F в MS Visual Studio), находим процедурку SetWanderDrive(), для которой написано в комментариях: This is the default drive configuration for wander mode. То есть, эта процедура назначает действия по умолчанию для робота, переходящего в режим свободного поиска.

Она состоит всего из двух строк, в первой изменяем текущее состояние робота на Wander, а вот во второй вызываем пока непонятную процедурку InternalDrivingMilliseconds().

Что же, жмем по названию этой процедуры правой кнопкой, выбираем в меню Go to definion. Смотрим заголовок процедуры:

public void InternalDrivingMilliseconds(int left, int right, double ms)

Что же, все понятно. Эта процедура задает скорости для двух моторов - левого, и правого - на определенное число миллисекунд (которое тоже задается). Скорости моторов, насколько я понял из кода, варьируются от -500 до +500, а число миллисекунд можно задать какое угодно, лишь бы больше нуля, естественно :)...

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

Вот что получилось у меня в итоге:

private void SetWanderDrive()
{
   _state.SumoMode = SumoMode.Wander;
   InternalDrivingMilliseconds(0, 400, 250.0);
}

Запускаем, смотрим: все именно так, как нужно. Наш робот вертится на месте, а его противник бегает туда-сюда. Правда, для весомого преимущества этого недостаточно.

Давайте попробуем воплотить вторую мою мысль.

Нам необходим фрагмент кода внутри процедуры RobotUpdateFloorSensorsHandler, содержащий текст "SudoMode.AvoidBoundary". Этот фрагмент отвечает за реакцию робота на засечение им линии впереди. Поскольку сам собой наш робот не движется, то если он засек линию - это может означать только лишь тот факт, что его толкает сзади враг. А значит, нужно срочно на всех парах мчаться назад! Ничего особенного, кроме уже известной нам процедуры InternalDrivingMilliseconds, в этом фрагменте нет. Вот что получилось у меня в итоге:

if (_state.Sensors.LineDetected)
{
  _state.SumoMode = SumoMode.AvoidBoundary;
  LogVerbose(LogGroups.Console, "Sumo Mode: AvoidBoundary");
  if (_state.Sensors.LineLeft && 
     !_state.Sensors.LineRight && 
     !_state.Sensors.LineFrontRight)
    InternalDrivingMilliseconds(-100, -400, 200.0);
  else if (_state.Sensors.LineRight && 
          !_state.Sensors.LineLeft && 
          !_state.Sensors.LineFrontLeft)
    InternalDrivingMilliseconds(-400, -100, 200.0);
  else
    InternalDrivingMilliseconds(-500, -500, 400.0);
}

Запускаем, лучше всего штуки три матча подряд. У меня статистика трех матчей получилась такая: 2 раунда проиграно, 1 ничья, 6 раундов выиграно. Думаю, комментарии излишни :)

P.S. На написание данной статьи меня сподвиг Дмитрий Калинин (kalisha), за что ему огромное спасибо. Он прислал собственную статью, на тему создания робота для соревнований Microsoft ImagineCup, однако эта статья потребовала настолько глобальной переделки, что я ее полностью переписал, полностью сам разобрался в коде, адаптировал его под стандартную сумо-арену, ну и т.д. Очень надеюсь, что следующую статью Дмитрия я смогу выложить в менее "отредактированном" варианте :)

воскресенье, 21 декабря 2008 г.

Робот и микроконтроллер. COM-порт

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

Такая связка, как правило, производится через COM-порт. Все, кто с этим когда-либо связывался, наверняка знают, что у COM-порта компьютера, и у UART микроконтроллера - разные уровни сигналов логических "0" и "1". Для их согласования используют микросхему MAX232, или ее аналоги. У меня в городе микросхем продается крайне мало, да и лишней пайки всегда стараюсь избежать - поэтому я сразу же пошел по альтернативному пути. А именно, использовал для согласования дата-кабель от мобильного телефона.

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

Итак, вот что у нас есть вначале:

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

Путем поиска в интернете удалось обнаружить, что два проводка посередине - это как раз нужные мне TxD и RxD. К одному подпаялся прямо на разъеме, а ко второму - на небольшом отдалении, зачистив изоляцию на проводке:

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

Закрыл крышку, получилась вот такая вот красота:

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

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

Думаю, данный рецепт вполне подойдет и для большинства других дата-кабелей. Главное, проверить, что кабель для подключения к COM-порту использует сигналы ttl-логики со стороны разъема телефона.

Напоминаю: нулю в TTL-логике соответствует напряжение до 0,4В, единице от 2,5В. В то же время в стандарте RS232 ноль - это от +5В до +15В, единица - от -15В до -5В, причем от -5В до +5В считается неопределённым состоянием.

Программирование COM-порта, как со стороны КПК/компьютера, так и со стороны микроконтроллера, обсудим в следующий раз. До скорых встреч!