Форум программистов, компьютерный форум, киберфорум
Наши страницы
Storm23
Войти
Регистрация
Восстановить пароль
Рейтинг: 5.00. Голосов: 4.

Neural Network Racing - Part 4

Запись от Storm23 размещена 12.04.2018 в 02:45
Обновил(-а) Storm23 18.04.2018 в 11:04

Итак, после того, как ИИ готово, можно перенести весь проект в Unity и сделать полноценную гонку в 3D.

Экспорт трека в Unity

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

BuildTerrain
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
    void BuildTerrain(Terrain t, Texture2D roadMap)
    {
        float[,,] maps = t.terrainData.GetAlphamaps(0, 0, t.terrainData.alphamapWidth, t.terrainData.alphamapHeight);
 
        var w = t.terrainData.alphamapWidth;
        var h = t.terrainData.alphamapHeight;
        var layers = t.terrainData.alphamapLayers;
 
        for (int y = 0; y < h; y++)
        {
            for (int x = 0; x < w; x++)
            {
                var pixel = roadMap.GetPixelBilinear((float)y / h, (float)x / w);
 
                var total = pixel.r + pixel.g + pixel.b;
                if (total > 1) total = 1;
                var k = 1 - Mathf.Sqrt(total);
 
                maps[x, y, 1] = pixel.r / total;
                maps[x, y, 2] = pixel.g / total;
                maps[x, y, 3] = pixel.b / total;
 
                maps[x, y, 0] *= k;
 
                for (int i = 4; i < layers; i++)
                    maps[x, y, i] *= k;
            }
        }
 
        t.terrainData.SetAlphamaps(0, 0, maps);
    }


В результате поверх террейна накладывается наш трек:



Помимо текстуры трека, мы открываем в Unity и XML файл самого трека.

Модель машины

Скачиваем простенькую модель машинки, у которой корпус и колеса идут отдельными мешами.
К машинке вешаем Rigidbody и коллайдер.
Здесь нужно более подробно остановиться на том, как будет управляться наша машинка.
Поскольку нейронная сеть обучалась на плоском двумерном треке, то и ездить они должна по плоскому двумерному треку. Для этого мы загружаем трек, который использовался при симуляции и наша НС будет ездить именно в этом двумерном треке. О трехмерной модели и террейне наша система управления машинкой ничего не знает и не должна знать. Просто к двумерной позиции мы привяжем нашу трехмерную модель машинки.
Из-за того, что наш симулятор ничего не знает о столкновениях и о трехмерном мире, мы не можем применить стандартную физику Unity к нашим машинкам.
Поэтому, для Rigidbody нашей машинки мы выставляем свободу движения только по оси Y, а вращения - только по осям X и Z.
Координаты X и Z будут выставляться из нашей двумерной модели - это координаты машины на плоскости, а вращение по оси Y будет совпадать с вращением вектора LookAt нашей машинки.
Также, нам нужно выключить стандартные столкновения между машинками. Это делается через настройки коллайдеров слоев.



Теперь система управления может свободно устанавливать координаты X и Z машинки, а также угол вращения по оси Y, не боясь что в это дело вмешается физика 3D движка.

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

OnTriggerStay
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
    protected void OnTriggerStay(Collider other)
    {
        if (other.gameObject.layer != LayerMask.NameToLayer("CarCollider"))
            return;
 
        var cc2 = other.gameObject.GetComponentInParent<CarController>();
 
        var d = (cc2.transform.position - transform.position).normalized;
        var dd = new System.Vector2(d.x, -d.z);
        var DDt = dd.Projection(Car.LookAt);
        var DDn = (dd - DDt).Normalized();
        DDt = DDt.Normalized();
        var newPos = Car.Pos - DDn * 0.2f - DDt * 0.05f;
        Car.Pos = Car.Pos.Lerp(newPos, Time.deltaTime * 2);
        Car.Velocity -= DDn * 0.2f + DDt * 0.04f;
    }


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

Дообучение нейронной сети

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

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

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

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

Остальной код проекта в Unity я не буду описывать. Он довольно тривиальный и не имеет особого отношения к теме нейронных сетей.

Результат

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

Вот как это выглядит (красная машинка управляется игроком, остальные - ботами):


(если gif не открылся, скачать можно здесь)

На youtube можно посмотреть как выглядит вождение глазами бота (все машинки на видео, включая желтую машинку - управляются ботами):



А здесь можно скачать демо-версию игры: https://www.dropbox.com/s/obezv8e0o4xus3k/NNRacing_Demo.zip?dl=0

Управление - стандартное WASD, тормоз - Space.
С помощью клавиш 1-7 можно переключать камеру на ботов. Клавиша 0 - переключает камеру обратно на машину игрока.

Можете попробовать посоревноваться с ботами. Интересно, какое место вы займете? ;)
Размещено в Без категории
Просмотров 726 Комментарии 4
Всего комментариев 4
Комментарии
  1. Старый комментарий
    Аватар для Sanya_sa
    Запись от Sanya_sa размещена 12.04.2018 в 15:56 Sanya_sa вне форума
  2. Старый комментарий
    Аватар для bedvit
    Интересный цикл статей и реализация обучения нейронных сетей для ботов в Unity.
    Было познавательно. Благодарю за материал!
    Запись от bedvit размещена 12.04.2018 в 18:46 bedvit вне форума
  3. Старый комментарий
    Отличный материал!
    А почему не хотите подобное и на хабре публиковать?
    Запись от sldp размещена 18.04.2018 в 09:53 sldp вне форума
  4. Старый комментарий
    Аватар для Рядовой
    Сказать, что статьи шикарны - ничего не сказать. Такой массы примеров по нс и моделированию в рунете наверно просто нет. Да еще и в одном месте. Лично для меня одна реализация генетического алгоритма - бомба, очень интересно.
    Немного недопонял момент с физикой автомобиля. При нажатии и отпускании кнопки поворота, руль не встает в исходное положение, и машина несется в заданом направлении. Получается стартуем ровно - руль вправо - машина несется вправо - руль влево - машина едет влево, ровно никак не поставить. Так и едем зигзагами.
    Боты чертовски хороши - обогнать не удалось ни одного. Надо чуть попроще)
    Нашел баг, на самых низких настройках графики запредельная скорость. Играть невозможно.
    Запись от Рядовой размещена 27.06.2018 в 13:09 Рядовой вне форума
 
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin® Version 3.8.9
Copyright ©2000 - 2018, vBulletin Solutions, Inc.
Рейтинг@Mail.ru