воскресенье, 4 июля 2021 г.

KeyKeeper - хранитель паролей для истинного параноика!


Почему именно наше устройство обеспечит сохранность Ваших паролей?

01 / Пароль от ваших данных не знает даже хранитель
Логины и пароли хранятся в устройстве в защищённом виде, при этом даже хранитель не знает правильный мастер-пароль от Ваших данных. Можно ввести любой мастер-пароль и хранитель выведет вам хранящиеся пароли, но расшифрованные неправильно, фактически они представляют случайный набор символов. При внешней угрозе вводите любой мастер-пароль, никто сможет понять, что он неправильный. Злоумышленник также не сможет получить его, взломав флеш-память устройства

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

03 / Универсальность
KeyKeeper работает с популярными операционными системами Windows и Linux. Поддерживаются специальные режимы для ssh-сессий, linux-терминала, учитываются особенности популярных браузеров, таких как Chrome, Opera, Firefох. C точки зрения компьютера KeyKeeper - обычная USB-клавиатура, поэтому можно входить даже в BIOS/UEFI

04 / Хранитель похож на флешку, но вовсе ей не является
С виду хранитель похож на обычную USB-флешку, поэтому злоумышленник вряд ли распознает, что это защищённый хранитель паролей. KeyKeeper не определяется операционной системой как носитель информации, поэтому вирусы направленные на поражение носителей информации не способны поразить хранителя. Они не понимают что в "клавиатуре" могут храниться данные

05 / Практически вечный храниетель информации
Память KeyKeeper рассчитана на 3 млн циклов записи. Это значительно больше чем на любых USB-флешках, в них предусмотрено обычно от 10 до 100 тысяч циклов записи. Хранитель крайне устойчив к перебоям в питании, поэтому можно смело выдергивать его из порта в любой момент и не беспокоиться что пароли могут быть повреждены

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

07 / Удобный менеджер паролей
Специальная утилита обеспечивает возможность добавления, редактирования базы паролей на устройстве, резервного копирования в открытом и защищенном форматах, изменения настроек ввода под разные типы аккаунтов. Есть специальные настройки для пользователей ноутбуков, на клавиатуре которых могут быть не все клавиши






понедельник, 6 июля 2020 г.

NeyroGamer 2

Всем привет, с вами Платон из команды "Карандаш и Самоделкин". Лето идёт полным ходом и наша команда не сидит без дела. Сегодня я готов представить вам продолжение проекта NeyroGamer, в котором я создам нейронную сеть, которая будет учиться  играть в видео игры.

Я всё глубже изучаю нейронные сети, и зачастую пишу код без использования библиотек с готовыми методами и инструментами для создания алгоритмов машинного обучения. Это связанно с желанием узнать, что находится «под капотом» у таких библиотек как Keras и TensorFlow. Однако алгоритм обратного распространения ошибки кажется мне несколько сложным, поэтому в NeyroGamer нейросети выступают в качестве особей генетического алгоритма. В сегодняшнем проекте я написал нейронную сеть с краткосрочной памятью с нуля, используя лишь стандартные библиотеки и NumPy.

В качестве игры, в которую будет учится играть мой алгоритм, снова выступит Nigel Mansell's World Championship Racing с платформы Sega Genesis. Так как я использую генетический алгоритм, использование эмулятора консоли и ретро игра дает мне следующие преимущества:

  •     Загрузка состояния с контрольной точки. Каждая особь начинает гонку со старта. 
  •     Простая графика для разбора OpenCV

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

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

 
32х8

64х16

Такой метод оказался намного лучше и обучение пошло значительно быстрее. Правда из-за того, что нейронная сеть очень сильно выросла (3000+ входов), пришлось заняться оптимизацией. Весь алгоритм обучения я переписал на NumPy. Самое «медленное» место в старом алгоритме оказалось в методе «предсказания»(использования) нейронной сети из-за «бега» циклом по 2м матрицам. После оптимизации этого места FPS вырос в 100 раз, с 6 до 600. Вместе с переработкой всего кода и его оптимизацией, была дополнена модель нейронной сети. Была добавлена краткосрочная память, то есть теперь я использую LSTM-сеть . 

С помощью моментов  на кадре выделяется данные о скорости (вырезается полоса с показаниями передачи и по ней берётся красный момент). Эти данные показывают текущую скорость — показатель по которому сортировались нейронные сети.

  

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


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

Автор - Платон, 14 лет ("Карандаш и Самоделкин")

вторник, 12 мая 2020 г.

Нейронная сеть учится играть в Lunar Lander

Lunar Lander — аркадная игра, выпущенная Atari, Inc. в 1979 году. Цель игры — управлять посадочным лунным модулем, чтобы произвести мягкую посадку на поверхность Луны. На посадочный модуль действует гравитация и инерция, кроме того количество топлива в нем ограничено. Управление производится 4 кнопками: наклоны посадочного модуля влево/вправо, управление тягой двигателя +/-.


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



Для ускорения обучения нейронной сети используется генетический алгоритм, в каждом поколении запускается 10 ее копий. Нейронная сеть имеет 2 скрытых слоя. Проект разрабатывается на Python, никакие дополнительные нейросетевые фреймворки/модули не используются. Автор - Тихон, 13 лет ("Карандаш и Самоделкин")
Исходный код



