Semana 26: Atenuaciones personalizadas basadas en GOOSE
Atenuaciones personalizadas basadas en GOOSE
Con el objetivo de aumentar lo máximo posible el realismo de las simulaciones LiDAR en CARLA, se ha desarrollado un sestema de atenuación por clase basado en datos empíricos, a diferencia de la atenuación basica de CARLA, que usa un valor fijo para todas las clases. Como se ha mencionado en otras semanas, para calcular la intensidad recibida, CARLA se basa en una fórmula que depende de la distancia y el coeficiente de atenuación:
I = I₀ * e^(-α·d)
donde:
-
I es la remisión observada (cuánto se refleja).
-
I₀ es la intensidad máxima (255.0)
-
d es la distancia del punto al sensor
-
α es un coeficiente de atenuación característico de la clase (material/objeto)
Cálculo de α por clase
Para conseguir valores empíricos de atenuación por clase, se procesaron varios escenarios del dataset GOOSE, agrupando los puntos por clase y calculando los valores de α de cada uno usando:
alpha = -log(I / I₀) / distancia
Después se calcularon la media y la desviación típica de cada clase.
1. Filtrado de escenarios
Debido a que GOOSE contiene escenarios con distintas condiciones meteorológicas, se tuvo que seleccionar los escenarios que mejor encajasen con los datasets que se están creando en CARLA para no tener valores erroneos de atenuación debido a lluvia, nieve, etc.
escenarios_validos = {
"2022-07-22_flight",
"2022-07-27_hoehenkirchner_forst",
"2022-08-18_putzbrunn_feldwege",
"2022-08-30_siegersbrunn_feldwege",
"2022-09-14_garching_uebungsplatz",
"2022-09-21_garching_uebungsplatz_2",
# "2022-10-12_sollninden_waldwege", # Nublado
"2022-10-14_hohenbrunn_feldwege_waldwege",
# "2022-11-04_campus_rain", # Lluvia
"2022-11-11_aying",
# "2022-11-29_campus_rain_2", # Lluvia
# "2022-12-07_aying_hills", # Nieve
# "2023-01-20_aying_mangfall_2", # Nieve
# "2023-01-20_campus_snow", # Nieve
# "2023-02-13_touareg_neuperlach", # Nublado
# "2023-02-23_campus_roads", # Nublado
"2023-03-02_garching",
"2023-03-03_garching_2",
"2023-04-05_flight_with_tiguan",
# "2023-04-20_campus", # Nublado y restos de lluvia
# "2023-05-15_neubiberg_rain", # Lluvia
"2023-05-17_neubiberg_sunny",
# "2023-05-24_neubiberg_cloudy" # Nublado
}
2. Procesamiento de nubes de puntos
Para cada archivo .bin:
- Se lee la nube de puntos con “np.fromfile”, separando las coordenadas XYZ y la remisión.
scan = np.fromfile(bin_path, dtype=np.float32).reshape((-1, 4)) puntos = scan[:, :3] remission = scan[:, 3]
-
Se calcula la distancia euclidiana al sensor.
- Se filtran valores que pueden producir errores (remisión < 0.01 o distancia < 0.1 m). Esta validación evita operaciones de logaritmo indefinido (por I=0), o divisiones por cero en distancias muy pequeñas.
mask_valid = (remission > 0.01) & (distancias > 0.1)
- Se carga la etiqueta semántica correspondiente de su archivo .label.
labels = np.fromfile(label_path, dtype=np.uint32)
3. Cálculo de atenuación
Para cada punto válido, se aplica:
alpha = -log(I / I₀) / distancia
Se eliminaron valores fuera del rango entre los percentiles 1% y 99%, lo que reduce la influencia de desviaciones grandes en los extremos que se hayan podido producir por errores (outliers).
4. Agregación estadística
Se acumulan los valores de alpha por etiqueta semántica, y al final se calcula:
-
media
-
mediana
-
desviación típica
-
número de puntos válidos
Que se guarda en un archivo llamado “atenuacion_global.json”.
Simulación de intensidades LiDAR
Finalmente, en el código “03d-semanticLidar-1.0.py” se realizan varias operaciones para generar las intensidades de cada punto:
1. Carga de estadísticas de atenuación
Se cargan las estadísticas generadas a partir de los escenarios de GOOSE.
with open("atenuacion_global.json") as f:
goose_stats = json.load(f)
2. Mapeo de etiquetas: GOOSE → CARLA
Debido a que GOOSE tiene más clases que CARLA y con diferente IDs, se ha definido un mapeo manual para poder usar los datos de GOOSE.
GOOSE_TO_CARLA_LABELS = {
"0": 0, # undefined
"23": 1, # asphalt → road
"21": 2, # sidewalk
"38": 3, # building
"39": 4, # wall
"41": 5, # fence
"45": 6, # pole
"19": 7, # traffic light
"46": 8, # traffic sign
"17": 9, # bush → vegetation
"50": 10, # low grass → terrain
"53": 11, # sky
"14": 12, # pedestrian
"32": 13, # rider
"12": 14, # car
"34": 15, # truck
"15": 16, # bus
"35": 17, # on_rail → train
"20": 18, # motorcycle
"13": 19, # bicycle
"4": 20, # obstacle → static
"4": 21, # dynamic
"4": 22, # other
"54": 23, # water
"11": 24, # road line
"31": 25, # soil → ground
"43": 26, # bridge
"26": 27, # rail track
"42": 28 # guard rail
}
3. Construcción del diccionario ATTENUATION_CARLA
Se ha decidido utilizar no solo la intensidad media esperada por clase, sino que también incorporar ruido gaussiano a través de la desviación típica, aportando mayor realismo a la escena, donde no todos los puntos de una misma clase reflejan igual. Para ello se extraen los datos y se reetiquetan para usarlos con las clases de CARLA.
ATTENUATION_CARLA = {}
for goose_label, carla_label in GOOSE_TO_CARLA_LABELS.items():
if goose_label in goose_stats:
ATTENUATION_CARLA[carla_label] = {
"mean": goose_stats[goose_label]["media"],
"std": goose_stats[goose_label]["std"]
}
4. Función “custom_intensity()”
def custom_intensity(points, semantic_tags, attenuation_dict, I0=255.0, add_noise=True):
Donde se crea la intensidad reflejada de cada punto, en base a:
-
La distancia al sensor
-
La etiqueta semántica del objeto
-
La atenuación por clase
-
Y opcionalmente un ruido Gaussiano, para representar la variabilidad real (la desviación típica)
Fórmula:
I = I₀ · exp(−α · d)
Conclusión
Gracias al uso del dataset GOOSE, ahora es posible emular intensidades LiDAR por clase de forma mucho más fiel a la realidad. Lo que permitirá generar datasets sintéticos más realistas y que puedan simular diversos escenarios con características meteorológicas distintas.