diff --git a/.github/workflows/build-all-images.yaml b/.github/workflows/build-all-images.yaml index 08b2bbb..30689b2 100644 --- a/.github/workflows/build-all-images.yaml +++ b/.github/workflows/build-all-images.yaml @@ -13,6 +13,8 @@ on: - 'components/visualizer/**' - 'components/simulator/**' - 'components/universe/**' + - 'tools/docker-bake.hcl' + - 'tools/carla-interface/**' schedule: - cron: '0 0 1 * *' workflow_dispatch: @@ -25,7 +27,9 @@ env: REGISTRY: ghcr.io IMAGE_PREFIX_COMMON: ghcr.io/${{ github.repository_owner }}/openadkit-common IMAGE_PREFIX_COMPONENT: ghcr.io/${{ github.repository_owner }}/openadkit + IMAGE_PREFIX_TOOL: ghcr.io/${{ github.repository_owner }}/autoware-tools BAKE_FILE: components/docker-bake.hcl + BAKE_FILE_TOOLS: tools/docker-bake.hcl jobs: prepare: @@ -278,7 +282,63 @@ jobs: *.cache-to=type=registry,ref=ghcr.io/${{ github.repository_owner }}/openadkit-buildcache:${{ matrix.target }}-${{ matrix.platform == 'linux/amd64' && 'amd64' || 'arm64' }}-${{ matrix.ros-distro }}-main,mode=max # ============================================================================= - # Stage 3: Build universe and universe-cuda images (parallel) + # Stage 3: Build tool images + # ============================================================================= + build-tools: + runs-on: ubuntu-22.04 + needs: [prepare, build-components] + permissions: + contents: read + packages: write + strategy: + fail-fast: false + matrix: + platform: [linux/amd64] + ros-distro: [humble] + target: + - carla-interface + steps: + - name: Checkout OpenADKit repository + uses: actions/checkout@v6 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Log in to Container Registry + uses: docker/login-action@v3 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Extract metadata for ${{ matrix.target }} + id: meta + uses: docker/metadata-action@v5 + with: + images: ${{ env.IMAGE_PREFIX_TOOL }} + bake-target: docker-metadata-action-${{ matrix.target }} + tags: | + type=raw,value=${{ matrix.target }},enable={{is_default_branch}} + type=raw,value=${{ matrix.target }}-${{ needs.prepare.outputs.build_tag }} + flavor: | + latest=false + + - name: Build and push ${{ matrix.target }} + uses: docker/bake-action@v5 + with: + files: | + ${{ env.BAKE_FILE_TOOLS }} + ${{ steps.meta.outputs.bake-file }} + targets: ${{ matrix.target }} + push: true + set: | + *.platform=${{ matrix.platform }} + *.args.SIMULATOR_IMAGE=${{ env.IMAGE_PREFIX_COMPONENT }}:simulator-amd64-${{ matrix.ros-distro }}-${{ needs.prepare.outputs.build_tag }} + *.cache-from=type=registry,ref=ghcr.io/${{ github.repository_owner }}/openadkit-buildcache:tool-${{ matrix.target }}-${{ matrix.platform == 'linux/amd64' && 'amd64' || 'arm64' }}-${{ matrix.ros-distro }}-main + *.cache-to=type=registry,ref=ghcr.io/${{ github.repository_owner }}/openadkit-buildcache:tool-${{ matrix.target }}-${{ matrix.platform == 'linux/amd64' && 'amd64' || 'arm64' }}-${{ matrix.ros-distro }}-main,mode=max + + # ============================================================================= + # Stage 4: Build universe and universe-cuda images (parallel) # ============================================================================= build-universe: runs-on: ${{ matrix.platform == 'linux/amd64' && 'ubuntu-22.04' || 'ubuntu-22.04-arm' }} @@ -378,11 +438,11 @@ jobs: *.cache-to=type=registry,ref=ghcr.io/${{ github.repository_owner }}/openadkit-buildcache:${{ matrix.target }}-${{ matrix.platform == 'linux/amd64' && 'amd64' || 'arm64' }}-${{ matrix.ros-distro }}-main,mode=max # ============================================================================= - # Stage 4: Create multi-arch manifests + # Stage 5: Create multi-arch manifests # ============================================================================= create-manifests: runs-on: ubuntu-22.04 - needs: [prepare, build-common, build-components, build-universe] + needs: [prepare, build-common, build-components, build-tools, build-universe] permissions: contents: read packages: write diff --git a/.github/workflows/release-all-images.yaml b/.github/workflows/release-all-images.yaml index 9356760..7e0e75d 100644 --- a/.github/workflows/release-all-images.yaml +++ b/.github/workflows/release-all-images.yaml @@ -13,7 +13,9 @@ env: REGISTRY: ghcr.io IMAGE_PREFIX_COMMON: ghcr.io/${{ github.repository_owner }}/openadkit-common IMAGE_PREFIX_COMPONENT: ghcr.io/${{ github.repository_owner }}/openadkit + IMAGE_PREFIX_TOOL: ghcr.io/${{ github.repository_owner }}/autoware-tools BAKE_FILE: components/docker-bake.hcl + BAKE_FILE_TOOLS: tools/docker-bake.hcl jobs: detect-autoware-tag: @@ -280,7 +282,60 @@ jobs: *.cache-to=type=registry,ref=ghcr.io/${{ github.repository_owner }}/openadkit-buildcache:${{ matrix.target }}-main,mode=max # ============================================================================= - # Stage 3: Build universe and universe-cuda images (parallel) + # Stage 3: Build tool images + # ============================================================================= + build-tools: + runs-on: ubuntu-22.04 + needs: [detect-autoware-tag, build-components] + if: needs.detect-autoware-tag.outputs.should_run == 'true' + permissions: + contents: read + packages: write + strategy: + fail-fast: false + matrix: + include: + - target: carla-interface + steps: + - name: Checkout OpenADKit repository + uses: actions/checkout@v6 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Log in to Container Registry + uses: docker/login-action@v3 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Extract metadata for ${{ matrix.target }} + id: meta + uses: docker/metadata-action@v5 + with: + images: ${{ env.IMAGE_PREFIX_TOOL }} + bake-target: docker-metadata-action-${{ matrix.target }} + tags: | + type=raw,value=${{ matrix.target }}-${{ needs.detect-autoware-tag.outputs.autoware_tag }} + flavor: | + latest=false + + - name: Build and push ${{ matrix.target }} + uses: docker/bake-action@v5 + with: + files: | + ${{ env.BAKE_FILE_TOOLS }} + ${{ steps.meta.outputs.bake-file }} + targets: ${{ matrix.target }} + push: true + set: | + *.args.SIMULATOR_IMAGE=${{ env.IMAGE_PREFIX_COMPONENT }}:simulator-${{ needs.detect-autoware-tag.outputs.autoware_tag }} + *.cache-from=type=registry,ref=ghcr.io/${{ github.repository_owner }}/openadkit-buildcache:tool-${{ matrix.target }}-main + *.cache-to=type=registry,ref=ghcr.io/${{ github.repository_owner }}/openadkit-buildcache:tool-${{ matrix.target }}-main,mode=max + + # ============================================================================= + # Stage 4: Build universe and universe-cuda images (parallel) # ============================================================================= build-universe: runs-on: ubuntu-22.04 diff --git a/deployments/README.md b/deployments/README.md index f10d20f..2d1fced 100644 --- a/deployments/README.md +++ b/deployments/README.md @@ -3,6 +3,7 @@ This directory contains deployment configurations for **Open AD Kit**. Each folder contains a README file with detailed instructions on how to deploy the deployment configuration. - **Sample deployment** configurations for development and testing. + - [CARLA Simulation](./samples/carla-simulation): Simple Open AD Kit deployment that demonstrates the open-source planning stack with CARLA as an external simulator. - [Planning Simulation](./samples/planning-simulation): Simple Open AD Kit deployment that demonstrates the autoware **planning features** with planning simulation. - [Logging Simulation](./samples/logging-simulation): Simple Open AD Kit deployment that demonstrates the autoware **end-to-end functionality** with sensor simulation using rosbag. - **Demo deployment** configurations with specific use case scenarios. diff --git a/deployments/samples/carla-simulation/README.md b/deployments/samples/carla-simulation/README.md new file mode 100644 index 0000000..e42c30e --- /dev/null +++ b/deployments/samples/carla-simulation/README.md @@ -0,0 +1,115 @@ +# Autoware Open AD Kit CARLA E2E Simulation + +This sample runs closed-loop CARLA 0.9.16 end-to-end simulation with modular OpenADKit containers and Autoware's `autoware_carla_interface`. + +The default runtime uses the official CARLA Ubuntu 22 container image. It does not use the CARLA ROS bridge, dummy vehicle, dummy perception, a monolithic Autoware container, or a host-installed CARLA runtime. + +## Runtime + +- CARLA: `carlasim/carla:0.9.16` +- CARLA interface image: `ghcr.io/autowarefoundation/autoware-tools:carla-interface` +- Autoware modules: standard OpenADKit split images +- CARLA map: `Town01` +- Autoware map assets: `$HOME/autoware_data/maps/Town01` +- RViz/noVNC: `ghcr.io/autowarefoundation/openadkit:visualizer` + +## Requirements + +- Docker with NVIDIA Container Toolkit +- Access to `carlasim/carla:0.9.16` +- A working host X display, usually `DISPLAY=:0` +- Host NVIDIA Vulkan ICD at `/usr/share/vulkan/icd.d/nvidia_icd.json` + +## Start + +```bash +./start-carla-e2e-demo.sh +``` + +The helper will: + +- Use the CI-built `ghcr.io/autowarefoundation/autoware-tools:carla-interface` image. +- Download the official CARLA Autoware Town01 map assets if missing. +- Start `carlasim/carla:0.9.16` as `carla-e2e` on `DISPLAY=:0`. +- Preload `Town01`. +- Start modular OpenADKit map, system, CARLA interface, sensing, perception, localization, planning, vehicle, control, and API containers. +- Start the browser RViz/noVNC visualizer. +- Verify localization, CARLA LiDAR, and the CARLA ego actor. + +Use `--build` to build the CARLA interface image locally from `tools/carla-interface` before starting the stack. + +The default behavior is no-drive. Set the route and engage manually in RViz. + +## Open RViz + +If running remotely, forward noVNC from the machine running the sample: + +```bash +ssh -L 8080:localhost:6080 @ +``` + +Open: + +```text +http://localhost:8080/vnc.html +``` + +Password: + +```text +openadkit +``` + +In RViz: + +- Use `2D Goal Pose` to set a route. +- Wait for routing and planning to become available. +- Click `Auto` to engage autonomous driving. + +## Optional Drive Check + +To automatically set a short forward route, engage autonomous mode, and verify movement: + +```bash +./start-carla-e2e-demo.sh --drive +``` + +Expected success output includes: + +```text +Route set: start=(..., ...) goal=(..., ...) +Autonomous mode active +Moved ... m: start=(..., ...) current=(..., ...) +``` + +## Faster Relaunch + +The helper does not build the CARLA interface image unless `--build` is passed, so relaunching uses the configured image directly: + +```bash +./start-carla-e2e-demo.sh +``` + +For start-only behavior with an explicit no-drive flag: + +```bash +./start-carla-e2e-demo.sh --no-drive +``` + +## Command Path + +Closed-loop control uses this path: + +```text +/control/command/control_cmd + -> autoware_raw_vehicle_cmd_converter + -> /control/command/actuation_cmd + -> autoware_carla_interface + -> CARLA ego vehicle +``` + +## Stop + +```bash +docker compose --env-file carla-simulation.e2e.env -f docker-compose.yaml down +``` diff --git a/deployments/samples/carla-simulation/carla-simulation.e2e.env b/deployments/samples/carla-simulation/carla-simulation.e2e.env new file mode 100644 index 0000000..cdcf688 --- /dev/null +++ b/deployments/samples/carla-simulation/carla-simulation.e2e.env @@ -0,0 +1,84 @@ +# ROS settings +ROS_DOMAIN_ID=1 +USE_SIM_TIME=true + +# Runtime images +CARLA_INTERFACE_IMAGE=ghcr.io/autowarefoundation/autoware-tools:carla-interface +CARLA_INTERFACE_CONTAINER=autoware-carla-interface +CARLA_PYTHON_VERSION=0.9.16 +SENSING_PERCEPTION_IMAGE=ghcr.io/autowarefoundation/openadkit:sensing-perception-cuda + +# CARLA runtime on the host X display. +CARLA_CONTAINER_IMAGE=carlasim/carla:0.9.16 +CARLA_CONTAINER_NAME=carla-e2e +CARLA_DISPLAY=:0 +CARLA_VK_ICD_HOST_PATH=/usr/share/vulkan/icd.d/nvidia_icd.json +CARLA_VK_ICD_CONTAINER_PATH=/usr/share/vulkan/icd.d/nvidia_icd.json +CARLA_RPC_HOST=127.0.0.1 +CARLA_RPC_PORT=2000 +CARLA_WORLD_PORT=2000 +CARLA_WORLD=Town01 +CARLA_QUALITY=Low +CARLA_RESX=1280 +CARLA_RESY=720 +CARLA_START_TIMEOUT=120 +CARLA_LOAD_TIMEOUT=120 + +# Official CARLA Autoware map assets +CARLA_E2E_MAP_PATH=$HOME/autoware_data/maps/Town01 +CARLA_E2E_POINTCLOUD_URL=https://bitbucket.org/carla-simulator/autoware-contents/raw/master/maps/point_cloud_maps/Town01.pcd +CARLA_E2E_LANELET2_URL=https://bitbucket.org/carla-simulator/autoware-contents/raw/master/maps/vector_maps/lanelet2/Town01.osm + +# Autoware map settings +MAP_PATH=$HOME/autoware_data/maps/Town01 +LANELET2_MAP_FILE=lanelet2_map.osm +POINTCLOUD_MAP_FILE=pointcloud_map.pcd +DATA_PATH=$HOME/autoware_data + +# Common Autoware settings +POINTCLOUD_CONTAINER_NAME=pointcloud_container +SCENARIO_SIMULATION=false +SENSOR_MODEL=carla_sensor_kit +VEHICLE_ID=default +VEHICLE_MODEL=sample_vehicle + +# Planning and control settings +ENABLE_ALL_MODULES_AUTO_MODE=false +IS_SIMULATION_MODE=true +PLANNING_MODULE_PRESET=default +CHECK_EXTERNAL_EMERGENCY_HEARTBEAT=false +CONTROL_MODULE_PRESET=default +LATERAL_CONTROLLER_MODE=mpc +LONGITUDINAL_CONTROLLER_MODE=pid + +# Vehicle and system settings +LAUNCH_VEHICLE_INTERFACE=false +SYSTEM_RUN_MODE=online +LAUNCH_SYSTEM_MONITOR=false +LAUNCH_DUMMY_DIAG_PUBLISHER=false + +# Sensing and perception settings +LAUNCH_POINTCLOUD_CONTAINER_SENSING=true +LAUNCH_SENSING_DRIVER=false +LAUNCH_POINTCLOUD_CONTAINER_PERCEPTION=false +LIDAR_DETECTION_MODEL=clustering +OCCUPANCY_GRID_MAP_METHOD=pointcloud_based +PERCEPTION_MODE=lidar + +# Autoware launch settings +AUTOWARE_E2E_TIMEOUT=120 +AUTOWARE_E2E_EMPTY_OBJECTS=false +AUTOWARE_E2E_USE_TRAFFIC_LIGHT_RECOGNITION=false +AUTOWARE_E2E_START_VISUALIZER=true +AUTOWARE_E2E_VERIFY_TIMEOUT=180 +AUTOWARE_E2E_VERIFY_INTERVAL=5 + +# Visualization settings +RVIZ_CONFIG=/opt/autoware/share/autoware_launch/rviz/autoware.rviz + +# Demo route and engage settings +AUTOWARE_E2E_AUTO_DRIVE=false +AUTOWARE_E2E_ROUTE_FORWARD_DISTANCE=55.0 +AUTOWARE_E2E_ROUTE_SETTLE_TIMEOUT=90 +AUTOWARE_E2E_DRIVE_VERIFY_TIMEOUT=90 +AUTOWARE_E2E_DRIVE_VERIFY_DISTANCE=3.0 diff --git a/deployments/samples/carla-simulation/docker-compose.yaml b/deployments/samples/carla-simulation/docker-compose.yaml new file mode 100644 index 0000000..eb807eb --- /dev/null +++ b/deployments/samples/carla-simulation/docker-compose.yaml @@ -0,0 +1,290 @@ +services: + carla: + image: ${CARLA_CONTAINER_IMAGE} + container_name: ${CARLA_CONTAINER_NAME} + network_mode: host + init: true + ipc: host + gpus: all + environment: + - DISPLAY=${CARLA_DISPLAY} + - SDL_VIDEODRIVER=x11 + - NVIDIA_VISIBLE_DEVICES=all + - NVIDIA_DRIVER_CAPABILITIES=compute,utility,graphics,display + - VK_ICD_FILENAMES=${CARLA_VK_ICD_CONTAINER_PATH} + volumes: + - /tmp/.X11-unix:/tmp/.X11-unix + - ${CARLA_VK_ICD_HOST_PATH}:${CARLA_VK_ICD_CONTAINER_PATH}:ro + command: > + /bin/bash ./CarlaUE4.sh + -windowed + -vulkan + -nosound + -ResX=${CARLA_RESX} + -ResY=${CARLA_RESY} + -quality-level=${CARLA_QUALITY} + -carla-rpc-port=${CARLA_RPC_PORT} + -world-port=${CARLA_WORLD_PORT} + -stdout + -FullStdOutLogOutput + restart: on-failure + + carla-interface: + image: ${CARLA_INTERFACE_IMAGE} + container_name: ${CARLA_INTERFACE_CONTAINER} + network_mode: host + init: true + ipc: host + environment: + - RMW_IMPLEMENTATION=rmw_cyclonedds_cpp + - ROS_DOMAIN_ID=${ROS_DOMAIN_ID} + depends_on: + - carla + command: > + ros2 launch autoware_carla_interface autoware_carla_interface.launch.xml + host:=${CARLA_RPC_HOST} + port:=${CARLA_RPC_PORT} + timeout:=${CARLA_LOAD_TIMEOUT} + carla_map:=${CARLA_WORLD} + sensor_kit_name:=carla_sensor_kit_description + restart: on-failure + + map: + image: ghcr.io/autowarefoundation/openadkit:localization-mapping + container_name: autoware-map + network_mode: host + init: true + ipc: host + environment: + - RMW_IMPLEMENTATION=rmw_cyclonedds_cpp + - ROS_DOMAIN_ID=${ROS_DOMAIN_ID} + volumes: + - ${MAP_PATH}:/autoware_map:ro + command: > + ros2 launch autoware_launch tier4_map_component.launch.xml + lanelet2_map_file:=${LANELET2_MAP_FILE} + map_path:=/autoware_map + pointcloud_map_file:=${POINTCLOUD_MAP_FILE} + use_sim_time:=${USE_SIM_TIME} + vehicle_model:=${VEHICLE_MODEL} + restart: on-failure + + system: + image: ghcr.io/autowarefoundation/openadkit:vehicle-system + container_name: autoware-system + network_mode: host + init: true + ipc: host + pid: service:map + environment: + - RMW_IMPLEMENTATION=rmw_cyclonedds_cpp + - ROS_DOMAIN_ID=${ROS_DOMAIN_ID} + depends_on: + - map + command: > + ros2 launch autoware_launch tier4_system_component.launch.xml + diagnostic_graph_aggregator_graph_path:=/opt/autoware/share/autoware_launch/config/system/diagnostics/autoware-carla.yaml + launch_dummy_diag_publisher:=${LAUNCH_DUMMY_DIAG_PUBLISHER} + launch_system_monitor:=${LAUNCH_SYSTEM_MONITOR} + sensor_model:=${SENSOR_MODEL} + system_run_mode:=${SYSTEM_RUN_MODE} + use_sim_time:=${USE_SIM_TIME} + restart: on-failure + + sensing: + image: ${SENSING_PERCEPTION_IMAGE} + container_name: autoware-sensing + network_mode: host + init: true + ipc: host + pid: service:map + gpus: all + environment: + - RMW_IMPLEMENTATION=rmw_cyclonedds_cpp + - ROS_DOMAIN_ID=${ROS_DOMAIN_ID} + - NVIDIA_VISIBLE_DEVICES=all + - NVIDIA_DRIVER_CAPABILITIES=compute,utility + depends_on: + - map + - carla-interface + - system + command: > + ros2 launch autoware_launch tier4_sensing_component.launch.xml + launch_pointcloud_container:=${LAUNCH_POINTCLOUD_CONTAINER_SENSING} + launch_sensing_driver:=${LAUNCH_SENSING_DRIVER} + pointcloud_container_name:=${POINTCLOUD_CONTAINER_NAME} + sensor_model:=${SENSOR_MODEL} + use_sim_time:=${USE_SIM_TIME} + vehicle_model:=${VEHICLE_MODEL} + restart: on-failure + + perception: + image: ${SENSING_PERCEPTION_IMAGE} + container_name: autoware-perception + network_mode: host + init: true + ipc: host + pid: service:map + gpus: all + environment: + - RMW_IMPLEMENTATION=rmw_cyclonedds_cpp + - ROS_DOMAIN_ID=${ROS_DOMAIN_ID} + - NVIDIA_VISIBLE_DEVICES=all + - NVIDIA_DRIVER_CAPABILITIES=compute,utility + volumes: + - ${DATA_PATH}:/autoware_data:rw + depends_on: + - map + - sensing + - system + command: > + ros2 launch autoware_launch tier4_perception_component.launch.xml + data_path:=/autoware_data + launch_pointcloud_container:=${LAUNCH_POINTCLOUD_CONTAINER_PERCEPTION} + lidar_detection_model:=${LIDAR_DETECTION_MODEL} + occupancy_grid_map_method:=${OCCUPANCY_GRID_MAP_METHOD} + perception_mode:=${PERCEPTION_MODE} + pointcloud_container_name:=${POINTCLOUD_CONTAINER_NAME} + use_empty_dynamic_object_publisher:=${AUTOWARE_E2E_EMPTY_OBJECTS} + use_sim_time:=${USE_SIM_TIME} + use_traffic_light_recognition:=${AUTOWARE_E2E_USE_TRAFFIC_LIGHT_RECOGNITION} + vehicle_model:=${VEHICLE_MODEL} + restart: on-failure + + localization: + image: ghcr.io/autowarefoundation/openadkit:localization-mapping + container_name: autoware-localization + network_mode: host + init: true + ipc: host + pid: service:map + environment: + - RMW_IMPLEMENTATION=rmw_cyclonedds_cpp + - ROS_DOMAIN_ID=${ROS_DOMAIN_ID} + depends_on: + - map + - sensing + - perception + - system + command: > + ros2 launch autoware_launch tier4_localization_component.launch.xml + system_run_mode:=${SYSTEM_RUN_MODE} + use_sim_time:=${USE_SIM_TIME} + vehicle_model:=${VEHICLE_MODEL} + restart: on-failure + + planning: + image: ghcr.io/autowarefoundation/openadkit:planning-control + container_name: autoware-planning + network_mode: host + init: true + ipc: host + pid: service:map + environment: + - RMW_IMPLEMENTATION=rmw_cyclonedds_cpp + - ROS_DOMAIN_ID=${ROS_DOMAIN_ID} + depends_on: + - map + - localization + - perception + - system + command: > + ros2 launch autoware_launch tier4_planning_component.launch.xml + component_wise_launch:=true + enable_all_modules_auto_mode:=${ENABLE_ALL_MODULES_AUTO_MODE} + is_simulation:=${IS_SIMULATION_MODE} + module_preset:=${PLANNING_MODULE_PRESET} + pointcloud_container_name:=${POINTCLOUD_CONTAINER_NAME} + use_sim_time:=${USE_SIM_TIME} + vehicle_model:=${VEHICLE_MODEL} + restart: on-failure + + vehicle: + image: ghcr.io/autowarefoundation/openadkit:vehicle-system + container_name: autoware-vehicle + network_mode: host + init: true + ipc: host + pid: service:map + environment: + - RMW_IMPLEMENTATION=rmw_cyclonedds_cpp + - ROS_DOMAIN_ID=${ROS_DOMAIN_ID} + depends_on: + - map + - carla-interface + command: > + ros2 launch tier4_vehicle_launch vehicle.launch.xml + launch_vehicle_interface:=${LAUNCH_VEHICLE_INTERFACE} + raw_vehicle_cmd_converter_param_path:=/opt/autoware/share/autoware_launch/config/vehicle/raw_vehicle_cmd_converter/raw_vehicle_cmd_converter.param.yaml + sensor_model:=${SENSOR_MODEL} + use_sim_time:=${USE_SIM_TIME} + vehicle_id:=${VEHICLE_ID} + vehicle_model:=${VEHICLE_MODEL} + restart: on-failure + + control: + image: ghcr.io/autowarefoundation/openadkit:planning-control + container_name: autoware-control + network_mode: host + init: true + ipc: host + pid: service:map + environment: + - RMW_IMPLEMENTATION=rmw_cyclonedds_cpp + - ROS_DOMAIN_ID=${ROS_DOMAIN_ID} + depends_on: + - map + - planning + - system + command: > + ros2 launch autoware_launch tier4_control_component.launch.xml + check_external_emergency_heartbeat:=${CHECK_EXTERNAL_EMERGENCY_HEARTBEAT} + component_wise_launch:=true + lateral_controller_mode:=${LATERAL_CONTROLLER_MODE} + longitudinal_controller_mode:=${LONGITUDINAL_CONTROLLER_MODE} + module_preset:=${CONTROL_MODULE_PRESET} + use_sim_time:=${USE_SIM_TIME} + vehicle_id:=${VEHICLE_ID} + vehicle_model:=${VEHICLE_MODEL} + restart: on-failure + + api: + image: ghcr.io/autowarefoundation/openadkit:api + container_name: autoware-api + network_mode: host + init: true + ipc: host + pid: service:map + environment: + - RMW_IMPLEMENTATION=rmw_cyclonedds_cpp + - ROS_DOMAIN_ID=${ROS_DOMAIN_ID} + depends_on: + - map + - system + command: > + ros2 launch autoware_launch tier4_autoware_api_component.launch.xml + component_wise_launch:=true + scenario_simulation:=${SCENARIO_SIMULATION} + use_sim_time:=${USE_SIM_TIME} + vehicle_model:=${VEHICLE_MODEL} + restart: on-failure + + visualizer: + image: ghcr.io/autowarefoundation/openadkit:visualizer + container_name: autoware-e2e-visualizer + network_mode: host + init: true + ipc: host + pid: service:map + environment: + - RMW_IMPLEMENTATION=rmw_cyclonedds_cpp + - ROS_DOMAIN_ID=${ROS_DOMAIN_ID} + - RVIZ_CONFIG=${RVIZ_CONFIG} + - USE_SIM_TIME=${USE_SIM_TIME} + depends_on: + - map + - api + - planning + - control + - carla-interface + restart: on-failure diff --git a/deployments/samples/carla-simulation/start-carla-e2e-demo.sh b/deployments/samples/carla-simulation/start-carla-e2e-demo.sh new file mode 100755 index 0000000..b3ccfda --- /dev/null +++ b/deployments/samples/carla-simulation/start-carla-e2e-demo.sh @@ -0,0 +1,531 @@ +#!/usr/bin/env bash +set -euo pipefail + +SCRIPT_DIR=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd) +REPO_ROOT=$(cd -- "$SCRIPT_DIR/../../.." && pwd) +ENV_FILE=carla-simulation.e2e.env +DRY_RUN=false +BUILD_IMAGE=false +SKIP_VERIFY=false +START_VISUALIZER_OVERRIDE= +AUTO_DRIVE_OVERRIDE= + +usage() { + cat <<'EOF' +Usage: ./start-carla-e2e-demo.sh [options] + +Starts the closed-loop CARLA e2e demo using CARLA on the host display and +Autoware's in-tree autoware_carla_interface. + +Options: + --build Build the local CARLA interface image from tools + --skip-build Do not build the local CARLA interface image (default) + --skip-verify Skip topic, actor, and localization verification + --no-visualizer Do not start the browser RViz/noVNC visualizer container + --drive Set a forward route and engage autonomous mode + --no-drive Start the stack without setting a route or engaging + --dry-run Print planned commands without running them + -h, --help Show this help text +EOF +} + +while (($#)); do + case "$1" in + --build) + BUILD_IMAGE=true + ;; + --skip-build) + BUILD_IMAGE=false + ;; + --skip-verify) + SKIP_VERIFY=true + ;; + --no-visualizer) + START_VISUALIZER_OVERRIDE=false + ;; + --drive) + AUTO_DRIVE_OVERRIDE=true + ;; + --no-drive) + AUTO_DRIVE_OVERRIDE=false + ;; + --dry-run) + DRY_RUN=true + ;; + -h|--help) + usage + exit 0 + ;; + *) + printf 'Unknown option: %s\n\n' "$1" >&2 + usage >&2 + exit 2 + ;; + esac + shift +done + +run() { + printf '+ %s\n' "$*" + if [[ "$DRY_RUN" == false ]]; then + "$@" + fi +} + +run_compose() { + printf '+ docker compose --env-file %s -f docker-compose.yaml %s\n' "$ENV_FILE" "$*" + if [[ "$DRY_RUN" == false ]]; then + docker compose --env-file "$ENV_FILE" -f docker-compose.yaml "$@" + fi +} + +load_env() { + set -a + # shellcheck source=/dev/null + source "$ENV_FILE" + set +a + + if [[ -n "$START_VISUALIZER_OVERRIDE" ]]; then + AUTOWARE_E2E_START_VISUALIZER=$START_VISUALIZER_OVERRIDE + fi + + if [[ -n "$AUTO_DRIVE_OVERRIDE" ]]; then + AUTOWARE_E2E_AUTO_DRIVE=$AUTO_DRIVE_OVERRIDE + fi +} + +prepare_map() { + if [[ "$DRY_RUN" == true ]]; then + printf '+ prepare CARLA Town01 map at %s\n' "$CARLA_E2E_MAP_PATH" + return 0 + fi + + mkdir -p "$CARLA_E2E_MAP_PATH" + + if [[ ! -s "$CARLA_E2E_MAP_PATH/pointcloud_map.pcd" ]]; then + run curl -L --fail -A Mozilla/5.0 -o "$CARLA_E2E_MAP_PATH/pointcloud_map.pcd" "$CARLA_E2E_POINTCLOUD_URL" + fi + + if [[ ! -s "$CARLA_E2E_MAP_PATH/lanelet2_map.osm" ]]; then + run curl -L --fail -A Mozilla/5.0 -o "$CARLA_E2E_MAP_PATH/lanelet2_map.osm" "$CARLA_E2E_LANELET2_URL" + fi + + printf 'projector_type: Local\n' > "$CARLA_E2E_MAP_PATH/map_projector_info.yaml" +} + +build_image() { + if [[ "$BUILD_IMAGE" != true ]]; then + return 0 + fi + + run docker buildx bake \ + --load \ + --progress=plain \ + -f "$REPO_ROOT/tools/docker-bake.hcl" \ + --set "*.context=$REPO_ROOT" \ + --set "carla-interface.tags=$CARLA_INTERFACE_IMAGE" \ + --set "carla-interface.args.CARLA_PYTHON_VERSION=$CARLA_PYTHON_VERSION" \ + carla-interface +} + +wait_for_carla_rpc() { + printf '+ wait for CARLA RPC on %s:%s\n' "$CARLA_RPC_HOST" "$CARLA_RPC_PORT" + if [[ "$DRY_RUN" == true ]]; then + return 0 + fi + + local deadline=$((SECONDS + CARLA_START_TIMEOUT)) + while ((SECONDS < deadline)); do + if timeout 1 bash -c "/dev/null 2>&1; then + return 0 + fi + sleep 2 + done + + printf 'Timed out waiting for CARLA RPC on %s:%s\n' "$CARLA_RPC_HOST" "$CARLA_RPC_PORT" >&2 + return 1 +} + +wait_for_carla_api() { + printf '+ wait for CARLA Python API on %s:%s\n' "$CARLA_RPC_HOST" "$CARLA_RPC_PORT" + if [[ "$DRY_RUN" == true ]]; then + return 0 + fi + + local deadline=$((SECONDS + CARLA_START_TIMEOUT)) + while ((SECONDS < deadline)); do + if docker run --rm --network host "$CARLA_INTERFACE_IMAGE" bash -lc "python3 - <<'PY' +import carla +client = carla.Client('$CARLA_RPC_HOST', int('$CARLA_RPC_PORT')) +client.set_timeout(5) +print(client.get_world().get_map().name) +PY" >/dev/null 2>&1; then + return 0 + fi + sleep 5 + done + + printf 'Timed out waiting for CARLA Python API on %s:%s\n' "$CARLA_RPC_HOST" "$CARLA_RPC_PORT" >&2 + return 1 +} + +stop_container_carla() { + run docker rm -f "$CARLA_CONTAINER_NAME" || true +} + +start_container_carla() { + if [[ "$DRY_RUN" == false && ! -S /tmp/.X11-unix/X${CARLA_DISPLAY#:} ]]; then + printf 'X display socket for %s was not found under /tmp/.X11-unix\n' "$CARLA_DISPLAY" >&2 + return 1 + fi + + stop_container_carla + run_compose up -d --force-recreate carla + + wait_for_carla_rpc + wait_for_carla_api +} + +preload_carla_world() { + if [[ "$DRY_RUN" == true ]]; then + run docker run --rm --network host "$CARLA_INTERFACE_IMAGE" bash -lc "python3 - <<'PY' +import carla +client = carla.Client('$CARLA_RPC_HOST', int('$CARLA_RPC_PORT')) +client.set_timeout(float('$CARLA_LOAD_TIMEOUT')) +world = client.load_world('$CARLA_WORLD') +print(world.get_map().name) +PY" + return 0 + fi + + local deadline=$((SECONDS + CARLA_LOAD_TIMEOUT)) + while ((SECONDS < deadline)); do + if docker run --rm --network host "$CARLA_INTERFACE_IMAGE" bash -lc "python3 - <<'PY' +import carla +client = carla.Client('$CARLA_RPC_HOST', int('$CARLA_RPC_PORT')) +client.set_timeout(30) +world = client.load_world('$CARLA_WORLD') +print(world.get_map().name) +PY"; then + return 0 + fi + sleep 5 + done + + printf 'Timed out preloading CARLA world %s\n' "$CARLA_WORLD" >&2 + return 1 +} + +start_autoware() { + if docker container inspect autoware-e2e-carla >/dev/null 2>&1; then + run docker rm -f autoware-e2e-carla + fi + run_compose up -d --force-recreate \ + map \ + system \ + carla-interface \ + sensing \ + perception \ + localization \ + planning \ + vehicle \ + control \ + api +} + +start_visualizer() { + if [[ "$AUTOWARE_E2E_START_VISUALIZER" != true ]]; then + return 0 + fi + + run docker rm -f autoware-e2e-visualizer || true + run_compose up -d --force-recreate visualizer +} + +verify_runtime() { + if [[ "$SKIP_VERIFY" == true ]]; then + return 0 + fi + + printf '+ verify CARLA e2e runtime\n' + if [[ "$DRY_RUN" == true ]]; then + return 0 + fi + + local deadline=$((SECONDS + AUTOWARE_E2E_VERIFY_TIMEOUT)) + local output + while ((SECONDS < deadline)); do + if output=$(docker exec "$CARLA_INTERFACE_CONTAINER" bash -lc "source /opt/ros/humble/setup.bash; source /opt/autoware/setup.bash; python3 - <<'PY' +import time + +import carla +import rclpy +from nav_msgs.msg import Odometry +from rclpy.qos import qos_profile_sensor_data +from sensor_msgs.msg import PointCloud2 + +rclpy.init() +node = rclpy.create_node('openadkit_e2e_runtime_verifier') +latest_odom = None +latest_lidar = None + +def on_odom(msg): + global latest_odom + latest_odom = msg + +def on_lidar(msg): + global latest_lidar + latest_lidar = msg + +node.create_subscription(Odometry, '/localization/kinematic_state', on_odom, 10) +node.create_subscription(PointCloud2, '/sensing/lidar/top/pointcloud_before_sync', on_lidar, qos_profile_sensor_data) + +deadline = time.time() + 20.0 +while time.time() < deadline and (latest_odom is None or latest_lidar is None): + rclpy.spin_once(node, timeout_sec=0.2) + +if latest_odom is None: + raise RuntimeError('Timed out waiting for /localization/kinematic_state') +if latest_lidar is None: + raise RuntimeError('Timed out waiting for /sensing/lidar/top/pointcloud_before_sync') + +client = carla.Client('$CARLA_RPC_HOST', int('$CARLA_RPC_PORT')) +client.set_timeout(10) +world = client.get_world() +world.tick(2) +vehicles = [a for a in world.get_actors() if a.type_id.startswith('vehicle.')] +ego = [v for v in vehicles if v.attributes.get('role_name') == 'ego_vehicle'] +print(world.get_map().name) +print('vehicles', len(vehicles), 'ego', len(ego)) +if not ego: + raise SystemExit(1) +node.destroy_node() +rclpy.shutdown() +PY" 2>&1); then + printf '%s\n' "$output" + return 0 + fi + sleep "$AUTOWARE_E2E_VERIFY_INTERVAL" + done + + docker compose --env-file "$ENV_FILE" -f docker-compose.yaml logs --tail 160 >&2 || true + printf 'Timed out waiting for CARLA e2e verification\n' >&2 + return 1 +} + +start_autonomous_drive() { + if [[ "$AUTOWARE_E2E_AUTO_DRIVE" != true ]]; then + return 0 + fi + + printf '+ set forward route and engage autonomous mode\n' + if [[ "$DRY_RUN" == true ]]; then + return 0 + fi + + docker exec \ + -e AUTOWARE_E2E_ROUTE_FORWARD_DISTANCE="$AUTOWARE_E2E_ROUTE_FORWARD_DISTANCE" \ + -e AUTOWARE_E2E_ROUTE_SETTLE_TIMEOUT="$AUTOWARE_E2E_ROUTE_SETTLE_TIMEOUT" \ + "$CARLA_INTERFACE_CONTAINER" \ + bash -lc "source /opt/ros/humble/setup.bash; source /opt/autoware/setup.bash; python3 - <<'PY' +import os +import time + +import rclpy +from autoware_adapi_v1_msgs.msg import OperationModeState, RouteState +from autoware_adapi_v1_msgs.srv import ChangeOperationMode, ClearRoute, SetRoutePoints +from geometry_msgs.msg import Pose +from nav_msgs.msg import Odometry + +forward_distance = float(os.environ['AUTOWARE_E2E_ROUTE_FORWARD_DISTANCE']) +settle_timeout = float(os.environ['AUTOWARE_E2E_ROUTE_SETTLE_TIMEOUT']) +autonomous_mode = getattr(OperationModeState, 'AUTONOMOUS', 2) + +rclpy.init() +node = rclpy.create_node('openadkit_e2e_route_and_engage') +latest_odom = None +latest_operation = None +latest_route = None + +def on_odom(msg): + global latest_odom + latest_odom = msg + +def on_operation(msg): + global latest_operation + latest_operation = msg + +def on_route(msg): + global latest_route + latest_route = msg + +node.create_subscription(Odometry, '/localization/kinematic_state', on_odom, 10) +node.create_subscription(OperationModeState, '/api/operation_mode/state', on_operation, 10) +node.create_subscription(RouteState, '/api/routing/state', on_route, 10) + +def spin_until(predicate, timeout, description): + deadline = time.time() + timeout + while time.time() < deadline: + rclpy.spin_once(node, timeout_sec=0.2) + if predicate(): + return True + raise RuntimeError(f'Timed out waiting for {description}') + +def wait_for_service(client, timeout, name): + deadline = time.time() + timeout + while time.time() < deadline: + if client.wait_for_service(timeout_sec=1.0): + return + raise RuntimeError(f'Timed out waiting for service {name}') + +def call_service(client, request, timeout, name): + wait_for_service(client, timeout, name) + future = client.call_async(request) + deadline = time.time() + timeout + while time.time() < deadline: + rclpy.spin_once(node, timeout_sec=0.2) + if future.done(): + response = future.result() + if response is None: + raise RuntimeError(f'{name} returned no response') + status = getattr(response, 'status', None) + if status is not None and not status.success: + raise RuntimeError(f'{name} failed: {status.message}') + return response + raise RuntimeError(f'Timed out waiting for response from {name}') + +spin_until(lambda: latest_odom is not None, settle_timeout, '/localization/kinematic_state') +start_pose = latest_odom.pose.pose + +clear_route = node.create_client(ClearRoute, '/api/routing/clear_route') +set_route = node.create_client(SetRoutePoints, '/api/routing/set_route_points') +change_route = node.create_client(SetRoutePoints, '/api/routing/change_route_points') +change_to_autonomous = node.create_client(ChangeOperationMode, '/api/operation_mode/change_to_autonomous') + +call_service(clear_route, ClearRoute.Request(), settle_timeout, '/api/routing/clear_route') + +clear_deadline = time.time() + min(10.0, settle_timeout) +while time.time() < clear_deadline: + rclpy.spin_once(node, timeout_sec=0.2) + if latest_route is not None and latest_route.state == RouteState.UNSET: + break + +request = SetRoutePoints.Request() +request.header.frame_id = 'map' +request.option.allow_goal_modification = True +request.goal = Pose() +request.goal.position.x = start_pose.position.x + forward_distance +request.goal.position.y = start_pose.position.y +request.goal.position.z = 0.0 +request.goal.orientation = start_pose.orientation + +try: + call_service(set_route, request, settle_timeout, '/api/routing/set_route_points') +except RuntimeError as error: + if 'route is already set' not in str(error).lower() and 'invalid_state' not in str(error).lower(): + raise + call_service(change_route, request, settle_timeout, '/api/routing/change_route_points') +print( + 'Route set: ' + f'start=({start_pose.position.x:.2f}, {start_pose.position.y:.2f}) ' + f'goal=({request.goal.position.x:.2f}, {request.goal.position.y:.2f})' +) + +spin_until( + lambda: latest_operation is not None and latest_operation.is_autonomous_mode_available, + settle_timeout, + 'autonomous mode availability', +) +call_service(change_to_autonomous, ChangeOperationMode.Request(), settle_timeout, '/api/operation_mode/change_to_autonomous') +spin_until( + lambda: latest_operation is not None and latest_operation.mode == autonomous_mode, + settle_timeout, + 'autonomous mode transition', +) +print('Autonomous mode active') +node.destroy_node() +rclpy.shutdown() +PY" +} + +verify_autonomous_drive() { + if [[ "$AUTOWARE_E2E_AUTO_DRIVE" != true || "$SKIP_VERIFY" == true ]]; then + return 0 + fi + + printf '+ verify autonomous CARLA motion\n' + if [[ "$DRY_RUN" == true ]]; then + return 0 + fi + + docker exec \ + -e AUTOWARE_E2E_DRIVE_VERIFY_TIMEOUT="$AUTOWARE_E2E_DRIVE_VERIFY_TIMEOUT" \ + -e AUTOWARE_E2E_DRIVE_VERIFY_DISTANCE="$AUTOWARE_E2E_DRIVE_VERIFY_DISTANCE" \ + "$CARLA_INTERFACE_CONTAINER" \ + bash -lc "source /opt/ros/humble/setup.bash; source /opt/autoware/setup.bash; python3 - <<'PY' +import math +import os +import time + +import rclpy +from nav_msgs.msg import Odometry + +timeout = float(os.environ['AUTOWARE_E2E_DRIVE_VERIFY_TIMEOUT']) +required_distance = float(os.environ['AUTOWARE_E2E_DRIVE_VERIFY_DISTANCE']) + +rclpy.init() +node = rclpy.create_node('openadkit_e2e_motion_verifier') +latest_odom = None + +def on_odom(msg): + global latest_odom + latest_odom = msg + +node.create_subscription(Odometry, '/localization/kinematic_state', on_odom, 10) + +deadline = time.time() + timeout +while latest_odom is None and time.time() < deadline: + rclpy.spin_once(node, timeout_sec=0.2) + +if latest_odom is None: + raise RuntimeError('Timed out waiting for /localization/kinematic_state') + +start = latest_odom.pose.pose.position +start_x = start.x +start_y = start.y + +while time.time() < deadline: + rclpy.spin_once(node, timeout_sec=0.2) + current = latest_odom.pose.pose.position + distance = math.hypot(current.x - start_x, current.y - start_y) + if distance >= required_distance: + print( + f'Moved {distance:.2f} m: ' + f'start=({start_x:.2f}, {start_y:.2f}) ' + f'current=({current.x:.2f}, {current.y:.2f})' + ) + node.destroy_node() + rclpy.shutdown() + raise SystemExit(0) + +current = latest_odom.pose.pose.position +distance = math.hypot(current.x - start_x, current.y - start_y) +raise RuntimeError(f'Autonomous motion verification failed: moved {distance:.2f} m') +PY" +} + +main() { + cd "$SCRIPT_DIR" + load_env + prepare_map + build_image + start_container_carla + preload_carla_world + start_autoware + start_visualizer + verify_runtime + start_autonomous_drive + verify_autonomous_drive +} + +main diff --git a/docs/deployments/index.md b/docs/deployments/index.md index 0cc64ad..c6b4f9d 100644 --- a/docs/deployments/index.md +++ b/docs/deployments/index.md @@ -6,6 +6,7 @@ A **deployment** is a running instance of Open AD Kit, a specific combination of Sample deployment configurations to help you get started. Recommended for **learning and development**. +- [CARLA Simulation](samples/carla-simulation/index.md) - Run Autoware planning/control with CARLA as the external simulator. - [Planning Simulation](samples/planning-simulation/index.md) - Run the Autoware planning simulation with a sample map. - [Logging Simulation](samples/logging-simulation/index.md) - Run the Autoware logging simulation with a sample rosbag. diff --git a/docs/deployments/samples/carla-simulation/index.md b/docs/deployments/samples/carla-simulation/index.md new file mode 100644 index 0000000..52f6964 --- /dev/null +++ b/docs/deployments/samples/carla-simulation/index.md @@ -0,0 +1,120 @@ +# Autoware Open AD Kit CARLA E2E Simulation + +This sample runs a closed-loop CARLA 0.9.16 end-to-end simulation with modular OpenADKit containers and Autoware's `autoware_carla_interface`. + +The default flow uses the official CARLA Ubuntu 22 container image and does not use the CARLA ROS bridge, dummy vehicle, dummy perception, a monolithic Autoware container, or a host-installed CARLA runtime. + +## Runtime + +- CARLA: `carlasim/carla:0.9.16` +- CARLA interface image: `ghcr.io/autowarefoundation/autoware-tools:carla-interface` +- Autoware modules: standard OpenADKit split images +- CARLA map: `Town01` +- Autoware map assets: downloaded to `$HOME/autoware_data/maps/Town01` +- RViz/noVNC: `ghcr.io/autowarefoundation/openadkit:visualizer` + +The CARLA container runs on the host X display with NVIDIA GPU access. The helper mounts `/tmp/.X11-unix` and the host NVIDIA Vulkan ICD so CARLA renders through the NVIDIA driver instead of Mesa lavapipe. + +## Requirements + +- Docker with NVIDIA Container Toolkit +- Access to `carlasim/carla:0.9.16` +- A working host X display, usually `DISPLAY=:0` +- Host NVIDIA Vulkan ICD at `/usr/share/vulkan/icd.d/nvidia_icd.json` + +## Start + +Run from the sample directory: + +```bash +cd deployments/samples/carla-simulation +./start-carla-e2e-demo.sh +``` + +The helper will: + +- Use the CI-built `ghcr.io/autowarefoundation/autoware-tools:carla-interface` image. +- Download the official CARLA Autoware Town01 map assets if missing. +- Start `carlasim/carla:0.9.16` as `carla-e2e` on `DISPLAY=:0`. +- Preload `Town01`. +- Start modular OpenADKit map, system, CARLA interface, sensing, perception, localization, planning, vehicle, control, and API containers. +- Start the browser RViz/noVNC visualizer. +- Verify localization, CARLA LiDAR, and the CARLA ego actor. + +Use `--build` to build the CARLA interface image locally from `tools/carla-interface` before starting the stack. + +The default behavior is no-drive. Set the route and engage manually in RViz. + +## Open RViz + +If running remotely, forward noVNC from the machine running the sample: + +```bash +ssh -L 8080:localhost:6080 @ +``` + +Open: + +```text +http://localhost:8080/vnc.html +``` + +Password: + +```text +openadkit +``` + +In RViz: + +- Use `2D Goal Pose` to set a route. +- Wait for routing and planning to become available. +- Click `Auto` to engage autonomous driving. + +## Optional Drive Check + +To automatically set a short forward route, engage autonomous mode, and verify movement: + +```bash +./start-carla-e2e-demo.sh --drive +``` + +Expected success output includes: + +```text +Route set: start=(..., ...) goal=(..., ...) +Autonomous mode active +Moved ... m: start=(..., ...) current=(..., ...) +``` + +## Faster Relaunch + +The helper does not build the CARLA interface image unless `--build` is passed, so relaunching uses the configured image directly: + +```bash +./start-carla-e2e-demo.sh +``` + +For start-only behavior with an explicit no-drive flag: + +```bash +./start-carla-e2e-demo.sh --no-drive +``` + +## Command Path + +Closed-loop control uses this path: + +```text +/control/command/control_cmd + -> autoware_raw_vehicle_cmd_converter + -> /control/command/actuation_cmd + -> autoware_carla_interface + -> CARLA ego vehicle +``` + +## Stop + +```bash +docker compose --env-file carla-simulation.e2e.env -f docker-compose.yaml down +``` diff --git a/docs/deployments/samples/index.md b/docs/deployments/samples/index.md index dffc6e3..71f9a55 100644 --- a/docs/deployments/samples/index.md +++ b/docs/deployments/samples/index.md @@ -2,5 +2,6 @@ Sample deployment configurations to help you get started. Recommended for **learning and development**. +- [CARLA Simulation](carla-simulation/index.md) - [Planning Simulation](planning-simulation/index.md) - [Logging Simulation](logging-simulation/index.md) diff --git a/tools/README.md b/tools/README.md index 0064057..9915b18 100644 --- a/tools/README.md +++ b/tools/README.md @@ -1,3 +1,6 @@ -# Open AD Kit Tools -TBD +# Open AD Kit Tools This directory offers tools for Open AD Kit to make development and deployment easier. These tools are also containerized and can be integrated into deployments as needed. + +- `carla-interface`: CARLA Python API and Autoware CARLA interface runtime for the CARLA e2e sample. +- `scenario-simulator`: Scenario simulator runtime image. diff --git a/tools/carla-interface/Dockerfile b/tools/carla-interface/Dockerfile new file mode 100644 index 0000000..a39a8cd --- /dev/null +++ b/tools/carla-interface/Dockerfile @@ -0,0 +1,35 @@ +ARG SIMULATOR_IMAGE=ghcr.io/autowarefoundation/openadkit:simulator +FROM ${SIMULATOR_IMAGE} AS carla-interface + +ARG CARLA_PYTHON_VERSION=0.9.16 + +ENV DEBIAN_FRONTEND=noninteractive + +RUN apt-get update \ + && apt-get install -y --no-install-recommends \ + ros-humble-compressed-image-transport \ + ros-humble-topic-tools \ + && python3 -m pip install --no-cache-dir \ + carla==${CARLA_PYTHON_VERSION} \ + transforms3d \ + && rm -rf /var/lib/apt/lists/* + +# CARLA can crash when the interface reloads the same visible map during +# startup. Preload Town01 in the launcher, then skip only this redundant +# load_world call while preserving map reloads for other maps. +RUN python3 - <<'PY' +from pathlib import Path + +path = Path('/opt/autoware/lib/python3.10/site-packages/autoware_carla_interface/carla_autoware.py') +text = path.read_text() +old = ' client.load_world(self.carla_map)\n self.world = client.get_world()\n' +new = ''' current_world = client.get_world() + current_map = current_world.get_map().name + if not current_map.endswith("/" + self.carla_map): + client.load_world(self.carla_map) + self.world = client.get_world() +''' +if old not in text: + raise SystemExit('autoware_carla_interface load_world block not found') +path.write_text(text.replace(old, new, 1)) +PY diff --git a/tools/carla-interface/README.md b/tools/carla-interface/README.md new file mode 100644 index 0000000..8d4f65f --- /dev/null +++ b/tools/carla-interface/README.md @@ -0,0 +1,9 @@ +# CARLA Interface Tool + +This tool image packages Autoware's CARLA interface with the CARLA 0.9.16 Python API for the CARLA e2e sample. + +```bash +docker run --rm --network host ghcr.io/autowarefoundation/autoware-tools:carla-interface +``` + +The image is normally built by GitHub Actions from `tools/docker-bake.hcl`. diff --git a/tools/docker-bake.hcl b/tools/docker-bake.hcl index c2d1d7b..c381088 100644 --- a/tools/docker-bake.hcl +++ b/tools/docker-bake.hcl @@ -1,14 +1,22 @@ group "default" { targets = [ "scenario-simulator", + "carla-interface", ] } // For docker/metadata-action target "docker-metadata-action-scenario-simulator" {} +target "docker-metadata-action-carla-interface" {} target "scenario-simulator" { inherits = ["docker-metadata-action-scenario-simulator"] dockerfile = "docker/tools/scenario-simulator/Dockerfile" target = "scenario-simulator" } + +target "carla-interface" { + inherits = ["docker-metadata-action-carla-interface"] + dockerfile = "tools/carla-interface/Dockerfile" + target = "carla-interface" +}