cosplay: Touhou/Houjuu Nue #4

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

58
nhf/parts/fibre.py Normal file
View File

@ -0,0 +1,58 @@
"""
A fibre, for bearing tension
"""
import cadquery as Cq
from dataclasses import dataclass
import nhf.utils
def tension_fibre(
length: float,
hole_diam: float,
hole_twist: float=0,
thickness: float=0.5) -> Cq.Workplane:
"""
A fibre which holds tension, with an eyes on each end.
"""
eye_female = Cq.Solid.makeTorus(
radius1=hole_diam/2 + thickness/2,
radius2=thickness/2,
dir=(1,0,0),
)
hole_length_male = hole_diam * 2.5
hole_height_male = hole_diam * 1.2
eye_male = Cq.Solid.makeBox(
length=hole_length_male + thickness * 2,
width=thickness,
height=hole_height_male + thickness * 2,
).located(
Cq.Location((-hole_length_male/2-thickness, -thickness/2, -hole_height_male/2-thickness))
).cut(Cq.Solid.makeBox(
length=hole_length_male,
width=thickness,
height=hole_height_male,
).located(Cq.Location((-hole_length_male/2, -thickness/2, -hole_height_male/2))))
height = length - hole_diam - thickness
assert height > 0, "String is too short to support the given hole sizes"
h1 = length/2 - hole_diam/2 - thickness/2
h2 = length/2 - hole_height_male - thickness/2
result = (
Cq.Workplane('XY')
.cylinder(
radius=thickness/2,
height=h1,
centered=(True, True, False),
)
.copyWorkplane(Cq.Workplane('YX'))
.cylinder(
radius=thickness/2,
height=h2,
centered=(True, True, False),
)
.union(eye_female.located(Cq.Location((0, 0,length/2))))
.union(eye_male.located(Cq.Location((0, 0,-length/2+hole_height_male/2+thickness/2), (0,0,1), hole_twist)))
)
result.copyWorkplane(Cq.Workplane(Cq.Plane(origin=(0,0,length/2), normal=(1,0,0)))).tagPlane("female")
conn1_normal, _ = (Cq.Location((0,0,0),(0,0,1),hole_twist) * Cq.Location((1,0,0))).toTuple()
result.copyWorkplane(Cq.Workplane(Cq.Plane(origin=(0,0,-length/2), normal=conn1_normal))).tagPlane("male")
return result

View File

