Robo code game challenge

Code Challenge Game – захоплюючий формат змагань, який викликає у студентів великий інтерес. В рамках IV Всеросійської молодіжної школи з робототехніки, вбудовуваних систем та комп'ютерного зору (http://roboschool.org), яка пройшла в листопаді у Волгограді, ми хотіли піднести учасникам щось нове. Так народилася ідея RoboCGC – поєднати роботів і CGC. Подробиці під катом.

image

Вступ
Отже, для початку, кому цікаво, що з себе представляє класичний CGC, але він недостатньо обізнаний: почитати можна тут і тут.

Що ж таке RoboCGC? Це класичний Code Game Challenge, в якому стратегії учасників запускаються не тільки в грі, але і на реальних роботах. В іншому ж він нічим не відрізняється – учасники точно так само реалізують програму для керування роботом, і виробляють її запуск в симуляторі для оцінки якості роботи. В симуляторі їм доступні базові стратегії, які надали організатори змагань, і стратегії інших учасників.

На цей раз в ігровому світі перед учасниками стояло завдання управління роботом, оснащеним лазером. Лазер зафіксований і сонаправлен з роботом. Залп (снаряд) лазера володіє нескінченною швидкістю. Знаряддя має три стани:
  • перезарядка – від моменту пострілу до моменту, коли знаряддя можна знову заряджати, має пройти час перезарядки;
  • простий – після того як знаряддя перезарядилось, але не почало накопичувати заряд, воно простоює;
  • накопичення заряду по команді користувача знаряддя з режиму простою переходить в режим накопичення заряду.


Як тільки заряд знаряддя досягне максимального значення, постріл відбудеться автоматично. Крім того, користувач може в будь-який момент від початку заряду і до автоматичного пострілу зробити постріл примусово. Шкоди, яка завдається при попаданні, лінійно залежить від накопиченого заряду.

На карті присутні два робота (один на кожну команду) і кілька перешкод. Перешкоди рухливі – робот може їх штовхати.

Занурення
Тепер перейдемо до самої системи.

Система складається з декількох компонент:

image

Розглянемо кожен з модулів окремо

Користувацька стратегія
Інтерфейс класу користувача дуже простий і складається всього з однієї функції, яка викликається на кожному такті ігрового світу. У функцію передається інформація про ігровому світі, що включає в себе інформацію про стан юнітів і їх стан, інформацію про стан перешкод, а також інформацію про ігровому світі (ігровий час, розміри поля і т. п.).

#pragma once
#include "Common/IWorld.h"

using namespace GameWorld::Strategy;

namespace Client
{
namespace Strategy
{
class Player
{
public: 
void Move(IWorld* world);
};
}
}


Користувач може працювати в рамках одного файлу, т. к. на сервер вирушає один файл для компіляції. На сервері цей файл компілюється разом з обгорткою, яка забезпечує взаємодію з симулятором.

Раніше ми використовували модель DLL – стратегії компілювати в окремі DLL і завантажувалися в симулятор, а далі симулятор викликав методи цієї DLL. На цей раз ми перейшли до моделі окремих процесів – в цьому випадку ми отримуємо кілька переваг: більш вільний контроль часу виконання і споживання пам'яті (у випадку порушення обмежень ми просто завершуємо процес зі стратегією, не побоюючись, що в симуляторі може зіпсуватися купа чи залишаться незвільнення ресурси). Взаємодія між клієнтським кодом і модулем ігрового світу здійснюється через сокети.

Послідовність роботи користувальницького коду наступна:

image

Для передачі повідомлень ми використовували вирівняні структури – найпростіший спосіб без додаткової серіалізації/десеріалізації.

Заголовок пакета з інформацією про ігровому світі виглядає так:
struct StateHeader
{
int TotalUnits; // кількість юнітів
int TotalObstacles; // кількість перешкод
int Tick; // ігровий час

double Width; // ширина ігрового світу
double Height; // висота ігрового світу

int CurrentTeam; // поточна команда
int TotalTeams; // кількість команд
int IsFinished; // прапор завершення
};


