Empezando con adquisición y modelado de datos
Tras haber creado ya brain(), el siguiente paso será el de generar y procesar datos del simulador. Para empezar con esta fase y con la búsqueda de un modelo, los datos que generaremos, por ahora, serán los frames de la cámara frontal del vehículo en Carla. Además, vamos a entrenar un modelo, esta semana probaremos con MobileNet, que en función de las features de los frames y sus etiquetas, sea capaz de darnos una salida que sea el comportamiento que deben de seguir los actuadores del vehículo (steer, throttle, brake).
Adquisición de datos
Para utilizar un modelo de Imitation Learning, nuestro conjunto de datos iniciales serán muestras de conducción profesional o experta de un vehículo en el simulador. Así, el modelo aprenderá “cómo hay que conducir” o lo que sería una buena conducción. Para ello, esta semana se ha optado por teleoperar manualmente el vehículo en el simulador mientras se capturan los frames de su vista frontal. Además, para cada frame se han almacenado en un fichero csv el valor de los actuadores del vehículo: steer, vehicle y brake.
- Líneas de código añadidas a la función brain():
while True:
# Procesar control del vehículo
throttle, steer, brake = control(ego_vehicle)
# Usar snapshot frame para sincronizar labels con los sensores
snapshot = world.get_snapshot()
frame_id = snapshot.frame
# Etiquetas para cadad frame capturado
labels.append({
'frame_id': frame_id,
'steer': steer,
'throttle': throttle,
'brake': brake,
})
- Líneas de código añadidas al callback de la cámara frontal:
# Función para procesar imágenes de la cámara def camera_callback(image, display): # VISUALIZACIÓN array = np.frombuffer(image.raw_data, dtype=np.uint8) array = np.reshape(array, (image.height, image.width, 4)) array_bgra = array[:, :, :3] # quitar canal alfa array_rgb = array_bgra[:, :, ::-1] # BGRA → RGB surface = pygame.surfarray.make_surface(array_rgb.swapaxes(0, 1)) display.blit(surface, (0, 0)) pygame.display.update(display.get_rect()) # CAPTURA DE DATOS output_dir = '/dataset/images' os.makedirs(output_dir, exist_ok=True) frame_name = f"{image.frame:06d}.png" cv2.imwrite(os.path.join(output_dir, frame_name), array_rgb) print(f"Frame saved: {frame_name}")
Prueba con MobileNet
Tras la adquisición del conjunto de datos, de 8131 frames, hemos hecho una prueba con el modelo MobileNet, para la extracción de características visuales. Esta prueba ha sido únicamente para comprobar la adquisición de los datos y su posterior modelado. Las próximas semanas quedará seguir investigando acerca de MobileNet y ver cómo encaja este modelo con los objetivos de este proyecto. A priori, esta arquitectura será una buena opción para la conducción autónoma de nuestro futuro robot, pues está hecho para dispositivos con recursos limitados y no requiere de tanta memoria, a diferencia de otros modelos. Para modelar de forma sencilla nuestro conjunto de datos, se han cargado y dividido en: píxeles de los frames (matriz X) y valores de los actuadores (matriz y).
# Configuración
IMG_WIDTH, IMG_HEIGHT = 160, 120
DATASET_DIR = 'dataset3'
CSV_PATH = os.path.join(DATASET_DIR, 'labels.csv')
# Cargar CSV
df = pd.read_csv(CSV_PATH)
# Cargar imágenes y etiquetas
images = []
labels = []
for _, row in df.iterrows():
frame_id = f"{int(row['frame_id']):06d}.png"
img_path = os.path.join(DATASET_DIR, 'images', frame_id)
if os.path.exists(img_path):
img = cv2.imread(img_path)
img = cv2.resize(img, (IMG_WIDTH, IMG_HEIGHT))
img = img / 255.0 # Normalizar [0,1]
images.append(img)
labels.append([row['steer'], row['throttle'], row['brake']])
X = np.array(images, dtype=np.float32)
y = np.array(labels, dtype=np.float32)
Posteriormente, se ha balanceado el dataset, es decir, se han asigando un 80% de los datos al entrenamiento y un 20% al test:
# Balance del dataset
from sklearn.model_selection import train_test_split
# Dividir 80% entrenamiento y 20% validación
X_train, X_test, y_train, y_test = train_test_split( X, y, test_size=0.2, random_state=42, shuffle=True)
Finalmente, hemos entrenado el modelo. Se ha empleado la versión V2 de MobileNet sobre imágenes del simulador de tamaño 120×160×3, utilizando pesos preentrenados en ImageNet. Se han congelado las capas base para evitar sobreentrenamiento, y se ha añadido un cabezal de regresión formado por una capa de GlobalAveragePooling2D y dos capas densas intermedias (128 y 64 neuronas, activación ReLU), seguido de una capa de salida de 3 neuronas con activación tanh para predecir los actuadores del vehículo (steer, throttle, brake) en rango [-1,1]. El modelo se ha compilado con el optimizador Adam (lr=1e-4) y función de pérdida MSE, y se entrenó durante 15 iteraciones con batch_size 32, monitorizando su desempeño sobre un conjunto de validación, con el objetivo de generar un modelo ligero capaz de predecir en tiempo real los actuadores a partir de las frames capturados en Carla.
from tensorflow.keras.applications import MobileNetV2
from tensorflow.keras import layers, models
base_model = MobileNetV2(input_shape=(IMG_HEIGHT, IMG_WIDTH, 3),
include_top=False,
weights='imagenet')
# Congelar las capas base
base_model.trainable = False
# Añadir cabezal de regresión
inputs = tf.keras.Input(shape=(IMG_HEIGHT, IMG_WIDTH, 3))
x = base_model(inputs, training=False)
x = layers.GlobalAveragePooling2D()(x)
x = layers.Dense(128, activation='relu')(x)
x = layers.Dense(64, activation='relu')(x)
outputs = layers.Dense(3, activation='tanh')(x)
model = models.Model(inputs, outputs)
model.compile(optimizer=tf.keras.optimizers.Adam(1e-4), loss='mse')
model.summary()
history = model.fit(X_train, y_train, validation_data=(X_test, y_test), epochs=15, batch_size=32)
Tras el entrenamiento, se obtiene un valor de pérdida de entrenamiento de 0.0467 (loss) y de 0.0738 de pérdida en el conjunto de test. Aclarar que esta implementación de MobileNet ha sido a modo de prueba, tanto para la adquisición del conjunto datos como para examinar el uso de esta arquitectura. Queda seguir investigando acerca de ello.
Además, se ha guardado el modelo entrenado y se ha probado predecir los actuadores en cada frame capturado (en directo) mientras se teleopera el vehículo en el simulador. No obstante, hay que seguir probándolo y examinar bien los resultados obtenidos de predicción durante la conducción.
Futuros avances
Una vez testeado MobileNetV2 y habiendo realizado de manera exitosa la extracción de datos, queda seguir investigando MobileNet y ver cómo optimizar sus hiperparámetros, o la selección del conjunto de datos (o ambas). También queda generar un dataset con el modo auto-pilot (a modo de prueba), cambiar de escenarios en el simulador a otros que sean más parecidos a los del ítem de estudio, y extraer los datos del sensor LiDAR para modelar en MobileNetV2.