Motor assembly
This commit is contained in:
parent
6709e4f32e
commit
0f151bd279
|
@ -34,7 +34,7 @@ class Role(Flag):
|
|||
STRUCTURE = auto()
|
||||
DECORATION = auto()
|
||||
ELECTRONIC = auto()
|
||||
MOTION = auto()
|
||||
MOTOR = auto()
|
||||
|
||||
# Fasteners, etc.
|
||||
CONNECTION = auto()
|
||||
|
@ -69,7 +69,7 @@ ROLE_COLOR_MAP = {
|
|||
Role.STRUCTURE: _color('gray', 0.4),
|
||||
Role.DECORATION: _color('lightseagreen', 0.4),
|
||||
Role.ELECTRONIC: _color('mediumorchid', 0.7),
|
||||
Role.MOTION: _color('thistle3', 0.7),
|
||||
Role.MOTOR: _color('thistle3', 0.7),
|
||||
Role.CONNECTION: _color('steelblue3', 0.8),
|
||||
Role.HANDLE: _color('tomato4', 0.8),
|
||||
}
|
||||
|
|
|
@ -35,7 +35,7 @@ class FlatHeadBolt(Item):
|
|||
centered=(True, True, False))
|
||||
)
|
||||
rod.faces("<Z").tag("tip")
|
||||
rod.faces(">Z").tag("root")
|
||||
rod.tagAbsolute("root", (0, 0, self.height_thread), direction="-Z")
|
||||
rod = rod.union(head.located(Cq.Location((0, 0, self.height_thread))))
|
||||
return rod
|
||||
|
||||
|
|
|
@ -31,6 +31,15 @@ BOLT_COMMON = FlatHeadBolt(
|
|||
height_thread=30.0,
|
||||
pitch=1.0,
|
||||
)
|
||||
BOLT_LONG = FlatHeadBolt(
|
||||
# FIXME: measure
|
||||
mass=0.0,
|
||||
diam_head=12.8,
|
||||
height_head=2.8,
|
||||
diam_thread=6.0,
|
||||
height_thread=50.0,
|
||||
pitch=1.0,
|
||||
)
|
||||
BOLT_BEARING = FlatHeadBolt(
|
||||
# FIXME: measure
|
||||
mass=0.0,
|
||||
|
@ -41,6 +50,59 @@ BOLT_BEARING = FlatHeadBolt(
|
|||
pitch=1.0,
|
||||
)
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class FlangeCoupler(Model):
|
||||
|
||||
diam_thread: float = 8.0
|
||||
diam_inner: float = 10.0
|
||||
diam_outer: float = 22.0
|
||||
height: float = 12.0
|
||||
height_flange: float = 2.0
|
||||
|
||||
diam_thread_flange: float = 4.0
|
||||
n_hole_flange: int = 4
|
||||
r_hole_flange: float = 8.0 # FIXME: Measure!
|
||||
|
||||
def generate(self) -> Cq.Workplane:
|
||||
result = (
|
||||
Cq.Workplane()
|
||||
.cylinder(
|
||||
radius=self.diam_outer/2,
|
||||
height=self.height_flange,
|
||||
centered=(True, True, False),
|
||||
)
|
||||
.faces(">Z")
|
||||
.cylinder(
|
||||
radius=self.diam_inner/2,
|
||||
height=self.height - self.height_flange,
|
||||
centered=(True, True, False),
|
||||
)
|
||||
.faces(">Z")
|
||||
.hole(self.diam_thread)
|
||||
)
|
||||
holes = (
|
||||
Cq.Workplane()
|
||||
.sketch()
|
||||
.regularPolygon(
|
||||
self.r_hole_flange,
|
||||
self.n_hole_flange,
|
||||
mode="c",
|
||||
tag="holes",
|
||||
)
|
||||
.vertices(tag="holes")
|
||||
.circle(self.diam_thread_flange/2)
|
||||
.finalize()
|
||||
.extrude(self.height_flange)
|
||||
)
|
||||
result -= holes
|
||||
result.tagAbsolute("top", (0, 0, self.height), direction="+Z")
|
||||
result.tagAbsolute("bot", (0, 0, 9), direction="-Z")
|
||||
for i in range(self.n_hole_flange):
|
||||
loc = Cq.Location.rot2d(i * 360 / self.n_hole_flange) * Cq.Location(self.r_hole_flange, 0)
|
||||
result.tagAbsolute(f"holeT{i}", loc * Cq.Location(0, 0, self.height_flange), direction="+Z")
|
||||
result.tagAbsolute(f"holeB{i}", loc, direction="-Z")
|
||||
return result
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class Motor(Model):
|
||||
|
@ -53,13 +115,15 @@ class Motor(Model):
|
|||
power: float = 30.0 # watts
|
||||
|
||||
diam_thread: float = 4.0
|
||||
diam_shaft: float = 8.0
|
||||
diam_body: float = 51.0
|
||||
height_body: float = 83.5
|
||||
diam_ring: float = 25.93
|
||||
height_ring: float = 6.55
|
||||
height_shaft: float = 38.1
|
||||
height_base_shaft: float = 20.0 # FIXME: Measure
|
||||
# Distance between anchor and the body
|
||||
dx_anchor: float = 20.2
|
||||
dx_anchor: float = 20.2 # FIXME: Measure
|
||||
height_anchor: float = 10.4
|
||||
|
||||
def __post_init__(self):
|
||||
|
@ -68,6 +132,13 @@ class Motor(Model):
|
|||
assert self.dx_anchor < self.diam_body / 2
|
||||
pass
|
||||
|
||||
@property
|
||||
def dist_mount_rotor(self):
|
||||
"""
|
||||
Distance between mount point and shaft
|
||||
"""
|
||||
return self.height_base_shaft + self.height_ring
|
||||
|
||||
def generate(self) -> Cq.Workplane:
|
||||
result = (
|
||||
Cq.Workplane()
|
||||
|
@ -83,17 +154,26 @@ class Motor(Model):
|
|||
centered=(True, True, False)
|
||||
)
|
||||
)
|
||||
base_shaft = Cq.Solid.makeBox(
|
||||
length=self.diam_shaft,
|
||||
width=self.diam_shaft,
|
||||
height=self.height_base_shaft,
|
||||
).moved(-self.diam_shaft/2, -self.diam_shaft/2, self.height_body)
|
||||
shaft = Cq.Solid.makeCylinder(
|
||||
radius=self.diam_thread/2,
|
||||
radius=self.diam_shaft/2,
|
||||
height=self.height_shaft,
|
||||
pnt=(0, 0, self.height_body)
|
||||
pnt=(0, 0, self.height_body + self.height_base_shaft)
|
||||
)
|
||||
z_anchor = self.height_body - self.height_ring
|
||||
anchor = Cq.Solid.makeCylinder(
|
||||
radius=self.diam_thread/2,
|
||||
height=self.height_anchor,
|
||||
pnt=(0, 0, self.height_body - self.height_ring)
|
||||
pnt=(0, 0, z_anchor)
|
||||
)
|
||||
result = result + shaft + anchor.moved(self.dx_anchor, 0, 0) + anchor.moved(-self.dx_anchor, 0, 0)
|
||||
result = result + base_shaft + shaft + anchor.moved(self.dx_anchor, 0, 0) + anchor.moved(-self.dx_anchor, 0, 0)
|
||||
result.tagAbsolute("anchor1", (self.dx_anchor, 0, z_anchor), direction="+Z")
|
||||
result.tagAbsolute("anchor2", (-self.dx_anchor, 0, z_anchor), direction="+Z")
|
||||
result.tagAbsolute("shaft", (0, 0, self.height_body + self.height_base_shaft), direction="+Z")
|
||||
return result
|
||||
|
||||
|
||||
|
@ -172,10 +252,11 @@ class Onbashira(Model):
|
|||
# Extra bind sites for stator to prevent warping
|
||||
stator_bind_extra: int = 2
|
||||
rotor_inner_radius: float = 36.0
|
||||
rotor_bind_bolt_diam: float = BOLT_COMMON.diam_thread
|
||||
rotor_bind_bolt_diam: float = BOLT_BEARING.diam_thread
|
||||
rotor_bind_radius: float = 82.0
|
||||
rotor_bind_extra: int = 1
|
||||
rotor_spacer_outer_diam: float = 15.0
|
||||
stator_spacer_outer_diam: float = 15.0
|
||||
rotor_spacer_outer_diam: float = 12.0
|
||||
|
||||
handle_base_height: float = 10.0
|
||||
handle_thickness: float = 17.0
|
||||
|
@ -183,6 +264,8 @@ class Onbashira(Model):
|
|||
handle_height: float = 50.0
|
||||
|
||||
motor: Motor = Motor()
|
||||
flange_coupler: FlangeCoupler = FlangeCoupler()
|
||||
auxiliary_thickness: float = 25.4 / 8
|
||||
|
||||
material_side: Material = Material.WOOD_BIRCH
|
||||
material_bearing: Material = Material.PLASTIC_PLA
|
||||
|
@ -191,6 +274,7 @@ class Onbashira(Model):
|
|||
material_barrel: Material = Material.ACRYLIC_BLACK
|
||||
material_brace: Material = Material.PLASTIC_PLA
|
||||
material_fastener: Material = Material.STEEL_STAINLESS
|
||||
material_auxiliary: Material = Material.WOOD_BIRCH
|
||||
|
||||
def __post_init__(self):
|
||||
assert self.n_side >= 3
|
||||
|
@ -235,7 +319,15 @@ class Onbashira(Model):
|
|||
theta = math.pi / self.n_side
|
||||
dt = self.angle_joint_thickness * math.tan(theta)
|
||||
return dt * 2
|
||||
|
||||
@property
|
||||
def angle_joint_bind_pos(self) -> Cq.Location:
|
||||
"""
|
||||
Planar position of the joint bind position
|
||||
"""
|
||||
th = math.pi / self.n_side
|
||||
x = self.angle_joint_bind_radius * math.cos(th)
|
||||
y = self.angle_joint_bind_radius * math.sin(th)
|
||||
return Cq.Location.from2d(x, y)
|
||||
|
||||
@property
|
||||
def angle_dihedral(self) -> float:
|
||||
|
@ -270,6 +362,153 @@ class Onbashira(Model):
|
|||
.extrude(self.side_width * 1.5)
|
||||
)
|
||||
|
||||
@target(name="motor-driver-shaft")
|
||||
def motor_driver_shaft(self) -> Cq.Workplane:
|
||||
"""
|
||||
Driver shaft which connects to each barrel to move them.
|
||||
|
||||
The driver shaft reaches
|
||||
"""
|
||||
return (
|
||||
Cq.Workplane()
|
||||
.cylinder(
|
||||
radius=self.barrel_diam/2,
|
||||
height=20.0
|
||||
)
|
||||
)
|
||||
|
||||
@target(name="motor-driver-disk", kind=TargetKind.DXF)
|
||||
def profile_motor_driver_disk(self) -> Cq.Sketch:
|
||||
"""
|
||||
A drive disk mounts onto the motor, and extends into gun barrels to turn them.
|
||||
"""
|
||||
return (
|
||||
Cq.Sketch()
|
||||
.circle(self.rotor_radius)
|
||||
# Drill out the centre which will accomodate the motor shaft
|
||||
.circle(self.motor.diam_shaft/2, mode="s")
|
||||
# Drill out couplers
|
||||
.reset()
|
||||
.regularPolygon(
|
||||
self.flange_coupler.r_hole_flange,
|
||||
self.flange_coupler.n_hole_flange,
|
||||
mode="c",
|
||||
tag="hole",
|
||||
)
|
||||
.vertices(tag="hole")
|
||||
.circle(self.flange_coupler.diam_thread_flange/2, mode="s")
|
||||
.reset()
|
||||
.regularPolygon(
|
||||
self.rotation_radius,
|
||||
self.n_side,
|
||||
mode="c",
|
||||
tag="const",
|
||||
)
|
||||
.vertices(tag="const")
|
||||
.circle(BOLT_COMMON.diam_thread/2, mode="s")
|
||||
)
|
||||
def motor_driver_disk(self) -> Cq.Workplane:
|
||||
result = (
|
||||
Cq.Workplane()
|
||||
.placeSketch(self.profile_motor_driver_disk())
|
||||
.extrude(self.auxiliary_thickness)
|
||||
)
|
||||
n = self.flange_coupler.n_hole_flange
|
||||
for i in range(n):
|
||||
loc = Cq.Location.rot2d(i * 360 / n) * Cq.Location(self.flange_coupler.r_hole_flange, 0)
|
||||
result.tagAbsolute(f"holeT{i}", loc * Cq.Location(0, 0, self.auxiliary_thickness), direction="+Z")
|
||||
result.tagAbsolute(f"holeB{i}", loc, direction="-Z")
|
||||
return result
|
||||
|
||||
@target(name="motor-mount-plate", kind=TargetKind.DXF)
|
||||
def profile_motor_mount_plate(self) -> Cq.Sketch:
|
||||
assert self.n_side == 6
|
||||
bx, by = self.angle_joint_bind_pos.to2d_pos()
|
||||
gap = 10.0
|
||||
hole_dx = self.motor.dx_anchor
|
||||
return (
|
||||
Cq.Sketch()
|
||||
.rect((bx + gap) * 2, (by + gap) * 2)
|
||||
.reset()
|
||||
.rect(bx * 2, by * 2, mode="c", tag="corner")
|
||||
.vertices(tag="corner")
|
||||
.circle(BOLT_COMMON.diam_thread/2, mode="s")
|
||||
.reset()
|
||||
.push([
|
||||
(hole_dx, 0),
|
||||
(-hole_dx, 0),
|
||||
])
|
||||
.circle(self.motor.diam_thread/2, mode="s")
|
||||
)
|
||||
def motor_mount_plate(self) -> Cq.Workplane:
|
||||
result = (
|
||||
Cq.Workplane()
|
||||
.placeSketch(self.profile_motor_mount_plate())
|
||||
.extrude(self.auxiliary_thickness)
|
||||
)
|
||||
result.tagAbsolute("anchor1", (self.motor.dx_anchor, 0, 0), direction="-Z")
|
||||
result.tagAbsolute("anchor2", (-self.motor.dx_anchor, 0, 0), direction="-Z")
|
||||
bp = self.angle_joint_bind_pos
|
||||
for i in range(self.n_side):
|
||||
if i in {1, 4}:
|
||||
continue
|
||||
angle = i * 360 / self.n_side
|
||||
x, y = (Cq.Location.rot2d(angle) * bp).to2d_pos()
|
||||
result.tagAbsolute(f"holeF{i}", (x, y, self.auxiliary_thickness), direction="+Z")
|
||||
result.tagAbsolute(f"holeB{i}", (x, -y, 0), direction="-Z")
|
||||
return result
|
||||
|
||||
@assembly()
|
||||
def assembly_motor(self) -> Cq.Assembly:
|
||||
a = (
|
||||
Cq.Assembly()
|
||||
.addS(
|
||||
self.motor.generate(),
|
||||
name="motor",
|
||||
role=Role.MOTOR,
|
||||
)
|
||||
.addS(
|
||||
self.flange_coupler.generate(),
|
||||
name="flange_coupler",
|
||||
role=Role.CONNECTION | Role.STRUCTURE,
|
||||
material=self.material_fastener,
|
||||
)
|
||||
.addS(
|
||||
self.motor_driver_disk(),
|
||||
name="driver_disk",
|
||||
role=Role.CONNECTION | Role.STRUCTURE,
|
||||
material=self.material_auxiliary,
|
||||
)
|
||||
.addS(
|
||||
self.motor_mount_plate(),
|
||||
name="mount_plate",
|
||||
role=Role.CONNECTION | Role.STRUCTURE,
|
||||
material=self.material_auxiliary,
|
||||
)
|
||||
.constrain(
|
||||
"mount_plate?anchor1",
|
||||
"motor?anchor1",
|
||||
"Plane",
|
||||
)
|
||||
.constrain(
|
||||
"mount_plate?anchor2",
|
||||
"motor?anchor2",
|
||||
"Plane",
|
||||
)
|
||||
.constrain(
|
||||
"flange_coupler?top",
|
||||
"motor?shaft",
|
||||
"Plane"
|
||||
)
|
||||
)
|
||||
for i in range(self.flange_coupler.n_hole_flange):
|
||||
a = a.constrain(
|
||||
f"flange_coupler?holeB{i}",
|
||||
f"driver_disk?holeB{i}",
|
||||
"Plane",
|
||||
)
|
||||
return a.solve()
|
||||
|
||||
@target(name="stator-coupler")
|
||||
def stator_coupler(self) -> Cq.Workplane:
|
||||
"""
|
||||
|
@ -358,13 +597,16 @@ class Onbashira(Model):
|
|||
br * math.sin(-angle2),
|
||||
).tagPlane(f"holeE{i}", direction="-Z")
|
||||
return result
|
||||
@property
|
||||
def rotor_radius(self) -> float:
|
||||
return self.bearing_track_radius - self.bearing_gap/2
|
||||
@target(name="bearing-rotor", kind=TargetKind.DXF)
|
||||
def profile_bearing_rotor(self) -> Cq.Sketch:
|
||||
bolt_angle = (180 / self.n_side) * 1.5
|
||||
n_binds = 1 + self.rotor_bind_extra
|
||||
return (
|
||||
Cq.Sketch()
|
||||
.circle(self.bearing_track_radius - self.bearing_gap/2)
|
||||
.circle(self.rotor_radius)
|
||||
.circle(self.rotor_inner_radius, mode="s")
|
||||
.reset()
|
||||
.regularPolygon(
|
||||
|
@ -420,7 +662,7 @@ class Onbashira(Model):
|
|||
@target(name="stator-spacer")
|
||||
def stator_spacer(self) -> Cq.Solid:
|
||||
outer = Cq.Solid.makeCylinder(
|
||||
radius=self.rotor_spacer_outer_diam/2,
|
||||
radius=self.stator_spacer_outer_diam/2,
|
||||
height=self.bearing_disk_gap,
|
||||
)
|
||||
inner = Cq.Solid.makeCylinder(
|
||||
|
@ -533,6 +775,10 @@ class Onbashira(Model):
|
|||
)
|
||||
)
|
||||
z = -self.bearing_disk_gap/2
|
||||
da_bind_stator = 360 / self.n_side
|
||||
da_bind_rotor = 360 / self.n_side
|
||||
da_bind_stator_minor = 360 / self.n_side / (1 + self.stator_bind_extra)
|
||||
da_bind_rotor_minor = 360 / self.n_side / (1 + self.rotor_bind_extra)
|
||||
for i in range(self.n_side):
|
||||
loc_barrel = Cq.Location.rot2d((i+1/2) * 360/self.n_side) * \
|
||||
Cq.Location(self.rotation_radius, 0, -self.barrel_length/2)
|
||||
|
@ -543,22 +789,26 @@ class Onbashira(Model):
|
|||
role=Role.DECORATION,
|
||||
loc=loc_barrel,
|
||||
)
|
||||
loc = Cq.Location.rot2d(i * 360/self.n_side) * Cq.Location(self.rotor_bind_radius, 0, z)
|
||||
#a = a.addS(
|
||||
# self.stator_spacer(),
|
||||
# name=f"spacerRotor{i}",
|
||||
# material=self.material_spacer,
|
||||
# role=Role.STRUCTURE,
|
||||
# loc=loc
|
||||
#)
|
||||
loc = Cq.Location.rot2d((i+0.5) * 360/self.n_side) * Cq.Location(self.angle_joint_bind_radius, 0, z)
|
||||
#a = a.addS(
|
||||
# self.rotor_spacer(),
|
||||
# name=f"spacerStator{i}",
|
||||
# material=self.material_spacer,
|
||||
# role=Role.STRUCTURE,
|
||||
# loc=loc
|
||||
#)
|
||||
for j in range(1 + self.rotor_bind_extra):
|
||||
angle = i * da_bind_rotor + (j+0.5) * da_bind_rotor_minor
|
||||
loc = Cq.Location.rot2d(angle) * Cq.Location(self.rotor_bind_radius, 0, z)
|
||||
a = a.addS(
|
||||
self.rotor_spacer(),
|
||||
name=f"spacer_rotor{i}_{j}",
|
||||
material=self.material_spacer,
|
||||
role=Role.STRUCTURE,
|
||||
loc=loc
|
||||
)
|
||||
for j in range(1 + self.stator_bind_extra):
|
||||
angle = i * da_bind_stator + (j+0.5) * da_bind_stator_minor
|
||||
loc = Cq.Location.rot2d(angle) * Cq.Location(self.stator_bind_radius, 0, z)
|
||||
a = a.addS(
|
||||
self.stator_spacer(),
|
||||
name=f"spacer_stator{i}_{j}",
|
||||
material=self.material_spacer,
|
||||
role=Role.STRUCTURE,
|
||||
loc=loc
|
||||
)
|
||||
for i in range(self.n_bearing_balls):
|
||||
ball = self.bearing_spindle()
|
||||
loc = Cq.Location.rot2d(i * 360/self.n_bearing_balls) * Cq.Location(self.bearing_track_radius, 0, 0)
|
||||
|
@ -1393,6 +1643,7 @@ class Onbashira(Model):
|
|||
material=self.material_brace,
|
||||
role=Role.HANDLE,
|
||||
)
|
||||
.add(self.assembly_motor(), name="motor")
|
||||
.add(self.assembly_machine(), name="machine")
|
||||
)
|
||||
# Add handle
|
||||
|
@ -1428,6 +1679,38 @@ class Onbashira(Model):
|
|||
f"machine/stator2?holeB{ir}",
|
||||
"Plane",
|
||||
)
|
||||
if i not in {1, 4}:
|
||||
a = a.constrain(
|
||||
f"motor/mount_plate?holeF{i}",
|
||||
f"ring2/side{(ir+2)%self.n_side}?holeStatorR",
|
||||
"Plane",
|
||||
)
|
||||
|
||||
name_bolt =f"stator_outer_bolt{i}"
|
||||
a = a.addS(
|
||||
BOLT_LONG.generate(),
|
||||
name=name_bolt,
|
||||
material=self.material_fastener,
|
||||
role=Role.CONNECTION,
|
||||
)
|
||||
a = a.constrain(
|
||||
f"{coupler_name}?holeOF",
|
||||
f"{name_bolt}?root",
|
||||
"Plane",
|
||||
)
|
||||
|
||||
name_bolt =f"chamber_back{i}boltFPI{i}"
|
||||
a = a.addS(
|
||||
BOLT_COMMON.generate(),
|
||||
name=name_bolt,
|
||||
material=self.material_fastener,
|
||||
role=Role.CONNECTION,
|
||||
)
|
||||
a = a.constrain(
|
||||
f"chamber_back?holeF{i}",
|
||||
f"{name_bolt}?root",
|
||||
"Plane",
|
||||
)
|
||||
for ih in range(len(self.angle_joint_bolt_position)):
|
||||
a = a.constrain(
|
||||
f"chamber/side{i}?holeFPI{ih}",
|
||||
|
@ -1452,19 +1735,6 @@ class Onbashira(Model):
|
|||
#)
|
||||
|
||||
# Generate bolts for the chamber back
|
||||
name_bolt =f"chamber_back{i}boltFPI{ih}"
|
||||
a = a.addS(
|
||||
BOLT_COMMON.generate(),
|
||||
name=name_bolt,
|
||||
material=self.material_fastener,
|
||||
role=Role.CONNECTION,
|
||||
)
|
||||
a = a.constrain(
|
||||
f"chamber_back?holeF{i}",
|
||||
f"{name_bolt}?root",
|
||||
"Plane",
|
||||
param=0,
|
||||
)
|
||||
for (nl, nc, nr) in [
|
||||
("section1", "ring1", "section2"),
|
||||
("section2", "ring2", "section3"),
|
||||
|
|
Loading…
Reference in New Issue