Introducing the pirouette FPV frame for GFPV
Signed-off-by: Arnaud Morin <arnaud.gfpv@mailops.fr>
This commit is contained in:
128
spar.py
Normal file
128
spar.py
Normal file
@@ -0,0 +1,128 @@
|
||||
# @author Arnaud Morin <arnaud.gfpv@mailops.fr>
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
import cadquery as cq
|
||||
|
||||
from frame_params import (CF_DENSITY, FIT, TENON_L, THICKNESS, TBONE_D)
|
||||
|
||||
# frame-wide facts -- hardcoded here to match frame_params' own values;
|
||||
# not linked by import any more, so keep them in step by hand
|
||||
MOTOR_GAP = 78.0 # motor-to-motor gap on a true X, same as the prop
|
||||
SPAR_H = 4.0 # the spar's own clear span
|
||||
SPAR_OFFSET = 2.5
|
||||
SPAR_TENON_POS = 9
|
||||
|
||||
LAP_W = THICKNESS + FIT
|
||||
# How much len we add to add a crossing between lap
|
||||
# I choose to add twice the thickness
|
||||
LAP_OFFSET = 2.0 * THICKNESS
|
||||
|
||||
# Spar len is motor gap + extra so two spar can cross and fit together
|
||||
# Extras are:
|
||||
# SPAR_OFFSET which is used to position the mortese in motor base
|
||||
# LAP_OFFSET to cross the two spar together
|
||||
SPAR_LEN = MOTOR_GAP + (SPAR_OFFSET + LAP_OFFSET) * 2
|
||||
# Motor axis
|
||||
MOTOR_CENTER = (SPAR_LEN - MOTOR_GAP) / 2.0
|
||||
# Tenon position
|
||||
TENON_CENTER = MOTOR_CENTER + SPAR_TENON_POS
|
||||
TENON_START = TENON_CENTER - TENON_L / 2.0
|
||||
TENON_END = TENON_CENTER + TENON_L / 2.0
|
||||
|
||||
|
||||
def spar():
|
||||
part = (
|
||||
cq.Workplane("XZ")
|
||||
.polyline([
|
||||
(0.0, 0.0),
|
||||
(0.0, SPAR_H/2.0),
|
||||
(TENON_START, SPAR_H/2),
|
||||
(TENON_START, SPAR_H/2 + THICKNESS),
|
||||
(TENON_END, SPAR_H/2 + THICKNESS),
|
||||
(TENON_END, SPAR_H/2),
|
||||
(SPAR_LEN - TENON_END, SPAR_H/2),
|
||||
(SPAR_LEN - TENON_END, SPAR_H/2 + THICKNESS),
|
||||
(SPAR_LEN - TENON_START, SPAR_H/2 + THICKNESS),
|
||||
(SPAR_LEN - TENON_START, SPAR_H/2),
|
||||
(SPAR_LEN, SPAR_H/2),
|
||||
(SPAR_LEN, 0.0),
|
||||
])
|
||||
.mirrorX()
|
||||
.extrude(THICKNESS)
|
||||
)
|
||||
|
||||
# Cut the overlaps: half-lap notches, centred LAP_OFFSET on each end
|
||||
cut = (
|
||||
cq.Workplane("XZ")
|
||||
.polyline([
|
||||
(LAP_OFFSET - LAP_W / 2.0, 0.0),
|
||||
(LAP_OFFSET + LAP_W / 2.0, 0.0),
|
||||
(LAP_OFFSET + LAP_W / 2.0, SPAR_H / 2.0),
|
||||
(LAP_OFFSET - LAP_W / 2.0, SPAR_H / 2.0),
|
||||
])
|
||||
.close()
|
||||
.extrude(THICKNESS)
|
||||
)
|
||||
part = part.cut(cut)
|
||||
|
||||
# Second cut
|
||||
cut = (
|
||||
cq.Workplane("XZ")
|
||||
.polyline([
|
||||
(SPAR_LEN - LAP_OFFSET + LAP_W / 2.0, 0.0),
|
||||
(SPAR_LEN - LAP_OFFSET - LAP_W / 2.0, 0.0),
|
||||
(SPAR_LEN - LAP_OFFSET - LAP_W / 2.0, SPAR_H / 2.0),
|
||||
(SPAR_LEN - LAP_OFFSET + LAP_W / 2.0, SPAR_H / 2.0),
|
||||
])
|
||||
.close()
|
||||
.extrude(THICKNESS)
|
||||
)
|
||||
part = part.cut(cut)
|
||||
|
||||
# T-Bones on tenons (x, z)
|
||||
tbones = [
|
||||
# First tenon (upper left)
|
||||
(TENON_START - TBONE_D/2 + 0.05, SPAR_H/2),
|
||||
(TENON_END + TBONE_D/2 - 0.05, SPAR_H/2),
|
||||
# lower left
|
||||
(TENON_START - TBONE_D/2 + 0.05, -SPAR_H/2),
|
||||
(TENON_END + TBONE_D/2 - 0.05, -SPAR_H/2),
|
||||
# upper right
|
||||
(SPAR_LEN - TENON_START + TBONE_D/2 - 0.05, SPAR_H/2),
|
||||
(SPAR_LEN - TENON_END - TBONE_D/2 + 0.05, SPAR_H/2),
|
||||
# lower right
|
||||
(SPAR_LEN - TENON_START + TBONE_D/2 - 0.05, - SPAR_H/2),
|
||||
(SPAR_LEN - TENON_END - TBONE_D/2 + 0.05, - SPAR_H/2),
|
||||
]
|
||||
|
||||
# Cut the T-Bones
|
||||
cut = cq.Workplane("XZ")
|
||||
for x, z in tbones:
|
||||
cut = cut.moveTo(x, z).circle(TBONE_D/2.0)
|
||||
part = part.cut(cut.extrude(THICKNESS))
|
||||
|
||||
# Center the part
|
||||
part = part.translate((-SPAR_LEN / 2.0, THICKNESS / 2.0, 0.0))
|
||||
|
||||
return part
|
||||
|
||||
|
||||
result = spar()
|
||||
|
||||
assert result.solids().size() == 1, "spar is not one solid"
|
||||
|
||||
# Mass report
|
||||
def report():
|
||||
vol = result.val().Volume()
|
||||
print(f" Mass = {vol * CF_DENSITY:5.2f} g")
|
||||
print(f" Mass x4 = {4 * vol * CF_DENSITY:5.2f} g")
|
||||
|
||||
# Running outside CQ-editor
|
||||
if "show_object" not in globals():
|
||||
def show_object(*args, **kwargs):
|
||||
pass
|
||||
|
||||
show_object(result)
|
||||
|
||||
if __name__ == "__main__":
|
||||
report()
|
||||
Reference in New Issue
Block a user