четверг, 31 декабря 2015 г.

С Новым годом!

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

суббота, 26 декабря 2015 г.

"Охота на Вампуса" из EV3 Home #31313

Мы переработали программу и конструкцию в нашем проекте EV3 "Охота на Вампуса" и теперь его можно собрать из домашнего набора EV3.


Скачать инструкцию по сборке и программу можно здесь.

Датчики подключаются следующим образом:
  • Порт 1. ИК-датчик отслеживает включенный маяк, это "направление вашего взгляда" в мире игры, поворот разворачивает персонажа
  • Порт 3. Датчик-кнопка используется для выстрела (для указания команды выстрела и последующего спуска тетивы после указания маршрута стрелы)
  • Порт 2. Цвето-световой датчик в режиме кнопки служит для перемещения "в направлении взгляда"

Мотор для спуска тетивы - порт А.




понедельник, 21 декабря 2015 г.

Решение задачи 14: Поиск места под солнцем

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


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

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

На псевдокоде решение задачи выглядит так:

// L - Освещенность (яркость внешнего освещения)
L = ДАТЧИК(ОСВЕЩЕННОСТЬ)
// T - смена направления поиска
T = ИСТИНА
ЦИКЛ (ВСЕГДА)
  ЕСЛИ (T = ИСТИНА)
    // ВЫБИРАЕМ НАПРАВЛЕНИЕ ПОВОРОТА
    N = СЛУЧАЙНОЕ_ЧИСЛО (2)
    ЕСЛИ (N = 1) МОТОРЫ(ПОВОРОТ_ВЛЕВО)
    ЕСЛИ (N = 2) МОТОРЫ(ПОВОРОТ_ВПРАВО)
  // ИЗМЕРЯЕМ ОСВЕЩЕННОСТЬ В НОВОЙ ОБЛАСТИ
  МОТОРЫ (ВПЕРЕД_НА_ДЛИНУ_РОБОТА)
  МОТОРЫ (СТОП)
  ЕСЛИ (ДАТЧИК(ОСВЕЩЕННОСТЬ) > L)
    L = ДАТЧИК(ОСВЕЩЕННОСТЬ)
    T = ЛОЖЬ
  ИНАЧЕ
    МОТОРЫ (НАЗАД_НА_ДЛИНУ_РОБОТА)
    МОТОРЫ (СТОП)
    Т = ИСТИНА
КОНЕЦ_ЦИКЛА

суббота, 19 декабря 2015 г.

EV3 Hunt the Wumpus. Игра "Охота на Вампуса"


Кто такой Вампус и зачем на него охотиться?




Hunt the Wumpus («Охота на Вампуса») — классическая текстовая компьютерная игра, написанная Грегори Йобом в 1972 году. Цель этой игры заключалась в том, чтобы, путешествуя по лабиринту, найти Вампуса и застрелить его. Часто саму игру называют просто «Вампусом». Вампус — фантастический большой и опасный монстр, обитающий в пещерах. Вампус источает отвратительный запах, имеет крайне чувствительный слух и питается людьми. Известно также, что Вампус очень тяжёл, ноги его имеют присоски, а сам он большую часть времени проводит в спячке.



Мир «Hunt the Wumpus» — это пещера из 20 пронумерованных комнат, каждая из которых соединена тоннелями с тремя другими, т. е. пещера представляет собой расплющенный додекаэдр.



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

Игрок имеет арбалет с пятью стрелами. Для убийства Вампуса достаточно и одной стрелы. Если стрела попала в Вампуса, то игрок победил и игра окончена. Стрелы у игрока не простые, а волшебные — они могут делать повороты во время полёта. Каждая стрела может пролететь 5 соседних комнат.

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




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

Игрок проигрывает и в том случае, если он истратит все 5 стрел, но так и не убьёт Вампуса. Попав в какую-либо комнату, игрок мог почувствовать отвратительный запах, услышать шум или почувствовать сквозняк.


Источником отвратительного запаха является спящий Вампус. Запах чувствуется, если Вампус находится в какой-то из соседних комнат. Сквозняк и шум всегда доносятся из соседней комнаты, но неизвестно, из какой именно. В комнатах, откуда идёт сквозняк, находятся бездонные ямы, попав в которые, игрок погибает и, соответственно, проигрывает. Из 20 комнат в пещере две имеют ямы.