воскресенье, 10 мая 2020 г.

Звездолеты 3D

Продолжение игры про звездолеты, теперь в 3D. Управление с помощью наэкранных кнопок. В демо 1 уровень, есть звуковые эффекты и музыка.



Язык C#, среда разработки - Unity.
Автор - Тихон, 13 лет ("Карандаш и Самоделкин")
Исходный код здесь

суббота, 9 мая 2020 г.

Звездолеты

Игра про звездолеты. Несколько уровней, разные типы врагов и оружия. Подбираемые боеприпасы и "аптечки". Управление движением - на гироскопе/акселерометре телефона, выстрелы разным типом оружия - тапы в разных зонах экрана. Есть звуковые эффекты и миникарта. Написана без использования готовых библиотек и конструкторов.


Язык Java, среда разработки - Android Studio.
Автор - Тихон, 13 лет ("Карандаш и Самоделкин")
Исходный код здесь



Tetris 2x

Классический тетрис представляет собой головоломку, построенную на использовании геометрических фигур «тетрамино» — разновидности полимино, состоящих из четырёх квадратов. В нашей версии стакан имеет глубину в 2 квадрата (слоя), заполнять необходимо оба горизонтальных ряда из 10 клеток, расположенных на одной высоте. Управление на стрелках клавиатуры, переворот игрового поля на другой слой - Ctrl.


Язык C#, среда разработки - Unity.
Автор - Тихон, 13 лет ("Карандаш и Самоделкин")
Скачать можно c Яндекс.Диск, исходный код здесь
Прототип игры сначала написал на Python, исходники по ссылке

NeyroGamer 1

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

NeyroGamer это своего рода бот, играющий в видео игры. Так как нам нравятся старые консоли, первая игра, в которую мы хотим научить играть нашего бота - гоночная аркада на Sega Genesis под названием Nigel Mansell's World Championship Racing. Игры запускаются на эмуляторе консоли (мы используем Fusion 3). Проект немного похож на один из наших прошлых проектов "Длинный хвост питона", так как использует машинное зрение с OpenCV.


Исходный код проекта

В коде реализовано 2 класса, каждый из которых отвечает за свою область.
   Класс ScreenReader содержит функциональность, позволяющую получать информацию о данных с экрана. Для этого делается скриншот. Мы пробовали множество вариантов — но самым быстрым был метод ImageGrab из модуля PIL. Он показал FPS в 25 кадров в секунду. Этого вполне достаточно для реагирования на повороты. После взятия изображения оно обрабатывается с помощью библиотеки OpenCV. Я накладываю маску, что бы выделить дорогу из кадра. Маска берётся не на всём кадре, а на 2 полосах экрана, первая из которых находится в самом низу кадра, а вторая почти под линией горизонта. Таким образом можно понять в какую сторону изгибается дорога. С помощью двух моментов можно притормаживать на поворотах и газовать максимально на прямых участках.
  Второй класс, Сontrol — отвечает за управление машинкой путем передачи в игру нажатия кнопок на виртуальном геймпаде.

Настройка эмулятора и виртуального геймпада. 
Для управления игрой в эмуляторе была установлена программа Vjoy, с помощью которой к компьютеру был подключен виртуальный геймпад. На геймпаде 11 кнопок и 1 стик. Для управления этим геймпадом из программы я использую модуль PyVjoy. Для корректной работы модуля внутрь папки требуется положить специальный DLL-файл, который генерируется в папке, куда вы установили Vjoy.  После этого вы сможете управлять виртуальным геймпадом из программы на Python. Необходимо установить в настройках эмулятора вход 1. На него следует поставить 3х кнопочный джойстик Genesis и выбрать в качестве него ваш виртуальный геймпад.
После этого надо запустить программу AssignKeys.py. Она будет нажимать кнопки виртуального геймпада в том порядке, какого требует эмулятор Fusion 3. Во время назначения клавиш фокус должен быть всё время на программе-эмуляторе. Когда эмулятор будет ждать клавиши старт, нажмите любую удобную кнопку на клавиатуре. Это нужно для возможности войти в гонку и поставить паузу. Всё готово! Вы можете запускать гонку в полноэкранном режиме предварительно запустив run.py.


  У вас наверно возник вопрос - почему проект носит порядковый номер 1? Всё просто. В репозитории пока открыт только демо-код, алгоритм которого не идеальный. Гонку мы пока проходим на 8-9 месте, поэтому будем дорабатывать NeyroGamer. Первая часть проекта - научиться взаимодействовать с игрой, получать игровые события, передавать команды управления. Сейчас мы работаем над нейронной сетью, которая улучшит игровые результаты.

Язык Python.
Автор - Платон, 14 лет ("Карандаш и Самоделкин")

четверг, 23 апреля 2020 г.

Lode Runner


Чем заняться на самоизоляции? Как вариант - написать игру, в которую можно играть всей семьей.