Інформація про юніті задається структурою:
struct UnitDTO
{
double X; // положення по осі Х
double Y; // положення по осі Y
double Angle; // кут повороту
double Radius; // радіус

int Charge; // стан знаряддя

int HP; // запас здоров'я
int TeamId; // ідентифікатор команди
int Id; // ідентифікатор юніта
};


Інформація про перешкоду:
struct ObstacleDTO
{
double X; // положення по осі Х
double Y; // положення по осі Y
double Angle; // кут повороту
double Radius; // радіус

int Id; // ідентифікатор перешкоди
};


Заголовок пакета з інформацією про хід гравця:
struct ControlHeader
{
int TotalUnits; // кількість юнітів, які здійснювали дії
int IsCrashed; // прапор «вилетіла» стратегії

char Name[31]; // ім'я команди
};


Інформація про дії юніта:
struct UnitControlDTO
{
double LeftControl; // керуючий вплив для лівого двигуна
double RightControl; // керуючий вплив для правого двигуна

bool IsStartCharging;// прапор початку заряду знаряддя
bool Fire; // прапор пострілу
int UnitId; // ідентифікатор юніта
};


Стартовий модуль
Даний модуль відповідає за читання конфігурації, створення відповідного оточення (симулятор або апаратна платформа), старт стратегії і здійснення ігрового циклу.

Опис ігрового світу визначається конфігураційними файлами, за допомогою яких можна встановити фізичні характеристики робота (радіус, колісна база, діаметр колеса тощо), параметри симуляції (розмір поля, тривалість симуляції, кількість гравців, кількість юнітів у кожного гравця, кількість перешкод, характеристики гармати тощо), стартові параметри (початкові положення роботів і перешкод), конфігурація апаратної платформи (IP-адреси роботів, маркери для роботів і перешкод).

Після читання конфігурації створюється оточення – небудь симулятор, або апаратна платформа. Обидва оточення мають однаковий інтерфейс, тому робота з ними виконується однаково:

class IEnvironment
{
public:
virtual void Initialize(boost::shared_ptr<GameWorld::Internal::World> world) = 0;
virtual void Step() = 0;

віртуальний bool CloseRequested() = 0;
віртуальний bool Paused() = 0; 
віртуальний bool Idle() = 0;
};


Далі запускаються процеси стратегії, і починається очікування готовності оточення (для чого це потрібно, опишу нижче). Як тільки підготовка оточення завершується, відбувається відправлення стартового повідомлення стратегіям і запускається цикл симуляції, всередині якого кожної стратегії відправляється повідомлення з станом ігрового світу і очікується відповідь з реакцією користувача. На цьому етапі контролюється часом виконання користувальницького коду – якщо відповідь не надійшла у встановлений час, користувацька стратегія завершує роботу. Після отримання відповідей від усіх стратегій відбувається їх обробка – дані передаються в оточення, де вони обробляються, і стан ігрового світу оновлюється. Потім відбувається перехід до наступної ітерації.

Модуль симуляції
Модуль симуляції використовується учасниками для перевірки своєї стратегії під час проведення очної частини змагання (тобто написання стратегій). Симулятор має просту графіку і виробляє симуляцію фізики (зіткнення, попадання) і т. п.

image

Модуль апаратного взаємодії
Самий складний і цікавий модуль. Цей модуль передає команди на роботів, а також одержує їх поточні координати.

Вся логіка виконується на комп'ютері, де запущена система, на роботів відправляються лише результуючі команди (на рівні управління двигунами і встановлення стану світлодіодів). Для зв'язку з роботами використовується WiFi з'єднання, до якої підключений комп'ютер-сервер і обидва робота.

Для отримання поточних координат над ігровим полем розташована камера, а на всіх рухомих об'єктах наклеєні маркери. Під час симуляції відбувається розпізнавання маркерів та визначення їх позиції.

В якості камери ми використовували Intel RealSense першого покоління. З камерою другого покоління виникли проблеми при роботі з довгим USB-кабелем, тому звернулися саме до першої версії.

Так як на відміну від симуляторів, нам необхідно дочекатися готовності роботів (переконатися, що робот підключені), ми ввели проміжну стадію стану оточення – як тільки роботи підключені, оточення надсилає повідомлення про готовність.

