Files
cf-autohover/README.md
K. Isom eed9778e59 Initial checkpoint: Crazyflie mission client (DearPyGui)
Two-step ARM -> LAUNCH -> LAND flight flow for a Crazyflie + Flow v2 deck:

- controller.py: cflib link + flight logic on one worker thread with a
  command queue. Supervisor arming, Kalman estimator reset/convergence
  wait, high-level-commander takeoff/land, configurable hover height
  (0.2-2.0 m), emergency stop, graceful land-on-shutdown, thread-safe
  snapshot()/scan(). States: DISCONNECTED/CONNECTING/READY/ARMING/ARMED/
  FLYING/LANDING/EMERGENCY/ERROR.
- mission_client.py: DearPyGui front-end, manual render loop polling
  snapshot() each frame. Context button ARM(green)->LAUNCH(blue)->LAND
  (yellow), secondary Disarm, hover-height slider, console log, and an
  Estimator state panel (x/y/z, yaw, flow counts) for drift inspection.
- uv-managed (pyproject.toml + uv.lock); run `uv run mission_client.py`.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-23 15:44:07 -07:00

142 lines
7.3 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Crazyflie Mission Client
A small [DearPyGui](https://github.com/hoffstadt/DearPyGui) cockpit for flying a
Bitcraze **Crazyflie** with a **Flow v2 deck** (same GUI binding as the
test-stand app). Arm it and the drone lifts off to **1 m** above the ground and
holds position; disarm to land gently; emergency-stop to cut the motors
instantly.
```
┌──────────────────────────────────────────────┐
│ Connection │
│ [ radio://0/80/2M/E7E7E7E7E7 ▾ ] [Scan][Connect]│
│ ──────────────────────────────────────────────│
│ Status │
│ Hovering at 1.40 m ← colour-coded │
│ Battery: 3.92 V Height: 1.41 m │
│ Flow v2 deck: attached │
│ Supervisor: armed, flying │
│ ──────────────────────────────────────────────│
│ Estimator state │
│ Pos (m): x +0.02 y -0.01 z +1.00 │
│ Yaw: +1.4 deg │
│ Flow counts: dx +5 dy -3 │
│ ──────────────────────────────────────────────│
│ Flight │
│ Hover height [====●========] 1.40 m │
│ [ LAUNCH (to 1.40 m) ] ← ARM→LAUNCH→LAND │
│ [ Disarm ] ← only while ARMED │
│ [ EMERGENCY STOP (SPACE) ] │
│ ──────────────────────────────────────────────│
│ Console [Clear] │
│ 12:00:01 I controller: [READY] … │
│ 12:00:03 I controller: [ARMED] ready to LAUNCH │
│ 12:00:08 I controller: [FLYING] Hovering 1.40 m│
│ 12:00:09 I crazyflie.console: ...fw output... │
└──────────────────────────────────────────────┘
```
Flight is a **two-step flow** with one context button:
| State | Button | Colour | Action |
|-------|--------|--------|--------|
| Ready | **ARM** | green | reset estimator + arm; drone idles armed on the ground |
| Armed | **LAUNCH (to N m)** | blue | take off to the hover height and hold |
| Flying | **LAND** | yellow | gentle descent + disarm |
While **ARMED** on the ground a separate **Disarm** button backs out without
launching (the firmware also auto-disarms after ~30 s if you never launch). The
**Hover height** slider (0.22.0 m) can be adjusted right up until LAUNCH (it
latches there) and locks once airborne. Below everything, a **console log** of
roughly the same height shows mission events, cflib messages, and firmware
console output (auto-scrolls while at the bottom; **Clear** empties it). Use
**Scan** to discover URIs, or type/select one in the combo before connecting.
## Hardware
- Crazyflie 2.x with up-to-date firmware (protocol v7+ recommended for the
supervisor arming system; older firmware falls back to the thrust-unlock path).
- **Flow v2 deck** mounted underneath (provides optical-flow XY + laser Z hold).
- Crazyradio PA / 2.0 plugged into the host.
- Charged battery, propellers in good shape, and **clear space above the drone**.
## Install
This project uses [uv](https://docs.astral.sh/uv/). From `autover/`:
```bash
uv sync
```
That creates `.venv/` and installs `cflib` and `dearpygui` (a self-contained
wheel with its own GL/windowing backend — no system GUI packages needed).
On Linux you also need udev permissions for the Crazyradio (see the
[cflib USB docs](https://www.bitcraze.io/documentation/repository/crazyflie-lib-python/master/installation/usb_permissions/)).
> `requirements.txt` is kept for non-uv (`pip install -r requirements.txt`) setups;
> `pyproject.toml` is the source of truth for uv.
## Run
```bash
uv run mission_client.py
# Use a non-default radio URI:
CFLIB_URI=radio://0/80/2M/E7E7E7E7E7 uv run mission_client.py
```
## Flying
1. **Connect** — opens the radio link, starts telemetry, and probes for the
Flow deck. (Optionally **Scan** first and pick a URI from the combo.)
2. **ARM** — runs pre-flight checks (Flow deck present, battery ≥ 3.10 V, not
tumbled/locked), resets and waits for the Kalman estimator to converge, and
arms the motors. The drone then **idles armed on the ground** (props at idle)
in the ARMED state; the button turns blue and reads **LAUNCH**. (Back out any
time with **Disarm**; the firmware also auto-disarms after ~30 s if idle.)
3. **Set the hover height** with the slider (0.22.0 m, default 1 m) — adjustable
until launch — then **LAUNCH**: takes off to the chosen height (climb timed
for a gentle ~0.35 m/s) and hovers. The button turns yellow and reads **LAND**.
4. **LAND** — descends gently, stops the commander, and disarms.
5. **EMERGENCY STOP** (button or **SPACE**) — cuts the motors immediately.
On protocol-v7 firmware this latches the supervisor, so you must **reboot the
drone** before it will fly again.
Closing the window (or **Ctrl-C**) lands the drone first (if airborne) before
disconnecting.
## Drift inspection
The **Estimator state** panel streams the estimator pose and raw optical-flow counts
(`stateEstimate.x/y` + `stateEstimate.z`, `stabilizer.yaw`, `motion.deltaX/deltaY`)
so you can diagnose a small circular/looping drift ("toilet-bowl effect"):
- **Yaw creeping steadily** while hovering → heading-estimate drift; the position
hold corrects in a rotated frame and orbits. Power-cycle for a clean gyro start.
- **x/y tracing a circle** rather than jittering around zero → same orbiting hold.
- **Flow counts near zero or erratic** while the drone is clearly moving → poor
surface/lighting for the deck (try a textured, matte, well-lit floor at ~0.71 m).
## Safety notes
- Place the drone on a flat, level surface before arming — the estimator and
supervisor expect it level and stationary.
- The Flow deck needs a textured floor and reasonable light to hold position;
over a blank/reflective surface it can drift.
- This client commands a stationary hover at the configured height (0.22.0 m)
with no horizontal movement. Keep a hand near the emergency stop (spacebar)
the first few flights.
## Layout
| File | Responsibility |
|---------------------|-------------------------------------------------------------|
| `controller.py` | All flight logic + cflib link on one worker thread. |
| `mission_client.py` | DearPyGui front-end; polls `controller.snapshot()` per frame.|
| `requirements.txt` | Python dependencies. |
`controller.py` has no UI dependencies, so the flight logic can be reviewed and
driven independently of the GUI. The UI reads state through the thread-safe
`snapshot()` each frame and issues commands (`connect`/`arm`/`disarm`/
`emergency_stop`) that the worker thread executes.