feat: Linear actuator assembly

This commit is contained in:
Leni Aniva 2024-07-21 05:46:18 -07:00
parent 579c10e373
commit b3a472add4
Signed by: aniva
GPG Key ID: 4D9B1C8D10EA4C50
3 changed files with 137 additions and 10 deletions

View File

@ -15,6 +15,10 @@ class FlatHeadBolt(Item):
def name(self) -> str: def name(self) -> str:
return f"Bolt M{int(self.diam_thread)} h{int(self.height_thread)}mm" return f"Bolt M{int(self.diam_thread)} h{int(self.height_thread)}mm"
@property
def role(self) -> Role:
return Role.CONNECTION
def generate(self) -> Cq.Assembly: def generate(self) -> Cq.Assembly:
head = Cq.Solid.makeCylinder( head = Cq.Solid.makeCylinder(
@ -30,13 +34,8 @@ class FlatHeadBolt(Item):
) )
rod.faces("<Z").tag("tip") rod.faces("<Z").tag("tip")
rod.faces(">Z").tag("root") rod.faces(">Z").tag("root")
rod = rod.union(head.located(Cq.Location((0, 0, self.height_thread))))
return ( return rod
Cq.Assembly()
.addS(rod, name="thread", role=Role.CONNECTION)
.addS(head, name="head", role=Role.CONNECTION,
loc=Cq.Location((0, 0, self.height_thread)))
)
@dataclass(frozen=True) @dataclass(frozen=True)

View File

