Aerie is a REST/WebSocket API that wraps MAVSDK-Python in a FastAPI server. It lets you control multiple drones (real or simulated) over HTTP — arm, takeoff, fly to coordinates, run missions, stream telemetry — with auto-generated OpenAPI documentation. A single Aerie instance manages all drones via a registry, with each drone addressed by a unique ID.
Previously, Aerie used a DroneManager singleton that held a single MAVSDK System instance. The new DroneRegistry holds a dict[str, System] — one MAVSDK connection per drone ID. Each drone is independently connected and controlled.
MAVSDK-Python supports multiple System() instances in one process, each connecting to a different drone via a separate gRPC server. This means no coordinator server is needed — one Aerie instance handles all drones.
All drone-specific endpoints are namespaced under /api/v1/drones/{drone_id}/...:
POST /api/v1/drones # Register a new drone
GET /api/v1/drones # List all drones
GET /api/v1/drones/{drone_id} # Get drone status
DELETE /api/v1/drones/{drone_id} # Remove a drone
POST /api/v1/drones/{drone_id}/action/arm # Arm specific drone
POST /api/v1/drones/{drone_id}/action/takeoff # Takeoff specific drone
GET /api/v1/drones/{drone_id}/telemetry/all # Telemetry for specific drone
WS /api/v1/drones/{drone_id}/ws/telemetry # Real-time stream for specific drone
POST /api/v1/drones with {"drone_id": "alpha", "address": "udpin://0.0.0.0:14540"}System(), connects to the drone via MAVLinkDELETE /api/v1/drones/alphaPX4 is open-source flight controller firmware that runs on real drone hardware. In SITL (Software-In-The-Loop) mode, it runs on a normal computer and simulates the flight controller. It exposes a MAVLink interface on UDP port 14540 — the same binary protocol used by real drones.
PX4 receives commands (arm, takeoff, goto) and sends back telemetry (GPS, battery, attitude). It handles all the low-level flight control: PID loops, sensor fusion, safety checks.
For multi-drone simulation, multiple PX4 instances run in the same container, each with a unique PX4_INSTANCE number. Instance N listens on port 14540 + N.
Gazebo serves two roles:
All drones share the same Gazebo world, so you can see them all in the 3D view simultaneously.
The challenge: Gazebo renders a GUI, but it's running inside a Docker container with no monitor. The solution is a 3-layer chain:
:99).So the chain is: Gazebo → Xvfb → x11vnc → websockify → your browser.
A Python FastAPI application that translates HTTP requests into MAVLink commands:
{drone_id}Requires Docker Desktop for Mac. Everything else runs natively.
# Install python3 -m venv .venv && source .venv/bin/activate pip install -e . # Run (2 drones by default) ./start.sh # Or run with a specific number of drones NUM_DRONES=3 ./start.sh
Requires Docker Desktop for Windows with WSL2 backend enabled.
# In PowerShell or Windows Terminal: # 1. Install Docker Desktop and enable WSL2 backend # 2. Clone the repo and set up Python python -m venv .venv .venv\Scripts\activate pip install -e . # 3. Build and start the simulation container (2 drones) set NUM_DRONES=2 docker compose up -d --build # 4. Wait ~40 seconds for PX4 to initialize, then start Aerie set PYTHONPATH=src uvicorn aerie.main:app --host 0.0.0.0 --port 8000
Note: On AMD64/Intel machines, change platform: linux/arm64 to linux/amd64 in docker-compose.yml.
Same as macOS. Install Docker Engine (not Desktop), Python 3.10+, then:
python3 -m venv .venv && source .venv/bin/activate pip install -e . NUM_DRONES=3 ./start.sh
On AMD64 Linux, change platform: linux/arm64 to linux/amd64 in docker-compose.yml.
Full interactive docs at /docs (Swagger UI).
| Endpoint | Method | Description |
|---|---|---|
/api/v1/drones | POST | Register a new drone ({"drone_id": "alpha", "address": "udpin://0.0.0.0:14540"}) |
/api/v1/drones | GET | List all registered drones |
/api/v1/drones/{id} | GET | Get drone status |
/api/v1/drones/{id} | DELETE | Remove a drone |
/api/v1/drones/{id}/action/arm | POST | Arm motors |
/api/v1/drones/{id}/action/disarm | POST | Disarm motors |
/api/v1/drones/{id}/action/takeoff | POST | Takeoff ({"altitude_m": 5.0}) |
/api/v1/drones/{id}/action/land | POST | Land at current position |
/api/v1/drones/{id}/action/goto | POST | Fly to GPS position |
/api/v1/drones/{id}/action/return-to-launch | POST | Return to home |
/api/v1/drones/{id}/action/hold | POST | Hold position |
/api/v1/drones/{id}/action/kill | POST | Emergency motor kill |
/api/v1/drones/{id}/mission | POST | Upload mission waypoints |
/api/v1/drones/{id}/mission | GET | Download current mission |
/api/v1/drones/{id}/mission | DELETE | Clear mission |
/api/v1/drones/{id}/mission/start | POST | Start mission |
/api/v1/drones/{id}/mission/pause | POST | Pause mission |
/api/v1/drones/{id}/mission/progress | GET | Current waypoint progress |
/api/v1/drones/{id}/telemetry/position | GET | GPS position |
/api/v1/drones/{id}/telemetry/attitude | GET | Roll/pitch/yaw |
/api/v1/drones/{id}/telemetry/battery | GET | Battery voltage/remaining |
/api/v1/drones/{id}/telemetry/health | GET | Sensor health checks |
/api/v1/drones/{id}/telemetry/flight-mode | GET | Current flight mode |
/api/v1/drones/{id}/telemetry/all | GET | Full telemetry snapshot |
ws://.../api/v1/drones/{id}/ws/telemetry | WS | Real-time telemetry stream |
| File | Purpose |
|---|---|
src/aerie/main.py | FastAPI app entry point, CORS, exception handlers, lifespan |
src/aerie/config.py | Pydantic settings (env vars: PORT, CORS, etc.) |
src/aerie/core/drone.py | DroneRegistry managing multiple MAVSDK System instances |
src/aerie/api/routes/drones.py | Drone registration/removal endpoints |
src/aerie/api/routes/action.py | Action endpoints (arm, takeoff, land, goto, etc.) |
src/aerie/api/routes/mission.py | Mission CRUD + start/pause/progress |
src/aerie/api/routes/telemetry.py | REST telemetry polling endpoints |
src/aerie/api/websockets/telemetry.py | WebSocket telemetry streaming with subscription model |
src/aerie/services/telemetry_service.py | Bridges MAVSDK async telemetry to WebSocket broadcasts |
sim/Dockerfile | Extends headless PX4 image with VNC for GUI access |
sim/entrypoint.sh | Starts Xvfb, x11vnc, noVNC, then N PX4 instances + Gazebo |
docker-compose.yml | Container orchestration (NUM_DRONES, ports, env vars) |
Aerie — MAVSDK REST API for multi-drone control