Ремейк игры Lode Runner (Apple II) с оригинальным набором уровней. Главная фишка - режим hot seat до 4 человек, где остальные игроки играют за противников главного героя. Однако от уровня к уровню могут играть разное количество игроков, так как количество противников разное на разных уровнях.


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


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


С переходом на более высокие уровни игра становится более «головоломной» чем игрой на скорость реакции, хотя и то и другое остаётся важным.

Язык C#, среда разработки - Unity.
Автор - Платон, 14 лет ("Карандаш и Самоделкин")
Скачать можно c rutracker.org или c Яндекс.Диск

воскресенье, 7 июля 2019 г.

Sudoku Hunter

В последнее время наш блог молчит и может показаться, что «Карандаш и Самоделкин» все, сдулись. На самом деле работа кипела, а отсутствие публикуемых материалов было связано с тем, что проекты раз за разом становятся все сложнее и требуют все больше времени на реализацию. Последние наши разработки были так или иначе связаны с нейросетями, поэтому логично было за это время подтянуть скиллы по этой теме, что и было сделано – был пройден ряд онлайн курсов по и даже пара оффлайновых, из которых особенно понравилась «Зимняя школа машинного обучения» (Machine Learning Winter School).


Проект, работа над которым шла больше 4 месяцев, мы назвали Sudoku Hunter. Изначально была идея собрать робота по мотивам NXTSudokuSolver, однако постепенно она трансформировалось в нечто иное, на наш взгляд не менее интересное.
Итак, Sudoku Hunter – это пистолет с EV3, веб-камерой и Raspberry Pi на борту, из которого можно «стрелять» по математическим головоломкам судоку, получая на экране EV3-блока их решение. В алгоритмической части используются технологии машинного зрения и машинного обучения, веб-технологии для создания отладочной видеоконсоли.


Если кто не помнит, судоку - головоломка на бумаге, игровое поле которой представляет собой квадрат размером 9×9, разделённый на меньшие квадраты со стороной в 3 клетки. Таким образом всё игровое поле состоит из 81 клетки. В них уже в начале игры стоят некоторые числа (от 1 до 9), называемые подсказками. От игрока требуется заполнить свободные клетки цифрами от 1 до 9 так, чтобы в каждой строке, в каждом столбце и в каждом малом квадрате 3×3 каждая цифра встречалась бы только один раз.


Конструкция нашего робота не особенно замысловата, поэтому инструкцию по сборке мы решили не делать – вы легко соберете что-то подобное просто глядя на фото. Мы использовали следующие компоненты:
  • Блок Lego Mindstroms EV3 и строительные детали из этого набора
  • Raspberry Pi
  • Веб-камера Sony Playstation Eye
  • Wi-fi адаптер для связи EV3 с RPi
  • Powerbank для питания RPi

Программ, которые необходимы для работы робота - несколько, все они написаны на Python:
  • Программа для EV3 - отвечает за вывод информации на экран блока, связь с Raspberry Pi, передачу на RPi состояния датчика-кнопки
  • Программа для Raspberry Pi - отвечает за обработку кадров с камеры, распознаванием цифр с использованием обученной нейросети, последующим решением распознанного судоку, связь с EV3, вывод вспомогательной и отладочной информации на веб-консоль,
  • Программа для Raspberry Pi, предназначенная для создания дата сета для обучения нейронной сети.
  • Программа для ПК, используется для создания и обучения нейросети на основе подготовленного дата сета

Основная программа для Raspberry Pi работает по следующему алгоритму: 
  • Подключение библиотеки машинного зрения OpenCV
  • Подключение библиотек машинного обучения keras(tensorflow), sklearn
Процесс 1:
  • Взятие кадра с камеры
  • Преобразование изображения из BGR в HSV 
  • Наложение маски для фокусировки на значимых элементах изображения
  • Запись результирующего кадра в объект в памяти
  • Соединение с EV3 с помощью протокола socket 
  • Основной цикл:
  • Чтение кадра из объекта в памяти
  • Поиск в изображении с наложенной маской объектов, напоминающих квадрат 
  • Передача данных о предполагаемом расположении квадрата на EV3, для вывода на экран блока
  • Приём данных с EV3 о состоянии кнопки 
  • Если кнопка нажата:
Процесс 3:
  • Запуск веб-сервера (используется модуль Flask)
  • Соединение с Raspberry Pi с помощью протокола socket
  • Получение данных о предполагаемом расположении квадрата 
  • Подключение библиотеки машинного зрения OpenCV
  • Подключение библиотек машинного обучения keras(tensorflow), sklearn
Процесс 1:
  • Взятие кадра с камеры
  • Преобразование изображения из BGR в HSV 
  • Наложение маски для фокусировки на значимых элементах изображения
  • Запись результирующего кадра в объект в памяти
  • Соединение с EV3 с помощью протокола socket
  • Чтение кадра из объекта в памяти
  • Поиск в изображении с наложенной маской объектов, напоминающих квадрат 
  • Передача данных о предполагаемом расположении квадрата на EV3, для вывода на экран блока
  • Приём данных с EV3 о состоянии кнопки 
  • Если кнопка нажата:
  • Запуск веб-сервера (используется модуль Flask)
  • Вывод информации на веб-сервер - видеоконсоль для удобства отладки
  • Подключение библиотек машинного обучения 
  • Загрузка дата сета изображений 
  • Разбиение дата сета на учебные и тренировочные данные
  • Создание нейронной сети с заданными параметрами 
  • Обучение нейронной сети
  • Сохранение модели 

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

