5 minute read

Esta semana me he dedicado a profundizar en el procesamiento de datos brutos LiDAR de GOOSE, mejorar el visualizador 3D para que represente muestras reales, realizar una primera toma de contacto con la simulación del sensor LiDAR en Carla y explorar el funcionamiento de ROSbrige. A continuación detallo más en profundidad los avances

Procesamiento Binario y Visualización 3D

He desarrollado una nueva versión del visualizador 3D, en la que ahora proceso datos binarios extraídos de un archivo que contiene puntos LiDAR en formato XYZI (coordenadas y valor de intensidad de remisión). Utilizando Plotly, he implementado la visualización de estos puntos de manera similar a la versión anterior, manteniendo la capacidad de representar la nube de puntos en un entorno interactivo. El objetivo es mejorarlo para que puede gestionar datos de distintas fuentes y mostrarlos en una linea temporal dinámica

Estructura del Código

El código se organiza en dos clases principales:

  1. LidarDataReader
    • Lee datos LiDAR en formato XYZI (coordenadas e intensidad) desde un archivo binario del dataset GOOSE utilizando
    • Para leer los datos, se utiliza numpy para cargar el archivo binario con el método np.fromfile(), que organiza los puntos en un array y los reestructura en un formato de 4 columnas: X, Y, Z e intensidad de remisión
  2. LidarVisualizer
    • Hereda de LidarDataReader y añade la visualización 3D usando Plotly
    • Convierte las intensidades de los puntos en colores mediante un colormap y muestra los puntos en un gráfico 3D interactivo

El código procesa los datos binarios y los visualiza con colores según la intensidad de remisión, manteniendo la estructura de la versión anterior con Plotly

class LidarDataReader:
    def __init__(self, file_path):
        self.file_path = file_path
        self.data = self.read_lidar_data_from_bin()

    # Función para leer datos LiDAR desde un archivo .bin del dataset GOOSE
    def read_lidar_data_from_bin(self):
        try:
            # Leer los datos del archivo binario
            scan = np.fromfile(self.file_path, dtype=np.float32)
            # Los datos LiDAR tienen 4 columnas: X, Y, Z, Intensidad
            points = scan.reshape((-1, 4))
            return points
        except FileNotFoundError:
            print(f"Error: No se encontró el archivo {self.file_path}")
            return np.array([])

class LidarVisualizer(LidarDataReader):
    def __init__(self, file_path):
        # Inicializar la clase base con el archivo de datos LiDAR
        super().__init__(file_path)

    # Función que convierte intensidad a color en formato hexadecimal
    def intensity_to_color(self, intensity):
        # Normalizamos la intensidad
        intensity_normalized = (intensity - intensity.min()) / (intensity.max() - intensity.min())
        # Usamos una colormap de matplotlib para visualizar las intensidades
        colormap = cm.get_cmap('plasma') 
        rgba_colors = colormap(intensity_normalized)
        # Convertimos los valores rgba a hex para poder mostrarlos con Plotly
        hex_colors = [f'#{int(r*255):02x}{int(g*255):02x}{int(b*255):02x}' for r, g, b, _ in rgba_colors]
        return hex_colors

    # Función que genera un gráfico 3D con Plotly
    def plot_lidar_data(self):
        if self.data.size == 0:
            print("No hay datos LiDAR para visualizar.")
            return

    # Vilualización con Plotly de la versión anterior      
            . . . . 
            . . . .
            . . . .          

Al ejecutar el código nos mostrará en 3D la muestra que se carga en la ruta del objeto LidarDataReader

Visor 3D

Sensor LiDAR simulado en Carla

He desarrollado un script que se ejecuta en paralelo al servidor principal de CARLA. Este script genera un vehículo que conduce automáticamente por las rutas preestablecidas del mundo virtual de CARLA. Al vehículo le acoplo un sensor LiDAR, el cual comienza a tomar muestras mientras el vehículo se desplaza

Estas muestras se muestran por terminal, permitiéndome tener una primera toma de contacto con la generación de datos LiDAR simulados en CARLA

