среда, 30 марта 2016 г.

Немного о футболе роботов WRO gen.III. Часть 2

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


Вооружившись познаниями в геометрии или онлайн-калькулятором прямоугольных треугольников можно рассчитать углы, при которых робот должен сходить с орбиты в каждой точке поля. Поле удобно представить в виде воображаемой координатной сетки, разбив его на нужное вам количество зон - в зависимости от требуемой точности наведения на ворота. На изображении выше для примера приведена разбивка на 9 зон, имеющих координаты от (1,1) до (3,3).
Для каждой зоны будет две точки схода - для движения вокруг мяча по часовой стрелке и против. Отслеживать точку схода удобно по компасу, сравнивая текущий азимут с требуемым с учетом некоторой заданной погрешности.
Но тут нас поджидает неприятность - дело в том, что цифровой компас Hitechnic - устройство несовершенное, и даже после тщательной калибровки в связи с наличием наводок на поле показания будут распределены по окружности неравномерно, в каком-то направлении "градусы будут чаще", в каком-то реже (см. рис).


К счастью сами направления при этом довольно точные, но вот повернуться от текущего направления например на 90 градусов вправо роботу точно не удастся, он фактически не довернется до нужной точки, если в целевом секторе показания меняются чаще чем 1 градус на фактический градус поворота, или наоборот, перелетит ее, если этот сектор с "разреженными" показаниями.

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

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


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

Положим точки схода роботу удалось достичь и он, развернувшись, смотрит прямо на мяч. Какие его дальнейшие действия? Как ему узнать, что на "линии его взгляда" - вражеские ворота, а перед ним мяч? На второй вопрос можно получить ответ смотря на показания ИК-поисковика, он должен быть в зоне 5, а вот с первым - опять не все так просто. На разных точках поля направление на центр ворот соперников - различно. Как быть?

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


В главе 6 "От джойстика до футбола" онлайн курса Сергея Филиппова "Основы робототехники" автор предлагает интересный способ забить мяч в ворота - сначала робот движется на мяч, а как только до него остается совсем небольшое расстояние - движется на ворота, тем самым направляя и подталкивая мяч в их направлении.

Как долго роботу следует это делать? Ответ на этот вопрос робот может получить из следующих источников:
- ИК-поисковик (мяч не на линии движения робота)
- Компас (движение не в направлении ворот соперников)
- Энкодеры и таймер ("атака"слишком затянулась).

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

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





воскресенье, 27 марта 2016 г.

EV3 Basic: подключаемся к Arduino

Уже довольно давно Dexter опубликовали способ взаимодействия EV3 и Arduino с использованием протокола i2c через один из портов датчиков. Этот способ позволяет передавать с Arduino любые данные, например показания подключенных к ней датчиков и использовать их в программе на EV3. Возможна передача данных и в противоположном направлении, например из программы на EV3 можно управлять моторами и реле, подключенными к Arduino.



Все бы хорошо, но способ этот работает только на EV3-G, так как основан на применении специального программного блока для этой среды программирования.


Мы же программируем на EV3 Basic и хотим использовать все преимущества такого межплатформенного взаимодействия в этом языке, поэтому написали аналог данного блока в среде EV3 Basic, он довольно простой, посмотрите:

'//  EV3 Basic, код для взаимодействие с Arduino
'//  EV3 - I2C master, Arduino - I2C slave

MSGSZEV3 = 30
MSGSZSLV = 30
// EV3 порт, к которому подключена Arduino
I2CPORT = 4
// i2c адрес Arduino, в нашем примере 0x04          '
I2CSLVADDR = 4       '

// массив байт, который мы будем передавать на Arduino
sendarray = Vector.Init(MSGSZEV3, 0) 

// массив байт, который мы будем принимать с Arduino
recvarray = Vector.Init(MSGSZSLV, 0)

// далее выполняем в бесконечном цикле
While "True" 

  // Если нужно что-то отослать на Arduino - записываем это в массив sendarray
  sendarray[0] = 255

  // запускаем функцию взаимодействия по i2c, sendarray и принимаем данные
  recvarray = Sensor.CommunicateI2C( I2CPORT, I2CSLVADDR , MSGSZEV3 , MSGSZSLV ,  sendarray )
  
  // выводим на экран первый элемент массива recvarray 
  LCD.Text(1, 0, 0,  2,  recvarray[0])

  // необязательная задержка - чтобы успеть рассмотреть вывод на экран
  Program.Delay(100) 

  // очищаем экран
  LCD.Clear()  
  