Программа на EV3 работает по следующему алгоритму:
  • Вывод контура квадрата на экран для визуального контроля (размер квадрата, угол разворота, степень его трапециевидности)
  • Передача состояния датчика-кнопки на Rpi
  • Если от RPi получен массив данных с решенной головоломкой - вывод решённого судоку на экран блока

Программа создания дата сета для обучения нейронной сети на Raspberry Pi имеет следующий алгоритм:
Процесс 2: 
    • Изображение обрабатывается и судоку «нарезается» на 81 квадрат в каждом из которых одна клетка головоломки 
    • Каждый квадрат сохраняется в папку, формируя дата сет
Процесс 3:

Программа на ПК, предназначенная для создания и обучения нейросети на основе подготовленного дата сета работает так: 
Стандартный алгоритм обучения нейронной сети мы усовершенствовали путём запуска функции обучения не на заданное количество эпох, а по одной эпохе, с проверкой результата на проверочной выборке после каждой. Если результат ухудшается несколько эпох подряд — прекращаем обучения с ошибкой. Если результаты на тестовой выборке выше 95% - прекращаем обучения, проверяем модель на отдельной выборке (которую она ещё не видела ни при обучении, ни при проверках после каждой эпохи).
Исходные коды всех программ проекта мы выложили здесь: https://github.com/darkavengersmr/Sudoku-Hunter
Демонстрация работы робота:

вторник, 8 января 2019 г.

PiTanks Online

Нам очень нравятся игры для программистов, такие как например Colobot и Ceebot. Писать код, загружать его в своего бота и запускать его для выполнения миссий - что может быть интереснее? Поддержка сетевого режима, когда другие игроки запускают своих прокачанных кодом ботов против твоего? Несомненно. Однако еще более интересным нам показалось самим разработать игру для программистов и поиграть в нее с друзьями.
Серверный игровой движок, многопоточность, асинхронный обмен данными, объекты и классы - все это мы приправим поддержкой дополненной реальности с использованием машинного зрения c OpenCV.


Приставка Pi в названии проекта "PiTanks Online" однозначно намекает на то, что в качестве серверной платформы мы использовали Raspberry Pi. Игровое поле в нашем проекте нарисовано от руки на листе бумаги.


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


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

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

Наши примеры ботов имеют следующие наборы "команд":
  • forward() - бот смещается на 1 единицу дистанции вперед (в сторону, в которую он в данный момент развернут)
  • backward() - бот смещается на 1 единицу дистанции назад
  • right() - бот поворачивается на месте на 1 градус вправо
  • left() - бот поворачивается на месте на 1 градус влево
  • radar(a) - запросить данные с радара по направлению a (угла в градусах 0..359 от текущего направления бота). Функция возвращает кортеж из символа, обозначающего объект, который обнаружил радар (t - живой бот противника, w - стена или мертвый бот противника, n - "ничего нет") и числа - расстояния до обнаруженного объекта (0 в случае объекта "ничего нет") 
  • fire() - бот производит выстрел, снаряд летит по направлению разворота бота
  • energy() - возвращает количество доступных очков действий. Очки действий тратятся на движение, стрельбу и работу радара. Пополняются каждую секунду. Введены, чтобы никто из игроков не мог получить приоритета в обработке его бота сервером. 
  • exit() - отключиться от сервера, выйти из боя
Каждая из функций forward, backward, left и right возвращает True или False давая понять боту, была ли отработана команда. Это может быть удобным, например для понимания не застрял ли бот при маневре, упершись в препятствие.

При желании можно не использовать готовые функции из примеров, а написать свои. Например, доработать функции поворота возможностью принимать параметр "угол поворота", а функции движения - параметр "дистанция". Логику управления ботом в целом можно накрутить довольно сложную. Вполне адекватно работают, например, ПИД-регуляторы по дальномерам ботов, можно использовать элементы SLAM для локализации на карте.

Написанный на Python бот может работать не только на ПК, подходит практически любое устройство с интерпретатором этого языка, например LEGO EV3, ESP8266 или смартфон:


Исходный код сервера и примеры ботов доступны по ссылке.


пятница, 30 ноября 2018 г.

Pong: Next

Читатели нашего блога вероятно помнят наш проект 2016 года Laser Pong, в котором мы создали робота для игры в одну из первых компьютерных игр – Pong. Этой осенью мы снова решили вернуться к теме двух ракеток и мяча, взглянуть на эту простую, но увлекательную игру несколько под иным углом. Причем взглянуть - в прямом смысле этого слова: робот с машинным зрением будет смотреть камерой на экран и нажимать кнопки на компьютерной клавиатуре, играя на эмуляторе классического игрового автомата. Никаких регуляторов, только хардкор в виде расчетов траекторий полета мяча и отскоков. Но и это еще не все - в рамках проекта мы разработали свой Pong, с нейросетями и прочим машинным обучением. Интересно кто победит - машинное зрение и четкая математика или нейронные сети и вероятностная логика?



