Aerial autonomy stack (AAS) is a software stack to:
- Develop end-to-end drone autonomy with ROS2
- Simulate perception and control in SITL, with YOLOv8, LiDAR and PX4 or ArduPilot
- Deploy in real drones with JetPack on NVIDIA Orin
aerial-autonomy-stack-v2.mp4
- Support for multiple quadrotors and VTOLs based on either PX4 or ArduPilot
- Vehicle-agnostic ROS2 action-based autopilot interface (via XRCE-DDS and MAVROS)
- Support for YOLOv8 (with ONNX GPU Runtimes) and LiDAR Odometry (with KISS-ICP)
- Dockerized simulation based on
nvcr.io/nvidia/cuda:12.8.1-cudnn-runtime-ubuntu22.04
- Dockerized deployment based on
nvcr.io/nvidia/l4t-jetpack:r36.4.0
- Windows 11 compatibility with GPU support via WSLg
- 3D worlds for PX4 and ArduPilot software-in-the-loop (SITL) simulation
- Zenoh inter-vehicle ROS2 bridge
- Support for PX4 Offboard mode (e.g. CTBR/
VehicleRatesSetpoint
for agile, GNSS-denied flight) - Support for ArduPilot Guided mode (i.e.
setpoint_velocity
,setpoint_accel
references) - Logs analysis with
flight_review
(.ulg
), MAVExplorer (.bin
), and PlotJuggler (rosbag
) - Support for Gazebo's wind effects plugin
- Steppable simulation
Read about the rationale for AAS in the supplementary/
material
AAS leverages the following frameworks: (click to expand)
Ubuntu 22.04 (LTS, ESM 4/2032),
nvidia-driver-580
(latest as of 9/2025), Docker Engine v28 (latest as of 9/2025), ROS2 Humble (LTS, EOL 5/2027), Gazebo Sim Harmonic (LTS, EOL 9/2028), PX4 1.16 interfaced via XRCE-DDS, ArduPilot 4.6 interfaced via MAVROS, YOLOv8 on ONNX Runtime 1.22 (latest stable releases as of 8/2025), L4T 36 (Ubuntu 22-based)/JetPack 6 (for deployment only, latest major release as of 8/2025), WSLg (for simulation and development on Windows 11 only)
Important
AAS is developed using Ubuntu 22.04 with nvidia-driver-580
on an i9-13 with RTX 3500 and an i7-11 with RTX 3060—an NVIDIA GPU is required for ideal performance
To setup the requirements: (i) Ubuntu, (ii) NVIDIA driver, (iii) Docker Engine, and (iv) NVIDIA Container Toolkit (with NVIDIA NGC API Key), read REQUIREMENTS_UBUNTU.md
Windows 11 support is available via WSLg, read REQUIREMENTS_WSL.md
# Install dependencies (git, Git LFS, Xterm)
sudo apt update
sudo apt install -y git git-lfs xterm xfonts-base
git lfs install # Without this, the `git clone` command will not download the large files in `simulation_resources/`
# Clone this repo
mkdir -p ~/git && cd ~/git
git clone https://github.com/JacopoPan/aerial-autonomy-stack.git
# Build the Docker images
cd ~/git/aerial-autonomy-stack/scripts
./sim_build.sh # The first build takes ~25', subsequent ones will take seconds to minutes thanks to the Docker cache
Warning
The 1st build takes ~45GB of space and ~25' with good internet (Ctrl + c
and restart if needed)
# Start a simulation
cd ~/git/aerial-autonomy-stack/scripts
AUTOPILOT=px4 NUM_QUADS=1 NUM_VTOLS=1 WORLD=swiss_town ./sim_run.sh # Check the script for more options (note: ArduPilot SITL takes ~40s to be ready to arm)
Note
On a low-mid range laptop—i7-11 with 16GB RAM and RTX 3060—AAS simulates three PX4 quads with camera and LiDAR at 99% real-time-factor (note that ArduPilot faster physics updates and more complex worlds have higher computational demands). Make sure you run sudo prime-select nvidia
and rebooted to effectively leverage GPU compute.
Available WORLD
s:
apple_orchard
, a GIS world created using BlenderGISimpalpable_greyness
, (default) an empty world with simple shapesshibuya_crossing
, a 3D world adapted from cgtraderswiss_town
, a photogrammetry world courtesy of Pix4D / pix4d.com
To advance the simulation in discrete time steps, e.g. 1s, from a terminal on the host, run:
docker exec simulation-container bash -c " \
gz service -s /world/\$WORLD/control \
--reqtype gz.msgs.WorldControl --reptype gz.msgs.Boolean \
--req 'multi_step: 250, pause: true'" # Adjust multi_step based on the value of max_step_size in the world's .sdf (defaults: 250 for PX4, 1000 for ArduPilot)
To add or disable wind effects, from a terminal on the host, run:
docker exec simulation-container bash -c " \
gz topic -t /world/\$WORLD/wind/ -m gz.msgs.Wind \
-p 'linear_velocity: {x: 0.0 y: 3.0}, enable_wind: true'" # Positive X blows from the West, positive Y blows from the South
docker exec simulation-container bash -c " \
gz topic -t /world/\$WORLD/wind/ -m gz.msgs.Wind \
-p 'enable_wind: false'" # Disable WindEffects
cd ~/git/aerial-autonomy-stack/scripts
AUTOPILOT=px4 NUM_QUADS=1 ./sim_run.sh # Or `ardupilot`, or `NUM_VTOLS=1`
# In aircraft 1's terminal
ros2 run mission mission --conops yalla \
--ros-args -r __ns:=/Drone$DRONE_ID -p use_sim_time:=true # This mission is a simple takeoff, followed by an orbit, and landing for any vehicle
# Finally, in the simulation's terminal
/simulation_resources/patches/plot_logs.sh # Analyze the flight logs at http://42.42.1.99:5006/browse or in MAVExplorer
To create a new mission, read the banner comments in ardupilot_interface.hpp
and px4_interface.hpp
for command line examples of takeoff, orbit, reposition, offboard, land
Once flown from CLI, implemented your mission in MissionNode.conops_callback()
Launching the sim_run.sh
script with MODE=dev
, does not start the simulation and mounts folders simulation_resources
, aircraft_resources
, and ros2_ws/src
as volumes to more easily track, commit, push changes while building and testing them within the containers
cd ~/git/aerial-autonomy-stack/scripts
MODE=dev ./sim_run.sh # Starts one simulation-image and one aircraft-image where the ros2_ws/src/ and *_resources/ folders are mounted from the host
To build changes made on the host in either simulation_ws/src
or aircraft_ws/src
cd ros2_ws/ # In the simulation and/or in the aircraft container
colcon build --symlink-install
To start the simulation (by default, this is a single PX4 quad, configure using sim_run.sh
)
tmuxinator start -p /simulation.yml.erb # In the simulation container
tmuxinator start -p /aircraft.yml.erb # In the aircraft container
Once done, detach Tmux with Ctrl + b
, then d
; kill everything with tmux kill-server && pkill -f gz
Tip
AAS Structure (click to expand)
aerial-autonomy-stack
│
├── aircraft
│ ├── aircraft_ws
│ │ └── src
│ │ ├── autopilot_interface # Ardupilot/PX4 high-level actions (Takeoff, Orbit, Offboard, Land)
│ │ ├── mission # Orchestrator of the actions in `autopilot_interface`
│ │ ├── offboard_control # Low-level references for the Offboard action in `autopilot_interface`
│ │ ├── state_sharing # Publisher of the `/state_sharing_drone_N` topic broadcasted by Zenoh
│ │ └── yolo_inference # GStreamer video acquisition and publisher of YOLO bounding boxes
│ │
│ └── aircraft.yml.erb # Aircraft docker tmux entrypoint
│
├── scripts
│ ├── docker
│ │ ├── Dockerfile.aircraft # Docker image for aircraft simulation and deployment
│ │ └── Dockerfile.simulation # Docker image for Gazebo and SITL simulation
│ │
│ ├── deploy_build.sh # Build `Dockerfile.aircraft` for arm64/Orin
│ ├── deploy_run.sh # Start the aircraft docker on arm64/Orin
│ │
│ ├── sim_build.sh # Build both dockerfiles for amd64/simulation
│ └── sim_run.sh # Start the simulation
│
└── simulation
├── simulation_resources
│ ├── aircraft_models
│ │ ├── alti_transition_quad # ArduPilot VTOL
│ │ ├── iris_with_ardupilot # ArduPilot quad
│ │ ├── sensor_camera
│ │ ├── sensor_lidar
│ │ ├── standard_vtol # PX4 VTOL
│ │ └── x500 # PX4 quad
│ └── simulation_worlds
│ ├── apple_orchard.sdf
│ ├── impalpable_greyness.sdf
│ ├── shibuya_crossing.sdf
│ └── swiss_town.sdf
│
├── simulation_ws
│ └── src
│ └── ground_system # Publisher of topic `/tracks` broadcasted by Zenoh
│
└── simulation.yml.erb # Simulation docker tmux entrypoint
Tmux and Docker Shortcuts (click to expand)
- Move between Tmux windows with
Ctrl + b
, thenn
,p
- Move between Tmux panes with
Ctrl + b
, thenarrow keys
- Enter copy mode to scroll back with
Ctrl + [
, thenarrow keys
, exit withq
- Split a Tmux window with
Ctrl + b
, then"
(horizontal) or%
(vertical) - Detach Tmux with
Ctrl + b
, thend
tmux list-sessions # List all sessions
tmux attach-session -t [session_name] # Reattach a session
tmux kill-session -t [session_name] # Kill a session
tmux kill-server # Kill all sessions
Docker hygiene:
docker ps -a # List containers
docker stop $(docker ps -q) # Stop all containers
docker container prune # Remove all stopped containers
docker images # List images
docker image prune # Remove untagged images
docker rmi <image_name_or_id> # Remove a specific image
docker builder prune # Clear the cache system wide
Important
These instructions are tested on a Holybro Jetson Baseboard (Pixhawk 6X + NVIDIA Orin NX 16GB)
To setup (i.a) PX4's DDS client or (i.b) ArduPilot MAVLink bridge, (ii) JetPack 6, (iii) Docker Engine, and (iv) NVIDIA Container Toolkit (with NVIDIA NGC API Key) on Orin, read SETUP_AVIONICS.md
# Install dependencies (git, Git LFS)
sudo apt update
sudo apt install -y git git-lfs
git lfs install # Without this, the `git clone` command will not download the large files in `simulation_resources/`
# Clone this repo
mkdir -p ~/git && cd ~/git
git clone https://github.com/JacopoPan/aerial-autonomy-stack.git
# On Jetson Orin NX, build for arm64 with TensorRT support
cd ~/git/aerial-autonomy-stack/scripts
./deploy_build.sh # The first build takes ~1h (mostly to build onnxruntime-gpu from source)
# On Jetson Orin NX, start and attach an aircraft-container (e.g., from ssh)
DRONE_TYPE=quad AUTOPILOT=px4 DRONE_ID=1 CAMERA=true LIDAR=false ./deploy_run.sh
docker exec -it aircraft-container tmux attach
Warning
The 1st run of ./deploy_run.sh
requires ~4' to build the TensorRT cache
- PX4 HITL simulation for Gazebo Harmonic
- Gymnasium RL interface
- Support for Betaflight SITL interfaced via UDP or MultiWii Serial Protocol (MSP)
- Support for SPARK-FAST-LIO/SuperOdom
You've done a man's job, sir. I guess you're through, huh?