Skip to content

Commit 2f727da

Browse files
Grigorii Fadeevclaude
andcommitted
Restructure project: extract PID controller, migrate to git submodules
- Replace vendored driver copies with git submodules (AS5600, BNO085, DShot) - Extract PID controller from main.py into pid.py (reusable class with anti-windup) - Add README, CLAUDE.md updates, and architecture decision records (ADR-001..003) - Remove old examples and resources (now live in respective submodule repos) - Update display driver with state-based drawing API Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent bf5442b commit 2f727da

27 files changed

Lines changed: 732 additions & 2815 deletions

.gitmodules

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
[submodule "AS5600"]
2+
path = AS5600
3+
url = https://github.com/c0ffee2code/AS5600.git
4+
[submodule "BNO085"]
5+
path = BNO085
6+
url = https://github.com/c0ffee2code/BNO085.git
7+
[submodule "DShot"]
8+
path = DShot
9+
url = https://github.com/c0ffee2code/DShot.git

AS5600

Submodule AS5600 added at 02de8bb

BNO085

Submodule BNO085 added at 05670e0

CLAUDE.md

Lines changed: 42 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,16 @@ Test bench for learning flight control systems, built around a Raspberry Pi Pico
1414

1515
## Development Approach
1616

17-
**Current milestone:** Characterize BNO085 sensor performance by measuring angle error and time lag against the AS5600 reference. This data is critical because a flight controller must compensate for sensor lag—controlling based on "where you are now" with a delayed sensor leads to instability and crashes. The control system needs to predict where it *will be* and adjust thrust accordingly.
17+
**Completed:** M1 — Single-axis PI(D) controller with AS5600 encoder, validated on hardware. Lever holds at 0° within ±3°. See `decision/ADR-001-pid-lever-stabilization.md`.
1818

19-
**Future milestones:**
20-
- Telemetry logging from both sensors (encoder + IMU)
21-
- Log lag and angle difference over time for troubleshooting and debugging
22-
- Implement control loops with lag compensation
23-
- Implement bidirectional DShot for ESC telemetry (RPM, voltage, temperature)
24-
- Analyze motor response lag (time between throttle command and RPM reaching target)
19+
**Current focus:** M2 — Switch PID input from AS5600 to BNO085 IMU. The IMU will be the primary and only control input (as on a real drone). AS5600 becomes telemetry-only ground truth for measuring IMU lag and angle error.
20+
21+
**Roadmap (see README.md for full details):**
22+
- M2: BNO085 as primary control input (depends on driver work)
23+
- M2a: Telemetry logging via Adalogger PiCowbell — RTC timestamps + SD card black box (see `decision/ADR-002-telemetry-logging.md`)
24+
- M3: Mixer abstraction (pure refactor)
25+
- M4: Cascaded PID — angle loop + rate loop using raw gyro (depends on M2, M2a, M3)
26+
- M5: Multi-axis control (depends on hardware evolution)
2527

2628
## Hardware Components
2729

@@ -30,43 +32,56 @@ Test bench for learning flight control systems, built around a Raspberry Pi Pico
3032
- **AS5600 Magnetic Encoder** - 12-bit absolute position encoder (reference sensor)
3133
- **Pimoroni Pico Display Pack** - 240x135 LCD for real-time data visualization
3234
- **2x Drone Motors + ESCs** - DShot protocol control via PIO
35+
- **Adafruit PiCowbell Adalogger** - PCF8523 RTC + MicroSD for telemetry logging (planned, see ADR-002)
3336
- **Power Distribution Board** - Motor power supply
3437

3538
## Project Structure
3639

3740
```
3841
├── main.py # Entry point - upload to Pico, runs on boot
39-
├── adafruit/ # Sensor drivers (upload to Pico)
42+
├── AS5600/ # Git submodule: github.com/c0ffee2code/AS5600
43+
│ └── driver/as5600.py
44+
├── BNO085/ # Git submodule: github.com/c0ffee2code/BNO085
45+
│ └── driver/
46+
│ ├── bno08x.py
47+
│ └── i2c.py
48+
├── DShot/ # Git submodule: github.com/c0ffee2code/DShot
49+
│ └── driver/
50+
│ ├── dshot_pio.py
51+
│ └── motor_throttle_group.py
4052
├── pimoroni/ # Display driver (upload to Pico)
41-
├── dshot/ # Motor control (upload to Pico)
42-
├── examples/ # Test scripts - do NOT upload to Pico
53+
├── decision/ # Architecture Decision Records
4354
└── resources/ # Docs, datasheets
4455
```
4556

