cosplay: Touhou/Houjuu Nue #4

Open
aniva wants to merge 189 commits from touhou/houjuu-nue into main
3 changed files with 137 additions and 10 deletions
Showing only changes of commit b3a472add4 - Show all commits

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,