Semana 7: PythonAPI
El objetivo de esta semana era estudir la PythonAPI de CARLA para crear un coche teleoperable y que tuviese el sensor LiDAR.
Tutorial PythonAPI
Primero se siguió un pequeño tutorial para entender los principios de la biblioteca de CARLA, investigando como conectar el cliente al servidor, genera un vehiculo, hacer que se mueva de forma automática por los caminos en CARLA, etc.
Script Creado
Se hicieron varios scripts de prueba para ir añadiendo el control manual, la cámara de visualización, tocar la físicas de distintos elementos para probar su funcionamiento, etc. Finalmente se hizo el script de prueba MovementLidar.py con todo.
control_vehicle()
Se trata de la función que detecta las teclas pulsadas en el teclado mediante Pygame y realiza una función dependiendo de cual tecla se haya presionado. Con W se acelera, con la S se frena, con la A se gira a la izquierda, la D para girar a la derecha y el ESPACIO para el freno de mano.
def control_vehicle(vehicle):
keys = pygame.key.get_pressed()
control = carla.VehicleControl()
control.throttle = 0.0
control.steer = 0.0
control.brake = 0.0
control.hand_brake = False
if keys[pygame.K_w]: # Acelerar
control.throttle = 1.0
if keys[pygame.K_s]: # Frenar
control.brake = 1.0
if keys[pygame.K_a]: # Girar a la izquierda
control.steer = -0.3
if keys[pygame.K_d]: # Girar a la derecha
control.steer = 0.3
if keys[pygame.K_SPACE]: # Freno de mano
control.hand_brake = True
# Aplicar el control al vehículo
vehicle.apply_control(control)
process_img()
Es la función que procesa el formato de las imágenes recibidas por la camara para hacerlas visualizables y las muestra en la pantalla mediante Pygame. Primero pasa la imagen sin procesar a uint8, se reorganiza el array en una estrictira de 3 dimensiones con 4 canales de color, se elimina el canal alfa, se pasa de BGR a RGB, se crea la superficie de Pygame y se dibuja por pantalla.
# Función de callback para recibir las imágenes de la cámara
def process_img(image, display):
array = np.frombuffer(image.raw_data, dtype=np.dtype("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)) # Dibujar en la pantalla
process_lidar()
Se encarga de procesar los datos recibido por el sensor LiDAR, extrayendo las coordenas y la intensidad de cada punto. Primero lee los datos LiDAR y los convierte a float32, se reorganizan los datos y se indica el número de puntos procesados.
# Función de callback para procesar los datos del LiDAR
def process_lidar(data):
points = np.frombuffer(data.raw_data, dtype=np.float32)
points = np.reshape(points, (int(points.shape[0] / 4), 4)) # [x, y, z, intensity]
print(f'Recibidos {points.shape[0]} puntos de LiDAR')
main()
Es el código principal del script se encarga de la conexión al servidor, la creación de un vehículo aleatoria y su aparición en uno de los spawn points, se añadio un sensor de colisión que indicase cada vez que habia una Y se configuró el modo síncrono, la cámara y el sensor LiDAR, que también guarda los datos .ply generados. Finalmente, tiene el bucle principal de simulación que escucha eventos de pygame y llama a control_vehicle() para manejar el vehículo generado y a world.tick() para avanzar un tick en la simulación de manera constante. También se hicieron algunas pruebas de las físicas del vehículo y de las ruedas que no se han añadido aqui.
def main():
# Conectar al servidor CARLA
client = carla.Client('localhost', 2000)
client.set_timeout(10.0)
world = client.get_world()
# Obtener el blueprint library para los vehículos
blueprint_library = world.get_blueprint_library()
vehicle_bp = blueprint_library.filter('vehicle.*')[0] # Selecciona un coche específico que tenga un modelo de físicas realista
# Obtener un spawn point aleatorio
spawn_points = world.get_map().get_spawn_points()
spawn_point = random.choice(spawn_points)
# Spawnear el vehículo
vehicle = world.try_spawn_actor(vehicle_bp, spawn_point)
if vehicle is not None:
print('Vehículo spawneado correctamente')
##################
# Configuracion de las colisiones del vehiculo
collision_bp = blueprint_library.find('sensor.other.collision')
collision_sensor = world.spawn_actor(collision_bp, carla.Transform(), attach_to=vehicle)
# Ejemplo de escucha del sensor de colisión
collision_sensor.listen(lambda event: print("Colisión detectada"))
#########################
# Configuración del vehículo
vehicle.set_autopilot(False) # Desactivar el piloto automático
######################## Modo sincrónico
settings = world.get_settings()
settings.synchronous_mode = True # Habilitar modo sincrónico
# Establecer delta de tiempo fijo (por ejemplo, 0.03 segundos)
settings.fixed_delta_seconds = 0.03
world.apply_settings(settings)
# Configuración de la cámara en tercera persona
camera_bp = blueprint_library.find('sensor.camera.rgb')
camera_bp.set_attribute('image_size_x', f'{width}')
camera_bp.set_attribute('image_size_y', f'{height}')
camera_bp.set_attribute('fov', '110') # Campo de visión amplio
# Posición de la cámara detrás y encima del coche
camera_transform = carla.Transform(carla.Location(x=-4.0,z=2.5), carla.Rotation(pitch=-15))
# Crear y spawnear la cámara
camera = world.spawn_actor(camera_bp, camera_transform, attach_to=vehicle)
# Configurar la cámara para enviar imágenes a la función process_img
camera.listen(lambda image: process_img(image, screen))
############################################### Añadir un sensor LiDAR
lidar_bp = blueprint_library.find('sensor.lidar.ray_cast')
lidar_bp.set_attribute('range', '50') # Rango del LiDAR en metros
lidar_bp.set_attribute('channels', '32') # Número de canales
lidar_bp.set_attribute('points_per_second', '56000') # Resolución del LiDAR
lidar_bp.set_attribute('rotation_frequency', '10') # Frecuencia de rotación en Hz
# Colocar el LiDAR en el techo del vehículo
lidar_transform = carla.Transform(carla.Location(x=0.0, z=2.5))
lidar = world.spawn_actor(lidar_bp, lidar_transform, attach_to=vehicle)
# Configurar el callback del LiDAR
lidar.listen(lambda data: process_lidar(data))
# Configurar el callback del LiDAR y guardar datos en disco
lidar.listen(lambda point_cloud: point_cloud.save_to_disk('/home/yuriy/Lidar/%.6d.ply' % point_cloud.frame))
try:
# Bucle principal para control del coche
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
# Controlar el coche con el teclado
control_vehicle(vehicle)
# Avanzar un tick en la simulación
world.tick()
# Obtener la velocidad del vehículo
velocity = vehicle.get_velocity() # Devuelve un vector (x, y, z)
speed = np.sqrt(velocity.x**2 + velocity.y**2 + velocity.z**2) # Magnitud de la velocidad
speed_kmh = speed * 3.6 # Convertir a km/h
# Mostrar la velocidad en pantalla
speed_text = font.render(f'Velocidad: {speed_kmh:.2f} km/h', True, (255, 255, 255))
screen.blit(speed_text, (50, 50))
pygame.display.flip() # Actualizar la pantalla
# Pequeño delay
time.sleep(0.05)
finally:
# Restaurar los ajustes originales
settings.synchronous_mode = False
world.apply_settings(settings)
# Destruir el vehículo, la cámara y el LiDAR
lidar.destroy()
camera.destroy()
vehicle.destroy()
print("Simulación terminada, vehículo, cámara y LiDAR destruidos.")
else:
print('Error: No se pudo spawnear el vehículo.')
Resultado
Problemas
-
Reinstalación: Debido a que al intentar iniciar el script en CARLA saltaba un error, al igual que con el manual_control.py proporcionado por CARLA, se buscaron soluciones en distintos foros, pero debido a la solución que proporcionaba un usuario se estropeo la instalación, por lo que se tuvo que volver a hacer toda la instalación de CARLA y Unreal Engine en la que surgieron algunos problemas de permisos que se acabaron solucionando y finalmente si se podían ejecutar los scripts, pero se perdió mucho tiempo debido a ello.
-
Open3D: Se intento visualizar en tiempo real con Open3D los puntos LiDAR generados, pero salia un erro de Violación de Segmentación tanto en el script de prueba MovementLidarVisualizer.py como en los scripts de compañeros que probaron su funcionamiento, pero todavía no se ha encontrado una solución.