Introducing the pirouette FPV frame for GFPV

Signed-off-by: Arnaud Morin <arnaud.gfpv@mailops.fr>
This commit is contained in:
Arnaud Morin
2026-09-03 23:39:06 +02:00
commit 0b96c6bcfe
17 changed files with 3948 additions and 0 deletions

172
README.md Normal file
View File

@@ -0,0 +1,172 @@
# pirouette
Parametric [CadQuery](https://cadquery.readthedocs.io) model of a 3" FPV race
quad frame. It is an **interlocking space frame**: the parts key into each
other with tenons and mortises, so the joints hold it square and there is no
jig to build and no bolted-together stack of spacers.
Everything is generated from source — change a number, rerun, get new cut
files. Nothing is drawn by hand in a GUI.
```
./build.sh
```
That is the whole workflow. It writes `build/`.
---
## Requirements
- **[uv](https://docs.astral.sh/uv/)** — that's it. `build.sh` creates the
virtualenv from `pyproject.toml` on the first run and reuses it afterwards.
```
curl -LsSf https://astral.sh/uv/install.sh | sh
```
- Python 3.11 or newer, which uv will fetch for you if the system one is older.
The first run downloads about 1.7 GB. That is not a mistake: CadQuery depends
on OpenCascade and pulls VTK and trame in with it, whether or not you ever open
a viewer. After that a full build takes roughly 50 seconds, most of it the
PNG renders — `./build.sh --no-png` cuts it to under 30.
`uv.lock` pins every version, so a build here is the build you get.
---
## What you get
| Directory | Files | What it's for |
|---|---|---|
| `build/dxf/` | 4 | flat patterns, one per carbon part — feed these to the router |
| `build/stl/` | 7 | meshes for the slicer (the printed parts) or for a quick look |
| `build/step/` | 7 | solid models, if you want to take a part into other CAD |
| `build/png/` | 4 | assembly renders: iso, top, front, and a grid of all three |
`build/` is generated and gitignored. Delete it freely.
Only four parts get a DXF. The other three are printed, so a flat pattern
would mean nothing for them.
**The DXF is the exact part outline, with no kerf compensation.** Offset it by
half your tool diameter in CAM, or every part comes out undersize by a whole
cutter width.
---
## Bill of materials
Straight from the last build — `build.sh` prints this table every time, so
trust the run over this copy of it.
| Part | Qty | Stock | Material | g each | g total |
|---|---|---|---|---|---|
| plate | 2 | 2 mm | carbon fibre | 3.86 | 7.72 |
| arm | 4 | 2 mm | carbon fibre | 0.75 | 3.02 |
| motor_base | 8 | 2 mm | carbon fibre | 0.87 | 6.97 |
| spar | 4 | 2 mm | carbon fibre | 1.19 | 4.76 |
| camera_mount | 1 | printed | TPU | 7.87 | 7.87 |
| standoff | 4 | Ø3.5 mm | aluminium | 0.39 | 1.56 |
| lollipop | 1 | printed | TPU | 1.15 | 1.15 |
| **frame** | **24** | | | | **33.05** |
Carbon at 1.55 g/cm³, TPU at 1.21, aluminium at 2.70.
**Not counted, and not modelled:** screws, motors, props, camera, VTX,
receiver, flight controller. The standoffs are the only hardware in the model.
### Hardware you supply
- 4 × M2 aluminium standoff, 3.5 mm across, **15 mm long** — that length is the
plate gap, so the standoffs are what sets it. Different standoffs mean a
different frame; see the levers below.
- M2 screws for those standoffs, and for the flight controller.
- The plates carry both a 20×20 and a 25.5×25.5 mounting pattern, drilled 2.2 mm
for M2.
### It is built around
- 3" props — **78 mm**, and neighbouring discs just touch on this true-X layout.
The renders draw them as rings so you can see that.
- A **Foxeer Predator 5 Nano** camera, which the camera mount is cut for.
- A **micro lollipop** antenna, held at 45° by `lollipop.py`, which clips over
the rear standoff pair.
---
## Making the parts
**Carbon** — the four DXFs cut from 2 mm plate. Every internal corner at a
joint already carries a T-bone relief, so a round cutter can actually make the
corner; don't "clean up" those little circles, the mating part needs that room.
**TPU** — `camera_mount.stl` and `lollipop.stl`. The STLs are exported at 0.01 mm
chordal deviation, far finer than an FDM printer resolves. Rerun the export at a
tighter tolerance if you are going to resin.
**Assembly** — dry fit before anything else. Horizontal parts (plates, motor
bases) carry the mortises, vertical parts (arms, spars) carry the tenons, and
every tenon should land *flush* with the far face it passes through. Proud jams
the joint; short leaves it unseated. The four spars half-lap their neighbours at
each corner and are handed — two notches up, two down, alternating round the
ring — so check the renders before you glue.
---
## Changing it
Shared dimensions live in `frame_params.py`, and every part imports them. The
usual things to reach for:
| You want | Change |
|---|---|
| a different prop size | the motor-to-motor gap |
| different carbon stock | the shared thickness |
| a shallower arm | the plate gap — costs stack room |
| looser or tighter joints | the shared fit allowance |
| a taller stack | the plate gap, and buy standoffs to match |
Rerun `./build.sh` and the cut files follow. Read `AGENT.md` first if you are
changing geometry rather than numbers — it records the invariants and the
CadQuery traps that have already cost time here.
---
## Working on a single part
Each part module builds and checks itself, so you can run one on its own
without the assembly:
```
uv run python arm.py # asserts it is one solid, then reports mass
uv run python frame.py # assembly, BOM, exports, renders
```
They are also CQ-editor scripts: open any of them and it shows the part.
`frame.py` shows the whole assembly.
---
## Licence
[Apache License 2.0](LICENSE). Use it, change it, build frames from it, sell
those frames. The conditions are light: keep the copyright notice, note in any
file you modify that you changed it, and pass on a copy of the licence.
Two things worth knowing about the fit between this licence and a physical part:
- **It covers the files, not the shape.** The Python, the comments and the
exported drawings are copyrighted work and the licence governs them.
The outline of a cut piece of carbon generally is not — functional shapes
aren't protected by copyright. Someone who buys a frame, measures it and
redraws it in their own CAD is outside this licence entirely. That is normal
for open hardware, and not something a different licence would fix.
- **It includes a patent grant.** Apache-2.0 §3 gives every user a patent
licence covering the contributions in here, and revokes it from anyone who
sues over the design. This is the main thing it adds over MIT.
No warranty of any kind — see §7. Worth taking literally on a frame: nothing
here has been flown, and the design is deliberately built close to its material
limits. Check the parts before you trust them at 100 km/h.