В комнатах, откуда доносится шум (таких комнат в пещере тоже две), обитают гигантские летучие мыши(«super bats»). Если игрок зашёл в комнату к этим мышам, то они хватают его и случайным образом переносят в любую другую комнату в пещере. В том числе, не исключён вариант, что мыши перенесут игрока в комнату с ямой или сбросят его на голову Вампусу.

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

Если игрок окажется в одной комнате с Вампусом, то он проиграл — Вампус съедает его. Если же Вампус проснулся от звуков выстрела в пещере, то у игрока ещё есть шансы.
Проснувшись, Вампус может либо заснуть снова (с вероятностью 0,25), либо перейти спать в одну из соседних комнат. Кстати, комнаты с ямами и летучими мышами Вампусу не страшны. У него на ногах есть присоски, с помощью которых он выбирается из любой ямы, а его огромный вес не даёт гигантским летучим мышам его переносить. Если Вампус отправляется спать в комнату, где находится игрок, то игрок проигрывает, а Вампус получает обед.

Из истории игры


«Охота на Вампуса» — первая в истории текстовая, а также первая приключенческая игра. Она была чрезвычайно популярна в 1970—1980-е годы. Программистам эта игра так полюбилась, что, пользуясь её размером (около 200 строк кода, половину из которых занимают вывод на экран и комментарии), они встраивали её в различные программы. Известно, что в корпоративной операционной системе R&D была системная команда «to Wumpus», запускавшая игру.



Позже появились и графические версии этой игры, отличающиеся от оригинальной другими лабиринтами и ловушками. В настоящее время версии «Hunt the Wumpus» доступны в Интернете для большинства операционных систем и устройств, включая GNU/Linux, Commodore, Sinclair ZX Spectrum,SunOS, Macintosh, Palm Pilot и мобильные телефоны.



EV3 версия игры


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



Для сборки мы использовали робототехническую платформу LEGO Mindstorms EV3 в ее образовательной версии. Вам понадобятся все 4 датчика из набора и большой мотор. Датчики используются следующим образом:

  • Порт 1. Гироскоп отслеживает "направление вашего взгляда" в мире игры и разворачивает персонажа
  • Порт 3. Датчик-кнопка используется для выстрела (для указания команды выстрела и последующего спуска тетивы после указания маршрута стрелы)
  • Порт 2. Датчик-кнопка служит для перемещения "в направлении взгляда"
  • Порт 4. Цвето-световой датчик подсвечивает события в игре (синий - все спокойно, красный - рядом опасность)


Для того, чтобы вы смогли собрать игру мы подготовили пошаговую инструкцию по сборке, ее можно скачать по ссылке. Для просмотра необходима программа LEGO Digital Designer или Adobe Reader. Не иатягивайте тетиву слишком туго, детали LEGO могут не выдержать такой нагрузки. Для тетивы арбалета лучше использовать эластичную нитку.


Программу вы также можете скачать по ссылке. Озвучка игры произведена генератором голоса с сайта www.ivona.com



Обратите внимание, что игра достаточно объемна и Вам возможно придется очистить всю память блока, чтобы она смогла в ней уместиться, либо использовать карту microSD.

В момент запуска программы рекомендуем расположить арбалет на ровной горизонтальной поверхности и не трогать его в первые 2-3 секунды. В это время производится устранение дрейфа гироскопа.

Карта пещеры взята нами из классической игры и имеет следующий вид:


Для удобства ориентации на карте Вы можете склеить себе 3D-модель додекаэдра и подписать на нем номера комнат.




Как играть в игру?


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

