{"id":51214846,"url":"https://github.com/szymdzum/tello-drone","last_synced_at":"2026-06-28T03:31:27.426Z","repository":{"id":364332818,"uuid":"1267344843","full_name":"szymdzum/tello-drone","owner":"szymdzum","description":"Fly a DJI Tello from scratch over raw UDP, keyboard FPV with an arcade HUD, face tracking, crash detection, drift damping, and a JSONL flight recorder. Stdlib-only protocol core.","archived":false,"fork":false,"pushed_at":"2026-06-12T16:20:15.000Z","size":807,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-12T17:12:37.725Z","etag":null,"topics":["computer-vision","dji-tello","drone","face-tracking","fpv","opencv","python","robotics","telemetry","tello","udp"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/szymdzum.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-06-12T13:03:51.000Z","updated_at":"2026-06-12T16:20:20.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/szymdzum/tello-drone","commit_stats":null,"previous_names":["szymdzum/tello-drone"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/szymdzum/tello-drone","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/szymdzum%2Ftello-drone","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/szymdzum%2Ftello-drone/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/szymdzum%2Ftello-drone/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/szymdzum%2Ftello-drone/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/szymdzum","download_url":"https://codeload.github.com/szymdzum/tello-drone/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/szymdzum%2Ftello-drone/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34876271,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-28T02:00:05.809Z","response_time":54,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["computer-vision","dji-tello","drone","face-tracking","fpv","opencv","python","robotics","telemetry","tello","udp"],"created_at":"2026-06-28T03:31:25.711Z","updated_at":"2026-06-28T03:31:27.415Z","avatar_url":"https://github.com/szymdzum.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Tello Drone Controller\n\n[![CI](https://github.com/szymdzum/tello-drone/actions/workflows/ci.yml/badge.svg)](https://github.com/szymdzum/tello-drone/actions/workflows/ci.yml)\n\n![FPV HUD with face follow locked on](docs/hud.jpg)\n*Live FPV: face follow engaged (`p`), HUD with attitude ladder, yaw tape,\ntelemetry, and virtual sticks — the drone is holding position on its own.*\n\nFly a Ryze/DJI Tello from scratch — raw UDP sockets, no drone framework. The\nprotocol core is stdlib-only; FPV flight needs `opencv-python` + `numpy`\n(+ optionally `av` for lower-latency video decode).\n\nWhat's in the box:\n\n- **Keyboard FPV** with live video and an arcade-style HUD (attitude ladder,\n  yaw tape, virtual sticks, battery/telemetry panels)\n- **Face follow** — press `p` and the drone tracks you: Haar-cascade detection\n  on a background thread feeding a P-controller; any stick key overrides\n- **Marker hold** — show [docs/marker0.png](docs/marker0.png) (printed, or just\n  on a phone screen), park the drone where you want it, press `m`: it locks\n  that position relative to the marker (ArUco detection, strafe-centered,\n  distance setpoint captured at engagement)\n- **Crash detection** — telemetry-based flip detection resyncs the controller\n  when the drone ends up on its back (tuned against real crash logs)\n- **Drift damper** — when the sticks are quiet, the loop counters the drone's\n  own reported drift instead of streaming zeros (the firmware's optical\n  position-hold goes blind on featureless floors)\n- **Flight recorder** — every session logged as JSONL (telemetry, commands,\n  rc stream, detections); `analyze.py` turns a log into a flight report and\n  matplotlib charts. Every safety feature above was built from these logs.\n- A hardened protocol layer: response-desync quarantine, ground-gated\n  keepalive, non-blocking action runner — lessons from a real crash, each\n  pinned by hardware-free tests\n\n## Quick Start\n\n1. **Power on** the Tello (press side button)\n2. **Fly** — `drone.py` auto-joins the Tello Wi-Fi if it's broadcasting\n   (`--ssid` to override the network name, empty to disable):\n\n```bash\npython drone.py          # FPV: live video window + keyboard flight (default)\npython drone.py repl     # raw SDK REPL for protocol debugging (stdlib only)\npython drone.py demo     # scripted square flight (stdlib only)\n```\n\n## FPV controls\n\nOpenCV both shows the video and reads the keys, so **click the video window to\ngive it focus** before flying. One hand per cluster:\n\n| Left hand (WASD) | Right hand (IJKL) |\n|---|---|\n| `W` / `S` forward / back | `I` / `K` up / down (throttle) |\n| `A` / `D` strafe left / right | `J` / `L` yaw left / right |\n\n`t` takeoff · `g` land · `f` flip · `h` hover · `p` **follow face** · `m` **marker hold** · `y`/`u` speed · **SPACE** = **EMERGENCY stop (drone drops!)** · **Esc**/`q` quit (lands first).\n\nHold a key to move; release and that axis coasts to a hover within ~0.5 s\n(tune `HOLD_S` / `RATE_S` in `tello_app/flight/controller.py`).\n\nDesign notes: video decodes on a background thread (PyAV if installed, else\nOpenCV/FFMPEG), latest-frame-wins; takeoff/land/flip run on a worker thread —\nso nothing can ever stall the rc stream or the HUD. Face detection follows the\nsame pattern: its own thread, latest-detection-wins, and the follow controller\nis pure math (`tello_app/flight/tracking.py`) tuned via flight logs.\n\n## Flight recorder\n\nEvery `drone.py` session writes a JSONL log to `logs/` (`--no-log` to disable):\ntelemetry at 10 Hz, every command with round-trip time, the full rc stream\ntagged by who was steering (`keys` / `follow` / `damp` / `keepalive`), face\ndetections, and flight events.\n\n```bash\npython analyze.py            # report on the newest flight: battery drain,\n                             # cmd latency, airborne rc gaps, follow share\npython analyze.py --plot     # + time-aligned charts (pip install matplotlib)\n```\n\nThe crash detector and drift damper in this repo were both designed — and\nsign-verified — from these logs. If something weird happens in the air, the\nanswer is usually one `jq` query away.\n\n## Troubleshooting\n\n- **macOS AWDL (AirDrop/AirPlay) stalls the Wi-Fi radio ~every second** — the\n  top cause of lost replies/laggy rc. `drone.py` warns at startup; fix for the\n  session: `sudo ifconfig awdl0 down`. One-time setup so it's done automatically\n  (no password):\n  ```bash\n  echo \"$USER ALL=(ALL) NOPASSWD: /sbin/ifconfig awdl0 down\" | sudo tee /etc/sudoers.d/awdl\n  ```\n- **Timeout on commands** → check you're on the Tello Wi-Fi (`ping -c 3 192.168.10.1`)\n- **No Wi-Fi network visible** → long-press power 5 s to reset Wi-Fi\n- **\"unactive\" response to `command`** → update firmware via the Tello mobile app\n- **`objc: Class AVFFrameReceiver is implemented in both ...`** at startup is\n  benign — cv2 and PyAV each bundle their own FFmpeg. Ignore it.\n- **`non-existing PPS 0 referenced` / `no frame!` at video startup is normal** —\n  the decoder is waiting for the first keyframe.\n- **`error while decoding MB x y` during flight = real packet loss** — get\n  closer, kill AWDL, avoid crowded 2.4 GHz.\n- **Hovering drone drifts at `rc 0 0 0 0`** → the vision positioning system is\n  losing the floor, not a control bug. Fly over a textured floor in good light.\n- **The official app is smoother than the SDK and that's expected** — it speaks\n  a different binary protocol (50 Hz un-acked stick packets). The text SDK still\n  flies + streams reliably as long as the control loop never blocks (see\n  `tello_app/shells/fpv.py`).\n\n## Project Structure\n\n```\ntello-drone/\n├── drone.py             # THE entry point: fpv (default) / repl / demo\n├── keepalive.py         # Hold a session open so the drone doesn't idle-power-off\n├── analyze.py           # Flight-log report + charts\n├── tello_app/\n│   ├── tello.py         # Tello class – UDP channels, commands, state receiver\n│   ├── flightlog.py     # JSONL flight recorder (fail-silent, thread-safe)\n│   ├── util.py          # Host-side helpers (Wi-Fi auto-join, macOS AWDL)\n│   ├── flight/          # Flight brain: keymap, controller, action runner,\n│   │                    #   crash monitor, follow/damper math, HUD content\n│   ├── vision/          # Face detector (background thread, latest-wins)\n│   ├── video/           # Background H.264 decode (PyAV / OpenCV), latest-frame-wins\n│   └── shells/          # Front-ends: fpv (video + keys), repl (raw SDK + demo)\n├── docs/                # Research notes (IDEAS, LIBRARIES, ALTERNATIVES, FLIX-BUILD)\n│   └── REPAIR.md        # Historical repair log for the original (grounded) unit\n├── tests/               # Hardware-free tests (fake UDP drone, mocked Tello)\n├── pyproject.toml       # ruff + basedpyright config\n└── CLAUDE.md            # Guidance for Claude Code\n```\n\nTests: `python -m unittest discover -s tests` — 93 of them, no drone needed\n(the protocol tests run against a fake UDP drone on localhost). CI runs tests,\nruff, and basedpyright on every push.\n\n## SDK Command Reference (REPL)\n\n| Command | Description |\n|---|---|\n| `takeoff` / `land` | Auto take off / land |\n| `up/down/left/right/forward/back \u003ccm\u003e` | Move 20–500 cm |\n| `cw/ccw \u003cdegrees\u003e` | Rotate 1–360° |\n| `flip \u003cl\\|r\\|f\\|b\u003e` | Do a flip |\n| `speed \u003ccm/s\u003e` | Set speed (10–100) |\n| `battery?` | Get battery % |\n| `state` | Show live telemetry |\n| `emergency` | Kill motors immediately |\n\n## Safety Notes\n\n- The drone **auto-lands after 15 seconds** of no commands\n- `emergency` kills motors instantly – the drone will **drop**\n- Always fly in an open area with sufficient ceiling height\n- Keep battery above 20% for stable flight\n\n## Hardware note\n\nThe original drone (dead motor + power-rail fault) is permanently grounded; a\nworking replacement is the flight unit. The full teardown/repair log lives in\n[docs/REPAIR.md](docs/REPAIR.md).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fszymdzum%2Ftello-drone","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fszymdzum%2Ftello-drone","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fszymdzum%2Ftello-drone/lists"}