Старший участник команды «Карандаш и Самоделкин» спроектировал, построил и запрограммировал:
  • Робота, использующего камеру и технологии машинного зрения для распознавания и анализа происходящего на экране, способного играть в Pong, нажимая кнопки на компьютерной клавиатуре
  • Алгоритм игры в Pong, основанный на математической модели движения мяча, учитывающий траекторию его движения, множественные столкновения со стенками и передающий роботу информацию о том, где должен оказаться мяч после серии отскоков
Робот собран на базе LEGO Mindstorms EV3 и использует мотор для нажатия 2-х кнопок на клавиатуре. Для полноценной обработки видеопотока с камеры в реальном времени вычислительные мощности блока EV3 усилены микрокомпьютером Raspberry Pi 2, объединенным с ним беспроводной сетью. EV3 выступает в роли контроллера двигателя, управляемого по сокету TCP/IP.



Все программы в проекте Pong:Next написаны на языке Python, на ПК используется ОС семейства Windows, на Raspberry Pi и EV3 – Linux.

Для робота разработаны три программы:

1. Программа для позиционирования камеры относительно монитора. Использует веб-сервер для вывода на него видеопотока с камеры.
2. Программа для блока EV3 содержит в себе socket-сервер и пропорциональный регулятор управления двигателем. Для их независимого функционирования используется модуль threading. Кроме этого робот выводит на экран EV3 текущий счет в игре.
3. Основная программа, запускаемая для Raspberry Pi, работает по следующему алгоритму:

  Однократно:
  • Устанавливаем соединение socket-клиентом с сервером на EV3
  • Берём пробный кадр с камеры и устанавливаем размеры игрового поля
  • Запускаем параллельный процесс, отвечающий за веб-сервер и вещание картинки с отладочной информацией
  Главный процесс:
  • Обновляем кадр с камеры
  • Производим контурный анализ кадра, выделяя на нем мяч и ракетки с использованием модуля OpenCV
  • По данным данного и одного из предыдущих кадров определяем направление движения мяча и рассчитываем точку его касания о линию движения ракетки. Для этого рекурсивно находим пересечения линий, образованных точками текущего положения мяча и его положением в предыдущий момент времени с боковыми частями поля до тех пор, пока не будет вычислена точка касания с линией, вдоль которой двигается ракетка.
  • Проверяем, был ли гол, основываясь на перескоке мяча в центр кадра после его пропадания около одной из ракеток
  • По данным разницы между фактическим положением ракетки в кадре и желаемым ее положением по расчетным данным, определяем, какие кнопки на клавиатуре нужно нажать
  • Отправляем на EV3 данным о кнопках, которые требуется нажать и о счете в игре для отображения на экране блока EV3

Младший участник команды «Карандаш и Самоделкин» в рамках проекта разработал:
Программа для игры в Pong для ПК воссоздает классический игровой автомат с квадратным пиксельным мячом. Реализованы «подкручивания» мяча в момент его отбивания ракеткой – для корректировки угла отскока. Алгоритм компьютерного игрока основывается на использовании простейшей однослойной нейронной сети. Мы разбиваем поле на прямоугольные зоны, в каждой из которых выделяем N направлений полета мяча. Кроме этого выделяем зоны, в которых можем находиться управляемая сетью ракетка. Такие сочетания образуют нейрон сети, выход нейрона – вероятность нажатия кнопок «вверх» и «вниз» (либо отсутствия нажатий кнопок вообще, если вероятность низка).

Алгоритм обучения сети мы провели в два этапа:

1. «Обучения с учителем», сеть учится базовым правилам игры, играя с игроком.
  • Поначалу вероятность нажатия кнопок «вверх», «вниз» и «не нажата» равны, управляемая сетью ракетка в каждый момент времени движется хаотично
  • Параллельно сеть наблюдает в какой зоне, при каком угле полета мяча и при каком начальном положении ракетки человека какие кнопки на клавиатуре он нажимает. Эту информацию она обрабатывается, накапливаясь в коэффициентах (весах) нейронов
  • Чем дольше человек обучает сеть, тем больше информации о игровых ситуациях и действиях в них будет аккумулировано в нейронной сети. Уже с первых секунд игры хаотичные движения ракетки начинают трансформироваться в более «разумные». Через час игры сеть почти полностью копирует стиль игры человека.
2. «Машинное обучение». Две копии обученной человеком сети играют друг с другом, улучшая навыки игры, разыгрывая в том числе ранее не встречавшиеся игровые ситуации:
  • Ведем историю последних N комбинаций положения мяча, угла его полета и положения управляемой сетью ракетки в этот момент
  • Если управляемая сетью ракетка отбила мяч, увеличиваем коэффициенты соответствующих нейронов, укрепляя желание сети сыграть таким же образом в схожей игровой ситуации
  • Если управляемая сетью ракетка пропустила мяч, уменьшаем коэффициенты нейронов для последних N/5 комбинаций в истории

Демонстрацию работы робота можно посмотреть на следующем видео:

суббота, 15 сентября 2018 г.

NXT for Speed

Похоже LEGO Mindstorms NXT уже всеми забыт и в мире пластиковой робототехники безраздельно правит EV3. Однако мы не спешим списывать его со счетов и в нашем сегодняшнем проекте он еще даст жару!


NXT for Speed - гоночный аркадный автомат с использованием руля и педалей. Основная цель проекта - изучение основных принципов работы с динамической векторной графикой, закрепление навыков программирования многопоточных приложений на С.


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

Инструкцию по сборке в формате LEGO Digital Designer можно скачать по ссылке.


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

Код:
int ohki=0;
int trig=1;
int r=23;
int ry=0;
int rx=0;
int mx=0;
int my;
float rz=2;
int l=0;
int c=0;
long t;
float m=500;
int f=4;
float x1=25,x2=75,y1=0,y2=0;
int rt=0;
int o=-10000000000,p=100000000;
int u=0;
float k=0.90;
int tmp =0;
float turn[48];
int ch=0;
float a=50;
int b=0;
int e=0;
int Sign(int x, int y,int v,int c)
{
  if(c==0){
    LineOut(x,y,x,y+v/2,DRAW_OPT_NORMAL);
    RectOut(x-v/4,y+v/2,v/2,v/2,DRAW_OPT_NORMAL);
  }
  else{
    LineOut(x,y,x,y+v/2,DRAW_OPT_CLEAR);
    RectOut(x-v/4,y+v/2,v/2,v/2,DRAW_OPT_CLEAR);
  }
}

int lines(int x, int y,int color)
{
  mx=x;

  if(color==0){
    RectOut(x-6,y+4,11,3,DRAW_OPT_NORMAL);
    RectOut(x-5,y+2,2,2,DRAW_OPT_NORMAL);
    RectOut(x+2,y+2,2,2,DRAW_OPT_NORMAL);
    LineOut(x+5,y+7,x+3,y+10,DRAW_OPT_NORMAL);
    LineOut(x-6,y+7,x-4,y+10,DRAW_OPT_NORMAL);
    LineOut(x-4,y+10,x+3,y+10,DRAW_OPT_NORMAL);
    if(CurrentTick()-t>500){
      TextOut(0,0,"    ");

      e=(500-m)/5;
      NumOut(0,0,e);
      t=CurrentTick();
    }
    if(MotorRotationCount(OUT_A)  > 15){
      LineOut(x+5,y+6,x+9,y+9,DRAW_OPT_NORMAL);
      LineOut(x+3,y+10,x+7,y+12,DRAW_OPT_NORMAL);
      LineOut(x-4,y+10,x-2,y+12,DRAW_OPT_NORMAL);
      LineOut(x,y+12,x+7,y+12,DRAW_OPT_NORMAL);
      LineOut(x+7,y+12,x+10,y+5,DRAW_OPT_NORMAL);
      LineOut(x+6,y+3,x+10,y+5,DRAW_OPT_NORMAL);
      LineOut(x+10,y+4,x+10,y+3,DRAW_OPT_NORMAL);
      LineOut(x+9,y+3,x+10,y+3,DRAW_OPT_NORMAL);

      if(x+10>=p+10){
        TextOut(35,20,"Over");
        rt=1;
      }
    }
    else if(MotorRotationCount(OUT_A)  <-15){

      LineOut(x+3,y+10,x+1,y+12,DRAW_OPT_NORMAL);
      LineOut(x-4,y+10,x-7,y+12,DRAW_OPT_NORMAL);
      LineOut(x+1,y+12,x-8,y+12,DRAW_OPT_NORMAL);
      LineOut(x-6,y+7,x-9,y+10,DRAW_OPT_NORMAL);
      LineOut(x-8,y+11,x-11,y+7,DRAW_OPT_NORMAL);
      LineOut(x-11,y+7,x-7,y+4,DRAW_OPT_NORMAL);
      LineOut(x-11,y+7,x-11,y+4,DRAW_OPT_NORMAL);

      LineOut(x-11,y+4,x-9,y+4,DRAW_OPT_NORMAL);
      if(x-11<=o-10){
        TextOut(35,20,"Over");
        rt=1;
      }
    }
    else{
      LineOut(x+3,y+10,x+1,y+12,DRAW_OPT_NORMAL);
      LineOut(x-4,y+10,x-2,y+12,DRAW_OPT_NORMAL);
      LineOut(x-2,y+12,x+1,y+12,DRAW_OPT_NORMAL);
      LineOut(x-5,y+11,x-5,y+11,DRAW_OPT_NORMAL);
      LineOut(x+4,y+11,x+4,y+11,DRAW_OPT_NORMAL);
      //if(x+10<=p || x-6>=o){
      //  TextOut(0,0,"Over");
      //  rt=1;
      //}
    }
    Wait(10);
  }
  else{
    RectOut(x-6,y+4,11,3,DRAW_OPT_CLEAR);
    RectOut(x-5,y+2,2,2,DRAW_OPT_CLEAR);
    RectOut(x+2,y+2,2,2,DRAW_OPT_CLEAR);
    LineOut(x+5,y+7,x+3,y+10,DRAW_OPT_CLEAR);
    LineOut(x-6,y+7,x-4,y+10,DRAW_OPT_CLEAR);
    LineOut(x-4,y+10,x+3,y+10,DRAW_OPT_CLEAR);

    LineOut(x+5,y+6,x+9,y+9,DRAW_OPT_CLEAR);
    LineOut(x+3,y+10,x+7,y+12,DRAW_OPT_CLEAR);
    LineOut(x-4,y+10,x,y+12,DRAW_OPT_CLEAR);
    LineOut(x,y+12,x+7,y+12,DRAW_OPT_CLEAR);
    LineOut(x+7,y+12,x+10,y+5,DRAW_OPT_CLEAR);
    LineOut(x+6,y+3,x+10,y+5,DRAW_OPT_CLEAR);
    LineOut(x+10,y+4,x+10,y+3,DRAW_OPT_CLEAR);
    LineOut(x+9,y+3,x+10,y+3,DRAW_OPT_CLEAR);
    LineOut(x+3,y+10,x+1,y+12,DRAW_OPT_CLEAR);
    LineOut(x-4,y+10,x-7,y+12,DRAW_OPT_CLEAR);
    LineOut(x+1,y+12,x-8,y+12,DRAW_OPT_CLEAR);
    LineOut(x-6,y+7,x-9,y+10,DRAW_OPT_CLEAR);
    LineOut(x-8,y+11,x-11,y+7,DRAW_OPT_CLEAR);
    LineOut(x-11,y+7,x-7,y+4,DRAW_OPT_CLEAR);
    LineOut(x-11,y+7,x-11,y+4,DRAW_OPT_CLEAR);
    LineOut(x-11,y+4,x-9,y+4,DRAW_OPT_CLEAR);
  }
  return 0;
}