EndWhile

На Arduino можно залить стандартный код от Dexter или, например, вот такой модифицированный нами скетч:

#include <Wire.h>

#define SLAVE_ADDRESS 0x04
void setup() 
{
    Wire.begin(SLAVE_ADDRESS);
    Wire.onReceive(receiveData);
    Wire.onRequest(sendData);
}
int pin,st,val=0,flag=0,index=0;
char buf[8];
byte b[1];
void loop()
{
  if(flag==1)
  {
    flag=0;
    Serial.println(pin);
    val=analogRead(pin);
    b[0] = map(val,0,1024, 100, 0);

  }
}

void receiveData(int byteCount)
{
    while(Wire.available()>0) 
    {
      pin=Wire.read();
      flag=1;
    }
}

void sendData()
{
  Wire.write(b,1);
}

Данный пример передает на EV3 данные с аналогового порта А0, пересчитав их в диапазон 0..100.

Конечно же, использовать можно не только Arduino Uno, как в примере от Dexter, но и более миниатюрные варианты плат, например мы использовали для теста Micro:

1. ИК-дальномер Sharp gp2y0a21yk0f, аналоговый, подключен к порту А0
2. Arduino Micro
3. Переходник на Mindstorms-кабель 
4-5. Для питания Arduino (и дальномера через нее) используется кабель microUSB (4), подключаем в боковой разъем EV3 блока (5). Питания от порта датчика не всегда хватает, такой способ более надежный.
6. Кабель датчика втыкается в свободный порт EV3.


пятница, 18 марта 2016 г.

Немного о футболе роботов WRO gen.III. Часть 1

В прошлом сезоне наша команда открыла для себя такие замечательные соревнования, как футбол роботов WRO.


Если вы помните, в цикле "Строим из LEGO Mindstorms и Technic" выходило несколько серий на эту тему:

1) Робот-футболист Карандаша и Самоделкина: Нападающий
2) Робот-футболист Карандаша и Самоделкина: Вратарь
3) Робот-футболист Карандаша и Самоделкина: Нападающий 2.0
4) Робот-футболист Карандаша и Самоделкина: Как это работает

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

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

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

Сезон 2016


WRO сезона 2016 привнесло в регламент футбола новые изменения и теперь он называется "третьим поколением футбола WRO" (WRO Football gen.III). Кардинально изменилось поле, теперь игра идет на ковре, на поле нет цветовой зональной разметки. Появились высокие борта для ориентации роботов на поле с применением дальномеров. Конструктивно изменились ворота - за счет изменения их глубины поле кажется огромным и появился больший простор для маневров. Выглядит поле теперь так:


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


Вратарю - когда его датчик ориентирован в направлении ворот, которые он защищает


Либо когда вратарь направлен на ворота соперников (датчик смотрит вправо) с оговоркой, что находясь вне штрафной площадки он не должен создавать УЗ-помех другим роботам:


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

Нападающий


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

Давайте подумаем, как бы человек действовал на месте робота? Наверное оглянулся бы в поисках мяча и, заметив его, развернулся к нему лицом, затем побежал бы в его сторону. Может ли робот сделать тоже самое? Может! Правда у него нет головы и шеи, поэтому ему придется развернуться всем корпусом, пока излучение от ИК-мяча не будет строго одинаково с его левой и правой стороны (шаг 1)


В какую сторону выгоднее разворачиваться? В отличии от человека, который развернет голову в поисках мяча наугад, робот чувствует мяч буквально затылком и может начать разворот выбрав сторону, с которой сила излучения мяча интенсивнее
Разворот на месте можно заменить разворотом в движении, тогда робот начав двигаться по луге, ляжет на курс, ведущий к мячу (вариант 1а):


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


Мы "бежим" на мяч, судорожно соображая, что делать дальше. Очеидно, что если робот продолжит прямолинейное движение он ударит меч и тот покатится в направлении движения робота, но ведь нам нужно подтолкнуть его к воротам, как же быть?

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


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

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

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