Estructura del Código

  1. Conexión al servidor
    • Se conecta al servidor local de CARLA y obtiene el entorno de simulación (world)
  2. Creación del vehículo
    • Se selecciona un vehículo de la biblioteca de actores de CARLA y se genera en un punto de spawn predefinido
    • El vehículo se configura en modo de conducción automática utilizando el método set_autopilot(True)
  3. Adición del sensor LiDAR
    • Se selecciona y configura un sensor LiDAR simulado con atributos como el rango, la frecuencia de rotación y el número de canales
    • El sensor se acopla al vehículo y comienza a capturar datos
  4. Callback para procesar datos LiDAR
    • Se define una función de callback (lidar_callback) para procesar los datos LiDAR en tiempo real, mostrando la información por terminal
  5. Lógica de Ejecución
    • El script se ejecuta en un bucle infinito hasta que se interrumpe manualmente
    • Al finalizar, se detienen y destruyen los actores (vehículo y sensor) creados
import carla

# --------------- Conectar al Servidor principal ------------------ #

# Conectarse al servidor principal de CARLA
client = carla.Client('localhost', 2000)
client.set_timeout(10.0)  # Tiempo límite para conectarse

# Obtener el MUNDO del simulador obtenido de la conexión con el servidor
world = client.get_world()

# Obtener Blueprint (Lista de actores disponibles en el Mundo conectado)
blueprint_library = world.get_blueprint_library()

# ----------------  Agregar un ACTOR Vehículo  -------------------- #

# Filtramos por tipo de actores: Vehiculo y seleccionamos el primero de la lista resultante
vehicle_00 = blueprint_library.filter('vehicle.*')[0]
# Obtener los puntos de Spawn del MAPA y elegir el primero de la lista resultante
spawn_point = world.get_map().get_spawn_points()[0]
# Spawnear el objeto de Tipo: Actor_Vehiculo (Se crea un nuevo objeto al generarlo)
vehicle = world.spawn_actor(vehicle_00, spawn_point)
# Activar el modo de conducción automática por defecto que odrece carla con el metodo set_autopilot(True)
vehicle.set_autopilot(True)

# ----------------  Agregar un ACTOR Sensor LiDAR y configurar sus atributos  -------------------- #

# Filtramos por tipo de actores: Sensores, lidar.raycast (Sensor LiDAR simulado en Carla)
lidar_bp = blueprint_library.find('sensor.lidar.ray_cast')

# Configuración del objeto obtenido de tipo actor_sensor_lidar.raycast
# Configurar Rango MAX del sensor
lidar_bp.set_attribute('range', '40')
# Configurar la Frecuencia de rotación en Hz
lidar_bp.set_attribute('rotation_frequency', '10')
# Configurar número de canales
lidar_bp.set_attribute('channels', '32')
# Configurar frecuencia de muestreo (56 kHz)
lidar_bp.set_attribute('points_per_second', '56000')
# Ubicar el objeto en el espacio (teniendo en cuenta que luego se acoplará al vehículo, solo altura)
lidar_position = carla.Transform(carla.Location(x=0, z=2.5))

# Spawnear el sensor y acoplarlo al objeto de Tipo Actor_Vehiculo

# El tercer parámetro vincula la posición del actor LiDAR a otro objeto de tipo Actor_Vehiculo.
lidar = world.spawn_actor(lidar_bp, lidar_position, attach_to=vehicle)
# Función de callback para procesar los datos LiDAR
def lidar_callback(point_cloud):
    print("LiDAR data received: ", point_cloud)

# Activación del método listen() del actor_sensor_lidar.raycast
# "Todos los Actores de Tipo Sensor tienen el método listen() para obtener la información"
lidar.listen(lidar_callback)

# -------------------- Lógica de Ejecución ----------------- #
try:
    # Mantener el script corriendo
    while True:
        world.wait_for_tick()
except KeyboardInterrupt:
    pass
finally:
    # Parar de escuchar
    lidar.stop()
    # Destruir objeto spawn_sensor
    lidar.destroy()
    # Destruir objeto spawn_vehiculo
    vehicle.destroy()

Al ejecutar el script mientras el servidor principal está corriendo, se muestran las medidas que va tomando el sensor

Visor 3D

Exploración de ROSbridge

He empezado a investigar sobre ROSbridge, una herramienta que permite que ROS se comunique con aplicaciones web o clientes externos que no estén escritos en lenguajes como Python o C++. Esto se logra usando WebSockets y JSON

En mi proyecto, será útil en fases más avanzadas. CARLA generará los datos simulados, y con el paquete carla-ros-bridge, estos se convertirán en mensajes ROS. ROSbridge actuará como el puente para extraer y gestionar los datos LiDAR

Gracias a la API en tiempo real de CARLA, los datos se integrarán fácilmente con ROS, permitiendo análisis y visualización avanzados