Al terminar sabrás…
- Entender qué es la función de recompensa y cuándo se ejecuta
- Conocer los parámetros del diccionario params y su significado
- Analizar las cuatro funciones de ejemplo oficiales y saber cuándo usar cada una
- Combinar varias recompensas evitando los errores de diseño más comunes
- Modificar la función por defecto y entrenar un clon para comprobar el efecto
Vídeo: La función de recompensa en 6 minutos
Guion del vídeo (texto)
¿Qué es la recompensa?. Piensa en cómo se adiestra a un perro: cuando hace algo bien, recibe un premio. Tu coche funciona igual. En cada paso de la simulación, la plataforma llama a tu función de recompensa, le pasa la situación del coche y tu función responde con un número. Durante el entrenamiento, el coche busca la forma de conducir que consigue más recompensa en total. Por eso esta función es tu principal herramienta.
El diccionario params. Tu función recibe un diccionario llamado params con todo lo que necesitas saber del coche en ese instante. Si tiene las cuatro ruedas en la pista, a qué distancia está del centro, a qué velocidad va, cuánto gira el volante, qué porcentaje de vuelta lleva o cuántos pasos ha dado. También trae los waypoints, unos puntos de referencia sobre la línea central, y datos de los obstáculos cuando los hay.
Ejemplo 1, seguir la línea central. La función que viene por defecto dibuja tres franjas imaginarias alrededor de la línea central. Si el coche va por la franja más estrecha, recibe la recompensa máxima. En la segunda, la mitad. En la tercera, muy poco. Y fuera de ellas, casi nada. Es un buen punto de partida porque enseña al coche a no salirse, pero no le dice nada sobre ir rápido.
Ejemplos 2 y 3. El segundo ejemplo oficial solo premia que el coche esté entre los dos bordes. Es fácil de entender, pero la guía avisa de que suele tardar más en aprender. El tercero parte de la línea central y añade una penalización: si el volante gira más de quince grados, la recompensa se multiplica por ocho décimas. Así el coche aprende a conducir suave y no va dando bandazos de un lado a otro.
Ejemplo 4, esquivar obstáculos. El cuarto ejemplo es para las carreras con obstáculos. Calcula dos cosas por separado: una recompensa por seguir dentro de la pista y otra que baja cuando el coche se acerca demasiado a un objeto que está en su mismo carril. Al final las suma, pero la de evitar el choque pesa cuatro veces más. Así el coche entiende que lo primero es no chocar.
Multiplicar o sumar. Cuando quieres premiar varias cosas a la vez tienes dos caminos. Multiplicar es como un freno: si una parte se hace mal, toda la recompensa baja, da igual lo bien que vaya el resto. Sumar con pesos es como la nota de una asignatura con examen y trabajos: cada parte aporta su porcentaje. Las dos opciones son válidas, pero conviene saber cuál estás usando y por qué.
Cuidado con las trampas. El coche no entiende tus intenciones, solo los números. Si das la misma recompensa en cada paso por estar centrado, el coche puede descubrir que ir despacio le da más pasos y, por tanto, más puntos. Si das un premio gigante por algo raro, todo lo demás deja de importar. Antes de entrenar, pregúntate siempre cómo podría hacer trampa tu coche.
Validar antes de entrenar. En el editor de la plataforma tienes el botón Validate. Revisa tu código en un entorno aislado, sin conexión a internet, y te avisa si hay un error de escritura, una librería no permitida o un fallo al ejecutarse. Solo puedes importar cinco librerías, y siempre fuera de la función. Valida cada cambio: un error descubierto a tiempo te ahorra horas de entrenamiento.
En el aprendizaje por refuerzo nadie le enseña al coche cómo se conduce. Lo descubre él solo, a base de probar, y la única pista que recibe es un número: la recompensa. Tú escribes la función que decide ese número. Es como adiestrar a un perro: no le explicas con palabras qué es "sentarse", le das un premio cuando lo hace. Si premias lo correcto, aprende lo correcto. Si premias otra cosa sin querer, aprenderá esa otra cosa con mucho entusiasmo.
Qué es y cuándo se ejecuta
La función de recompensa es una función de Python que recibe un diccionario params y devuelve un float. Este es el esqueleto oficial:
def reward_function(params) :
reward = ...
return float(reward)
Se ejecuta en cada paso de la simulación (el coche simulado toma unas 10 acciones por segundo). El ciclo es siempre el mismo:
- El coche ve una imagen con su cámara y elige una acción (velocidad y ángulo de dirección).
- El simulador mueve el coche y rellena
paramscon la nueva situación. - Tu función lee
paramsy devuelve la recompensa de ese paso. - Tras varios episodios, el entrenamiento ajusta la red neuronal para que las acciones que dieron más recompensa total sean más probables.
El objetivo del entrenamiento es encontrar la política que maximiza la recompensa total media, no la de un paso aislado. Esta idea te salvará de muchos errores de diseño.
Un ejemplo didáctico de la guía oficial: "A simple example might give 0 points for staying on track, -1 for going off track, and +1 for finishing."
El diccionario params
Este es el diccionario completo que recibe tu función, tal como aparece en la guía oficial:
{
"all_wheels_on_track": Boolean, # flag to indicate if the agent is on the track
"x": float, # agent's x-coordinate in meters
"y": float, # agent's y-coordinate in meters
"closest_objects": [int, int], # zero-based indices of the two closest objects to the agent's current position of (x, y).
"closest_waypoints": [int, int], # indices of the two nearest waypoints.
"distance_from_center": float, # distance in meters from the track center
"is_crashed": Boolean, # Boolean flag to indicate whether the agent has crashed.
"is_left_of_center": Boolean, # Flag to indicate if the agent is on the left side to the track center or not.
"is_offtrack": Boolean, # Boolean flag to indicate whether the agent has gone off track.
"is_reversed": Boolean, # flag to indicate if the agent is driving clockwise (True) or counter clockwise (False).
"heading": float, # agent's yaw in degrees
"objects_distance": [float, ], # list of the objects' distances in meters between 0 and track_length in relation to the starting line.
"objects_heading": [float, ], # list of the objects' headings in degrees between -180 and 180.
"objects_left_of_center": [Boolean, ], # list of Boolean flags indicating whether elements' objects are left of the center (True) or not (False).
"objects_location": [(float, float),], # list of object locations [(x,y), ...].
"objects_speed": [float, ], # list of the objects' speeds in meters per second.
"progress": float, # percentage of track completed
"speed": float, # agent's speed in meters per second (m/s)
"steering_angle": float, # agent's steering angle in degrees
"steps": int, # number steps completed
"track_length": float, # track length in meters.
"track_width": float, # width of the track
"waypoints": [(float, float), ] # list of (x,y) as milestones along the track center
}
Y aquí lo tienes traducido, con tipos y rangos:
| Parámetro | Tipo | Rango | Qué significa |
|---|---|---|---|
all_wheels_on_track |
Boolean | True/False | True si las cuatro ruedas están dentro de los dos bordes |
closest_waypoints |
[int, int] | índices de waypoints |
Waypoint más cercano detrás del coche y el más cercano delante |
closest_objects |
[int, int] | índices de objects_location |
Objeto más cercano detrás y delante; si solo hay uno, ambos valen 0 |
distance_from_center |
float | 0 a ~track_width/2 | Metros entre el centro del coche y la línea central |
heading |
float | −180 a +180 | Orientación del coche en grados respecto al eje x |
is_crashed |
Boolean | True/False | El coche ha chocado con un objeto (termina el episodio) |
is_left_of_center |
Boolean | True/False | True a la izquierda del centro, False a la derecha |
is_offtrack |
Boolean | True/False | El coche se ha salido de la pista (termina el episodio) |
is_reversed |
Boolean | True/False | True en sentido horario, False en antihorario |
objects_distance |
[float, …] | 0 a track_length | Distancia de cada objeto a la línea de salida, medida sobre la línea central |
objects_heading |
[float, …] | −180 a 180 | Orientación de cada objeto (0 si es estático) |
objects_left_of_center |
[Boolean, …] | True/False | Si cada objeto está a la izquierda del centro |
objects_location |
[(x,y), …] | (0:N, 0:N) | Posición de cada objeto |
objects_speed |
[float, …] | 0 a 12,0 | Velocidad de cada objeto en m/s (0 si es estático) |
progress |
float | 0 a 100 | Porcentaje de vuelta completado |
speed |
float | 0,0 a 5,0 | Velocidad observada en m/s |
steering_angle |
float | −30 a 30 | Ángulo de las ruedas delanteras: negativo a la derecha, positivo a la izquierda |
steps |
int | 0 a Nstep | Pasos completados (un paso es una acción) |
track_length |
float | 0 a Lmax | Longitud de la pista en metros |
track_width |
float | 0 a Dtrack | Anchura de la pista en metros |
x, y |
float | 0 a N | Posición en metros; el origen está en la esquina inferior izquierda del entorno |
waypoints |
lista de (x, y) | — | Hitos ordenados sobre la línea central; en pista cerrada el primero y el último coinciden |
Ejemplos por parámetro
La guía oficial trae un ejemplo pequeño para casi cada parámetro. Estos son los más útiles para empezar (distance_from_center y track_width los verás dentro de las funciones de la sección siguiente).
Velocidad con umbral (all_wheels_on_track + speed): premia ir rápido sin salirse.
def reward_function(params):
#############################################################################
'''
Example of using all_wheels_on_track and speed
'''
# Read input variables
all_wheels_on_track = params['all_wheels_on_track']
speed = params['speed']
# Set the speed threshold based your action space
SPEED_THRESHOLD = 1.0
if not all_wheels_on_track:
# Penalize if the car goes off track
reward = 1e-3
elif speed < SPEED_THRESHOLD:
# Penalize if the car goes too slow
reward = 0.5
else:
# High reward if the car stays on track and goes fast
reward = 1.0
return float(reward)
Mirar hacia donde va la pista (waypoints + closest_waypoints + heading): calcula la dirección de la línea central con los dos waypoints más cercanos y penaliza si el coche apunta a otro lado.
# Place import statement outside of function (supported libraries: math, random, numpy, scipy, and shapely)
# Example imports of available libraries
#
# import math
# import random
# import numpy
# import scipy
# import shapely
import math
def reward_function(params):
###############################################################################
'''
Example of using waypoints and heading to make the car point in the right direction
'''
# Read input variables
waypoints = params['waypoints']
closest_waypoints = params['closest_waypoints']
heading = params['heading']
# Initialize the reward with typical value
reward = 1.0
# Calculate the direction of the center line based on the closest waypoints
next_point = waypoints[closest_waypoints[1]]
prev_point = waypoints[closest_waypoints[0]]
# Calculate the direction in radius, arctan2(dy, dx), the result is (-pi, pi) in radians
track_direction = math.atan2(next_point[1] - prev_point[1], next_point[0] - prev_point[0])
# Convert to degree
track_direction = math.degrees(track_direction)
# Calculate the difference between the track direction and the heading direction of the car
direction_diff = abs(track_direction - heading)
if direction_diff > 180:
direction_diff = 360 - direction_diff
# Penalize the reward if the difference is too large
DIRECTION_THRESHOLD = 10.0
if direction_diff > DIRECTION_THRESHOLD:
reward *= 0.5
return float(reward)
El truco de 360 - direction_diff evita un falso problema: 179° y −179° parecen muy distintos, pero en realidad solo se diferencian en 2°.
Volante (steering_angle):
def reward_function(params):
'''
Example of using steering angle
'''
# Read input variable
abs_steering = abs(params['steering_angle']) # We don't care whether it is left or right steering
# Initialize the reward with typical value
reward = 1.0
# Penalize if car steer too much to prevent zigzag
ABS_STEERING_THRESHOLD = 20.0
if abs_steering > ABS_STEERING_THRESHOLD:
reward *= 0.8
return float(reward)
Ir por delante del plan (steps + progress): cada 100 pasos comprueba si llevas más vuelta de la prevista para terminar en 300 pasos y, si es así, da un premio extra.
def reward_function(params):
#############################################################################
'''
Example of using steps and progress
'''
# Read input variable
steps = params['steps']
progress = params['progress']
# Total num of steps we want the car to finish the lap, it will vary depends on the track length
TOTAL_NUM_STEPS = 300
# Initialize the reward with typical value
reward = 1.0
# Give additional reward if the car pass every 100 steps faster than expected
if (steps % 100) == 0 and progress > (steps / TOTAL_NUM_STEPS) * 100 :
reward += 10.0
return float(reward)
Las cuatro funciones de ejemplo oficiales
En el editor de la plataforma están en Reward function examples. Aquí van copiadas tal cual de la guía.
Ejemplo 1: seguir la línea central (Time trial)
"Gives higher reward if it is closer to the center of the track". Es la función por defecto de la plataforma (la versión cargada en el editor usa reward = 1.0 y return float(reward)).
def reward_function(params):
'''
Example of rewarding the agent to follow center line
'''
# Read input parameters
track_width = params['track_width']
distance_from_center = params['distance_from_center']
# Calculate 3 markers that are increasingly further away from the center line
marker_1 = 0.1 * track_width
marker_2 = 0.25 * track_width
marker_3 = 0.5 * track_width
# Give higher reward if the car is closer to center line and vice versa
if distance_from_center <= marker_1:
reward = 1
elif distance_from_center <= marker_2:
reward = 0.5
elif distance_from_center <= marker_3:
reward = 0.1
else:
reward = 1e-3 # likely crashed/ close to off track
return reward
Ejemplo 2: quedarse entre los dos bordes (Time trial)
"Easy to program and understand, but likely takes longer to converge." Da libertad para usar todo el ancho de la pista, pero apenas orienta al coche.
def reward_function(params):
'''
Example of rewarding the agent to stay inside the two borders of the track
'''
# Read input parameters
all_wheels_on_track = params['all_wheels_on_track']
distance_from_center = params['distance_from_center']
track_width = params['track_width']
# Give a very low reward by default
reward = 1e-3
# Give a high reward if no wheels go off the track and
# the car is somewhere in between the track borders
if all_wheels_on_track and (0.5*track_width - distance_from_center) >= 0.05:
reward = 1.0
# Always return a float value
return reward
Ejemplo 3: evitar el zig-zag (Time trial)
"The agent learns to drive smoothly in the simulator and likely keeps the same behavior when deployed to the physical vehicle." Útil también si llegas a las finales presenciales.
def reward_function(params):
'''
Example of penalize steering, which helps mitigate zig-zag behaviors
'''
# Read input parameters
distance_from_center = params['distance_from_center']
track_width = params['track_width']
abs_steering = abs(params['steering_angle']) # Only need the absolute steering angle
# Calculate 3 marks that are farther and father away from the center line
marker_1 = 0.1 * track_width
marker_2 = 0.25 * track_width
marker_3 = 0.5 * track_width
# Give higher reward if the car is closer to center line and vice versa
if distance_from_center <= marker_1:
reward = 1.0
elif distance_from_center <= marker_2:
reward = 0.5
elif distance_from_center <= marker_3:
reward = 0.1
else:
reward = 1e-3 # likely crashed/ close to off track
# Steering penality threshold, change the number based on your action space setting
ABS_STEERING_THRESHOLD = 15
# Penalize reward if the car is steering too much
if abs_steering > ABS_STEERING_THRESHOLD:
reward *= 0.8
return float(reward)
Ejemplo 4: quedarse en un carril sin chocar (Object avoidance)
"The total reward is a weighted sum of the reward and penalty. The example gives more weight to the penalty in effort to avoid crashes."
import math
def reward_function(params):
'''
Example of rewarding the agent to stay inside two borders
and penalizing getting too close to the objects in front
'''
all_wheels_on_track = params['all_wheels_on_track']
distance_from_center = params['distance_from_center']
track_width = params['track_width']
objects_location = params['objects_location']
agent_x = params['x']
agent_y = params['y']
_, next_object_index = params['closest_objects']
objects_left_of_center = params['objects_left_of_center']
is_left_of_center = params['is_left_of_center']
# Initialize reward with a small number but not zero
# because zero means off-track or crashed
reward = 1e-3
# Reward if the agent stays inside the two borders of the track
if all_wheels_on_track and (0.5 * track_width - distance_from_center) >= 0.05:
reward_lane = 1.0
else:
reward_lane = 1e-3
# Penalize if the agent is too close to the next object
reward_avoid = 1.0
# Distance to the next object
next_object_loc = objects_location[next_object_index]
distance_closest_object = math.sqrt((agent_x - next_object_loc[0])**2 + (agent_y - next_object_loc[1])**2)
# Decide if the agent and the next object is on the same lane
is_same_lane = objects_left_of_center[next_object_index] == is_left_of_center
if is_same_lane:
if 0.5 <= distance_closest_object < 0.8:
reward_avoid *= 0.5
elif 0.3 <= distance_closest_object < 0.5:
reward_avoid *= 0.2
elif distance_closest_object < 0.3:
reward_avoid = 1e-3 # Likely crashed
# Calculate reward by putting different weights on
# the two aspects above
reward += 1.0 * reward_lane + 4.0 * reward_avoid
return reward
Cómo combinar recompensas sin que el coche haga trampa
Multiplicar frente a sumar
| Forma | Ejemplo oficial | Cómo se comporta |
|---|---|---|
| Multiplicar por un factor | Ejemplo 3: reward *= 0.8 si gira mucho |
Actúa como un freno: si falla una parte, baja todo. Mantiene la escala (nunca supera la recompensa base) |
| Suma ponderada | Ejemplo 4: 1.0 * reward_lane + 4.0 * reward_avoid |
Cada parte aporta según su peso. Una parte buena puede compensar otra mala |
Una regla práctica: multiplica para condiciones que deben cumplirse siempre ("no vale de nada ir rápido si estás fuera") y suma con pesos cuando quieres repartir prioridades. Si sumas, los pesos dicen qué importa más; en el ejemplo 4, no chocar vale cuatro veces más que ir por el carril.
Errores de diseño típicos
- Premiar quedarse quieto o ir despacio. La recompensa total es la suma de todos los pasos. Si das 1 punto por paso por estar centrado y nada más, una vuelta lenta tiene más pasos y, por tanto, más puntos. Solución: añade algo que dependa de
speedo deprogress. - Recompensas que el coche puede "trucar". El coche busca el atajo matemático, no lo que tú querías decir. Si premias mucho mirar en la dirección de la pista, puede aprender a ir recto y lento. Pregúntate: "¿cómo conseguiría muchos puntos sin conducir bien?".
- Premios gigantes y raros. Un
+1000puntual hace que el resto de señales sean ruido. El ejemplo destepsusa+10.0cada 100 pasos, un tamaño razonable frente a la base de 1.0. - Recompensa cero o negativa sin motivo. Los ejemplos oficiales usan
1e-3en lugar de 0 porque "zero means off-track or crashed". - Cambiar muchas cosas a la vez. Si tocas tres umbrales y el modelo mejora, no sabrás cuál funcionó. Cambia una cosa cada vez.
Librerías, validación y límites
- Librerías permitidas:
math,random,numpy,scipyyshapely, siempre importadas fuera de la función. - Validate revisa el código en un contenedor aislado sin red. Puede devolver
SYNTAX_ERROR,IMPORT_ERRORoTEST_FAILURE. - Tamaño máximo: 140.000 caracteres (de sobra).
- Devuelve siempre un
float: termina conreturn float(reward).
Actividad práctica
Vas a mejorar la función por defecto con dos ideas: premiar la velocidad por encima de un umbral y penalizar el zig-zag. Después entrenarás un clon de tu primer modelo para comparar.
- Entra en Your models (tus modelos) y abre el modelo que entrenaste con la función por defecto. Anota la velocidad mínima y la máxima de su espacio de acciones (si no las recuerdas, las verás precargadas en el paso Define action space al clonarlo).
- Elige un umbral de velocidad (
SPEED_THRESHOLD) entre esas dos velocidades. Si el umbral queda por encima de tu velocidad máxima, el premio nunca se activará. - Escribe esta función en un intérprete de Python y pruébala con un
paramsinventado (velocidad alta y baja, volante recto y girado):
def reward_function(params):
# Leer parámetros
all_wheels_on_track = params['all_wheels_on_track']
track_width = params['track_width']
distance_from_center = params['distance_from_center']
speed = params['speed']
abs_steering = abs(params['steering_angle'])
# Ajusta estos dos umbrales a tu espacio de acciones
SPEED_THRESHOLD = 0.8
ABS_STEERING_THRESHOLD = 15
# Fuera de pista: casi nada
if not all_wheels_on_track:
return float(1e-3)
# Base: la función por defecto (seguir el centro)
marker_1 = 0.1 * track_width
marker_2 = 0.25 * track_width
marker_3 = 0.5 * track_width
if distance_from_center <= marker_1:
reward = 1.0
elif distance_from_center <= marker_2:
reward = 0.5
elif distance_from_center <= marker_3:
reward = 0.1
else:
reward = 1e-3
# Velocidad: si va lento, la recompensa baja a la mitad
if speed < SPEED_THRESHOLD:
reward *= 0.5
# Zig-zag: si gira mucho, penalización suave
if abs_steering > ABS_STEERING_THRESHOLD:
reward *= 0.8
return float(reward)
- En la plataforma, abre el menú Actions (acciones) de tu modelo y elige Clone (clonar). Se abre el asistente con la configuración precargada. Recuerda: al clonar no se puede cambiar el algoritmo.
- Ponle un nombre que diga qué cambia, por ejemplo
m1-clon-velocidad-zigzag(solo letras, números y guiones). - En Customize reward function, sustituye la función por la tuya y pulsa Validate hasta que no haya errores.
- Elige el tiempo de entrenamiento (Maximum time). La guía oficial recomienda entre 1 y 2 horas cuando se empieza a premiar la velocidad; ajústalo a las horas de cómputo que te queden. Pulsa Train your model.
- Cuando termine, evalúa el clon en la misma pista y con el mismo número de vueltas que el modelo original. Compara en una tabla: porcentaje de vuelta completado, tiempo por vuelta y salidas de pista.
- Escribe dos frases: ¿el coche va más rápido? ¿Conduce más suave o sigue dando bandazos?
Resumen
- La función de recompensa recibe
params, se ejecuta en cada paso y devuelve unfloat; el coche aprende a maximizar la recompensa total. paramstrae posición, velocidad, dirección, progreso, waypoints y datos de obstáculos: elige pocos parámetros y entiende bien cada uno.- Las cuatro funciones oficiales enseñan a seguir el centro, quedarse entre bordes, evitar el zig-zag y esquivar objetos; para obstáculos, usa la versión de la guía con distancia euclídea.
- Multiplica para condiciones imprescindibles, suma con pesos para repartir prioridades y pregúntate siempre cómo podría hacer trampa el coche.
- Valida cada cambio, clona para experimentar y cambia una sola cosa cada vez.
Test final
Comprueba lo que has aprendido. Necesitas 4 aciertos de 5 para superar el módulo.