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

Neural Network Racing - Part 2

Запись от Storm23 размещена 10.04.2018 в 21:24

Продолжаем.
После создания физической модели автомобиля, сделаем модель трека и сенсоры для нашей машинки.

Модель трека
Обычно, моделирование трека делают в 3D движках. Это делается потому, что в 3D движке типа Unity можно сделать стенки трека в виде коллайдеров и затем искать пересечение лучей сенсоров автомобиля со стенками. Таким образом реализуется машинное зрение, с помощью которого машина "видит" трек.
Я так делать не буду. Во-первых потому что я хочу сделать движок независимым от Unity. А во-вторых, моделирование в Unity будет относительно медленным. Если реализовать трек и зрение без Unity, то можно сделать высокоскоростное моделирование (вне реального времени) с помощью которого мы очень быстро будем обучать нашу нейронную сеть.

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

Процесс рисования трека - быстро и удобно


Трек представляет собой двусвязный кольцевой список точек типа PathPoint:

Класс PathPoint
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
    [Serializable]
    public class PathPoint
    {
        public Vector2 Point; //position in 2D space
 
        public float Width; //width of road (m)
 
        [XmlIgnore]
        public PathPoint Prev;//previous Point
        [XmlIgnore]
        public PathPoint Next;//next Point
        [XmlIgnore]
        public float DistanceFromStart;//distance from start to this point along track (m)
        [XmlIgnore]
        public Segment LeftWall; //segment of left wall
        [XmlIgnore]
        public Segment RightWall;//segment of right wall
        [XmlIgnore]
        public Vector2 Normal;//normal to direction of road
        [XmlIgnore]
        public Vector2 Dir; //direction of road
        [XmlIgnore]
        public float Length; //length of this part of track (m)
    }


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



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

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

Редактор трека позволяет быстро нарисовать трек, отредактировать его, задать ширину трека в разных точках, задать коэффициент трения для трассы.
В результате можно сделать довольно сложные трассы, например такую:



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

Зрение для автомобиля

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



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

Поиск расстояний до ближайших стен трека
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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
        public static void GetDistances(Car car, Track track, float[] sensors, int startIndex)
        {
            //get segments of walls
            {
                segments.Clear();
                var p = car.CapturedPoint;
                segments.Add(p.LeftWall);
                segments.Add(p.RightWall);
                while (track.DistanceBetweenPoints(car.CapturedPoint, p) <= 130)
                {
                    p = p.Next;
                    segments.Add(p.LeftWall);
                    segments.Add(p.RightWall);
                }
            }
            //enumarate sensors of car
            var angle = car.LookAt.Angle();
            for (int i = 0; i < Rays.Length; i++)
            {
                var p = Rays[i];
                p = p.Rotate(angle);//rotate sensor by car orientation
                var ray = new Segment(car.Pos, car.Pos + p);
                sensors[startIndex + i] = GetDistance(ray, segments);//calc distance to nearest wall segment
            }
        }
 
        public static float GetDistance(Segment seg, List<Segment> segments)
        {
            var minDist = seg.From.DistanceTo(seg.To);
 
            for (int i = 0; i < segments.Count; i++)
            {
                var s = segments[i];
                var p = SegmentIntersection.FindIntersection(seg.From, seg.To, s.From, s.To);
                if (p != null)
                {
                    var dist = p.Value.DistanceTo(seg.From);
                    if (dist < minDist)
                        minDist = dist;
                    return minDist;
                }
            }
 
            return minDist;
        }
 
        // Find the point of intersection between
        // the lines p1 --> p2 and p3 --> p4.
        public static PointF? FindIntersection(PointF p1, PointF p2, PointF p3, PointF p4)
        {
            // Get the segments' parameters.
            float dx12 = p2.X - p1.X;
            float dy12 = p2.Y - p1.Y;
            float dx34 = p4.X - p3.X;
            float dy34 = p4.Y - p3.Y;
 
            // Solve for t1 and t2
            float denominator = (dy12 * dx34 - dx12 * dy34);
 
            float t1 = ((p1.X - p3.X) * dy34 + (p3.Y - p1.Y) * dx34) / denominator;
            if (float.IsInfinity(t1))
            {
                // The lines are parallel (or close enough to it).
                return null;
            }
 
            float t2 = ((p3.X - p1.X) * dy12 + (p1.Y - p3.Y) * dx12) / -denominator;
 
            // The segments intersect if t1 and t2 are between 0 and 1.
            var segments_intersect =
                ((t1 >= 0) && (t1 <= 1) &&
                 (t2 >= 0) && (t2 <= 1));
 
            // Find the point of intersection.
            if(segments_intersect)
                return new PointF(p1.X + dx12 * t1, p1.Y + dy12 * t1);
 
            return null;
        }


Полный код для трека и редактора треков будет приведен в дальнейших частях статьи.

To be continue ...
Размещено в Без категории
Просмотров 399 Комментарии 0
Всего комментариев 0
Комментарии
 
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin® Version 3.8.9
Copyright ©2000 - 2018, vBulletin Solutions, Inc.
Рейтинг@Mail.ru