@ -2,10 +2,12 @@
Electronic components Electronic components
""" """
from dataclasses import dataclass from dataclasses import dataclass
from typing import Optional
import cadquery as Cq import cadquery as Cq
from nhf.materials import Role from nhf.materials import Role
from nhf.parts.item import Item from nhf.parts.item import Item
from nhf.parts.fasteners import FlatHeadBolt, HexNut from nhf.parts.fasteners import FlatHeadBolt, HexNut
import nhf.utils
@dataclass(frozen=True) @dataclass(frozen=True)
class LinearActuator(Item): class LinearActuator(Item):
@ -68,6 +70,7 @@ class LinearActuator(Item):
combine='cut', combine='cut',
) )
) )
front.copyWorkplane(Cq.Workplane('XZ')).tagPlane('conn')
if stroke_x > 0: if stroke_x > 0:
shaft = ( shaft = (
Cq.Workplane('YZ') Cq.Workplane('YZ')
@ -119,6 +122,7 @@ class LinearActuator(Item):
combine='cut', combine='cut',
) )
) )
back.copyWorkplane(Cq.Workplane('XZ')).tagPlane('conn')
result = ( result = (
Cq.Assembly() Cq.Assembly()
.add(front, name="front", .add(front, name="front",
@ -134,6 +138,71 @@ class LinearActuator(Item):
result.add(shaft, name="shaft") result.add(shaft, name="shaft")
return result return result
@dataclass(frozen=True)
class MountingBracket(Item):
"""
Mounting bracket for a linear actuator
"""
mass: float = 1.6
hole_diam: float = 4.0
width: float = 8.0
height: float = 12.20
thickness: float = 0.98
length: float = 13.00
hole_to_side_ext: float = 8.10
def __post_init__(self):
assert self.hole_to_side_ext - self.hole_diam / 2 > 0
@property
def name(self) -> str:
return f"MountingBracket M{int(self.hole_diam)}"
@property
def role(self) -> Role:
return Role.MOTION
def generate(self) -> Cq.Workplane:
result = (
Cq.Workplane('XY')
.box(
length=self.hole_to_side_ext,
width=self.width,
height=self.height,
centered=(False, True, True)
)
.copyWorkplane(Cq.Workplane('XY'))
.cylinder(
height=self.height,
radius=self.width / 2,
combine=True,
)
.copyWorkplane(Cq.Workplane('XY'))
.box(
length=2 * (self.hole_to_side_ext - self.thickness),
width=self.width,
height=self.height - self.thickness * 2,
combine='cut',
)
.copyWorkplane(Cq.Workplane('XY'))
.cylinder(
height=self.height,
radius=self.hole_diam / 2,
combine='cut'
)
.copyWorkplane(Cq.Workplane('YZ'))
.cylinder(
height=self.hole_to_side_ext * 2,
radius=self.hole_diam / 2,
combine='cut'
)
)
result.copyWorkplane(Cq.Workplane('YZ', origin=(self.hole_to_side_ext, 0, 0))).tagPlane("conn_side")
result.copyWorkplane(Cq.Workplane('XY', origin=(0, 0, self.height/2))).tagPlane("conn_top")
result.copyWorkplane(Cq.Workplane('YX', origin=(0, 0, -self.height/2))).tagPlane("conn_bot")
result.copyWorkplane(Cq.Workplane('XY')).tagPlane("conn_mid")
return result
LINEAR_ACTUATOR_SHOULDER = LinearActuator( LINEAR_ACTUATOR_SHOULDER = LinearActuator(
mass=34.0, mass=34.0,
@ -148,8 +217,67 @@ LINEAR_ACTUATOR_HEX_NUT = HexNut(
) )
LINEAR_ACTUATOR_BOLT = FlatHeadBolt( LINEAR_ACTUATOR_BOLT = FlatHeadBolt(
mass=1.7, mass=1.7,
diam_head=16.68, diam_head=6.68,
height_head=2.98, height_head=2.98,
diam_thread=4.0, diam_thread=4.0,
height_thread=15.83, height_thread=15.83,
) )
LINEAR_ACTUATOR_BRACKET = MountingBracket()
@dataclass(frozen=True)
class LinearActuatorAssembly:
# FIXME: Measure
actuator: LinearActuator = LINEAR_ACTUATOR_SHOULDER
nut: HexNut = LINEAR_ACTUATOR_HEX_NUT
bolt: FlatHeadBolt = LINEAR_ACTUATOR_BOLT
bracket: MountingBracket = LINEAR_ACTUATOR_BRACKET
def add_to(
self,
a: Cq.Assembly,
tag_prefix: Optional[str] = None,
tag_hole_front: Optional[str] = None,
tag_hole_back: Optional[str] = None,
tag_dir: Optional[str] = None):
"""
Adds the necessary mechanical components to this assembly. Does not
invoke `a.solve()`.
"""
if tag_prefix:
tag_prefix = tag_prefix + "_"
name_actuator = f"{tag_prefix}actuator"
name_bracket_front = f"{tag_prefix}bracket_front"
name_bracket_back = f"{tag_prefix}bracket_back"
name_bolt_front = f"{tag_prefix}front_bolt"
name_bolt_back = f"{tag_prefix}back_bolt"
name_nut_front = f"{tag_prefix}front_nut"
name_nut_back = f"{tag_prefix}back_nut"
(
a
.add(self.actuator.assembly(), name=name_actuator)
.add(self.bracket.assembly(), name=name_bracket_front)
.add(self.bolt.assembly(), name=name_bolt_front)
.add(self.nut.assembly(), name=name_nut_front)
.constrain(f"{name_actuator}/front?conn", f"{name_bracket_front}?conn_mid",
"Plane", param=0)
.constrain(f"{name_bolt_front}?root", f"{name_bracket_front}?conn_top",
"Plane", param=0)
.constrain(f"{name_nut_front}?bot", f"{name_bracket_front}?conn_bot",
"Plane")
.add(self.bracket.assembly(), name=name_bracket_back)
.add(self.bolt.assembly(), name=name_bolt_back)
.add(self.nut.assembly(), name=name_nut_back)
.constrain(f"{name_actuator}/back?conn", f"{name_bracket_back}?conn_mid",
"Plane", param=0)
.constrain(f"{name_bolt_back}?root", f"{name_bracket_back}?conn_top",
"Plane", param=0)
.constrain(f"{name_nut_back}?bot", f"{name_bracket_back}?conn_bot",
"Plane")
)
if tag_hole_front:
a.constrain(tag_hole_front, f"{name_bracket_front}?conn_side", "Plane")
if tag_hole_back:
a.constrain(tag_hole_back, f"{name_bracket_back}?conn_side", "Plane")
if tag_dir:
a.constrain(tag_dir, f"{name_bracket_front}?conn_mid", "Axis", param=0)

View File

@ -484,10 +484,10 @@ class ShoulderJoint(Model):
# Fasteners # Fasteners
.addS(self.bolt.assembly(), name="bolt_top", .addS(self.bolt.assembly(), name="bolt_top",
loc=Cq.Location((0, 0, bolt_z))) loc=Cq.Location((0, 0, bolt_z)))
.constrain("bolt_top/thread?root", 'Fixed') .constrain("bolt_top?root", 'Fixed')
.addS(self.bolt.assembly(), name="bolt_bot", .addS(self.bolt.assembly(), name="bolt_bot",
loc=Cq.Location((0, 0, -bolt_z), (1,0,0), 180)) loc=Cq.Location((0, 0, -bolt_z), (1,0,0), 180))
.constrain("bolt_bot/thread?root", 'Fixed') .constrain("bolt_bot?root", 'Fixed')
) )
TorsionJoint.add_constraints( TorsionJoint.add_constraints(
result, result,