46-
**Deployment:** Upload `main.py` + `adafruit/` + `pimoroni/` + `dshot/` to Pico. The `examples/` folder stays on your PC.
57+
**Deployment:** Upload the following files to Pico root (flat structure):
58+
- `main.py`
59+
- `AS5600/driver/as5600.py`
60+
- `BNO085/driver/bno08x.py` + `BNO085/driver/i2c.py`
61+
- `DShot/driver/dshot_pio.py` + `DShot/driver/motor_throttle_group.py`
62+
- `pimoroni/display/display_pack.py`
4763

48-
**Running examples:** Temporarily copy an example script to Pico as `main.py`, or run directly via Thonny/mpremote.
64+
All driver files are uploaded flat to Pico root so MicroPython resolves imports without `sys.path` manipulation.
4965

5066
## Architecture
5167

52-
### Sensor Drivers (`adafruit/`)
68+
### AS5600 Encoder (`AS5600/driver/as5600.py`)
5369

54-
- `adafruit/encoder/as5600.py` - AS5600 driver. Key function: `to_degrees(raw_angle, axis_center)` converts raw 12-bit readings to degrees relative to a calibrated center position.
55-
- `adafruit/imu/bradcar/bno08x.py` - BNO08x driver adapted from Adafruit. Uses interrupt-driven sensor updates with precise timestamp tracking.
56-
- `adafruit/imu/bradcar/i2c.py` - I2C transport layer for BNO08x.
70+
Magnetic rotary encoder driver. Key function: `to_degrees(raw_angle, axis_center)` converts raw 12-bit readings to degrees relative to a calibrated center position. Includes low-latency filter configuration and diagnostic telemetry.
5771

58-
### Display (`pimoroni/display/`)
72+
### BNO085 IMU (`BNO085/driver/`)
5973

60-
- `display_pack.py` - Display abstraction using PicoGraphics library.
74+
- `bno08x.py` - BNO08x driver with SHTP protocol, interrupt-driven sensor updates, quaternion/euler output, and precise timestamp tracking.
75+
- `i2c.py` - I2C transport layer for BNO08x. Handles non-standard clock stretching and fragment reassembly.
6176

62-
### Motor Control (`dshot/`)
77+
### Display (`pimoroni/display/`)
6378

64-
- `dshot/jrddupont/dshot_pio.py` - DShot protocol via RP2040 PIO. Supports DSHOT150/300/600/1200.
65-
- See `resources/dshot_protocol.md` for full protocol specification (packet structure, timing, special commands, bidirectional DShot, telemetry).
79+
- `display_pack.py` - Display abstraction using PicoGraphics library.
6680

67-
### Examples (`examples/`)
81+
### Motor Control (`DShot/driver/`)
6882

69-
Small standalone scripts for testing individual sensors, calibration routines, and experiments. Not part of the main application.
83+
- `dshot_pio.py` - Low-level DShot protocol via RP2040/RP2350 PIO. Supports DSHOT150/300/600/1200.
84+
- `motor_throttle_group.py` - Dual-core facade for managing multiple motors. Core 1 runs a dedicated 1kHz command loop for reliable ESC communication. Provides arming, throttle control, emergency stop, and health monitoring.
7085

7186
## Key Constants
7287

@@ -82,6 +97,8 @@ Small standalone scripts for testing individual sensors, calibration routines, a
8297

8398
- BNO085 reset: Pin 2
8499
- BNO085 interrupt: Pin 3
85-
- Motor 1 (DShot): Pin 4
86-
- Motor 2 (DShot): Pin 5
100+
- Motor 1 (DShot): Pin 4 (will move to Pin 6 when Adalogger is integrated — see ADR-002)
101+
- Motor 2 (DShot): Pin 5 (will move to Pin 7 when Adalogger is integrated — see ADR-002)
87102
- Display buttons: Pins 12 (A), 13 (B), 14 (X), 15 (Y)
103+
- Adalogger SD card (planned): MOSI=Pin 19, MISO=Pin 16, SCK=Pin 18, CS=Pin 17
104+
- Adalogger RTC I2C (planned): SDA=Pin 4, SCL=Pin 5 (I2C bus 1)

DShot

Submodule DShot added at c54c01b

