2 minute read

Semana 6

Durante esta semana he retomado el TFG. He añadido dos archivos: drive_and_record y pilot_net. Ambos archivos serán eplicados mas adelante.

Record Dataset

En este archivo se aplica una lógica para poder utilizar el coche usando un mando de consola. Además, contamos con la función para guardar los logs y poder reproducirlos más tarde para captar datos tanto de la cámara como del steering del coche.

Para mover el coche usamos pygame, el cual nos permite detectar los inputs del mando de Xbox. En este caso, el gatillo derecho es el acelerador, el izquierdo el freno y el joystick izquierdo el steering.

for event in pygame.event.get():
    if event.type == pygame.QUIT:
        return

steer = joystick.get_axis(0)
brake = max(0.0, (joystick.get_axis(2) + 1) / 2)
throttle = max(0.0, (joystick.get_axis(5) + 1) / 2)

control = carla.VehicleControl()
control.steer = steer
control.throttle = throttle
control.brake = brake

vehicle.apply_control(control)

Además, usamos las funciones:

client.start_recorder("dataset_record.log")
client.stop_recorder()

Estas funciones nos permiten empezar y parar la grabación del log.

Este es un pequeño GIF de cómo se ve en el simulador.

Movimiento del coche

PilotNet

Para crear el archivo de Python para PilotNet, me he basado en el paper de Nvidia en el cual se muestra la topología de la red neuronal de PilotNet. Esta topología la he introducido dentro de la clase PilotNet:


class PilotNet(nn.Module):
    def __init__(self):
        super(PilotNet, self).__init__()
        
        self.conv = nn.Sequential(
            nn.Conv2d(3, 24, 5, stride=2),
            nn.ReLU(),

            nn.Conv2d(24, 36, 5, stride=2),
            nn.ReLU(),

            nn.Conv2d(36, 48, 5, stride=2),
            nn.ReLU(),

            nn.Conv2d(48, 64, 3),
            nn.ReLU(),

            nn.Conv2d(64, 64, 3),
            nn.ReLU(),
        )

        self.fc = nn.Sequential(
            nn.Flatten(),
            nn.Linear(64 * 1 * 18, 100),
            nn.ReLU(),

            nn.Linear(100, 50),
            nn.ReLU(),

            nn.Linear(50, 10),
            nn.ReLU(),

            nn.Linear(10, 1)
        )
        
    def forward(self, x):
        x = self.conv(x)
        steering = self.fc(x)

        return steering

Esta estructura cuenta con 5 capas convolucionales y otras 4 lineales. Únicamente tiene una salida, puesto que solo usaremos el steering.

Además, hemos de tratar los datos y convertirlos a tensores:


class CarlaDataset(Dataset):
    def __init__(self, csv_file, img_dir):
        self.train_data = pd.read_csv(csv_file)
        self.dir_img = img_dir

        self.transform = T.Compose([
            T.Resize((66, 200)),
            T.ToTensor(),
            T.Normalize(mean=[0.5, 0.5, 0.5],
                        std=[0.5, 0.5, 0.5])
        ])

    def __len__(self):
        return len(self.train_data)

    def __getitem__(self, idx):

        img_path = os.path.join(self.dir_img, self.train_data.iloc[idx, 0])
        img = img = Image.open(img_path)
        img = self.transform(img)

        steering = torch.FloatTensor(self.train_data.iloc[idx, 1])
        return img, steering

Esta clase une las imágenes grabadas con su correspondiente steering. Además, hace un resize de las imágenes obtenidas en CARLA (800x600) a las que necesita PilotNet (66x200). Tengo que explorar si es posible mantener la imagen en su tamaño original, ya que aportaría más información. También cabe la opción de recortar la imagen proporcionada por CARLA y quedarnos únicamente con la carretera.

Por último, tenemos la etapa de training:


epochs = 10

for epoch in range(epochs):
    model.train()
    loss_epoch = 0.0

    for images, steering in loader:

        batch_size = steering.size

        images, steering = images.to(device), steering.to(device)

        optimizer.zero_grad()

        outputs = model(images)
        
        loss = criterion(outputs, steering)
        
        loss.backward()
 
        optimizer.step()

        loss_epoch += loss.item() * batch_size
        
    print(f"Epoch {epoch+1}/{epochs} - Loss: {loss_epoch/len(loader):.6f}")

torch.save(model.state_dict(), "pilotnet_steering.pth")

Este archivo está a falta de probar, puesto que no he podido obtener ni las imágenes ni los steerings.

Updated: