108 lines
3.4 KiB
Python
108 lines
3.4 KiB
Python
"""lyra — dexterous humanoid right hand concept for an advanced biped.
|
|
|
|
An aesthetically refined five-digit robotic hand: slim pearl-composite
|
|
shells over a graphite structural core, machined-aluminum precision
|
|
knuckle clevises with visible pivot pins, tendon-driven architecture
|
|
(dorsal tendon channels with a tensioner dial row at the wrist), integrated
|
|
tactile pads (palm array, per-phalanx strips, soft-touch fingertip caps),
|
|
an amber-ringed palm sensor, and a bolt-circle wrist flange. No logos.
|
|
|
|
Degrees of freedom (16, statically posed in the baked STEP):
|
|
- each finger (index/middle/ring/pinky): MCP, PIP, DIP flexion -> 12
|
|
- thumb: CMC yaw (opposition swing), CMC flex, MP, IP -> 4
|
|
|
|
Coordinates: RIGHT hand; wrist-flange mount face center = origin,
|
|
+Z distal (fingers up), +Y palmar, +X radial (thumb side). Units mm.
|
|
|
|
Chain offsets, joint limits, and the baked "relaxed" pose live in
|
|
lyra_parts/chain.py and are shared with the URDF/SRDF generators
|
|
(lyra_parts/description.py) and the CAD Viewer animation sidecar
|
|
(.lyra.step.js); edit them there.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from build123d import Compound
|
|
|
|
from cadpy.assembly import AssemblyHelper
|
|
|
|
from lyra_parts import chain
|
|
from lyra_parts.digits import (
|
|
build_finger_distal,
|
|
build_finger_middle,
|
|
build_finger_proximal,
|
|
build_thumb_base,
|
|
build_thumb_distal,
|
|
build_thumb_metacarpal,
|
|
build_thumb_proximal,
|
|
)
|
|
from lyra_parts.lib import revolute_attach
|
|
from lyra_parts.palm import build_palm
|
|
|
|
|
|
def _builders():
|
|
builders = {}
|
|
for finger in chain.FINGERS:
|
|
builders[f"{finger}_proximal"] = lambda f=finger: build_finger_proximal(f)
|
|
builders[f"{finger}_middle"] = lambda f=finger: build_finger_middle(f)
|
|
builders[f"{finger}_distal"] = lambda f=finger: build_finger_distal(f)
|
|
builders["thumb_base"] = build_thumb_base
|
|
builders["thumb_metacarpal"] = build_thumb_metacarpal
|
|
builders["thumb_proximal"] = build_thumb_proximal
|
|
builders["thumb_distal"] = build_thumb_distal
|
|
return builders
|
|
|
|
|
|
def _xref_for(axis) -> tuple:
|
|
return (0.0, 1.0, 0.0) if abs(axis[0]) > 0.9 else (1.0, 0.0, 0.0)
|
|
|
|
|
|
def assemble() -> Compound:
|
|
"""Labeled assembly baked in the chain's relaxed pose.
|
|
|
|
Occurrence order (#o1.N in the generated STEP) is palm first, then
|
|
chain.all_joints() child order: index, middle, ring, pinky
|
|
(proximal/middle/distal each), then thumb base/metacarpal/proximal/
|
|
distal — the animation sidecar relies on this order.
|
|
"""
|
|
asm = AssemblyHelper(chain.ROBOT_NAME)
|
|
builders = _builders()
|
|
pose = chain.named_poses_deg()[chain.BAKED_POSE_NAME]
|
|
|
|
parts = {"palm": asm.add(build_palm(), "palm")}
|
|
for joint in chain.all_joints():
|
|
child = asm.add(builders[joint["child"]](), joint["child"])
|
|
axis = joint["axis"]
|
|
xref = _xref_for(axis)
|
|
revolute_attach(
|
|
asm,
|
|
parts[joint["parent"]],
|
|
child,
|
|
joint["name"],
|
|
joint["origin_mm"],
|
|
axis,
|
|
xref,
|
|
(0.0, 0.0, 0.0),
|
|
axis,
|
|
xref,
|
|
pose[joint["name"]],
|
|
)
|
|
parts[joint["child"]] = child
|
|
return asm.build()
|
|
|
|
|
|
def gen_step():
|
|
return assemble()
|
|
|
|
|
|
def gen_urdf():
|
|
from lyra_parts.description import build_urdf
|
|
|
|
return {"xml": build_urdf()}
|
|
|
|
|
|
def gen_srdf():
|
|
from lyra_parts.description import build_srdf
|
|
|
|
return {"xml": build_srdf(), "urdf": "lyra.urdf"}
|