4 minute read

Visualización de datos con Lidar-Visualizer

Esta semana he estado investigando más en profundidad las funcionalidades del simulador Carla para poder visualizar nubes de puntos con la herramienta Lidar-Visualizer mientras se pilota un vehículo.

Escenario en Carla y captura de datos con sensor LiDAR

He creado un escenario con un vehículo y le he aplicado un sensor LiDAR para probar la generación de datos. A continuación, se muestran las líneas de código con el que se ha realizado la simulación:

import carla

# Conectar con el servidor del simulador
client = carla.Client('localhost', 2000)
world = client.get_world()

#Generar escenario
client.load_world('Town05')

# Librería de vehículos
vehicle_blueprints = world.get_blueprint_library().filter('*vehicle*')

# Cogemos todos los puntos de aparición predefinidos del escenario
spawn_points = world.get_map().get_spawn_points()

# Seleccionamos un vehículo de la librería
vehicle_bp = vehicle_blueprints.find('vehicle.audi.tt')

# Generamos nuestro vehículo en el primer puntos de aparición del escenario
ego_vehicle = world.spawn_actor((vehicle_bp), (spawn_points[0]))

# Asignar posición al objeto
lidar_transform = carla.Transform(carla.Location(z=2.5))

# Filtramos por tipo de actores: asignamos sensor LiDAR
lidar_bp = world.get_blueprint_library().find('sensor.lidar.ray_cast')
lidar_bp.set_attribute('channels', '128')        # Número de rayos
lidar_bp.set_attribute('range', '100')           # Alcance en metros
lidar_bp.set_attribute('rotation_frequency', '10')  # Hz
lidar_bp.set_attribute('points_per_second', '500000') 

# Asignamos sensor a nuestro vehículo
sensor_lidar = world.spawn_actor(lidar_bp, lidar_transform, attach_to=ego_vehicle)

# Iniciar sensor en modo escucha
sensor_lidar.listen(lidar_callback)

# Activar piloto automático del vehículo
ego_vehicle.set_autopilot(True)

Para extraer los datos que genere el sensor, he usado una función lidar_callback para almacenarlos automaticamente como ficheros .ply con la finalidad de poder visualizarlos como datos en modo local con el visualizador LiDAR (a modo de prueba). Esta función recibe como argumento el objeto de datos LiDAR (point_cloud). Para almacenar los ficheros .ply se ha usado el módulo plyfile:

def lidar_callback(point_cloud, frame_count=[0]):
   
    # Convertir datos LIDAR a un array Nx4: x, y, z, intensidad
    points = np.frombuffer(point_cloud.raw_data, dtype=np.float32)
    points = np.reshape(points, (-1, 4))

    #Crear array estructurado para PLY
    vertices = np.array(
        [tuple(p) for p in points],
        dtype=[('x','f4'),('y','f4'),('z','f4'),('intensity','f4')]
    )
    # Preparar carpeta de salida
    output_folder = 'Visualizador'
   
    # Generar el nombre del archivo .PLY
    # Se numera automáticamente usando frame_count
    file_path = os.path.join(output_folder, f"frame{frame_count[0]:06d}.ply")

    # Guardar el archivo en formato PLY de texto
    PlyData([PlyElement.describe(vertices, 'vertex')], text=True).write(file_path)
    frame_count[0] += 1
    print("LiDAR data received: ", point_cloud)

Posteriormente, he visualizado la nube de puntos generada en el simulador.

Lidar Visualizer

Control de un vehículo en Carla

El siguiente paso, fue el de conseguir controlar un vehículo mientras generaba datos con el sensor LiDAR (tarea que también ofrece el propio visualizador). Para comprender su funcionamiento, he realizado un script para teleoperar un vehículo y poder visualizar los datos generados por el LiDAR. Para detectar el teclado, manejar la pantalla de las cámaras y manejar la sincronización de los eventos, se ha usado la librería Pygame.

Para ello, se ha implementado:

def vehicle_controller(vehicle):

    control = carla.VehicleControl()

    # Leer teclado
    keys = pygame.key.get_pressed()

    # valores iniciales
    control.throttle = 0.0
    control.steer = 0.0
    control.brake = 0.0
    control.hand_brake = False

    # W acelerar
    if keys[pygame.K_w]:
        control.throttle = 1.0

    # S frenar
    if keys[pygame.K_s]:
        control.brake = 1.0

    # A girar a la izquierda
    if keys[pygame.K_a]:
        control.steer = -0.5  # negativo = izquierda

    # D : girar a la derecha
    if keys[pygame.K_d]:
        control.steer = 0.5   # positivo = derecha

    # Aplicar control al vehículo
    vehicle.apply_control(control)
  • camera_callback: para procesar las imágenes que genere la cámara situada en la parte superior del vehículo, con tal de poder visualizarlo durante su control.
def camera_callback(image, display):
    array = np.frombuffer(image.raw_data, dtype=np.uint8)
    array = np.reshape(array, (image.height, image.width, 4))
    array = array[:, :, :3]  # Quitar el canal alfa
    array = array[:, :, ::-1]  # Convertir de BGRA a RGB
    surface = pygame.surfarray.make_surface(array.swapaxes(0, 1))
    display.blit(surface, (0, 0))
    # Actualizar solo esta superficie en vez de toda la pantalla
    pygame.display.update(display.get_rect()) 

Además de las funciones comentadas anteriormente, he omitido el uso de las funciones set_lidar y lidar_callback, pues me han generado errores a resolver próximamente en el procesamiento de los datos del sensor. A continuación, la función main(), con la que he simulado el entorno en Carla:

def main():

    pygame.init()
    pygame.display.gl_set_attribute(pygame.GL_ACCELERATED_VISUAL, 0)
    width, height = 800, 600
    screen = pygame.display.set_mode((width, height), pygame.SRCALPHA)
    pygame.display.set_caption("Carla Simulator")

    client = carla.Client('localhost', 2000)
    client.set_timeout(10.0)
    client.load_world('Town05')

    world = client.get_world()
    blueprint_library = world.get_blueprint_library()
    traffic_manager = client.get_trafficmanager(8000)
    traffic_manager.set_synchronous_mode(True)
    traffic_manager.set_global_distance_to_leading_vehicle(2.5)

    settings = world.get_settings()
    delta = 0.05
    settings.fixed_delta_seconds = delta
    settings.synchronous_mode = True
    world.apply_settings(settings)

    
    # Librería de vehículos
    vehicle_blueprints = world.get_blueprint_library().filter('*vehicle*')

    # Cogemos todos los puntos de aparición predefinidos del escenario
    spawn_points = world.get_map().get_spawn_points()

    # Seleccionamos un vehículo de la librería (escogido a mi gusto)
    vehicle_bp = vehicle_blueprints.find('vehicle.audi.tt')

    ego_vehicle = world.spawn_actor(vehicle_bp, spawn_points[0])
    if ego_vehicle is not None:
        print('Vehículo spawneado correctamente')

    lidar = set_lidar(world,ego_vehicle)

    camera_bp = blueprint_library.find('sensor.camera.rgb')
    camera_bp.set_attribute('image_size_x', '800')
    camera_bp.set_attribute('image_size_y', '600')
    camera_bp.set_attribute('fov', '90')
    camera_transform = carla.Transform(carla.Location(x=-4.0, z=2.5))
    camera = world.spawn_actor(camera_bp, camera_transform, attach_to=ego_vehicle)

    actor_list = []
    actor_list.append(ego_vehicle)
    actor_list.append(camera)

    camera.listen(lambda image: camera_callback(image, screen))

    clock = pygame.time.Clock()

    while True:
        world.tick()                        # avanzar simulación
        vehicle_controller(ego_vehicle)     # control del vehículo

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

        pygame.display.flip()          # refrescar la ventana
        clock.tick(20)                # sincronizar a ~20 FPS
        if ego_vehicle is None:
            break

Resumen

Esta semana se ha logrado capturar y procesar datos generados por un sensor LiDAR en Carla, aptas para su visualización, y la simulación de un escenario en dicho simulador en que se pueda controlar un automóvil. Además de la mejor comprensión del simulador.

Solucionar: procesamiento y almacenamiento de datos mientras se controla el vehículo en el simulador.