Главная » Хабрахабр » [Из песочницы] Использование многослойной нейронной сети для обхода препятствий в играх

[Из песочницы] Использование многослойной нейронной сети для обхода препятствий в играх

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

Концепция

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

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

  1. На четыре входа обученной нейросети подаются четыре значения от датчиков столкновения
  2. Рассчитывается состояние нейросети. На выходе получаем три значения:
    a. Сила поворота модели против часовой стрелки (принимает значение от 0 до 1)
    b. Сила поворота модели по часовой стрелке (принимает значение от 0 до 1)
    c. Тормозящее ускорение (принимает значение от 0 до 1)
  3. К модели прилагаются усилия с соответствующими коэффициентами.

Реализация

Первом делом, я реализовал в Unity класс neuroNet. Честно говоря, я понятия не имел, получится ли из этой затеи хоть что-нибудь. По ходу дела сразу же возник вопрос в количестве слоёв сети. Не буду подробно останавливаться на коде классе, поскольку он представляет собой классический многослойный перцептрон. После череды экспериментов, я остановился на двенадцати слоях (три базовых состояния на четыре входа). Сколько их требуется, чтобы с одной стороны обеспечить нужную ёмкость, а с другой – приемлемую скорость расчётов?

Для этого пришлось создавать отдельное приложение, где использовался тот же самый класс neuroNet. Далее потребовалось реализовать процесс обучения нейронной сети. Первоначально я хотел использовать значения, полученные непосредственно от игрового приложения. И теперь во весь рост встала проблема данных для обучения. Но, посмотрев на получившийся результат, я впал в уныние. Для этого я организовал логирование данных от датчиков, чтобы в дальнейшем для каждого набора значений четырёх датчиков указывать программе обучения правильные значения выходов. Это очень важно для успешного обучения нейросети. Дело в том, что мало для каждого набора четырёх значений датчиков указать адекватное значение, нужно, чтобы эти значения были непротиворечивыми. К тому же не было никакой гарантии, что получившаяся выборка представляла все возможные ситуации.

За базовые варианты были приняты значения: 0. Альтернативным решением оказалась вручную составленная таблица базовых вариантов значений датчиков и выходов. 5 – препятствие на полпути, 1 – направление свободно. 01 – препятствие близко, 0. Это позволило сократить объём обучающей выборки.

Датчик 1

Датчик 2

Датчик 3

Датчик 4

Поворот по часовой

Поворот против часовой

Торможение

0,01

0,01

0,01

0,01

0,01

0,01

0,01

0,01

0,01

0,01

0,5

0,01

0,01

0,01

0,01

0,01

0,01

0,999

0,01

0,01

0,01

0,01

0,01

0,5

0,01

0,999

0,01

0,01

0,01

0,01

0,5

0,5

0,999

0,01

0,01

0,01

0,01

0,5

0,999

0,999

0,01

0,5

0,01

0,01

0,999

0,01

0,999

0,01

0,5

0,01

0,01

0,999

0,5

0,999

0,01

0,999

0,01

0,01

0,999

0,999

0,999

0,01

0,999

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

Результаты

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

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

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

1. Простой обход одного препятствия

Как видим, затруднений обход не вызвал.

2. Два препятствия (вариант 1)

Лёгкая задача. Модель легко нашла проход между двумя строениями.

3. Два препятствия (вариант 2)

Здания стоят ближе, но модель находит проход.

4. Два препятствия (вариант 3)

Вариант сложнее, но всё ещё решаем.

5. Три препятствия

Задача оказалась решена довольно быстро.

6. Тупик

На первых 30 секундах видео показано, что модель беспомощно барахтается в простой конфигурации зданий. Тут у модели возникли проблемы. Проблема тут скорее всего кроется не столько в нейросетевой модели, сколько в основном алгоритме движения по маршруту — он настойчиво пытается вернуть корабль на курс, несмотря на отчаянные попытки избежать столкновения.

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

Заключение

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

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

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


Оставить комментарий

Ваш email нигде не будет показан
Обязательные для заполнения поля помечены *

*

x

Ещё Hi-Tech Интересное!

Trekz Air — как на самом деле звучат наушники с костной проводимостью

Осенний привет хабровчанам! Большая аудиотека пополняется благодаря стриминговым сервисам. По жизни я — меломан, и провожу с музыкой непростительно много времени — по 8-12 часов в день. Всё сокровенное храню на плеере, используя наушники с большим количеством излучателей: TBA04, NuForce ...

[Перевод] Делаем проект по машинному обучению на Python. Часть 3

Перевод A Complete Machine Learning Walk-Through in Python: Part Three В этой статье мы постараемся разобраться, как созданная нами модель делает прогнозы и что она может рассказать о решаемой нами задаче. Многим не нравится, что модели машинного обучения представляют собой ...