Introducing the pirouette FPV frame for GFPV
Signed-off-by: Arnaud Morin <arnaud.gfpv@mailops.fr>
This commit is contained in:
153
arm.py
Normal file
153
arm.py
Normal file
@@ -0,0 +1,153 @@
|
||||
# @author Arnaud Morin <arnaud.gfpv@mailops.fr>
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
|
||||
import cadquery as cq
|
||||
|
||||
from frame_params import (CF_DENSITY, PLATE_GAP, SPAR_H, TENON_L,
|
||||
THICKNESS, TBONE_D)
|
||||
|
||||
# The arm spans the plate gap, so its height IS PLATE_GAP -- the body
|
||||
# reaches the plates' inner faces at +-HALF_H and every tenon stands one
|
||||
# THICKNESS proud of that, landing flush with the far face.
|
||||
HALF_H = PLATE_GAP / 2.0
|
||||
# and the lower motor base's top face, which hangs SPAR_H below the upper
|
||||
# one, i.e. below the top plate
|
||||
BASE_Z = HALF_H - SPAR_H
|
||||
|
||||
|
||||
def _custom_filets(wp):
|
||||
"""Every vertical edge of the flat pattern (parallel to Y) -- one per
|
||||
corner, found automatically instead of being listed by hand."""
|
||||
picked = []
|
||||
for e in wp.edges().vals():
|
||||
vs = e.Vertices()
|
||||
if len(vs) != 2:
|
||||
continue
|
||||
a, b = vs[0].toTuple(), vs[1].toTuple()
|
||||
if abs(a[0] - b[0]) > 1e-6 or abs(a[2] - b[2]) > 1e-6:
|
||||
continue
|
||||
picked.append(e)
|
||||
return wp.newObject(picked)
|
||||
|
||||
|
||||
def arm():
|
||||
part = cq.Workplane("XZ").polyline([
|
||||
(0.0, HALF_H),
|
||||
# first tenon, the one that fit in top plate
|
||||
(3.8, HALF_H),
|
||||
(3.8, HALF_H + THICKNESS),
|
||||
(3.8 + TENON_L, HALF_H + THICKNESS),
|
||||
(3.8 + TENON_L, HALF_H),
|
||||
# End of top plate
|
||||
(12, HALF_H),
|
||||
(12, HALF_H + THICKNESS),
|
||||
# Arm to motor base
|
||||
(20.0, HALF_H + THICKNESS),
|
||||
# Begining of motor base
|
||||
(20.0, HALF_H),
|
||||
(25.0, HALF_H),
|
||||
# Tenon into motor base
|
||||
(25, HALF_H + THICKNESS),
|
||||
(25 + TENON_L, HALF_H + THICKNESS),
|
||||
# Under the motor base
|
||||
(25 + TENON_L, HALF_H),
|
||||
(32, HALF_H),
|
||||
# Come back to bottom plate
|
||||
(32, BASE_Z),
|
||||
# Tenon under the motor base, symmetrical to the one above
|
||||
(25 + TENON_L, BASE_Z),
|
||||
(25 + TENON_L, BASE_Z - THICKNESS),
|
||||
(25, BASE_Z - THICKNESS),
|
||||
(25, BASE_Z),
|
||||
(20, BASE_Z),
|
||||
# Diag
|
||||
(13, -(HALF_H + THICKNESS)),
|
||||
(12, -(HALF_H + THICKNESS)),
|
||||
(12, -HALF_H),
|
||||
# Tenon in bottom plate
|
||||
(3.8 + TENON_L, -HALF_H),
|
||||
(3.8 + TENON_L, -(HALF_H + THICKNESS)),
|
||||
(3.8, -(HALF_H + THICKNESS)),
|
||||
(3.8, -HALF_H),
|
||||
(1.5, -HALF_H)
|
||||
])
|
||||
part = (
|
||||
part.lineTo(2, -7)
|
||||
.threePointArc(
|
||||
(5, 0.0),
|
||||
(0.0, 6)
|
||||
)
|
||||
)
|
||||
# Extrude. Both = True do the extrusion along the Y axis each face,
|
||||
# so no need to translate later :)
|
||||
part = part.close().extrude(THICKNESS / 2.0, both=True)
|
||||
|
||||
# Hole
|
||||
cut = (
|
||||
cq.Workplane('XZ')
|
||||
.polyline([
|
||||
(7, 4),
|
||||
(17, 4),
|
||||
(12.5, -5),
|
||||
(6.5, -5),
|
||||
])
|
||||
.threePointArc(
|
||||
(7.5, 0.0),
|
||||
(6, 4),
|
||||
)
|
||||
.close()
|
||||
.extrude(THICKNESS / 2.0, both=True)
|
||||
)
|
||||
|
||||
# smooth every sharp corner, no bare angles left
|
||||
cut = _custom_filets(cut).fillet(1.0)
|
||||
part = part.cut(cut)
|
||||
|
||||
# T-Bones on tenons (x, z)
|
||||
tbones = [
|
||||
# root tenon into top plate, shifted 1 mm right
|
||||
(3.8 - TBONE_D / 2 + 0.05, HALF_H),
|
||||
(3.8 + TENON_L + TBONE_D / 2 - 0.05, HALF_H),
|
||||
# tenon into motor base
|
||||
(25 - TBONE_D / 2 + 0.05, HALF_H),
|
||||
(25 + TENON_L + TBONE_D / 2 - 0.05, HALF_H),
|
||||
# tenon under the motor base
|
||||
(25 - TBONE_D / 2 + 0.05, BASE_Z),
|
||||
(25 + TENON_L + TBONE_D / 2 - 0.05, BASE_Z),
|
||||
# root tenon into bottom plate, shifted 1 mm right
|
||||
(3.8 - TBONE_D / 2 + 0.05, -HALF_H),
|
||||
(3.8 + TENON_L + TBONE_D / 2 - 0.05, -HALF_H),
|
||||
# 3 more sharp inside corners that need relief, away from any tenon:
|
||||
# where the flat top steps up into the raised motor-base flange...
|
||||
(12 - TBONE_D / 2 + 0.05, HALF_H),
|
||||
# ... its mirror on the bottom edge ...
|
||||
(12 - TBONE_D / 2 + 0.05, -HALF_H),
|
||||
# ... and the sharp corner where the diagonal brace meets the tip block
|
||||
(20 + TBONE_D / 2 - 0.05, HALF_H),
|
||||
]
|
||||
|
||||
# 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 / 2.0, both=True))
|
||||
|
||||
return part
|
||||
|
||||
|
||||
result = arm()
|
||||
|
||||
assert result.solids().size() == 1, "arm is not one solid"
|
||||
|
||||
if "show_object" not in globals(): # running outside CQ-editor
|
||||
def show_object(*args, **kwargs):
|
||||
pass
|
||||
|
||||
show_object(result)
|
||||
|
||||
if __name__ == "__main__":
|
||||
vol = result.val().Volume()
|
||||
m = vol * CF_DENSITY
|
||||
print(f"arm mass {m:.2f} g each, {4*m:.2f} g for four")
|
||||
|
||||
Reference in New Issue
Block a user