Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
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
116 changes: 116 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

## Project Overview

Open AD Kit provides containerized, microservice-based components for the [Autoware](https://github.com/autowarefoundation/autoware) autonomous driving stack. It packages Autoware into independent Docker images for modular deployment across cloud and edge (amd64/arm64). This is a SOAFEE Blueprint project under the Autoware Foundation.

## Build Commands

### Build container images (requires Docker buildx)

```bash
# Build all component images (default target) for current platform, ROS Humble
./build.sh

# Build options
./build.sh --platform linux/arm64 # Cross-build for ARM
./build.sh --platform jp62 # Jetson Linux 6.2 (arm64, CUDA always included)
./build.sh --ros-distro jazzy # Use ROS Jazzy (default: humble)
./build.sh --no-cuda # Skip CUDA image variants
./build.sh --target common # Build only base images (stage 1)
./build.sh --target components # Build components (stages 1+2, default)
./build.sh --target universe # Build everything (stages 1+2+3)
```

The build script first clones the Autoware repo and imports source via `vcs`, then runs `docker buildx bake` through three stages.

### Setup runtime environment

```bash
./setup.sh # Installs Docker, NVIDIA Container Toolkit
```

### Documentation (MkDocs)

```bash
make prepare # Build the MkDocs dev container
make serve # Serve docs locally at localhost:8000
make build # Build static site
make clean # Remove site/ directory
```

## Architecture

### Three-stage image hierarchy

The build produces layered Docker images defined in `components/docker-bake.hcl`:

1. **Common** (`components/common/`): Base and devel images built on top of ROS. Each has a CUDA variant and a JP62 variant.
- `common-base` / `common-base-cuda` / `common-base-jp62` — runtime base
- `common-devel` / `common-devel-cuda` / `common-devel-jp62` — build-time with Autoware dependencies
2. **Components** (7 independent images): Each built from `common-devel`, installed onto `common-base`. Each has its own Dockerfile under `components/<name>/`:
- `sensing-perception` (has CUDA variant), `localization-mapping`, `planning-control`, `vehicle-system`, `api`, `visualizer`, `simulator`
3. **Universe** (`components/universe/`): Merges all component install spaces into a single image. Has a CUDA variant.

### Platform variants

The image matrix spans `{platform} x {ros-distro}`:

| Platform | Arch | CUDA | Base image | Dockerfile |
|----------------|-------------|----------|---------------------------------------------|----------------------------|
| `amd64` | linux/amd64 | optional | `ros:{distro}-ros-base-{ubuntu}` | `Dockerfile` |
| `arm64` | linux/arm64 | no | `ros:{distro}-ros-base-{ubuntu}` | `Dockerfile` |
| `amd64` + cuda | linux/amd64 | yes | `ros:{distro}-ros-base-{ubuntu}` | `Dockerfile` (cuda stages) |
| `jp62` | linux/arm64 | always | `nvcr.io/nvidia/l4t-tensorrt:r10.3.0-devel` | `Dockerfile.jp62` |

JP62 images fulfill the same contract as CUDA images (`common-base-cuda` / `common-devel-cuda`), so downstream `Dockerfile.cuda` component files work unmodified by receiving JP62 images as their `COMMON_BASE_CUDA_IMAGE` / `COMMON_DEVEL_CUDA_IMAGE` args.

**JP62-specific concerns** (`components/common/Dockerfile.jp62`):
- L4T base has no ROS — installed from apt (`ros-humble-desktop`)
- L4T OpenCV 4.8.0 replaced with Ubuntu 4.5.4 (apt pin in `components/common/jp62/opencv-preferences`)
- L4T CMake 3.14 replaced with system CMake >= 3.22
- NVIDIA packages from L4T repos (not `ubuntu2204/sbsa`) — `setup-dev-env.sh` must use `--no-nvidia --no-cuda-drivers` to avoid conflicts
- `CUDAARCHS=87` (Orin) set to avoid native detection failures under QEMU
- spconv/cumm installed from pre-built Jetson ARM `.deb` packages
- `ros-humble-tensorrt-cmake-module` and `ros-humble-cudnn-cmake-module` installed explicitly (skipped by `--no-nvidia` ansible)
- colcon mixin index must be explicitly registered (not inherited from ros: Docker base image)
- CMake 3.28 from Kitware PPA required: system cmake 3.22 has a `find_library()` bug where the `ament_cmake_export_libraries` template's `set(_lib "NOTFOUND")` pattern causes the search to be skipped. Additionally, the template reuses a shared `_lib` cache variable across packages, causing cross-package pollution ([ament_cmake#182](https://github.com/ament/ament_cmake/issues/182)). Both fixed by cmake 3.28 + a sed patch in the Dockerfile. Pinned to 3.28 to stay below 4.0
- JP62 images must be built on native Jetson (arm64); x86 cross-compilation via QEMU hits intermittent `find_library` failures in cmake subprocess calls
- Autoware 1.7.1 hardcodes `-gencode arch=compute_101` (Blackwell) in 14 CUDA CMakeLists.txt files; CUDA 12.6 on JP62 only supports up to `compute_90`. Run `components/common/jp62/patch-cuda-arch.sh autoware/src` after cloning sources to gate these behind `CUDA_VERSION >= 12.8`

### Deployment samples

- `deployments/samples/planning-simulation/` — planning stack with AWSIM simulator
- `deployments/samples/logging-simulation/` — end-to-end replay with rosbag
- `deployments/demos/zenoh-bridge/` — remote visualization via Zenoh bridge

### Platform configurations

- `platforms/autosd/` — Automotive-grade Linux (CentOS Stream AutoSD)

### Tag scheme

CI tags images as `{variant}-{platform}-{distro}[-{date}]`:
- `base-amd64-humble`, `devel-cuda-amd64-humble`, `base-jp62-humble`
- Component/universe: `sensing-perception-amd64-humble`, `universe-jp62-humble`

Multi-arch manifests (amd64+arm64) strip the platform: `base-humble-{date}`. CUDA and JP62 images are single-arch.

## CI/CD

GitHub Actions workflows in `.github/workflows/`:

- **build-all-images.yaml**: Main CI. Builds all images for amd64+arm64, humble+jazzy. Triggered on push to main, monthly schedule, or manual dispatch. Pushes to `ghcr.io`.
- **release-all-images.yaml**: Runs every 6 hours. Detects latest Autoware release tag and publishes versioned images.
- **deploy-docs.yaml**: Builds and deploys MkDocs site to GitHub Pages on push to main.
- **semantic-pull-request.yaml**: Enforces conventional commit style on PR titles.

## Key Conventions

- Image registry: `ghcr.io/autowarefoundation/openadkit`
- ROS distributions: `humble` (Ubuntu Jammy) and `jazzy` (Ubuntu Noble)
- PR titles must follow [Conventional Commits](https://www.conventionalcommits.org/) (enforced by CI)
- The `autoware/` directory is git-ignored — it's cloned at build time by `build.sh`
- No unit test suite; validation happens through Docker build success in CI
192 changes: 134 additions & 58 deletions build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ print_help() {
echo "Usage: build.sh [OPTIONS]"
echo "Options:"
echo " -h | --help Display this help message"
echo " --platform Specify the platform (linux/amd64 or linux/arm64, default: current platform)"
echo " --platform Specify the platform (linux/amd64, linux/arm64, or jp62; default: current platform)"
echo " --ros-distro Specify ROS distribution (humble or jazzy, default: humble)"
echo " --no-cuda Do not build CUDA images (default: false)"
echo " --target Specify the target images to build (common, components, universe, default: components)"
Expand Down Expand Up @@ -76,6 +76,12 @@ set_platform() {
platform="linux/arm64"
fi
fi

# JP62 (Jetson Linux 6.2) is always arm64
if [ "$platform" = "jp62" ]; then
is_jp62=true
platform="linux/arm64"
fi
}

# Clone autoware repositories
Expand All @@ -97,6 +103,14 @@ clone_repositories() {
fi
}

# Apply platform-specific source patches
apply_patches() {
if [ "$is_jp62" = "true" ]; then
echo "Applying JP62 patches to Autoware sources..."
"$SCRIPT_DIR/components/common/jp62/patch-cuda-arch.sh" "$WORKSPACE_ROOT/autoware/src"
fi
}

# Build images
build_images() {
# https://github.com/docker/buildx/issues/484
Expand All @@ -114,6 +128,7 @@ build_images() {
echo "Building images with:"
echo " Target: $target"
echo " Platform: $platform"
echo " JP62: $([ "$is_jp62" = "true" ] && echo "yes" || echo "no")"
echo " ROS distro: $ros_distro"
echo " Base image: $base_image"
echo " CUDA: $([ "$option_no_cuda" = "true" ] && echo "disabled" || echo "enabled")"
Expand All @@ -123,26 +138,38 @@ build_images() {
# =========================================================================
# Stage 1: Common images
# =========================================================================
docker buildx bake --allow=ssh --load --progress=plain -f "$bake_file" \
--set "*.context=$WORKSPACE_ROOT" \
--set "*.ssh=default" \
--set "*.platform=$platform" \
--set "*.args.ROS_DISTRO=$ros_distro" \
--set "*.args.BASE_IMAGE=$base_image" \
--set "common-base.tags=${image_common}:base" \
--set "common-devel.tags=${image_common}:devel" \
common-base common-devel

if [ "$option_no_cuda" != "true" ]; then
if [ "$is_jp62" = "true" ]; then
# JP62: build Jetson-specific common images (CUDA is always included)
docker buildx bake --allow=ssh --load --progress=plain -f "$bake_file" \
--set "*.context=$WORKSPACE_ROOT" \
--set "*.ssh=default" \
--set "*.platform=$platform" \
--set "*.args.ROS_DISTRO=$ros_distro" \
--set "common-base-jp62.tags=${image_common}:base-jp62" \
--set "common-devel-jp62.tags=${image_common}:devel-jp62" \
common-base-jp62 common-devel-jp62
else
docker buildx bake --allow=ssh --load --progress=plain -f "$bake_file" \
--set "*.context=$WORKSPACE_ROOT" \
--set "*.ssh=default" \
--set "*.platform=$platform" \
--set "*.args.ROS_DISTRO=$ros_distro" \
--set "*.args.BASE_IMAGE=$base_image" \
--set "common-base-cuda.tags=${image_common}:base-cuda" \
--set "common-devel-cuda.tags=${image_common}:devel-cuda" \
common-base-cuda common-devel-cuda
--set "common-base.tags=${image_common}:base" \
--set "common-devel.tags=${image_common}:devel" \
common-base common-devel

if [ "$option_no_cuda" != "true" ]; then
docker buildx bake --allow=ssh --load --progress=plain -f "$bake_file" \
--set "*.context=$WORKSPACE_ROOT" \
--set "*.ssh=default" \
--set "*.platform=$platform" \
--set "*.args.ROS_DISTRO=$ros_distro" \
--set "*.args.BASE_IMAGE=$base_image" \
--set "common-base-cuda.tags=${image_common}:base-cuda" \
--set "common-devel-cuda.tags=${image_common}:devel-cuda" \
common-base-cuda common-devel-cuda
fi
fi

if [ "$target" = "common" ]; then
Expand All @@ -153,32 +180,61 @@ build_images() {
# =========================================================================
# Stage 2: Component images
# =========================================================================
docker buildx bake --allow=ssh --load --progress=plain -f "$bake_file" \
--set "*.context=$WORKSPACE_ROOT" \
--set "*.ssh=default" \
--set "*.platform=$platform" \
--set "*.args.ROS_DISTRO=$ros_distro" \
--set "*.args.COMMON_BASE_IMAGE=${image_common}:base" \
--set "*.args.COMMON_DEVEL_IMAGE=${image_common}:devel" \
--set "sensing-perception.tags=${image_component}:sensing-perception" \
--set "localization-mapping.tags=${image_component}:localization-mapping" \
--set "planning-control.tags=${image_component}:planning-control" \
--set "vehicle-system.tags=${image_component}:vehicle-system" \
--set "api.tags=${image_component}:api" \
--set "visualizer.tags=${image_component}:visualizer" \
--set "simulator.tags=${image_component}:simulator" \
sensing-perception localization-mapping planning-control vehicle-system api visualizer simulator

if [ "$option_no_cuda" != "true" ]; then
if [ "$is_jp62" = "true" ]; then
# JP62: feed JP62 common images into the CUDA Dockerfiles.
# The JP62 base/devel images fulfill the same contract as common-base-cuda / common-devel-cuda.
docker buildx bake --allow=ssh --load --progress=plain -f "$bake_file" \
--set "*.context=$WORKSPACE_ROOT" \
--set "*.ssh=default" \
--set "*.platform=$platform" \
--set "*.args.ROS_DISTRO=$ros_distro" \
--set "*.args.COMMON_BASE_CUDA_IMAGE=${image_common}:base-cuda" \
--set "*.args.COMMON_DEVEL_CUDA_IMAGE=${image_common}:devel-cuda" \
--set "sensing-perception-cuda.tags=${image_component}:sensing-perception-cuda" \
--set "*.args.COMMON_BASE_CUDA_IMAGE=${image_common}:base-jp62" \
--set "*.args.COMMON_DEVEL_CUDA_IMAGE=${image_common}:devel-jp62" \
--set "sensing-perception-cuda.tags=${image_component}:sensing-perception-jp62" \
sensing-perception-cuda

docker buildx bake --allow=ssh --load --progress=plain -f "$bake_file" \
--set "*.context=$WORKSPACE_ROOT" \
--set "*.ssh=default" \
--set "*.platform=$platform" \
--set "*.args.ROS_DISTRO=$ros_distro" \
--set "*.args.COMMON_BASE_IMAGE=${image_common}:base-jp62" \
--set "*.args.COMMON_DEVEL_IMAGE=${image_common}:devel-jp62" \
--set "localization-mapping.tags=${image_component}:localization-mapping-jp62" \
--set "planning-control.tags=${image_component}:planning-control-jp62" \
--set "vehicle-system.tags=${image_component}:vehicle-system-jp62" \
--set "api.tags=${image_component}:api-jp62" \
--set "visualizer.tags=${image_component}:visualizer-jp62" \
--set "simulator.tags=${image_component}:simulator-jp62" \
localization-mapping planning-control vehicle-system api visualizer simulator
else
docker buildx bake --allow=ssh --load --progress=plain -f "$bake_file" \
--set "*.context=$WORKSPACE_ROOT" \
--set "*.ssh=default" \
--set "*.platform=$platform" \
--set "*.args.ROS_DISTRO=$ros_distro" \
--set "*.args.COMMON_BASE_IMAGE=${image_common}:base" \
--set "*.args.COMMON_DEVEL_IMAGE=${image_common}:devel" \
--set "sensing-perception.tags=${image_component}:sensing-perception" \
--set "localization-mapping.tags=${image_component}:localization-mapping" \
--set "planning-control.tags=${image_component}:planning-control" \
--set "vehicle-system.tags=${image_component}:vehicle-system" \
--set "api.tags=${image_component}:api" \
--set "visualizer.tags=${image_component}:visualizer" \
--set "simulator.tags=${image_component}:simulator" \
sensing-perception localization-mapping planning-control vehicle-system api visualizer simulator

if [ "$option_no_cuda" != "true" ]; then
docker buildx bake --allow=ssh --load --progress=plain -f "$bake_file" \
--set "*.context=$WORKSPACE_ROOT" \
--set "*.ssh=default" \
--set "*.platform=$platform" \
--set "*.args.ROS_DISTRO=$ros_distro" \
--set "*.args.COMMON_BASE_CUDA_IMAGE=${image_common}:base-cuda" \
--set "*.args.COMMON_DEVEL_CUDA_IMAGE=${image_common}:devel-cuda" \
--set "sensing-perception-cuda.tags=${image_component}:sensing-perception-cuda" \
sensing-perception-cuda
fi
fi

if [ "$target" = "components" ]; then
Expand All @@ -189,40 +245,59 @@ build_images() {
# =========================================================================
# Stage 3: Universe images
# =========================================================================
docker buildx bake --allow=ssh --load --progress=plain -f "$bake_file" \
--set "*.context=$WORKSPACE_ROOT" \
--set "*.ssh=default" \
--set "*.platform=$platform" \
--set "*.args.ROS_DISTRO=$ros_distro" \
--set "*.args.COMMON_BASE_IMAGE=${image_common}:base" \
--set "*.args.COMMON_DEVEL_IMAGE=${image_common}:devel" \
--set "*.args.SENSING_PERCEPTION_IMAGE=${image_component}:sensing-perception" \
--set "*.args.LOCALIZATION_MAPPING_IMAGE=${image_component}:localization-mapping" \
--set "*.args.PLANNING_CONTROL_IMAGE=${image_component}:planning-control" \
--set "*.args.VEHICLE_SYSTEM_IMAGE=${image_component}:vehicle-system" \
--set "*.args.API_IMAGE=${image_component}:api" \
--set "*.args.VISUALIZER_IMAGE=${image_component}:visualizer" \
--set "*.args.SIMULATOR_IMAGE=${image_component}:simulator" \
--set "universe.tags=${image_component}:universe" \
universe

if [ "$option_no_cuda" != "true" ]; then
if [ "$is_jp62" = "true" ]; then
docker buildx bake --allow=ssh --load --progress=plain -f "$bake_file" \
--set "*.context=$WORKSPACE_ROOT" \
--set "*.ssh=default" \
--set "*.platform=$platform" \
--set "*.args.ROS_DISTRO=$ros_distro" \
--set "*.args.COMMON_BASE_CUDA_IMAGE=${image_common}:base-jp62" \
--set "*.args.COMMON_DEVEL_CUDA_IMAGE=${image_common}:devel-jp62" \
--set "*.args.SENSING_PERCEPTION_CUDA_IMAGE=${image_component}:sensing-perception-jp62" \
--set "*.args.LOCALIZATION_MAPPING_IMAGE=${image_component}:localization-mapping-jp62" \
--set "*.args.PLANNING_CONTROL_IMAGE=${image_component}:planning-control-jp62" \
--set "*.args.VEHICLE_SYSTEM_IMAGE=${image_component}:vehicle-system-jp62" \
--set "*.args.API_IMAGE=${image_component}:api-jp62" \
--set "*.args.VISUALIZER_IMAGE=${image_component}:visualizer-jp62" \
--set "*.args.SIMULATOR_IMAGE=${image_component}:simulator-jp62" \
--set "universe-cuda.tags=${image_component}:universe-jp62" \
universe-cuda
else
docker buildx bake --allow=ssh --load --progress=plain -f "$bake_file" \
--set "*.context=$WORKSPACE_ROOT" \
--set "*.ssh=default" \
--set "*.platform=$platform" \
--set "*.args.ROS_DISTRO=$ros_distro" \
--set "*.args.COMMON_BASE_CUDA_IMAGE=${image_common}:base-cuda" \
--set "*.args.COMMON_DEVEL_CUDA_IMAGE=${image_common}:devel-cuda" \
--set "*.args.SENSING_PERCEPTION_CUDA_IMAGE=${image_component}:sensing-perception-cuda" \
--set "*.args.COMMON_BASE_IMAGE=${image_common}:base" \
--set "*.args.COMMON_DEVEL_IMAGE=${image_common}:devel" \
--set "*.args.SENSING_PERCEPTION_IMAGE=${image_component}:sensing-perception" \
--set "*.args.LOCALIZATION_MAPPING_IMAGE=${image_component}:localization-mapping" \
--set "*.args.PLANNING_CONTROL_IMAGE=${image_component}:planning-control" \
--set "*.args.VEHICLE_SYSTEM_IMAGE=${image_component}:vehicle-system" \
--set "*.args.API_IMAGE=${image_component}:api" \
--set "*.args.VISUALIZER_IMAGE=${image_component}:visualizer" \
--set "*.args.SIMULATOR_IMAGE=${image_component}:simulator" \
--set "universe-cuda.tags=${image_component}:universe-cuda" \
universe-cuda
--set "universe.tags=${image_component}:universe" \
universe

if [ "$option_no_cuda" != "true" ]; then
docker buildx bake --allow=ssh --load --progress=plain -f "$bake_file" \
--set "*.context=$WORKSPACE_ROOT" \
--set "*.ssh=default" \
--set "*.platform=$platform" \
--set "*.args.ROS_DISTRO=$ros_distro" \
--set "*.args.COMMON_BASE_CUDA_IMAGE=${image_common}:base-cuda" \
--set "*.args.COMMON_DEVEL_CUDA_IMAGE=${image_common}:devel-cuda" \
--set "*.args.SENSING_PERCEPTION_CUDA_IMAGE=${image_component}:sensing-perception-cuda" \
--set "*.args.LOCALIZATION_MAPPING_IMAGE=${image_component}:localization-mapping" \
--set "*.args.PLANNING_CONTROL_IMAGE=${image_component}:planning-control" \
--set "*.args.VEHICLE_SYSTEM_IMAGE=${image_component}:vehicle-system" \
--set "*.args.API_IMAGE=${image_component}:api" \
--set "*.args.VISUALIZER_IMAGE=${image_component}:visualizer" \
--set "*.args.SIMULATOR_IMAGE=${image_component}:simulator" \
--set "universe-cuda.tags=${image_component}:universe-cuda" \
universe-cuda
fi
fi

set +x
Expand All @@ -239,5 +314,6 @@ set_ros_distro
set_build_options
set_platform
clone_repositories
apply_patches
build_images
remove_dangling_images
Loading
Loading