После этого происходит расстановка персонажей на карте и начинается игра. Первый ход - игрока. Вы оказываетесь в одной из комнат пещеры, номер комнаты Вы  услышите и увидите на экране. Кроме этого появится и будет произнесена информация о номерах комнат, в которые Вы видите проходы и о возможных опасностях.
На каждом из ходов Вы должны выбрать - на что потратите свой ход - на движение или выстрел. Чтобы перейти в соседнюю комнату - однократно нажмите на кнопку под большим пальцем. После этого нужно выбрать в какую из трех соседних комнат вы пойдете. Для этого нужно развернуться на нее (вы услышите и увидите на экране подсказки, в какую комнату ведет проход перед вами) и снова нажать кнопк движения.
Если вместо движения Вы решите выстрелить - нужно нажать на кнопку выстрела. После этого нужно указать 5 последовательных комнат, через которые полетит стрела, разворачиваясь на них и нажимая движение. Затем нажать на выстрел. Можно выстрелить ближе, чем на 5 комнат, нажав ан выстрел на любом из 5 шагов планирования маршрута стрелы.
Вы можете выиграть только одним способом - попасть стрелой в Вампуса, а вот погибнуть у игрока - целых семь способов:
1) зайдя в комнату, где спит Вампус
2) от неудачного выстрела Вампус может проснуться и пойти Вас искать - если он зайдет в комнату, где вы сейчас находитесь - Вы проиграли
3) Вы можете провалиться в одну из двух бездонных ям
4) Летучие мыши могут схватить Вас и сбросить в комнате, где спит Вампус
5) У Вас могут закончиться стрелы
6) Летучие мыши могут сбросить Вас в бездонную яму
7) Вы можеет попасть стрелой сами в себя

Как выглядит наша программа?


Программу мы написали в среде LEGO EV3-G. Так как EV3-G не поддерживает многомерные массивы, нам пришлось сделать "эмуляцию" двумерного массива с помощью пользовательских блоков. При желании вы можете посмотреть в программе как это реализовано.


Голосовое вступление, как и прочая игровая озвучка, записано с помощью генератора голоса IVONA-MAXIM:



В массиве L мы храним начальное (и последующее на время игры) положение игрока (ячейка 1), Вампуса (ячейка 2_, бездонных ям (3 и 4) и летучих мышей (5 и 6).
В "двумерном" массиве S - карту пещеры, каждая строка содержит три номера комнат, с которыми связана комната с номером, соответствующим номеру данной строки.
В массиве arrows будем хранить маршрут стрелы во время планирования выстрела.



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



Блок INFO оповещает игрока об опасностях - наличии в соседних комнатах Вампуса, бездонных ям и летучих мышей: выводом на экран, голосом и цветом подсветки датчика.



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



Выбрав команду "выстрел" игрок запускает блок планирования маршрута стрелы. Полет стрелы может пролегать через 1..5 комнат, в которых проверяется совпадение с комнатами Вампуса и игрока.



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

// массив S содержит карту пещеры
МАССИВ S(20,3) = {2,5,8,
                  1,3,10,
                  2,4,12,
                  3,5,14,
                  1,4,6,
                  5,7,15,
                  6,8,17,
                  1,7,9,
                  8,10,18,
                  2,9,11,
                  10,12,19,
                  3,11,13,
                  12,14,20,
                  4,13,15,
                  6,14,16,
                  15,17,20,
                  7,16,18,
                  9,17,19,
                  11,18,20,
                  13,16,19}

// массив L с номерами комнат 1я ячейка игрок , 2 = Вампус, 3 & 4 = ямы, 5 & 6 = летучие мыши
МАССИВ L(6) 

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

ПЕЩЕРА = 0  - начальная пещера
ЦИКЛ_ПОКА ПЕЩЕРА < 6
  НОМЕР = СЛУЧАЙНОЕ_ЧИСЛО(20) 
  // проверяем, нет ли в ней кого-то
  // триггер 1=комната открыта, 0=комната используется
  ОК = 1  
  СЧЕТЧИК i ОТ 1 ДО ПЕЩЕРА
    // если истина - комната используется
    ЕСЛИ НОМЕР=L(i) ТОГДА ОК = 0 
  СЛЕДУЮЩИЙ i
  // если комната свободна, заполняем ее
  ЕСЛИ ОК=1 ТОГДА   
    ПЕЩЕРА = ПЕЩЕРА + 1
    L(ПЕЩЕРА) = НОМЕР    
КОНЕЦ_ЦИКЛА_ПОКА

// количество стрел у игрока
A=5 
// номер комнаты, где сейчас игрок
L=L(1) 
// F - исход игры
F = 0   

// НАЧАЛО ОСНОВНОГО ИГРОВОГО ЦИКЛА

ЦИКЛ_ПОКА (F=0)

  БЛОК(ИНФО)
  ЖДАТЬ НАЖАТИЯ КНОПКИ (2 ИЛИ 3)
  // Выбор Идти или стрелять
  ЕСЛИ НАЖАТА КНОПКА(2) ТОГДА БЛОК (ВЫСТРЕЛ)
  ЕСЛИ НАЖАТА КНОПКА(3) ТОГДА БЛОК (ИДТИ)

КОНЕЦ_ЦИКЛА_ПОКА

// КОНЕЦ ОСНОВНОГО ИГРОВОГО ЦИКЛА

ЕСЛИ F=1 ТОГДА СКАЗАТЬ "Вы победили!"
ЕСЛИ F=-1 ТОГДА СКАЗАТЬ "Вы вошли в комнату Вампуса и он Вас съел"
ЕСЛИ F=-2 ТОГДА СКАЗАТЬ "Вы попали стрелой сами в себя и проиграли"
ЕСЛИ F=-3 ТОГДА СКАЗАТЬ "Вы потратили все стрелы, но так и не убили Вампуса, Вы проиграли"
ЕСЛИ F=-4 ТОГДА СКАЗАТЬ "Вы провалились в бездонную яму и проиграли"
ЕСЛИ F=-5 ТОГДА СКАЗАТЬ "Ваш выстрел разбудил Вампуса, он пошел Вас искать, нашел и съел"
ЕСЛИ F=-6 ТОГДА СКАЗАТЬ "Летучие мыши сбросили Вас прямо на Вампуса и он Вами пообедал"
ЕСЛИ F=-7 ТОГДА СКАЗАТЬ "Летучие мыши сбросили Вас прямо в бездоннуя яму. Вы проиграли"

КОНЕЦ_ПРОГРАММЫ

// далее - подпрограммы

БЛОК (ИНФО)
  СЧЕТЧИК J ОТ 2 ДО 6
    СЧЕТЧИК K ОТ 1 to 3
      ЕСЛИ S(L(1),K)=L(J) ТОГДА
        ЕСЛИ J = 2 ТОГДа СКАЗАТЬ "Я чувствую запах Вампуса!"
        ЕСЛИ J = 3 ИЛИ J = 4 ТОГДА СКАЗАТЬ "Я чувствую сквозняк!"
        ЕСЛИ J = 5 ИЛИ J = 6 ТОГДА СКАЗАТЬ  "Я слsшу летучих мышей!"      
    СЛЕДУЮЩИЙ K
  СЛУДУЮЩИЙ J
  СКАЗАТЬ "Вы находитесь в комнате ";L(1)
  СКАЗАТЬ "Туннели ведут к комнаты " S(L,1),S(L,2),S(L,3)
  Print

БЛОК (ВЫСТРЕЛ)
  L=L(1)
  СКАЗАТЬ "У Вас осталось", A, "стрел"
  СКАЗАТЬ "Выберите 5 комнат, связанных общим тоннелем"

  // массив с маршрутом стрелы
  Arrow = {0,0,0,0,0,0}

  Arrow(1) = L(1)

  P = 1
  ЦИКЛ_ПОВТОРЕНИЯ (5)
    ЕСЛИ НЕ (F = 0) ТОГДА ВЫХОД_ИЗ_ЦИКЛА
    P = P + 1
    ЖДАТЬ НАЖАТИЯ КНОПКИ (2 ИЛИ 3)
    // Выбор маршрут или стрелять
    ЕСЛИ НАЖАТА КНОПКА(2) ТОГДА ВЫХОД_ИЗ_ЦИКЛА
    ЕСЛИ НАЖАТА КНОПКА(3) ТОГДА 
      Arrow(P) = S(Arrow(P-1),БЛОК (ГИРОСКОП))
    // вы можте попасть стрелой в себя
    ЕСЛИ Arrow(P) = L(1) ТОГДА F=-2
    ЕСЛИ Arrow(P) = L(2) ТОГДА F=1
  КОНЕЦ_ЦИКЛА
    А = А - 1
    ЕСЛИ F=0 ТОГДА СКАЗАТЬ "Выстрел был неудачный, Вы не попали в Вампуса"
    ЕСЛИ СЛУЧАЙНОЕ_ЧИСЛО(100) > 75 ТОГДА
      L(2) = S(L(2),СЛУЧАЙНОЕ_ЧИСЛО(3))
      ЕСЛИ L(2) = L(1) ТОГДА F=-5
    ЕСЛИ А = 0 ТОГДА F=-3

БЛОК (ИДТИ)

  ЦИКЛ (ВСЕГДА)
    СКАЗАТЬ "Вы можете перейти в комнаты с номерами" S(L(1),1), S(L(1),2), S(L(1),3)
    ЕСЛИ НАЖАТА КНОПКА(3) ТОГДА ВЫХОД_ИЗ_ЦИКЛА
  КОНЕЦ_ЦИКЛА

  L(1) = S(L(1),БЛОК (ГИРОСКОП))

  ЕСЛИ L(1) = L(2) ТОГДА F=-1
  ЕСЛИ L(1) = L(3) ИЛИ L(1) = L(4) ТОГДА F=-4
  ЕСЛИ L(1) = L(5) ИЛИ L(1) = L(6) ТОГДА
    L(1) = СЛУЧАЙНОЕ_ЧИСЛО(20)
    СКАЗАТЬ "Летучие мыши схватили Вас и перенесли в комнату", L(!)
    ЕСЛИ L(1) = L(2) ТОГДА F = -6
    ЕСЛИ L(1) = L(3) ИЛИ L(1) = L(4) ТОГДА F = -7

БЛОК (ГИРОСКОП)
  G = СЕНСОР(ГИРОСКОП) + 36000 % 360
  ЕСЛИ G >= 0 И G <=120 ТОГДА ВЕРНУТЬ(1)
  ЕСЛИ G > 120 И G <=240 ТОГДА ВЕРНУТЬ(2)
  ЕСЛИ G > 240 И G <=359 ТОГДА ВЕРНУТЬ(3)




понедельник, 14 декабря 2015 г.

Решение задачи №11: Разворот на центр объекта

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


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

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



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

Пусть моторы подключены к портам В и С. На псевдокоде программа c использованием энкодера будет выглядеть следующим образом:

МОТОРЫ (50,-50)
ЖДИ (РАССТОЯНИЕ < 50)
А = ЭНКОДЕР (В)
ЖДИ (РАССТОЯНИЕ > 50)
B = ЭНКОДЕР (В)
МОТОРЫ (СТОП)
С = (B -A) / 2
МОТОРЫ_НА)ГРАДУСЫ(-50,50,С)

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

