Introducing the pirouette FPV frame for GFPV
Signed-off-by: Arnaud Morin <arnaud.gfpv@mailops.fr>
This commit is contained in:
172
README.md
Normal file
172
README.md
Normal 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.
|
||||
Reference in New Issue
Block a user