feat: Torsion resistant shoulder
This commit is contained in:
parent
a9b3aa8f70
commit
b8c6fb51fd
|
@ -8,7 +8,9 @@ from nhf.parts.springs import TorsionSpring
|
||||||
from nhf.parts.fasteners import FlatHeadBolt, HexNut, ThreaddedKnob
|
from nhf.parts.fasteners import FlatHeadBolt, HexNut, ThreaddedKnob
|
||||||
from nhf.parts.joints import TorsionJoint, HirthJoint
|
from nhf.parts.joints import TorsionJoint, HirthJoint
|
||||||
from nhf.parts.box import Hole, MountingBox, box_with_centre_holes
|
from nhf.parts.box import Hole, MountingBox, box_with_centre_holes
|
||||||
from nhf.touhou.houjuu_nue.electronics import Flexor, LinearActuator
|
from nhf.touhou.houjuu_nue.electronics import (
|
||||||
|
Flexor, LinearActuator, LINEAR_ACTUATOR_21,
|
||||||
|
)
|
||||||
import nhf.geometry
|
import nhf.geometry
|
||||||
import nhf.utils
|
import nhf.utils
|
||||||
|
|
||||||
|
@ -286,17 +288,18 @@ class ShoulderJoint(Model):
|
||||||
|
|
||||||
# Generates a child guard which covers up the internals. The lip length is
|
# Generates a child guard which covers up the internals. The lip length is
|
||||||
# relative to the +X surface of the guard.
|
# relative to the +X surface of the guard.
|
||||||
child_guard_ext: float = 30.0
|
child_guard_ext: float = 20.0
|
||||||
child_guard_width: float = 25.0
|
child_guard_width: float = 25.0
|
||||||
# guard length measured from axle
|
# guard length measured from axle
|
||||||
child_lip_length: float = 40.0
|
child_lip_ext: float = 50.0
|
||||||
child_lip_width: float = 20.0
|
child_lip_width: float = 20.0
|
||||||
|
child_lip_height: float = 40.0
|
||||||
|
child_lip_thickness: float = 5.0
|
||||||
child_conn_hole_diam: float = 4.0
|
child_conn_hole_diam: float = 4.0
|
||||||
# Measured from centre of axle
|
# Measured from centre of axle
|
||||||
child_conn_hole_pos: list[float] = field(default_factory=lambda: [8, 19, 30])
|
child_conn_hole_pos: list[float] = field(default_factory=lambda: [-15, -5, 5, 15])
|
||||||
child_core_thickness: float = 3.0
|
child_core_thickness: float = 3.0
|
||||||
|
|
||||||
|
|
||||||
# Rotates the torsion joint to avoid collisions or for some other purpose
|
# Rotates the torsion joint to avoid collisions or for some other purpose
|
||||||
axis_rotate_bot: float = 90
|
axis_rotate_bot: float = 90
|
||||||
axis_rotate_top: float = 0
|
axis_rotate_top: float = 0
|
||||||
|
@ -311,10 +314,16 @@ class ShoulderJoint(Model):
|
||||||
spool_height: float = 5.0
|
spool_height: float = 5.0
|
||||||
spool_groove_inset: float = 2.0
|
spool_groove_inset: float = 2.0
|
||||||
|
|
||||||
|
flip: bool = False
|
||||||
|
actuator: LinearActuator = LINEAR_ACTUATOR_21
|
||||||
|
|
||||||
def __post_init__(self):
|
def __post_init__(self):
|
||||||
assert self.parent_lip_length * 2 < self.height
|
assert self.parent_lip_length * 2 < self.height
|
||||||
|
assert self.child_guard_ext > self.torsion_joint.radius_rider
|
||||||
assert self.spool_groove_depth < self.spool_radius < self.torsion_joint.radius_rider - self.child_core_thickness
|
assert self.spool_groove_depth < self.spool_radius < self.torsion_joint.radius_rider - self.child_core_thickness
|
||||||
assert self.spool_base_height > self.spool_groove_depth
|
assert self.spool_base_height > self.spool_groove_depth
|
||||||
|
assert self.child_lip_height < self.height
|
||||||
|
assert self.draft_length <= self.actuator.stroke_length
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def radius(self):
|
def radius(self):
|
||||||
|
@ -327,6 +336,13 @@ class ShoulderJoint(Model):
|
||||||
"""
|
"""
|
||||||
return (self.spool_radius - self.spool_groove_depth / 2) * math.radians(self.angle_max_deflection)
|
return (self.spool_radius - self.spool_groove_depth / 2) * math.radians(self.angle_max_deflection)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def draft_height(self):
|
||||||
|
"""
|
||||||
|
Position of the middle of the spool measured from the middle
|
||||||
|
"""
|
||||||
|
return self.height / 2 - self.torsion_joint.total_height - self.spool_base_height / 2
|
||||||
|
|
||||||
def parent_lip_loc(self, left: bool=True) -> Cq.Location:
|
def parent_lip_loc(self, left: bool=True) -> Cq.Location:
|
||||||
"""
|
"""
|
||||||
2d location of the arm surface on the parent side, relative to axle
|
2d location of the arm surface on the parent side, relative to axle
|
||||||
|
@ -335,10 +351,15 @@ class ShoulderJoint(Model):
|
||||||
sign = 1 if left else -1
|
sign = 1 if left else -1
|
||||||
loc_dir = Cq.Location((0,sign * dy,0), (0, 0, 1), sign * 90)
|
loc_dir = Cq.Location((0,sign * dy,0), (0, 0, 1), sign * 90)
|
||||||
return Cq.Location.from2d(self.parent_lip_ext, 0, 0) * loc_dir
|
return Cq.Location.from2d(self.parent_lip_ext, 0, 0) * loc_dir
|
||||||
|
def child_lip_loc(self) -> Cq.Location:
|
||||||
|
"""
|
||||||
|
2d location to middle of lip
|
||||||
|
"""
|
||||||
|
return Cq.Location.from2d(self.child_lip_ext - self.child_guard_ext, 0, 180)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def _max_contraction_angle(self) -> float:
|
def _max_contraction_angle(self) -> float:
|
||||||
return self.angle_max_deflection + self.angle_neutral
|
return 180 - self.angle_max_deflection + self.angle_neutral
|
||||||
|
|
||||||
def _contraction_cut_geometry(self, parent: bool = False, mirror: bool=False) -> Cq.Solid:
|
def _contraction_cut_geometry(self, parent: bool = False, mirror: bool=False) -> Cq.Solid:
|
||||||
"""
|
"""
|
||||||
|
@ -346,7 +367,8 @@ class ShoulderJoint(Model):
|
||||||
"""
|
"""
|
||||||
aspect = self.child_guard_width / self.parent_arm_width
|
aspect = self.child_guard_width / self.parent_arm_width
|
||||||
theta = math.radians(self._max_contraction_angle)
|
theta = math.radians(self._max_contraction_angle)
|
||||||
theta_p = math.atan(math.sin(theta) / (math.cos(theta) + aspect))
|
#theta_p = math.atan(math.sin(theta) / (math.cos(theta) + aspect))
|
||||||
|
theta_p = math.atan2(math.sin(theta), math.cos(theta) + aspect)
|
||||||
angle = math.degrees(theta_p)
|
angle = math.degrees(theta_p)
|
||||||
assert 0 <= angle <= 90
|
assert 0 <= angle <= 90
|
||||||
# outer radius of the cut, overestimated
|
# outer radius of the cut, overestimated
|
||||||
|
@ -431,10 +453,7 @@ class ShoulderJoint(Model):
|
||||||
|
|
||||||
@target(name="parent-bot")
|
@target(name="parent-bot")
|
||||||
def parent_bot(self) -> Cq.Assembly:
|
def parent_bot(self) -> Cq.Assembly:
|
||||||
result = (
|
return self.parent(top=False)
|
||||||
self.parent(top=False)
|
|
||||||
)
|
|
||||||
return result
|
|
||||||
@target(name="parent-top")
|
@target(name="parent-top")
|
||||||
def parent_top(self) -> Cq.Assembly:
|
def parent_top(self) -> Cq.Assembly:
|
||||||
return self.parent(top=True)
|
return self.parent(top=True)
|
||||||
|
@ -485,18 +504,15 @@ class ShoulderJoint(Model):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
joint = self.torsion_joint
|
joint = self.torsion_joint
|
||||||
assert all(r < self.child_lip_length for r in self.child_conn_hole_pos)
|
|
||||||
|
|
||||||
# Half of the height of the bridging cylinder
|
# Half of the height of the bridging cylinder
|
||||||
dh = self.height / 2 - joint.total_height
|
dh = self.height / 2 - joint.total_height
|
||||||
core_start_angle = 30
|
core_start_angle = 30
|
||||||
core_end_angle1 = 90
|
|
||||||
core_end_angle2 = 180
|
|
||||||
|
|
||||||
radius_core_inner = joint.radius_rider - self.child_core_thickness
|
radius_core_inner = joint.radius_rider - self.child_core_thickness
|
||||||
core_profile1 = (
|
core_profile1 = (
|
||||||
Cq.Sketch()
|
Cq.Sketch()
|
||||||
.arc((0, 0), joint.radius_rider, core_start_angle, core_end_angle1-core_start_angle)
|
.arc((0, 0), joint.radius_rider, core_start_angle, self.angle_max_deflection)
|
||||||
.segment((0, 0))
|
.segment((0, 0))
|
||||||
.close()
|
.close()
|
||||||
.assemble()
|
.assemble()
|
||||||
|
@ -504,12 +520,28 @@ class ShoulderJoint(Model):
|
||||||
)
|
)
|
||||||
core_profile2 = (
|
core_profile2 = (
|
||||||
Cq.Sketch()
|
Cq.Sketch()
|
||||||
.arc((0, 0), joint.radius_rider, -core_start_angle, -(core_end_angle2-core_start_angle))
|
.arc((0, 0), joint.radius_rider, -core_start_angle, -(90 - self.angle_neutral))
|
||||||
.segment((0, 0))
|
.segment((0, 0))
|
||||||
.close()
|
.close()
|
||||||
.assemble()
|
.assemble()
|
||||||
.circle(radius_core_inner, mode='s')
|
.circle(radius_core_inner, mode='s')
|
||||||
)
|
)
|
||||||
|
lip_extension = (
|
||||||
|
Cq.Solid.makeBox(
|
||||||
|
length=self.child_lip_ext - self.child_guard_ext,
|
||||||
|
width=self.child_lip_width,
|
||||||
|
height=self.child_lip_height,
|
||||||
|
).cut(Cq.Solid.makeBox(
|
||||||
|
length=self.child_lip_ext - self.child_guard_ext,
|
||||||
|
width=self.child_lip_width - self.child_lip_thickness * 2,
|
||||||
|
height=self.child_lip_height,
|
||||||
|
).located(Cq.Location((0, self.child_lip_thickness, 0))))
|
||||||
|
.located(Cq.Location((
|
||||||
|
self.child_guard_ext,
|
||||||
|
-self.child_lip_width / 2,
|
||||||
|
-self.child_lip_height / 2,
|
||||||
|
)))
|
||||||
|
)
|
||||||
core_guard = (
|
core_guard = (
|
||||||
Cq.Workplane('XY')
|
Cq.Workplane('XY')
|
||||||
.box(
|
.box(
|
||||||
|
@ -518,6 +550,14 @@ class ShoulderJoint(Model):
|
||||||
height=self.height,
|
height=self.height,
|
||||||
centered=(False, True, True),
|
centered=(False, True, True),
|
||||||
)
|
)
|
||||||
|
#.copyWorkplane(Cq.Workplane('XY'))
|
||||||
|
#.box(
|
||||||
|
# length=self.child_lip_ext,
|
||||||
|
# width=self.child_guard_width,
|
||||||
|
# height=self.child_lip_height,
|
||||||
|
# combine=True,
|
||||||
|
# centered=(False, True, True),
|
||||||
|
#)
|
||||||
.copyWorkplane(Cq.Workplane('XY'))
|
.copyWorkplane(Cq.Workplane('XY'))
|
||||||
.cylinder(
|
.cylinder(
|
||||||
radius=self.radius,
|
radius=self.radius,
|
||||||
|
@ -527,12 +567,13 @@ class ShoulderJoint(Model):
|
||||||
)
|
)
|
||||||
.copyWorkplane(Cq.Workplane('XY'))
|
.copyWorkplane(Cq.Workplane('XY'))
|
||||||
.box(
|
.box(
|
||||||
length=self.child_guard_ext,
|
length=self.child_lip_ext,
|
||||||
width=self.child_lip_width,
|
width=self.child_lip_width,
|
||||||
height=self.height - self.torsion_joint.total_height * 2,
|
height=self.height - self.torsion_joint.total_height * 2,
|
||||||
combine='cut',
|
combine='cut',
|
||||||
centered=(False, True, True),
|
centered=(False, True, True),
|
||||||
)
|
)
|
||||||
|
.union(lip_extension)
|
||||||
.cut(self._contraction_cut_geometry(parent=False))
|
.cut(self._contraction_cut_geometry(parent=False))
|
||||||
)
|
)
|
||||||
core = (
|
core = (
|
||||||
|
@ -548,13 +589,17 @@ class ShoulderJoint(Model):
|
||||||
.union(core_guard)
|
.union(core_guard)
|
||||||
)
|
)
|
||||||
assert self.child_lip_width / 2 <= joint.radius_rider
|
assert self.child_lip_width / 2 <= joint.radius_rider
|
||||||
lip_thickness = joint.rider_disk_height
|
sign = 1 if self.flip else -1
|
||||||
lip = box_with_centre_holes(
|
holes = [Hole(x=sign * x) for x in self.child_conn_hole_pos]
|
||||||
length=self.child_lip_length,
|
lip_obj = MountingBox(
|
||||||
|
length=self.child_lip_height,
|
||||||
width=self.child_lip_width,
|
width=self.child_lip_width,
|
||||||
height=lip_thickness,
|
thickness=self.child_lip_thickness,
|
||||||
hole_loc=self.child_conn_hole_pos,
|
holes=holes,
|
||||||
hole_diam=self.child_conn_hole_diam,
|
hole_diam=self.child_conn_hole_diam,
|
||||||
|
centred=(True, True),
|
||||||
|
generate_side_tags=False,
|
||||||
|
generate_reverse_tags=False,
|
||||||
)
|
)
|
||||||
theta = self.torsion_joint.spring.angle_neutral - self.torsion_joint.rider_slot_span
|
theta = self.torsion_joint.spring.angle_neutral - self.torsion_joint.rider_slot_span
|
||||||
loc_rotate = Cq.Location((0, 0, 0), (1, 0, 0), 180)
|
loc_rotate = Cq.Location((0, 0, 0), (1, 0, 0), 180)
|
||||||
|
@ -562,6 +607,7 @@ class ShoulderJoint(Model):
|
||||||
loc_axis_rotate_top = Cq.Location((0, 0, 0), (0, 0, 1), self.axis_rotate_top + self.angle_neutral)
|
loc_axis_rotate_top = Cq.Location((0, 0, 0), (0, 0, 1), self.axis_rotate_top + self.angle_neutral)
|
||||||
spool_dz = self.height / 2 - self.torsion_joint.total_height
|
spool_dz = self.height / 2 - self.torsion_joint.total_height
|
||||||
spool_angle = 180 + self.angle_neutral
|
spool_angle = 180 + self.angle_neutral
|
||||||
|
loc_spool_flip = Cq.Location((0,0,0),(0,1,0),180) if self.flip else Cq.Location()
|
||||||
result = (
|
result = (
|
||||||
Cq.Assembly()
|
Cq.Assembly()
|
||||||
.add(core, name="core", loc=Cq.Location())
|
.add(core, name="core", loc=Cq.Location())
|
||||||
|
@ -569,12 +615,10 @@ class ShoulderJoint(Model):
|
||||||
loc=loc_axis_rotate_top * Cq.Location((0, 0, dh), (0, 0, 1), -90) * Cq.Location((0, 0, 0), (0, 0, 1), theta))
|
loc=loc_axis_rotate_top * Cq.Location((0, 0, dh), (0, 0, 1), -90) * Cq.Location((0, 0, 0), (0, 0, 1), theta))
|
||||||
.add(joint.rider(rider_slot_begin=180), name="rider_bot",
|
.add(joint.rider(rider_slot_begin=180), name="rider_bot",
|
||||||
loc=loc_axis_rotate_bot * Cq.Location((0, 0, -dh), (0, 0, 1), -90) * loc_rotate)
|
loc=loc_axis_rotate_bot * Cq.Location((0, 0, -dh), (0, 0, 1), -90) * loc_rotate)
|
||||||
.add(lip, name="lip_top",
|
.add(lip_obj.generate(), name="lip",
|
||||||
loc=Cq.Location((self.child_guard_ext, 0, dh)))
|
loc=Cq.Location((self.child_lip_ext - self.child_lip_thickness,0,0), (0,1,0), 90))
|
||||||
.add(lip, name="lip_bot",
|
|
||||||
loc=Cq.Location((self.child_guard_ext, 0, -dh)) * loc_rotate)
|
|
||||||
.add(self._spool(), name="spool",
|
.add(self._spool(), name="spool",
|
||||||
loc=Cq.Location((0, 0, -spool_dz), (0, 0, 1), spool_angle))
|
loc=loc_spool_flip * Cq.Location((0, 0, -spool_dz), (0, 0, 1), spool_angle))
|
||||||
)
|
)
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
|
@ -337,6 +337,19 @@ class WingProfile(Model):
|
||||||
flip_y=self.flip,
|
flip_y=self.flip,
|
||||||
generate_reverse_tags=True,
|
generate_reverse_tags=True,
|
||||||
)
|
)
|
||||||
|
@submodel(name="spacer-s0-shoulder-act")
|
||||||
|
def spacer_s0_shoulder_act(self) -> MountingBox:
|
||||||
|
dx = self.shoulder_joint.draft_height
|
||||||
|
return MountingBox(
|
||||||
|
holes=[Hole(x=dx), Hole(x=-dx)],
|
||||||
|
hole_diam=self.shoulder_joint.actuator.back_hole_diam,
|
||||||
|
length=self.root_height,
|
||||||
|
width=10.0,
|
||||||
|
centred=(True, True),
|
||||||
|
thickness=self.spacer_thickness,
|
||||||
|
flip_y=self.flip,
|
||||||
|
generate_reverse_tags=True,
|
||||||
|
)
|
||||||
|
|
||||||
def surface_s0(self, top: bool = False) -> Cq.Workplane:
|
def surface_s0(self, top: bool = False) -> Cq.Workplane:
|
||||||
base_dx = -(self.base_width - self.root_joint.child_width) / 2 - 10
|
base_dx = -(self.base_width - self.root_joint.child_width) / 2 - 10
|
||||||
|
@ -352,6 +365,8 @@ class WingProfile(Model):
|
||||||
self.shoulder_axle_loc * axle_rotate * self.shoulder_joint.parent_lip_loc(left=True)),
|
self.shoulder_axle_loc * axle_rotate * self.shoulder_joint.parent_lip_loc(left=True)),
|
||||||
("shoulder_right",
|
("shoulder_right",
|
||||||
self.shoulder_axle_loc * axle_rotate * self.shoulder_joint.parent_lip_loc(left=False)),
|
self.shoulder_axle_loc * axle_rotate * self.shoulder_joint.parent_lip_loc(left=False)),
|
||||||
|
("shoulder_act",
|
||||||
|
self.shoulder_axle_loc * axle_rotate * Cq.Location.from2d(150, -40)),
|
||||||
("base", Cq.Location.from2d(base_dx, base_dy, 90)),
|
("base", Cq.Location.from2d(base_dx, base_dy, 90)),
|
||||||
("electronic_mount", Cq.Location.from2d(-45, 75, 64)),
|
("electronic_mount", Cq.Location.from2d(-45, 75, 64)),
|
||||||
]
|
]
|
||||||
|
@ -393,6 +408,7 @@ class WingProfile(Model):
|
||||||
for o, tag in [
|
for o, tag in [
|
||||||
(self.spacer_s0_shoulder(left=True).generate(), "shoulder_left"),
|
(self.spacer_s0_shoulder(left=True).generate(), "shoulder_left"),
|
||||||
(self.spacer_s0_shoulder(left=False).generate(), "shoulder_right"),
|
(self.spacer_s0_shoulder(left=False).generate(), "shoulder_right"),
|
||||||
|
(self.spacer_s0_shoulder_act().generate(), "shoulder_act"),
|
||||||
(self.spacer_s0_base().generate(), "base"),
|
(self.spacer_s0_base().generate(), "base"),
|
||||||
(self.spacer_s0_electronic_mount().generate(), "electronic_mount"),
|
(self.spacer_s0_electronic_mount().generate(), "electronic_mount"),
|
||||||
]:
|
]:
|
||||||
|
@ -599,13 +615,11 @@ class WingProfile(Model):
|
||||||
)
|
)
|
||||||
return profile
|
return profile
|
||||||
def surface_s1(self, front: bool = True) -> Cq.Workplane:
|
def surface_s1(self, front: bool = True) -> Cq.Workplane:
|
||||||
shoulder_h = self.shoulder_joint.child_height
|
|
||||||
h = (self.shoulder_joint.height - shoulder_h) / 2
|
|
||||||
tags_shoulder = [
|
tags_shoulder = [
|
||||||
("shoulder_bot", Cq.Location.from2d(0, h, 90)),
|
("shoulder",
|
||||||
("shoulder_top", Cq.Location.from2d(0, h + shoulder_h, 270)),
|
Cq.Location((0, self.shoulder_height / 2, 0)) *
|
||||||
|
self.shoulder_joint.child_lip_loc()),
|
||||||
]
|
]
|
||||||
h = self.elbow_height / 2
|
|
||||||
rot_elbow = Cq.Location.rot2d(self.elbow_rotate)
|
rot_elbow = Cq.Location.rot2d(self.elbow_rotate)
|
||||||
loc_elbow = rot_elbow * self.elbow_joint.parent_arm_loc()
|
loc_elbow = rot_elbow * self.elbow_joint.parent_arm_loc()
|
||||||
tags_elbow = [
|
tags_elbow = [
|
||||||
|
@ -622,16 +636,20 @@ class WingProfile(Model):
|
||||||
profile, self.panel_thickness, tags, reverse=front)
|
profile, self.panel_thickness, tags, reverse=front)
|
||||||
@submodel(name="spacer-s1-shoulder")
|
@submodel(name="spacer-s1-shoulder")
|
||||||
def spacer_s1_shoulder(self) -> MountingBox:
|
def spacer_s1_shoulder(self) -> MountingBox:
|
||||||
|
sign = -1 if self.flip else 1
|
||||||
holes = [
|
holes = [
|
||||||
Hole(x)
|
Hole(x=sign * x)
|
||||||
for x in self.shoulder_joint.child_conn_hole_pos
|
for x in self.shoulder_joint.child_conn_hole_pos
|
||||||
]
|
]
|
||||||
return MountingBox(
|
return MountingBox(
|
||||||
length=50.0, # FIXME: magic
|
length=self.shoulder_joint.child_lip_height,
|
||||||
width=self.s1_thickness,
|
width=self.s1_thickness,
|
||||||
thickness=self.spacer_thickness,
|
thickness=self.spacer_thickness,
|
||||||
holes=holes,
|
holes=holes,
|
||||||
|
centred=(True, True),
|
||||||
hole_diam=self.shoulder_joint.child_conn_hole_diam,
|
hole_diam=self.shoulder_joint.child_conn_hole_diam,
|
||||||
|
centre_left_right_tags=True,
|
||||||
|
centre_bot_top_tags=True,
|
||||||
)
|
)
|
||||||
@submodel(name="spacer-s1-elbow")
|
@submodel(name="spacer-s1-elbow")
|
||||||
def spacer_s1_elbow(self) -> MountingBox:
|
def spacer_s1_elbow(self) -> MountingBox:
|
||||||
|
@ -664,8 +682,7 @@ class WingProfile(Model):
|
||||||
param=self.s1_thickness)
|
param=self.s1_thickness)
|
||||||
)
|
)
|
||||||
for o, t in [
|
for o, t in [
|
||||||
(self.spacer_s1_shoulder(), "shoulder_bot"),
|
(self.spacer_s1_shoulder(), "shoulder"),
|
||||||
(self.spacer_s1_shoulder(), "shoulder_top"),
|
|
||||||
(self.spacer_s1_elbow(), "elbow_top"),
|
(self.spacer_s1_elbow(), "elbow_top"),
|
||||||
(self.spacer_s1_elbow(), "elbow_bot"),
|
(self.spacer_s1_elbow(), "elbow_bot"),
|
||||||
(self.spacer_s1_elbow_act(), "elbow_act"),
|
(self.spacer_s1_elbow_act(), "elbow_act"),
|
||||||
|
@ -988,13 +1005,8 @@ class WingProfile(Model):
|
||||||
if "s1" in parts:
|
if "s1" in parts:
|
||||||
result.add(self.assembly_s1(), name="s1")
|
result.add(self.assembly_s1(), name="s1")
|
||||||
if "s1" in parts and "shoulder" in parts:
|
if "s1" in parts and "shoulder" in parts:
|
||||||
(
|
for i in range(len(self.shoulder_joint.child_conn_hole_pos)):
|
||||||
result
|
result.constrain(f"s1/shoulder?conn{i}", f"shoulder/child/lip?conn{i}", "Plane")
|
||||||
.constrain("s1/shoulder_top?conn0", f"shoulder/child/lip_{tag_top}?conn0", "Plane")
|
|
||||||
.constrain("s1/shoulder_top?conn1", f"shoulder/child/lip_{tag_top}?conn1", "Plane")
|
|
||||||
.constrain("s1/shoulder_bot?conn0", f"shoulder/child/lip_{tag_bot}?conn0", "Plane")
|
|
||||||
.constrain("s1/shoulder_bot?conn1", f"shoulder/child/lip_{tag_bot}?conn1", "Plane")
|
|
||||||
)
|
|
||||||
if "elbow" in parts:
|
if "elbow" in parts:
|
||||||
angle = self.elbow_joint.motion_span * elbow_wrist_deflection
|
angle = self.elbow_joint.motion_span * elbow_wrist_deflection
|
||||||
result.add(self.elbow_joint.assembly(
|
result.add(self.elbow_joint.assembly(
|
||||||
|
@ -1327,6 +1339,7 @@ class WingL(WingProfile):
|
||||||
self.wrist_joint.angle_neutral = self.wrist_bot_loc.to2d_rot() + 30.0
|
self.wrist_joint.angle_neutral = self.wrist_bot_loc.to2d_rot() + 30.0
|
||||||
self.wrist_rotate = -self.wrist_joint.angle_neutral
|
self.wrist_rotate = -self.wrist_joint.angle_neutral
|
||||||
self.wrist_joint.flip = False
|
self.wrist_joint.flip = False
|
||||||
|
self.shoulder_joint.flip = True
|
||||||
|
|
||||||
super().__post_init__()
|
super().__post_init__()
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue