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>
This commit is contained in:
141
README.md
Normal file
141
README.md
Normal file
@@ -0,0 +1,141 @@
|
||||
# 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.2–2.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.2–2.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.7–1 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.2–2.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.
|
||||
Reference in New Issue
Block a user