task mot()
{
  Wait(3000);
  float P=0.7;
  //P=0.3*e;
  float Kp=e/100;
  float P2=0.7;
  float P3=0.7;
  float ERR1=0;
  float ERR2=0;
  float ERR3=0;
  float u1=0;
  float u2=0;
  float u3=0;
  while(1){
    if(rt==1){
      break;
    }

    float Kp=e/100;
    ERR1=0-MotorRotationCount(OUT_A);
    u1=P*ERR1*Kp;
    ERR2=0-MotorRotationCount(OUT_B);
    u2=P2*ERR2;
    ERR3=0-MotorRotationCount(OUT_C);
    u3=P3*ERR3;
    if(mx-11<=o+3){
      u1=Random(70)-35;
    }
    if(mx+10>=p-3){
      u1=Random(70)-35;
    }

    if(u1>100)u1=100;
    if(u1<-100)u1=-100;
    if(u1<0){
      OnRev(OUT_A,-1*u1);
    }
    else{
      OnFwd(OUT_A,u1);
    }
    if(u2>100)u2=100;
    if(u2<-100)u2=-100;
    if(u2<0){
      OnRev(OUT_B,-1*u2);
    }
    else{
      OnFwd(OUT_B,u2);
    }
    if(u3>100)u3=100;
    if(u3<-100)u3=-100;
    if(u3<0){
      OnRev(OUT_C,-1*u3);
    }
    else{
      OnFwd(OUT_C,u2);
    }
    Wait(100);
  }
}

