Files
pirouette/motor_base.py
Arnaud Morin e70d2abed2 Increase motor_base hole clearance to 5mm
Signed-off-by: Arnaud Morin <arnaud.gfpv@mailops.fr>
2026-09-14 22:05:05 +02:00

125 lines
4.5 KiB
Python

# @author Arnaud Morin <arnaud.gfpv@mailops.fr>
# SPDX-License-Identifier: Apache-2.0
#
from math import sqrt
import cadquery as cq
from frame_params import (CF_DENSITY, FIT, TBONE_D, TENON_L, THICKNESS)
ARM_MORTISE_R = 8.6543 # arm mortise centre, inboard of the
# motor axis. Not a free number: the
# arm's tip tenon sits at radius 46.5
# (its root radius 19.5, plus arm.py's
# own 25, plus half a tenon), and the
# motor axis is at 55.1543, so anything
# else pushes the tenon off centre in
# its mortise. At 8.8 it overhung the
# outer end by 0.046 and fouled the base.
# spar mortise centre, in the base's own local frame: on the spar
# centreline, SPAR_OFFSET outboard of the motor axis and SPAR_TENON_POS back
# along the spar -- both shared with the spar's own tenon, so the mortise
# and the tenon it keys into can't drift apart. Not linked by import to
# frame_params or spar.py any more -- keep this pair in step by hand.
SPAR_OFFSET = 2.5
SPAR_TENON_POS = 9
SPAR_MORTISE_X = (SPAR_OFFSET + SPAR_TENON_POS) / sqrt(2.0)
SPAR_MORTISE_Y = (SPAR_OFFSET - SPAR_TENON_POS) / sqrt(2.0)
SPAR_MORTISE_ANGLE = 45.0 # +-45 deg to the arm axis
MORTISE_L = TENON_L + FIT # 4.2
MORTISE_W = THICKNESS
BORE_D = 5.0 # 1404 bell / boss clearance
BOLT_SQ = 9.0 # 1404/1105 M2 pattern -- the DIAGONAL between
# opposite bolt holes, not the square's side
BOLT_D = 2.2 # M2 clearance
BOLT_R = BOLT_SQ / (2.0 * sqrt(2.0))
def bolt_points():
return [(sx * BOLT_R, sy * BOLT_R) for sx in (-1, 1) for sy in (-1, 1)]
def mortises():
"""(x, y, angle) of the three mortises, in the motor base's own frame
(origin = motor axis, +Y outboard, arm mortise at -Y). All three are
MORTISE_L x MORTISE_W -- only position and angle differ."""
return [
(0.0, -ARM_MORTISE_R, 90.0),
(-SPAR_MORTISE_X, SPAR_MORTISE_Y, 90.0 - SPAR_MORTISE_ANGLE),
(+SPAR_MORTISE_X, SPAR_MORTISE_Y, 90.0 + SPAR_MORTISE_ANGLE),
]
def motor_base():
# the open half-profile has to start and end ON the mirror axis (x = 0)
# for mirrorY() to stitch it into one closed loop -- otherwise the two
# halves just don't meet
part = (
cq.Workplane("XY")
.polyline([
(0.0, -15.5),
(2.0, -15.5),
(4.0, -10.0),
(8.5, -9.5),
(13.0, -4.5),
(4.0, 7.0),
(0.0, 7.0),
])
.mirrorY()
.extrude(THICKNESS)
)
#return part
part = part.edges("|Z").fillet(2.8)
# motor bell / boss clearance, then the M2 bolt pattern
part = (part.faces(">Z").workplane(centerOption="ProjectedOrigin")
.hole(BORE_D))
part = (part.faces(">Z").workplane(centerOption="ProjectedOrigin")
.pushPoints(bolt_points()).hole(BOLT_D))
# the three mortises: arm inboard at -Y, spars at +-45 deg, T-bone
# relieved on the long edges. One canonical cutter, all the same size,
# rotated and translated into each of the 3 spots.
hl, hw = MORTISE_L / 2.0, MORTISE_W / 2.0
r = TBONE_D / 2.0
body = (cq.Workplane("XY")
.polyline([(-hl, -hw), (hl, -hw), (hl, hw), (-hl, hw)])
.close().extrude(THICKNESS))
bones = cq.Workplane("XY")
for u in (-(hl - r), hl - r):
for v in (-hw, hw):
bones = bones.moveTo(u, v).circle(r)
bones = bones.extrude(THICKNESS)
for x, y, angle in mortises():
# two separate cuts, not one combined cutter -- a compound of the
# rectangle plus 4 tangent circles confuses the boolean into leaving
# sliver solids behind
part = part.cut(body.rotate((0, 0, 0), (0, 0, 1), angle).translate((x, y, 0)))
part = part.cut(bones.rotate((0, 0, 0), (0, 0, 1), angle).translate((x, y, 0)))
return part
result = motor_base()
assert result.solids().size() == 1, "motor base is not one solid"
if "show_object" not in globals(): # running outside CQ-editor
def show_object(*args, **kwargs):
pass
show_object(result)
# 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")
if __name__ == "__main__":
report()