Далее начинается самое интересное - в какой точке мы должны сойти с орбиты? Очевидно, что это будет точка ее пересечения с прямой, проложенной через мяч и центр вражеских ворот. Для схода с орбиты мы должны опираться на показания датчика компаса и знание о том, по часовой стрелке мы описываем орбиту или против, но об этом - в следующей части статьи



среда, 16 марта 2016 г.

Соединяем EV3 и Arduino по Bluetooth



В наших предыдущих проектах, таких как "EV3 Bluetooth - гитара" и "Электроскрипка из LEGO EV3" мы использовали Bluetooth для передачи данных с EV3 на Arduino.

 


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

Мы долго ждали, пока кто-нибудь не напишет библиотеку для Arduino, позволяющую этой платформе полноценно взаимодействовать с EV3, но так и не дождались, поэтому решили написать такой инструмент сами. Надеемся он будет полезен Mindstorms-сообществу и пригодится в ваших проектах. 

Исходники  EV3 открыты и мы заглянули в них в поисках описания Bluetooth-протокола. Оказалось, что он неплохо прокомментирован и понятен. EV3 может отправлять и принимать через именованные майл-слоты три типа данных - числа, текст и логические значения.

Например, передавая текст "text" в удаленный слот с именем text вы отправите по Bluetooth последовательно 11000100819E05746578740005007465787400, а отправляя число -3,14 в слот с именем Numeric отправите 13000100819E084E756D65726963000400C3F548C0. Непонятно, правда? 

Чтобы не иметь дело с длинными  строками цифр мы публикуем ряд функций для Arduino, используя которые вы сможете общаться с вашим EV3. Мы не оформляли их в виде библиотеки, так как писали их в общем-то для собственных нужд.


Первым делом вы должны "спарить" ваш EV3 и Bluetooth-модуль, подключенный к Arduino и узнать имя модуля на стороне Arduino. Ваш EV3 не будет знать, что общается не с себе подобным, а с кучей микросхем и проводов, притворяющихся другим EV3. Это здорово, потому что общаться с Arduino вы  сможете из любого языка программирования для платформы EV3, который умеет работать с Bluetooth, будь то EV3-G, EV3 Basic или RobotC.

Например, на языке EV3-G код, который будет отправлять данные на Arduino будет выглядеть так:


Первый синий блок устанавливет соединение в Arduino-Bluetooth-модулем, в нем важно указать корректное имя модуля, которое вы ухнали при "спаривании". В данном примере мы отправляем логическое значение "Истина" в майл-слот с именем Status по нажатию кнопки, подключенной в первому порту.

Принимает данные с Arduino вот такая конструкция, Важно указать тип принимаемых данных и имя майл-слота.


Давайте смотреть скачанный по ссылке выше код.

Функция AskForEV3Message() опрашивает приемный буфер на предмет пакета данных, присланных с EV3. при их наличии она разбирает пакет и записывает в переменную in_message_type единицу, если пришло логическое значение, 2 - для числа и 3 - для текста. Если сообщения не было - в переменной будет 0. В случае наличия сообщения в переменную in_mailbox запишется имя слота, а в одну из переменных in_value_logic, in_value_numeric или in_value_chars - принятое значение, в зависимости от его типа в in_message_type.

Таким образом в программе вы можете использовать функцию приема данных AskForEV3Message()  с EV3 например таким образом:

AskForEV3Message();
if (in_message_type != 0) Serial.println(in_mailbox);
if (in_message_type == 1) Serial.println(in_value_logic);
if (in_message_type == 2) Serial.println(in_value_numeric);
if (in_message_type == 3) Serial.println(in_value_chars);

Смотрим дальше, теперь давайте отправим данные на удаленный EV3 из вашей Arduino-программы. Вы можете использовать три функция MakeLogicEV3Message, MakeNumericEV3Message и MakeTextEV3Message для отправки данных соответствующих типов, пример:

// отправляем в EV3-слот TestLogic истину
MakeLogicEV3Message("TestLogic", true);

// отправляем в в EV3-слот MyNumeric случайное число от 0 до 100
MakeNumericEV3Message("MyNumeric", (int)random(100));

// отправляем в в EV3-слот EV3Text строку "text_example"
MakeTextEV3Message("EV3Text", "text_example");

Давайте договоримся, что вы откроете код вашего будущего проекта также, как мы делаем это для вас. Удачи!

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