task road()
{
  for(int i =0;i<47;i++){
    turn[i]=0;
  }
  while(true){

    ch++;
    f--;
    if(f==0){
      f=4;
    }

    l+=1;

    for(int i =0;i<23;i+=1){

      LineOut(25+turn[i]+i*0.9,i*2,25+turn[i+1]+(i+1)*0.9,i*2+3,DRAW_OPT_NORMAL);
      LineOut(75+turn[i]-i*0.9,i*2,75+turn[i+1]-(i+1)*0.9,i*2+3,DRAW_OPT_NORMAL);
      if(i==r){
        if(trig==1){
          rx=25+turn[i]+i*0.9-5;
          ry=i*2;
        }
        else{
          rx=75+turn[i]-i*0.9+5;
          ry=i*2;
        }
      }
      if(i%4==f  && i>5){
        LineOut(50+turn[i],i*2,50+turn[i+1],i*2+3,DRAW_OPT_NORMAL );
      }
    }
    Sign(rx,ry,rz,0);
    m+=5;
    m+=m*0.2;
    if(m>500){
      m=500;
    }
    if(m<0){
      m=0;
    }
    if(abs(MotorRotationCount(OUT_B))> 5){
      if(m<=500){
        m-=1.7*abs(0-abs(MotorRotationCount(OUT_B))*2);

      }
      if(m>500){
        m=500;
      }
      if(m<0){
        m=0;
      }
    }
    if(abs(MotorRotationCount(OUT_C))> 5){
      if(m>=0){
        m+=1.7*abs(0-abs(MotorRotationCount(OUT_C)));

      }
      if(m>500){
        m=500;
      }
      if(m<0){
        m=0;
      }
    }


    Wait(m);

    for(int i =0;i<23;i+=1){
      LineOut(25+turn[i]+i*0.9,i*2,25+turn[i+1]+(i+1)*0.9,i*2+3,DRAW_OPT_CLEAR);
      LineOut(75+turn[i]-i*0.9,i*2,75+turn[i+1]-(i+1)*0.9,i*2+3,DRAW_OPT_CLEAR);

      if(i%4==f && i>5)
      {
        LineOut(50+turn[i],i*2,50+turn[i+1],i*2+3,DRAW_OPT_CLEAR);
      }
      if(i==3){
        o=25+turn[i];
        p=75+turn[i];
      }
    }
    Sign(rx,ry,rz,1);
    rz+=0.5;
    r--;

    if(rz>=13){
      rz=2;
      r=23;
      trig=trig*-1;
    }
    for(int i =0;i<47;i++){
      turn[i]=turn[i+1];
    }
    if(ch==23){
      ohki+=1;
      ch=0;
      u = Random(100);
      turn[47]=u-50;
      for(int i =23;i<47;i++){
        turn[i]=turn[i-1]*k+turn[47]*(1-k);
      }
    }

    if(rt==1){
      break;
    }
    if(l==1){
      LineOut(70,0,70,35,DRAW_OPT_NORMAL);
      LineOut(70,35,30,35,DRAW_OPT_NORMAL);
      LineOut(70,17,30,17,DRAW_OPT_NORMAL);
      LineOut(70,0,30,0,DRAW_OPT_NORMAL);
      Wait(1000);
      LineOut(70,0,70,35,DRAW_OPT_CLEAR);
      LineOut(70,35,30,35,DRAW_OPT_CLEAR);
      LineOut(70,17,30,17,DRAW_OPT_CLEAR);
      LineOut(70,0,30,0,DRAW_OPT_CLEAR);
      LineOut(30,0,70,35,DRAW_OPT_NORMAL);
      LineOut(30,35,70,35,DRAW_OPT_NORMAL);
      LineOut(70,0,30,0,DRAW_OPT_NORMAL);

      Wait(1000);
      LineOut(30,0,70,35,DRAW_OPT_CLEAR);
      LineOut(30,35,70,35,DRAW_OPT_CLEAR);
      LineOut(70,0,30,0,DRAW_OPT_CLEAR);
      LineOut(30,17,70,35,DRAW_OPT_NORMAL);
      LineOut(70,0,70,35,DRAW_OPT_NORMAL);

      Wait(1000);
      LineOut(30,17,70,35,DRAW_OPT_CLEAR);
      LineOut(70,0,70,35,DRAW_OPT_CLEAR);
    }
  }
}

task main()
{
  t=CurrentTick();
  SetSensorTouch(IN_4);
  start road;
  start mot;

  ClearScreen();
  LineOut(0,48,100,48,DRAW_OPT_NORMAL);

  lines(a,b,0);

  while(true){
    NumOut(80,0,ohki);
    c=30-((a-50)/2);
    LineOut(c-50,49,c-30,53,DRAW_OPT_NORMAL);
    LineOut(c-10,49,c-30,53,DRAW_OPT_NORMAL);
    LineOut(c-10,49,c,55,DRAW_OPT_NORMAL);
    LineOut(c+10,49,c,55,DRAW_OPT_NORMAL);
    LineOut(c+5,51,c+15,55,DRAW_OPT_NORMAL);
    LineOut(c+25,51,c+15,55,DRAW_OPT_NORMAL);
    LineOut(c+20,51,c+35,55,DRAW_OPT_NORMAL);
    LineOut(c+40,49,c+35,55,DRAW_OPT_NORMAL);
    LineOut(c+40,51,c+55,55,DRAW_OPT_NORMAL);
    LineOut(c+60,49,c+55,55,DRAW_OPT_NORMAL);
    LineOut(c+60,51,c+85,55,DRAW_OPT_NORMAL);
    LineOut(c+100,49,c+85,55,DRAW_OPT_NORMAL);
    lines(a,b,1);
    if(abs(MotorRotationCount(OUT_A))>=15){
      a+=(MotorRotationCount(OUT_A))/100.0;
    }
    LineOut(c-10,49,c,55,DRAW_OPT_CLEAR);
    LineOut(c+10,49,c,55,DRAW_OPT_CLEAR);
    LineOut(c+5,51,c+15,55,DRAW_OPT_CLEAR);
    LineOut(c+25,51,c+15,55,DRAW_OPT_CLEAR);
    LineOut(c+20,51,c+35,55,DRAW_OPT_CLEAR);
    LineOut(c+40,49,c+35,55,DRAW_OPT_CLEAR);
    LineOut(c+40,51,c+55,55,DRAW_OPT_CLEAR);
    LineOut(c+60,49,c+55,55,DRAW_OPT_CLEAR);
    LineOut(c+60,51,c+85,55,DRAW_OPT_CLEAR);
    LineOut(c+100,49,c+85,55,DRAW_OPT_CLEAR);
    LineOut(c-50,49,c-30,53,DRAW_OPT_CLEAR);
    LineOut(c-10,49,c-30,53,DRAW_OPT_CLEAR);
    lines(a,b,0);
    if(rt==1){
      break;
    }
  }
  Wait(1500);
}



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