Tutorial

Create a New Project

Use an Airfield project when you want one workspace to own multiple packages, shared dependency manifests, and runnable plans.

1. Create the project

airfield project init ./my_robot --ros-distro jazzy
cd my_robot

This creates:

my_robot/
  airfield.yaml
  packages/
  dependencies/
    x86_64/
    arm64/
  plans/
    example.yaml
  .dockerignore
  .gitignore

airfield.yaml marks the directory as an Airfield project:

kind: project
name: my_robot
version: 0.1.0
ros_distro: jazzy

The ros_distro value becomes the default for packages created inside the project unless a package overrides it. Supported distributions are noetic, humble, jazzy, kilted, and rolling. The generated .gitignore keeps machine-local files (.air, .airfield/, build/, log/, install/) out of the repository.

Two optional project-level fields are worth knowing about early:

2. Check the project

airfield status

The status output should show Project status, the project root, the selected ROS distribution, dependency target folders, and available plans.

3. Add dependency manifests

Project-scoped dependency manifests live under:

dependencies/x86_64/*.yaml     # x86_64-only recipes
dependencies/arm64/*.yaml      # arm64-only recipes
dependencies/xplatform/*.yaml  # recipes that work on both

project init scaffolds the two architecture folders; create xplatform/ yourself when you first need it.

When a package build resolves a dependency name, Airfield searches in order: the project’s dependencies/<target-device>/, the project’s dependencies/xplatform/, then the same two folders in the shared Airfield packages repository. The first manifest found wins, so local project manifests always override shared ones.

Example:

name: tqdm
version: 1.0.0
system: []
user:
  - python3 -m pip install --break-system-packages tqdm

Note the --break-system-packages flag: recent ROS base images run on Ubuntu 24.04, where the system Python is marked externally managed (PEP 668) and a plain pip install refuses to run. Inside a dedicated container image the flag is safe, and pip manifests need it to work on jazzy and newer.

4. Add packages

Create a package inside the project:

airfield package init nav_stack

The package is created at:

packages/nav_stack/
  airfield.yaml
  src/
  README.md
  .dockerignore
  .gitignore

See Create a Package for the package metadata fields and how to wrap existing ROS code.

5. Launch the example plan

project init scaffolds plans/example.yaml:

name: example
windows:
  - name: hello
    layout: main-vertical
    panes:
      - echo "Hello from plan 'example'. Replace these panes with launch commands."
      # Run a command inside a package's container:
      # - package: my_package
      #   cmd: ros2 launch my_package bringup.launch.py

Launch it as a tmux session:

airfield project up example

Plans are how a whole robot stack comes up with one command. See Create a Plan for the full format.