Крім пошуку маркерів і визначення їх позицій, модуль також виробляє рендеринг об'єктів доповненої реальності. Під час змагання картинка з камери транслювалася на проектор. Крім зображення з камери, на нього виводилося стан роботів і отрисовывались постріли.

image

Робот
Робот зібраний на базі Intel Galileo. Додатково було встановлено MotorShiled для управління двигунами і модуль WiFi. Також для управління світлодіодами була виготовлена плата зі зсувними регістром (після установки MotorShield'a на Arduino залишається дуже мало вільних пінів). Як актуаторов використовувалися два двигуна постійного струму, що працюють в діапазоні 3.3 – 9 Ст. Харчувалося це все від li-po акумулятор на 7.4 В х 1500 мА.

image

Турнірне
Крім цього ми підготували турнірне ПО – клієнт і сервер, які використовувалися під час очної частини змагання. Сервер дозволяв відправляти стратегії на компіляцію і отримувати скомпилированную стратегію, а також отримувати скомпільовані стратегії інших учасників і виробляти симуляцію з їх участю.

image

Як це виглядало
Установка в зборі:
image

Запис з камери
Симулятор

Проблеми та їх вирішення
У ході розробки та проведення змагань ми зіткнулися з низкою проблем, частину з яких ми вирішили, частина залишилася нам для роботи над помилками.

Камери
Спочатку ми планували використовувати камеру, яка стояла б з одного боку від поля

image

Однак в процесі тестування ми зрозуміли, що об'єкти, розташовані в дальньому кінці поля, дуже погано розпізнаються. Тому потрібно було або сильно піднімати камеру, що б змінити кут, або вішати її над полем. У підсумку зупинилися на останньому варіанті.

image

Освітлення
Великою проблемою стало мерехтливе освітлення. Дуже сильно не хотілося робити додаткову обробку зображення для компенсації мерехтіння газорозрядних ламп. У підсумку проблему «підперли» милицею – перейшли до 25 кадрів (тобто до частотою, кратною частоті мерехтіння) і додали невелику коригування за порогової контрастності, яку можна було міняти «на льоту».

WiFi
Спочатку в нашому розпорядженні були два WiFi модуля без антен. В цілому для розробки цього вистачало, але при першому ж запуску стало ясно, що для подальшої роботи цей варіант не підійде – сигнал постійно губився, пакети приходили з великою затримкою. У підсумку оснастили роботи антенами.

Позиціонування
Оскільки система неодноразово збиралася і розбиралася і розташування частин (поля щодо камери) постійно змінювалося, ми додали процедуру калібрування – розставляючи маркери по заздалегідь визначеним позиціям після монтажу установки, ми проводили калібрування і зберігали її у файлі конфігурації для подальших запусків.

Фізика
Найбільша проблема. Фізика в симуляторі в результаті відрізнялася від фізики реального робота. Це відмінність складалося з декількох складових:
  • Часовий лаг – в симуляторі учасники отримували відразу актуальну позицію, у випадку з апаратною платформою вони отримували положення робота з деяким запізненням, що призводило до «перерегулированию», дуже багато роботів крутилися вліво-вправо, тому що не могли потрапити в потрібний кут.
  • • Відмінність самої фізики – фізика пересування перешкод у симуляторі і в реальності не відповідали (ми трохи промахнулися з масою, і реальні перешкоди рухалися складніше).


Подальші розвиток
Незважаючи на всі труднощі, студентів зацікавила наша захід, і ми плануємо розвивати платформу, щоб усунути існуючі проблеми та реалізувати всі наші задумки.

Істотним обмеження на ігрову механіку було відсутність можливості візуалізувати снаряди. Це можна вирішити, розмістивши над полем проектор, який би проецировал додаткові об'єкти на ігровому полі. Це відкриває широкий спектр механік: ми зможемо додати бонуси, снаряди з кінцевою швидкістю, індикацію здоров'я та інших станів. Плюс до всього це цікаве завдання.

Так само хотілося б збільшити розміри ігрового поля, але тут ми були обмежені однією камерою. Тому в планах є використання декількох камер.

Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.