-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathBirdAgentController.cs
More file actions
311 lines (274 loc) · 14.6 KB
/
BirdAgentController.cs
File metadata and controls
311 lines (274 loc) · 14.6 KB
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
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
using Unity.MLAgents;
using Unity.MLAgents.Sensors;
using UnityEngine;
public class BirdAgentController : Agent
{
public enum Heuristica
{
Joystick,
CurvaBezier
};
public Heuristica heuristica;
public bool normal;
private BirdMovementController controller;
private FlyMovement fly;
private BirdLookAtController lookAtController;
public Transform nextCheckpoint;
// Bezier attributes
float totalDistanceToTarget;
Vector3 startPoint;
Vector3 p1;
// Agent
private Vector3 startPosition;
private Vector3 checkpointDir;
private Vector3 myDir;
private Vector3 myLookAtDir;
private float startDistanceToTarget;
private float distanceToTarget;
private float heightDifference;
private float dotProduct_LookAt;
private float dotProduct_Body;
private float angleY_LookAt;
private float angleX_LookAt;
private float angleY_Body;
private float angleX_Body;
private readonly float maxSpeed = 7 * (1 + 2 * Mathf.Sin(40 * Mathf.PI / 180)); // Maxima velocidad a la que puede spawnear un pajaro
private float startEpisodeTime;
private bool reset;
private Transform checkpoint1;
// Start is called before the first frame update
public override void Initialize()
{
controller = GetComponentInChildren<BirdMovementController>();
fly = GetComponentInChildren<FlyMovement>();
lookAtController = GetComponentInChildren<BirdLookAtController>();
startPosition = controller.transform.position;
checkpoint1 = GameObject.Find("Sphere (1)").transform;
}
public override void OnEpisodeBegin()
{
reset = false;
startEpisodeTime = Time.time;
SetNextCheckpoint(checkpoint1);
GetComponent<BirdRaceController>().nextCheckpoint = checkpoint1;
float randomX = Random.Range(-40, 89);
float randomY = Random.Range(0, 360);
// SPAWNS ALEATORIOS
controller.myTransform.position = startPosition + Random.insideUnitSphere * 40;
controller.myTransform.rotation = Quaternion.Euler(randomX, randomY, 0);
lookAtController.myTransform.rotation = controller.myTransform.rotation;
fly.currentSpeed = Random.Range(4, maxSpeed);
checkpointDir = nextCheckpoint.position - controller.myTransform.position;
myDir = lookAtController.myTransform.forward;
startDistanceToTarget = checkpointDir.sqrMagnitude;
distanceToTarget = startDistanceToTarget;
dotProduct_LookAt = Vector3.Dot(checkpointDir.normalized, myDir);
GenerateBezierCurve();
}
public override void CollectObservations(VectorSensor sensor)
{
checkpointDir = nextCheckpoint.position - controller.myTransform.position;
myDir = lookAtController.myTransform.forward;
myLookAtDir = lookAtController.myTransform.forward;
// Calculo de observaciones
distanceToTarget = checkpointDir.sqrMagnitude;
heightDifference = (controller.myTransform.position.y - nextCheckpoint.position.y);
dotProduct_LookAt = Vector3.Dot(checkpointDir.normalized, myLookAtDir);
dotProduct_Body = Vector3.Dot(checkpointDir.normalized, myDir);
angleY_LookAt = Vector3.SignedAngle(myLookAtDir, checkpointDir, Vector3.up);
angleX_LookAt = Mathf.DeltaAngle(0, lookAtController.myTransform.eulerAngles.x);
angleY_Body = Vector3.SignedAngle(myDir, checkpointDir, Vector3.up);
angleX_Body = Mathf.DeltaAngle(0, controller.myTransform.eulerAngles.x);
// ---------------------------------------- CASOS DE ESTUDIO ----------------------------------------------
// Caso 1.Solo posiciones - no aprende(7 variables)
//sensor.AddObservation(controller.myTransform.position); // Posicion global de pajaro
//sensor.AddObservation(nextCheckpoint.position); // Posicion global del checkpoint pajaro
//sensor.AddObservation(fly.currentSpeed); // Velocidad actual del pajaro
// Caso 2. Posiciones globales (10 variables)
//sensor.AddObservation(controller.myTransform.position); // Posicion global de pajaro
//sensor.AddObservation(nextCheckpoint.position); // Posicion global del checkpoint pajaro
//sensor.AddObservation(myLookAtDir); // Direccion del pajaro
//sensor.AddObservation(fly.currentSpeed); // Velocidad actual del pajaro
//// Caso 3. Posiciones relativas (7 variables)
//sensor.AddObservation(checkpointDir); // Posicion del checkpoint respecto al pajaro, o direccion hacia el checkpoint
//sensor.AddObservation(myLookAtDir); // Direccion del pajaro
//sensor.AddObservation(fly.currentSpeed); // Velocidad actual del pajaro
// Caso 4. No normalizado o simplificado (5 variables)
//sensor.AddObservation(distanceToTarget); // Distancia del pajaro al objetivo
//sensor.AddObservation(heightDifference); // Altura del pajaro respecto al objetivo
//sensor.AddObservation(angleAxisY); // Angulo horizontal (eje Y) del pajaro respecto al objetivo
//sensor.AddObservation(angleX); // Inclinacion del pajaro (eje X)
//sensor.AddObservation(fly.currentSpeed); // Velocidad actual del pajaro
// Caso 5. Normalizado (5 variables)
//sensor.AddObservation(distanceToTarget / 6800); // Distancia del pajaro al objetivo
//sensor.AddObservation(heightDifference / 40); // Altura del pajaro respecto al objetivo
//sensor.AddObservation(angleY_LookAt / 180); // Angulo horizontal (eje Y) del pajaro respecto al objetivo
//sensor.AddObservation(angleX_LookAt / 90); // Inclinacion del pajaro (eje X)
//sensor.AddObservation(fly.currentSpeed / maxSpeed); // Velocidad actual del pajaro
// Caso optimo (7 variables)
sensor.AddObservation(distanceToTarget / 6800); // Distancia del pajaro al objetivo
sensor.AddObservation(heightDifference / 40); // Altura del pajaro respecto al objetivo
sensor.AddObservation(angleY_LookAt / 180); // Angulo horizontal (eje Y) del pajaro respecto al objetivo
sensor.AddObservation(angleX_LookAt / 90); // Inclinacion del pajaro (eje X)
sensor.AddObservation(angleY_Body / 180); // Angulo horizontal (eje Y) del cuerpo del pajaro respecto al objetivo
sensor.AddObservation(angleX_Body / 90); // Inclinacion del cuerpo del pajaro (eje X)
sensor.AddObservation(fly.currentSpeed / maxSpeed); // Velocidad actual del pajaro
}
public override void OnActionReceived(float[] vectorAction)
{
// Acciones recibidas
lookAtController.rotarInclinacion.Value = Mathf.Clamp(vectorAction[0], -1, 1) * 120 * Time.deltaTime; // Arriba-abajo
lookAtController.rotarDireccion.Value = Mathf.Clamp(vectorAction[1], -1, 1) * 125 * Time.deltaTime; // Izq-Der
controller.inputV.Value = 1;
}
private void FixedUpdate()
{
// ENTRENAMIENTO NORMALIZADO
//AddReward(-0.002f); // Pierde recompensa con el tiempo (así intentará llegar en el menor tiempo posible y sin perder tiempo)
//if (dotProduct_LookAt > 0)
// AddReward(0.1f * dotProduct_LookAt); // Gana más recompensa cuanto más orientado al objetivo esté
//AddReward(0.001f * (fly.currentSpeed / fly.maxSpeed)); // Fomenta que el pajaro vaya a la maxima velocidad posible
//// POSITIVAS Y NEGATIVAS
//AddReward(-0.002f); // Pierde recompensa con el tiempo (así intentará llegar en el menor tiempo posible y sin perder tiempo)
//AddReward(0.1f * dotProduct); // Gana recompensa cuanto más orientado al objetivo esté y la pierde si va en sentido contrario
//AddReward(0.001f * (fly.currentSpeed / fly.maxSpeed)); // Fomenta que el pajaro vaya a la maxima velocidad posible
// VELOCIDAD ALTA
//AddReward(-0.002f); // Pierde recompensa con el tiempo (así intentará llegar en el menor tiempo posible y sin perder tiempo)
//if (dotProduct > 0)
// AddReward(0.1f * dotProduct); // Gana más recompensa cuanto más orientado al objetivo esté
//AddReward(0.1f * (fly.currentSpeed / fly.maxSpeed)); // Fomenta que el pajaro vaya a la maxima velocidad posible
// RECOMPENSAS ALTAS (ESCALADAS)
//AddReward(-0.002f * 20); // Pierde recompensa con el tiempo (así intentará llegar en el menor tiempo posible y sin perder tiempo)
//if (dotProduct > 0)
// AddReward(0.1f * dotProduct * 20); // Gana más recompensa cuanto más orientado al objetivo esté
//AddReward(0.001f * (fly.currentSpeed / fly.maxSpeed) * 20); // Fomenta que el pajaro vaya a la maxima velocidad posible
// POCAS RECOMPENSAS (MENOS VARIEDAD)
//AddReward(-0.002f); // Pierde recompensa con el tiempo (así intentará llegar en el menor tiempo posible y sin perder tiempo)
// ENTRENAMIENTO CORTO
//AddReward(-0.004f); // Pierde recompensa con el tiempo (así intentará llegar en el menor tiempo posible y sin perder tiempo)
//if (dotProduct > 0)
// AddReward(0.1f * dotProduct); // Gana más recompensa cuanto más orientado al objetivo esté
// ENTRENAMIENTO OPTIMO
AddReward(-0.02f); // Pierde recompensa con el tiempo (así intentará llegar en el menor tiempo posible y sin perder tiempo)
if (dotProduct_LookAt > 0)
AddReward(0.005f * dotProduct_LookAt); // Gana más recompensa cuanto más orientado al objetivo esté
if (dotProduct_Body > 0)
AddReward(0.005f * dotProduct_Body); // Gana más recompensa cuanto más orientado al objetivo esté
AddReward(0.001f * (fly.currentSpeed / fly.maxSpeed)); // Fomenta que el pajaro vaya a la maxima velocidad posible
if (reset || Time.time > startEpisodeTime + 10)
{
EndEpisode();
}
}
public void Reset()
{
reset = true;
}
// Algoritmo de IA tradicional basado en la curva Bezier
public override void Heuristic(float[] actionsOut)
{
if (heuristica == Heuristica.Joystick)
{
// Arriba-abajo
actionsOut[0] = -Input.GetAxis("Camera Y");
// Izquierda-derecha
actionsOut[1] = Input.GetAxis("Camera X");
}
else
{
float distanceToTarget = Vector3.SqrMagnitude(nextCheckpoint.position - controller.myTransform.position);
Vector3 directionToTarget = (nextCheckpoint.position - controller.myTransform.position).normalized;
float angleAxisY = Vector3.SignedAngle(lookAtController.myTransform.forward, directionToTarget, Vector3.up);
float t = (totalDistanceToTarget - distanceToTarget) / totalDistanceToTarget;
float bezierY = CalculateQuadraticBezierPoint(t, startPoint, p1, nextCheckpoint.position).y;
// Si estoy por encima de la curva
if (controller.myTransform.position.y > bezierY)
{
// Si estoy a menos de 5 metros sobre la curva
if (controller.myTransform.position.y - bezierY < 5)
{
float idealAngle = (controller.myTransform.position.y - bezierY) / 5 * lookAtController.maxAngleX;
// Empiezo a enderezar, calculo el ángulo ideal del pajaro en función de lo cerca
// que está de la curva.
// Si la separación es de 5 metros o más, el ángulo del pájaro será el máximo permitido.
// Si la separación es de 0 metros, el ángulo del pájaro deberá ser 0.
if (lookAtController.myTransform.eulerAngles.x > idealAngle && lookAtController.myTransform.eulerAngles.x < 90)
{
actionsOut[0] = -1; // SUBE
}
else
{
actionsOut[0] = 1; // BAJA
}
}
else
{
actionsOut[0] = 1; // BAJA
}
}
// Si estoy por debajo de la curva
else
{
// Si estoy a menos de 2 metros bajo la curva
if (controller.myTransform.position.y - bezierY > -2)
{
float idealAngle = ((bezierY - controller.myTransform.position.y) / 2) * lookAtController.minAngleX;
// Empiezo a enderezar, calculo el ángulo ideal del pajaro en funcion de lo cerca
// que está de la curva.
// Si la separación es de 1 metro o más, el ángulo del pájaro será el máximo permitido.
// Si la separación es de 0 metros, el ángulo del pájaro deberá ser 0.
if (lookAtController.myTransform.eulerAngles.x < 360 + idealAngle && lookAtController.myTransform.eulerAngles.x > 270)
{
actionsOut[0] = 1; // BAJA
}
else
{
actionsOut[0] = -1; // SUBE
}
}
else
{
actionsOut[0] = -1; // SUBE
}
}
// EL TARGET ESTA A LA DERECHA
if (angleAxisY > 0)
{
actionsOut[1] = 1; // GIRA A LA DERECHA
}
// EL TARGET ESTA A LA IZQUIERDA
else
{
actionsOut[1] = -1; // GIRA A LA IZQUIERDA
}
if (distanceToTarget < 0.5)
{
SetNextCheckpoint(nextCheckpoint.GetComponent<CheckpointController>().nextCheckpoint);
}
}
}
private void GenerateBezierCurve()
{
float distanceY = Mathf.Abs(controller.myTransform.position.y - nextCheckpoint.position.y);
totalDistanceToTarget = Vector3.SqrMagnitude(nextCheckpoint.position - controller.myTransform.position);
startPoint = controller.myTransform.position;
if (controller.myTransform.position.y > nextCheckpoint.position.y)
{
p1 = controller.myTransform.position - Vector3.up * distanceY;
}
else
p1 = controller.myTransform.position + Vector3.up * distanceY;
}
public Vector3 CalculateQuadraticBezierPoint(float t, Vector3 p0, Vector3 p1, Vector3 p2)
{
float u = 1 - t;
Vector3 position = u * u * p0;
position += 2 * u * t * p1;
position += t * t * p2;
return position;
}
public void SetNextCheckpoint(Transform checkpoint)
{
nextCheckpoint = checkpoint;
}
}