README.md

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
# Flight Benchy
2+
3+
Test bench for learning flight control systems, built around a Raspberry Pi Pico 2. Experiment with sensor fusion, control loops, and motor control in a controlled single-axis environment before applying concepts to real drones.
4+
5+
## Hardware
6+
7+
- **Raspberry Pi Pico 2** — Main microcontroller (RP2350, dual-core)
8+
- **AS5600 Magnetic Encoder** — 12-bit absolute position at pivot (ground truth reference, ~0.088° resolution)
9+
- **BNO085 IMU** — 9-axis IMU with onboard sensor fusion (future primary control input)
10+
- **2x Drone Motors + ESCs** — DShot600 protocol via PIO, mounted on opposite ends of lever
11+
- **Pimoroni Pico Display Pack** — 240x135 LCD for real-time status
12+
- **Adafruit PiCowbell Adalogger** — PCF8523 RTC + MicroSD for black box telemetry (planned)
13+
- **Power Distribution Board** — Motor power supply
14+
15+
## Mechanical Setup
16+
17+
Metal frame with 3D-printed elements forming a swinging lever pivoting around a central axis. Two drone motors on opposite ends produce **downward** thrust (inverted for safety — bench can't fly off the desk). Differential thrust creates torque to control lever angle. Mechanical range is approximately ±50°.
18+
19+
## Roadmap
20+
21+
### M1: Single-axis PID with encoder — DONE
22+
23+
Single PI(D) loop at 50 Hz using AS5600 encoder feedback. Differential thrust mixer for 2 motors. Lever holds at 0° within ±3°. See [ADR-001](decision/ADR-001-pid-lever-stabilization.md).
24+
25+
### M2: Switch to BNO085 IMU as primary control input
26+
27+
Replace AS5600 with BNO085 quaternion/euler output as the sole PID input. AS5600 becomes telemetry-only for measuring IMU-vs-encoder error and lag. This is the critical step — a real drone has no encoder.
28+
29+
**Depends on:** BNO085 driver delivering reliable, low-latency quaternion data.
30+
31+
### M2a: Telemetry logging (black box)
32+
33+
Log timestamped data from both sensors + PID internals to SD card via Adalogger PiCowbell. Enables post-experiment analysis with Python/pandas. See [ADR-002](decision/ADR-002-telemetry-logging.md).
34+
35+
**Depends on:** Adalogger hardware integration, motor pin reassignment (GPIO 4/5 → 6/7).
36+
37+
### M3: Mixer abstraction
38+
39+
Extract `base ± output` motor mapping into a configurable mixer module. Makes code drone-topology-agnostic — swap a mix table to support different frame types (2-motor lever, quadcopter X-frame, etc.).
40+
41+
**Depends on:** M1 (pure code refactor, no hardware dependency).
42+
43+
### M4: Cascaded PID (angle loop + rate loop)
44+
45+
Replace single angle PID with two nested loops: an outer angle loop (~50-100 Hz) feeding desired rotation rate to an inner rate loop (~500+ Hz) using raw gyro data. This is how real flight controllers (Betaflight, ArduPilot) work — the inner loop uses near-zero-lag gyro data for crisp response.
46+
47+
**Depends on:** M2 (IMU as input), M2a (telemetry to validate improvement), M3 (clean mixer).
48+
49+
### M5: Multi-axis control
50+
51+
Add roll and/or yaw axes. Requires either mechanical modifications to the bench or moving to an actual drone frame.
52+
53+
**Depends on:** M3 (mixer), M4 (cascaded PID), hardware evolution.
54+
55+
## Test Bench vs Real Drone
56+
57+
See [ADR-001, "Test Bench vs Real Drone" section](decision/ADR-001-pid-lever-stabilization.md) for a detailed comparison covering: single axis vs three axes, single PID vs cascaded PIDs, encoder vs IMU, and fixed pivot vs free flight.
58+
59+
## Project Structure
60+
61+
```
62+
├── main.py # Entry point — upload to Pico, runs on boot
63+
├── AS5600/ # Git submodule: github.com/c0ffee2code/AS5600
64+
├── BNO085/ # Git submodule: github.com/c0ffee2code/BNO085
65+
├── DShot/ # Git submodule: github.com/c0ffee2code/DShot
66+
├── pimoroni/display/ # Display driver
67+
├── decision/ # Architecture Decision Records
68+
└── resources/ # Datasheets, protocol docs
69+
```
70+
71+
## Deployment
72+
73+
Clone with submodules:
74+
```bash
75+
git clone --recurse-submodules <repo-url>
76+
```
77+
78+
Upload to Pico root (flat structure):
79+
- `main.py`
80+
- `AS5600/driver/as5600.py`
81+
- `BNO085/driver/bno08x.py` + `i2c.py`
82+
- `DShot/driver/dshot_pio.py` + `motor_throttle_group.py`
83+
- `pimoroni/display/display_pack.py`
84+
85+
## License
86+
87+
See individual submodule repositories for their licenses.

adafruit/encoder/as5600.py

Lines changed: 0 additions & 106 deletions
This file was deleted.

0 commit comments

Comments
 (0)