Aerie is a REST/WebSocket API that wraps MAVSDK-Python in a FastAPI server. It lets you control a drone (real or simulated) over HTTP — arm, takeoff, fly to coordinates, run missions, stream telemetry — with auto-generated OpenAPI documentation.
PX4 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.
Gazebo serves two roles:
The physics and rendering are separate concerns. The "headless" Docker image runs physics without rendering. Our custom image enables both.
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). It's a standard X11 display server, just without physical hardware.So the chain is: Gazebo → Xvfb (fake screen) → x11vnc (capture) → websockify (TCP→WebSocket) → your browser.
A Python FastAPI application that translates HTTP requests into MAVLink commands:
The API uses a DroneManager singleton that holds the MAVSDK System instance. All routes depend-inject this via FastAPI's Depends().
Requires Docker Desktop for Mac. Everything else runs natively.
# Install python3 -m venv .venv && source .venv/bin/activate pip install -e . # Run ./start.sh
Requires Docker Desktop for Windows with WSL2 backend enabled.
# In PowerShell or Windows Terminal: # 1. Install Docker Desktop and enable WSL2 backend # (Settings > General > "Use the WSL 2 based engine") # 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 docker compose up -d --build # 4. Wait ~30 seconds for PX4 to initialize, then start Aerie set PYTHONPATH=src uvicorn aerie.main:app --host 0.0.0.0 --port 8000
Then open:
Note: The Docker container runs Linux (ARM64/AMD64). Docker Desktop handles the translation layer automatically. If you're on an AMD64/Intel machine, remove platform: linux/arm64 from docker-compose.yml or change it to linux/amd64.
Same as macOS. Install Docker Engine (not Desktop), Python 3.8+, then:
python3 -m venv .venv && source .venv/bin/activate pip install -e . ./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/connection/connect | POST | Connect to drone ({"address": "udpin://0.0.0.0:14540"}) |
/api/v1/connection/disconnect | POST | Disconnect |
/api/v1/connection/status | GET | Check connection state |
/api/v1/action/arm | POST | Arm motors |
/api/v1/action/disarm | POST | Disarm motors |
/api/v1/action/takeoff | POST | Takeoff ({"altitude_m": 5.0}) |
/api/v1/action/land | POST | Land at current position |
/api/v1/action/goto | POST | Fly to GPS position ({"latitude_deg", "longitude_deg", "altitude_m"}) |
/api/v1/action/return-to-launch | POST | Return to home |
/api/v1/action/hold | POST | Hold position |
/api/v1/action/kill | POST | Emergency motor kill |
/api/v1/mission | POST | Upload mission waypoints |
/api/v1/mission | GET | Download current mission |
/api/v1/mission | DELETE | Clear mission |
/api/v1/mission/start | POST | Start mission |
/api/v1/mission/pause | POST | Pause mission |
/api/v1/mission/progress | GET | Current waypoint progress |
/api/v1/telemetry/position | GET | GPS position |
/api/v1/telemetry/attitude | GET | Roll/pitch/yaw |
/api/v1/telemetry/battery | GET | Battery voltage/remaining |
/api/v1/telemetry/health | GET | Sensor health checks |
/api/v1/telemetry/flight-mode | GET | Current flight mode |
/api/v1/telemetry/all | GET | Full telemetry snapshot |
ws://.../api/v1/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: DRONE_ADDRESS, PORT, etc.) |
src/aerie/core/drone.py | DroneManager singleton wrapping MAVSDK System |
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 PX4+Gazebo |
docker-compose.yml | Container orchestration (ports, env vars, shared memory) |
Aerie — MAVSDK REST API for drone control