A = ТАЙМЕР (1)
МОТОРЫ (50,-50)
ЖДИ (РАССТОЯНИЕ < 50)
B = ТАЙМЕР (1)
ЖДИ (РАССТОЯНИЕ > 50)
C = ТАЙМЕР (1)
МОТОРЫ (СТОП)
МОТОРЫ(50,-50)
ЖДИ (С-A)
МОТОРЫ (СТОП)
D = (B - A) + (C -B) / 2
МОТОРЫ (50,-50)
ЖДИ (D)
МОТОРЫ (СТОП)

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

понедельник, 7 декабря 2015 г.

Решение задачи №8: Заводной робот

Детская заводная игрушка РОБОТ, выпуска 70-80гг. решала нашу восьмую задачу без всякого сложного программирования и даже не обладая никакой электроникой.



Но так как мы с вами учимся "робототехнировать" %),  для решения задачи нам потребуется собрать 3 моторного робота - два мотора для движения и мотор-"заводной ключ" для "завода пружины". Поворачивая вал третьего "мотора с ключом" мы будем заводить воображаемую пружину.


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

Алгоритм решения данной задачи приведен ниже. 

// Робот начнет движение, если его "завести"
// и не трогать 1 секунду
// Переменная С равна 1 пока мы "заводим" робота
С = 1

ЦИКЛ_ПОКА (C равно 1)
  {
  // В переменные E1 и E2 записываем показания
  // с датчика оборотов с разницей в 1 секунду
  E1 = ДАТЧИК_ОБОРОТОВ(В)
  ЖДАТЬ (1 сек)
  E2 = ДАТЧИК_ОБОРОТОВ(В)
  ЕСЛИ (E2 равно E1) 
    {
    // Робота перестали "заводить", выходии из цикла
    С = 0
    }
  }