@ -8,6 +8,7 @@ import cadquery as Cq
from nhf.build import Model, TargetKind, target, assembly, submodel
from nhf.materials import Role, Material
from nhf.parts.box import MountingBox, Hole
from nhf.parts.fibre import tension_fibre
from nhf.parts.item import Item
from nhf.parts.fasteners import FlatHeadBolt, HexNut
from nhf.touhou.houjuu_nue.common import NUT_COMMON, BOLT_COMMON
@ -333,7 +334,7 @@ LINEAR_ACTUATOR_BRACKET = MountingBracket()
BATTERY_BOX = BatteryBox18650()
@dataclass
@dataclass(kw_only=True)
class Flexor:
"""
Actuator assembly which flexes, similar to biceps
@ -346,17 +347,30 @@ class Flexor:
nut: HexNut = LINEAR_ACTUATOR_HEX_NUT
bolt: FlatHeadBolt = LINEAR_ACTUATOR_BOLT
bracket: MountingBracket = LINEAR_ACTUATOR_BRACKET
# Length of line attached to the flexor
line_length: float = 0.0
line_thickness: float = 0.5
# By how much is the line permitted to slack. This reduces the effective stroke length
line_slack: float = 0.0
# FIXME: Add a compression spring so the serviceable distances are not as fixed
def __post_init__(self):
assert self.line_slack <= self.line_length < self.actuator.stroke_length
@property
def mount_height(self):
return self.bracket.hole_to_side_ext
@property
def d_open(self):
return self.actuator.conn_length + self.actuator.stroke_length + self.line_length - self.line_slack
@property
def d_closed(self):
return self.actuator.conn_length + self.line_length
def open_pos(self) -> Tuple[float, float, float]:
r, phi, r_ = nhf.geometry.contraction_span_pos_from_radius(
d_open=self.actuator.conn_length + self.actuator.stroke_length,
d_closed=self.actuator.conn_length,
d_open=self.d_open,
d_closed=self.d_closed,
theta=math.radians(self.motion_span),
r=self.arm_radius,
smaller=self.pos_smaller,
@ -376,8 +390,8 @@ class Flexor:
result = math.sqrt(r * r + rp * rp - 2 * r * rp * math.cos(th))
#result = math.sqrt((r * math.cos(th) - rp) ** 2 + (r * math.sin(th)) ** 2)
assert self.actuator.conn_length <= result <= self.actuator.conn_length + self.actuator.stroke_length, \
f"Illegal length: {result} in {self.actuator.conn_length}+{self.actuator.stroke_length}"
assert self.d_closed -1e-6 <= result <= self.d_open + 1e-6,\
f"Illegal length: {result} not in [{self.d_closed}, {self.d_open}]"
return result
@ -393,7 +407,9 @@ class Flexor:
Adds the necessary mechanical components to this assembly. Does not
invoke `a.solve()`.
"""
pos = (target_length - self.actuator.conn_length) / self.actuator.stroke_length
draft = max(0, target_length - self.d_closed - self.line_length)
pos = draft / self.actuator.stroke_length
line_l = target_length - draft - self.actuator.conn_length
if tag_prefix:
tag_prefix = tag_prefix + "_"
else:
@ -411,8 +427,6 @@ class Flexor:
.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",
@ -427,6 +441,30 @@ class Flexor:
.constrain(f"{name_nut_back}?bot", f"{name_bracket_back}?conn_bot",
"Plane")
)
if self.line_length == 0.0:
a.constrain(
f"{name_actuator}/front?conn",
f"{name_bracket_front}?conn_mid",
"Plane", param=0)
else:
(
a
.addS(tension_fibre(
length=line_l,
hole_diam=self.nut.diam_thread,
thickness=self.line_thickness,
), name="fibre", role=Role.CONNECTION)
.constrain(
f"{name_actuator}/front?conn",
"fibre?male",
"Plane"
)
.constrain(
f"{name_bracket_front}?conn_mid",
"fibre?female",
"Plane"
)
)
if tag_hole_front:
a.constrain(tag_hole_front, f"{name_bracket_front}?conn_side", "Plane")
if tag_hole_back:

View File

@ -1067,7 +1067,7 @@ class ElbowJoint(Model):
flip: bool = False
angle_neutral: float = 30.0
actuator: Optional[LinearActuator]
actuator: Optional[LinearActuator] = None
flexor: Optional[Flexor] = None
# Rotates the entire flexor
flexor_offset_angle: float = 0
@ -1076,6 +1076,8 @@ class ElbowJoint(Model):
flexor_mount_angle_child: float = -90
flexor_pos_smaller: bool = True
flexor_child_arm_radius: Optional[float] = None
flexor_line_length: float = 0.0
flexor_line_slack: float = 0.0
def __post_init__(self):
assert self.child_arm_radius > self.disk_joint.radius_housing
@ -1087,6 +1089,8 @@ class ElbowJoint(Model):
motion_span=self.motion_span,
pos_smaller=self.flexor_pos_smaller,
arm_radius=self.flexor_child_arm_radius,
line_length=self.flexor_line_length,
line_slack=self.flexor_line_slack,
)
def hole_loc_tags(self):

View File

@ -1291,6 +1291,10 @@ class WingL(WingProfile):
angle_neutral=30.0,
flexor_mount_angle_child=170,
flexor_mount_angle_parent=-30,
flexor_line_length=30.0,
flexor_line_slack=5.0,
#flexor_line_length=0.0,
#flexor_line_slack=0.0,
flexor_offset_angle=15,
child_lip_extra_length=5.0,
flexor_child_arm_radius=60.0,