Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Python Target] CARLA Demo #97

Merged
merged 6 commits into from
Apr 14, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
74 changes: 74 additions & 0 deletions examples/Python/src/CARLA/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
# CARLA

This guide provides detailed instructions for running the CARLA simulator with LF. Ensure the following prerequisites are met before proceeding with the installation:

- **System requirements.** CARLA is built for Windows and Linux systems.
- **An adequate GPU.** CARLA aims for realistic simulations, so the server needs at least a 6 GB GPU although we would recommend 8 GB. A dedicated GPU is highly recommended for machine learning.
- **Disk space.** CARLA will use about 20 GB of space.
- **Two TCP ports and good internet connection.** 2000 and 2001 by default. Make sure that these ports are not blocked by firewalls or any other applications.
- **Other requirements.** CARLA requires some Python dependencies. Install the dependencies with the following command:

```bash
pip3 install --user pygame numpy carla
```

### **Downloading CARLA**

Download CARLA version 0.9.15 from [the official repository](https://github.com/carla-simulator/carla/releases/tag/0.9.15/). Extract the release file, which contains a precompiled version of the simulator.

### **Running the CARLA Server**

1. **On Windows:** Navigate to the extracted CARLA folder and double-click the `CarlaUE4.exe` file to start the server. A window will appear indicating the server is active.
2. **On Linux:** In the terminal, navigate to the CARLA folder and run `./CarlaUE4.sh` to initiate the server.

Note: Please restart the CARLA Server before running each of the examples below.

### **Compiling LF**

Compile the `carla_manual_control.lf` and `carla_circle.lf` using the `lfc` command.

### Synchronous Example - Manual Control

Run the generated script `/.carla_manual_control` which connects to the CARLA server in synchronous mode and initiates a driving simulator in pygame. Use ARROWS or WASD keys for control. Image and IMU data will be displayed on the console.

<img src="img/carla_manual.png" alt="drawing" width="800"/>

### Asynchronous Example - Driving in Circles

Run the generated script `/.carla_circles` which connects to the CARLA server in asynchronous mode and initiates a driving simulator in pygame. The vehicle will drive in circles. Image and IMU data will be displayed on the console.

<img src="img/carla_circles.gif" alt="drawing" width="800"/>

### Possible Configurations of CARLA Server

The configuration of time-step and synchrony, leads for different settings. Here is a brief summary on the possibilities.

| | Fixed time-step | Variable time-step |
| ----------------- | ---------------------------------------------------------------------- | ---------------------------------- |
| Synchronous mode | Client is in total control over the simulation and its information. | Risk of non reliable simulations. |
| Asynchronous mode | Good time references for information. Server runs as fast as possible. | Non easily repeatable simulations. |

- **Synchronous mode + variable time-step.** This is almost for sure a non-desirable state. Physics cannot run properly when the time-step is bigger than 0.1s and. If the server has to wait for the client to compute the steps, this is likely to happen. Simulation time and physics will not be in synchrony. The simulation will not be reliable.
lhstrh marked this conversation as resolved.
Show resolved Hide resolved
- **Asynchronous mode + variable time-step.** This is the default CARLA state. Client and server are asynchronous. The simulation time flows according to the real time. Reenacting the simulation needs to take into account float-arithmetic error, and possible differences in time steps between servers.
jackyk02 marked this conversation as resolved.
Show resolved Hide resolved
- **Asynchronous mode + fixed time-step.** The server will run as fast as possible. The information retrieved will be easily related with an exact moment in the simulation. This configuration makes possible to simulate long periods of time in much less real time, if the server is fast enough.
- **Synchronous mode + fixed time-step.** The client will rule the simulation. The time step will be fixed. The server will not compute the following step until the client sends a tick. This is the best mode when synchrony and precision is relevant. Especially when dealing with slow clients or different elements retrieving information.
lhstrh marked this conversation as resolved.
Show resolved Hide resolved

**Fixed time-step**

Fixed delta seconds can be set in the world settings.

```python
settings = world.get_settings()
settings.fixed_delta_seconds = 0.05
world.apply_settings(settings)
```

**Variable time-step**

The default mode in CARLA. The simulation time that goes by between steps will be the time that the server takes to compute these.

```python
settings = world.get_settings()
settings.fixed_delta_seconds = None # Set a variable time-step
world.apply_settings(settings)
```
242 changes: 242 additions & 0 deletions examples/Python/src/CARLA/carla_circle.lf
Original file line number Diff line number Diff line change
@@ -0,0 +1,242 @@
target Python {
threading: True
}

preamble {=
import carla
jackyk02 marked this conversation as resolved.
Show resolved Hide resolved
import weakref
import random
import pygame
from pygame.locals import K_ESCAPE
from pygame.locals import K_SPACE
from pygame.locals import K_a
from pygame.locals import K_d
from pygame.locals import K_s
from pygame.locals import K_w
import numpy as np

VIEW_WIDTH = 1920//2
VIEW_HEIGHT = 1080//2
VIEW_FOV = 90

BB_COLOR = (248, 64, 24)


def process_image_data(image):
"""
Processes the raw image data from the camera sensor.
"""
array = np.frombuffer(image.raw_data, dtype=np.dtype("uint8"))
array = np.reshape(array, (image.height, image.width, 4))
array = array[:, :, :3]
array = array[:, :, ::-1]
return array


class BasicSynchronousClient(object):
"""
Basic implementation of a synchronous client.
"""

def __init__(self):
self.client = None
self.world = None
self.camera = None
self.car = None

self.display = None
self.image = None
self.capture = True

def camera_blueprint(self):
"""
Returns camera blueprint.
"""
camera_bp = self.world.get_blueprint_library().find("sensor.camera.rgb")
camera_bp.set_attribute("image_size_x", str(VIEW_WIDTH))
camera_bp.set_attribute("image_size_y", str(VIEW_HEIGHT))
camera_bp.set_attribute("fov", str(VIEW_FOV))
return camera_bp

def set_synchronous_mode(self, synchronous_mode):
"""
Sets synchronous mode.
"""
settings = self.world.get_settings()
settings.fixed_delta_seconds = 0.05
settings.synchronous_mode = synchronous_mode
self.world.apply_settings(settings)

def setup_car(self):
"""
Spawns actor-vehicle to be controled.
lhstrh marked this conversation as resolved.
Show resolved Hide resolved
"""
car_bp = self.world.get_blueprint_library().filter("vehicle.*")[0]
location = random.choice(self.world.get_map().get_spawn_points())
self.car = self.world.spawn_actor(car_bp, location)

def setup_imu(self):
"""
Spawns actor-IMU sensor to be used to get IMU data.
"""
imu_bp = self.world.get_blueprint_library().find("sensor.other.imu")
imu_transform = carla.Transform(carla.Location(x=0.5, z=2.8))
self.imu = self.world.spawn_actor(
imu_bp, imu_transform, attach_to=self.car)
weak_self = weakref.ref(self)
self.imu.listen(lambda data: weak_self().process_imu_data(data))

def setup_camera(self):
"""
Spawns actor-camera to be used to render view.
"""
camera_transform = carla.Transform(carla.Location(
x=-5.5, z=2.8), carla.Rotation(pitch=-15))
self.camera = self.world.spawn_actor(
self.camera_blueprint(), camera_transform, attach_to=self.car)
weak_self = weakref.ref(self)
self.camera.listen(
lambda image: weak_self().set_image(weak_self, image))

calibration = np.identity(3)
calibration[0, 2] = VIEW_WIDTH / 2.0
calibration[1, 2] = VIEW_HEIGHT / 2.0
calibration[0, 0] = calibration[1, 1] = VIEW_WIDTH / \
(2.0 * np.tan(VIEW_FOV * np.pi / 360.0))
self.camera.calibration = calibration

def control(self, car):
control = car.get_control()
control.throttle = 1
control.steer = 1
car.apply_control(control)
return False

def process_imu_data(self, data):
"""
Processes and stores IMU data.
"""
self.imu_data = data

@staticmethod
def set_image(weak_self, img):
self = weak_self()
if self.capture:
self.image = img
self.capture = False

def render(self, display):
"""
Renders the image on the display.
"""
if self.image is not None:
array = process_image_data(self.image)
surface = pygame.surfarray.make_surface(array.swapaxes(0, 1))
display.blit(surface, (0, 0))
return array

def game_start(self):
"""
Initializes the game, setting up the client, car, camera, IMU, and display.
"""
pygame.init()

self.client = carla.Client("localhost", 2000)
self.client.set_timeout(10.0)
self.world = self.client.get_world()

self.setup_car()
self.setup_camera()
self.setup_imu()

self.display = pygame.display.set_mode(
(VIEW_WIDTH, VIEW_HEIGHT), pygame.HWSURFACE | pygame.DOUBLEBUF)
self.pygame_clock = pygame.time.Clock()

self.set_synchronous_mode(False)
vehicles = self.world.get_actors().filter("vehicle.*")

def game_step(self):
"""
Simulates one step in the game, processing inputs and rendering the image.
"""
self.world.tick()
self.capture = True
self.pygame_clock.tick_busy_loop(20)
image = self.render(self.display)

pygame.display.flip()
pygame.event.pump()

sensor_data = None
if not self.image or hasattr(self, "imu_data"):
sensor_data = [self.image, self.imu_data.accelerometer.x]

if self.control(self.car):
return sensor_data

return sensor_data
=}

reactor Carla {
input actions
output raw_image
output imu
state client

reaction(startup) -> raw_image, imu {=
self.client = BasicSynchronousClient()
self.client.game_start()
sensor_data = self.client.game_step()
raw_image.set(sensor_data[0])
imu.set(sensor_data[1])
=}

reaction(actions) -> raw_image, imu {=
sensor_data = self.client.game_step()
raw_image.set(sensor_data[0])
imu.set(sensor_data[1])
=}
}

reactor Image {
input raw_image
output processed_image

reaction(startup) {= =}

reaction(raw_image) -> processed_image {=
if raw_image.value:
array = process_image_data(raw_image.value)
processed_image.set(array)
else:
processed_image.set(None)
=}
}

reactor Fusion {
input imu
input processed_image
output actions

reaction(startup) {= =}

reaction(imu, processed_image) -> actions {=
if imu is not None:
print("IMU Data: ", imu.value)
if processed_image.value is not None:
print("Image Data: ", processed_image.value[0][0])
actions.set(0)
=}
}

main reactor {
carla = new Carla()
image = new Image()
fusion = new Fusion()

carla.raw_image -> image.raw_image
carla.imu -> fusion.imu
image.processed_image -> fusion.processed_image
fusion.actions -> carla.actions after 50 ms
}
Loading
Loading