// запускаем моторы B и C на E2 градусов вперед
МОТОРы (ВС, E2)


пятница, 4 декабря 2015 г.

Решение задачи №2. Длинные и короткие нажатия

Продолжаем публикацию решений к нашим "Задачкам по робототехнике". Давайте решим вторую задачу, в ней речь идет о работе с датчиком-кнопкой:

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



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

' Устанавливаем режим работы датчика касания
Sensor.SetMode(1,0)

' Переменные K и D - количество длинных и коротких нажатий
K = 0
D = 0

' Далее начинаем бесконечный цикл
While "True"
  ' Ждем пока датчик не будет нажат
  While Sensor.ReadPercent(1) = 0
  EndWhile

  ' Запоминаем в переменной А время когда нажали кнопку
  A = EV3.Time

  'Ждем пока датчик не отпустят
  While Sensor.ReadPercent(1) = 100
  EndWhile

  ' Запоминаем в переменной В время отпускания кнопки
  B = EV3.Time

  ' Вычисляем время удерживания кнопки нажатой
  X = B - A
  
  ' Если удерживаали долее 500мс - нажатие длинное
  If X > 500 Then
    D = D + 1
  Else
    K = K + 1
  EndIf

  ' Выводим данные на экран
  LCD.Clear()
  LCD.Write(0, 0, "short:")
  LCD.Write(0, 32, K)
  LCD.Write(0, 64, "long:")
  LCD.Write(0, 96, D)

EndWhile

воскресенье, 29 ноября 2015 г.

Решение задачи №1. Проехать заданное расстояние за определенное время

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


Казалось бы - что тут сложного? Дели расстояние на время и получай скорость. Если Вы попробуете подобрать мощность на моторах таким образом, чтобы робот проезжал заданное расстояние за определенное время то обнаружите, что и робот движется не с постоянной скоростью (ему нужно время на разгон) и поверхность пола/стола не идеальна и от заряда батареек зависит мощность, причем нелинейно. 

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

// пусть роботу нужно проехать 2 м за 35 секунд
R = 2000
T = 35
pi = 3.14
// Размер колеса робота, мм
W = 56
// Рассчитываем, сколько градусов всего нужно проехать, с учетом размера колес
S = R * 360 / (W * pi)
// Рассчитываем среднюю скорость в град/сек
С = S / T
// Мощность на моторах, с которой робот проезжает заданное расстояние за +/- приблизительное заданное время
P = 50
// начинаем движение
Моторы(P)
// Коэф-т ускорения
K = 2
ЦИКЛ
  {
  E1 = считываем показание энкодера моторов
  Ждать (1 сек)
  E2 = считываем показание энкодера моторов   
  // смотрим сколько проехал робот за секунду
  E = E2 - E1  
  // корректируем мощность на моторах
  ЕСЛИ (E > C) ТОГДА P = P - K
  ЕСЛИ (E < C) ТОГДА P = P + K
  Моторы(P)
  // корректируем среднюю скорость с учетом оставшегося расстояния, осталось проехать:
  S = R * 360 / (W * pi) - E2
  // обновление средней скорости
  C = S / (T - таймер)  
  }

Второй вариант решения, с использованием ПИД-регулятора:

// пусть роботу нужно проехать 2 м за 35 секунд
R = 2000
T = 35
pi = 3.14
// Размер колеса робота, мм
W = 56
// Рассчитываем, сколько мм он должен проезжать за 0.1 сек 
S = R / (T * 10)
// Теперь в градусах оборотов мотора c учетом размера колес
S = ((R / (T * 10)) * 360 / (W * pi)
// Мощность на моторах, с которой робот проезжает заданное расстояние за +/- приблизительное заданное время
P = 50

// Коэфт-ты ПИД регулятора
Kp = 0.1
Ki = 0.0001
Kd = 0.2

// начальное значение ошибки регулятора
E = 0

ЦИКЛ
  {
  // предыдущее значение ошибки
  Eold = E
  E1 = считываем показание энкодера моторов
  Ждать (0.1 сек)
  E2 = считываем показание энкодера моторов
  ?/ вычисляем ошибку - на сколько градусов 
  // быстрее или медленнее у робота получилось
  E = (E2-E1) - S
  // сумма всех ошибок - интеграл
  I = I + E
  // ПИД-регулятор
  V = (Kp * E) + (Ki * I) + (Kd * (E - Eold))
  // подаем мощность M на моторы 
  M = P - V
  Моторы(М)
  }


суббота, 28 ноября 2015 г.

NXT Mine Sweeper

Вы думаете EV3 так крут, что мы забыли об NXT? Ничего подобного! В этот раз мы собираем робота NXT Mine Sweeper по инструкции из старенькой книжки Daniele Benedettelli "Creating Cool MINDSTORMS NXT Robots" и программируем его в среде EV3-G.

Нашу программу для робота, написанную на EV3-G можно скачать по ссылке



суббота, 14 ноября 2015 г.

Справочник по аналогам команд EV3 Basic и EV3-G

Для тех робототехников, кто уже умеет немного программировать на EV3-G при первом знакомстве c EV3 Basic встает вопрос - каким программным блокам среды LEGO Mindstorms EV3-G какие команды или конструкции языка EV3 Basic соответствуют. Мы подготовили краткий справочник-шпаргалку с такими соответствиями.

Скачать наш справочник по аналогам команд EV3 Basic и EV3-G можно по ссылке
Документ будет дополняться, новые версии будут доступны по этой же, постоянной ссылке.


Примеры страниц справочника:




суббота, 7 ноября 2015 г.

Начинаем программировать на EV3 BASIC

Сегодня ребята из команды "Карандаш и Самоделкин" снова будут решать вместе с вами задачки по робототехнике. Программировать робота для решения задач мы будем на новом языке программирования EV3 BASIC. 
EV3 BASIC - свободно распространяемая среда программирования для платформы LEGO Mindstorms EV3, основанная на языке Бейсик. 
Наш перевод EV3 BASIC на русский язык вошел в новую версию среды программирования 1.0.9, которая уже доступна для скачивания
Инструкцию по сборке робота и программы из этой серии вы сможете скачать в разделе Инструкции по сборке 




Инструкция на учебного робота доступна в двух форматах - 3D модель в LEGO Digital Designer и пошаговая сборка в PDF.




Задача 1: Робот должен при первом нажатии датчика касания определиться с направлением движения (короткое - назад, длинное - вперед), при втором - с расстоянием, которое необходимо проехать в указанном направлении (чем дольше нажата кнопка - тем дальше едет робот), затем начать движение.

Код программы:

Sensor.SetMode(4,0)
While Sensor.ReadPercent(4) = 0
EndWhile
A = EV3.Time
While Sensor.ReadPercent(4) = 100
EndWhile
B = EV3.Time
X = B - A
Program.Delay(1000)
While Sensor.ReadPercent(4) = 0
EndWhile
A = EV3.Time
While Sensor.ReadPercent(4) = 100
EndWhile
B = EV3.Time
z = A - B
If X > 1000 Then
  Motor.Schedule("BC",100,z / 3,z / 3,z / 3,"True")
  Motor.Wait("BC")
Else
  Motor.Schedule("BC",-100,z / 3,z / 3,z / 3,"True")
  Motor.Wait("BC")
EndIf

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

Код программы:

Sensor.SetMode(1,0)
Sensor.SetMode(3,0)
Sensor.SetMode(2,0)
Sensor.SetMode(2,1)
Sensor.SetMode(2,0)
Sensor.SetMode(2,1)
Sensor.SetMode(2,0)

Bl_Keg = 100
Dl_keg = 0
Am_Bl_Keg = 0
Am_Dl_keg = 0
loop = "True"

Motor.StartSync("BC", 10, -10)

While loop
  
  Gyro = Sensor.ReadRawValue(2,0)
  IR = Sensor.ReadRawValue(1,0)
  If Gyro < -360 Then
    loop = "False"
  EndIf
  
  If IR < Bl_Keg Then
    Bl_Keg = IR
    Am_Bl_Keg = Gyro 
  EndIf
  
  If IR > Dl_keg Then
    Dl_Keg = IR
    Am_Dl_keg = Gyro
  EndIf
  
EndWhile

Motor.Stop("BC", "True")

LCD.Clear()
LCD.Text(1, 0, 0, 1, Bl_keg)
LCD.Text(1, 50, 0, 1, Am_Bl_keg)
LCD.Text(1, 0, 20, 1, Dl_keg)
LCD.Text(1, 50, 20, 1, Am_Dl_keg)

Motor.StartSync("BC", -10, 10)
While Sensor.ReadRawValue(2,0) < Am_Bl_Keg - 5  
EndWhile
Motor.Stop("BC", "True")

Motor.Schedule("BC", 30, 100, 10000, 100, "True")
While Sensor.ReadPercent(3) > 25    
EndWhile
Motor.Stop("BC","False")
Motor.Schedule("BC", 30, 0, 0, 100, "True")

Motor.Wait("BC")

Задача 3: Усложненный кегельринг. Робот должен выбить все кегли за пределы круга, при этом подвижный флажок, установленный на роботе должен всегда смотреть в направлении старта робота.

Код программы:

Sensor.SetMode(2,0)
Sensor.SetMode(2,1)
Sensor.SetMode(2,0)
Sensor.SetMode(1,0)
Sensor.SetMode(3,0)
Sensor.SetMode(4,0)

Thread.Run = FLAG

S = 15

Sub FLAG
  Motor.ResetCount("A")
  Pk = 0.15
  Kk = 0.0001
  While "True"
    Er = Sensor.ReadRawValue(2,0) + Motor.GetCount("A")
    V = Er*Pk + Er*Er*Er*Kk 
    Motor.Start("A",-V)
  EndWhile
EndSub

While Sensor.ReadPercent(4) < 50
  Program.Delay(1)
EndWhile
While "True"
  Motor.StartSync("BC", S, -S)
  If Sensor.ReadPercent(1) < 63 Then
    Motor.Stop("BC","True") 
    Motor.ResetCount("BC")
    Motor.StartSync("BC", 50, 50)
    While Sensor.ReadPercent(3) > 25
      Program.Delay(1)
    EndWhile
    Motor.Stop("BC","True")    
    Motor.MoveSync("BC", -50, -50, Motor.GetCount("C"), "True")
    Motor.Stop("BC","True")    
  EndIf

EndWhile 

Задача 4: Робот, двигаясь по линии, должен останавливаться перед препятствием и ожидать, пока его не уберут. Кроме этого должна быть возможность поставить робота "на паузу" по датчику-кнопке.

Код программы:

Sensor.SetMode(3,0)
Sensor.SetMode(1,0)

Pk = 0.09
Kk = 0.0016
Sp = 30
Ser = 48
Stop = 1
Trigger = 1

Thread.Run = Start_Stop

Sub Start_Stop
  While "True"
    If Sensor.ReadRawValue(1,0) < 40 Then
      Stop = 0
    Else
      Stop = 1
    EndIf
 
    If Sensor.ReadPercent(4) > 50 Then
      If Trigger = 0 Then
        Trigger = 1
      Else
        Trigger = 0
      EndIf
      Speaker.Note(75, "D5", 1000)
      Speaker.Wait()
    EndIf
  EndWhile
EndSub

While "True"
  Er = Sensor.ReadPercent(3) - Ser
  V = Er*Pk + Er*Er*Er*Kk/10
  Motor.StartSync("BC", (Sp+V) * Stop * Trigger, (Sp-V) * Stop * Trigger)
EndWhile

Задача 5: Движение по "восьмерке", используя только датчик-гироскоп.

Код программы:

Sensor.SetMode(2,0)
Sensor.SetMode(2,1)
Sensor.SetMode(2,0)
Sensor.SetMode(2,1)
Sensor.SetMode(2,0)

Napr = 0
Er = 0
Pk = 1.8
Kk = 0.003
Sp = 20

Thread.Run = NAPRAVLENIE

Sub NAPRAVLENIE
  While "True"
    
    For Napr = 0 To 360
      Program.Delay(30)
    EndFor
    
    For Napr = 360 To 0 Step -1
      Program.Delay(30)
    EndFor
    
  EndWhile
EndSub

While "True"
  Er = Napr - Sensor.ReadRawValue(2,0)
  V = Er * Pk + Er * Er * Er * Kk
  Motor.StartSync("BC", Sp-V, Sp+V)
EndWhile

Самое популярное