Files
2026-07-22 13:48:46 +08:00

195 lines
6.4 KiB
Python

"""Split-clamp actuator mounts for the leg-wheel example."""
from __future__ import annotations
import simplecadapi as scad
from leg_common import make_part_with_connectors_rpart
from leg_dimensions import (
BODY_STANDOFF_THICKNESS,
BODY_STANDOFF_Z,
CASE_CLAMP_INNER_RADIUS,
CASE_CLAMP_OUTER_RADIUS,
CASE_CLAMP_PINCH_AXIS_RADIUS,
CASE_CLAMP_PINCH_HALF_SPAN,
CASE_CLAMP_PINCH_HOLE_RADIUS,
CASE_CLAMP_SLIT_WIDTH,
CASE_CLAMP_WIDTH,
KNEE_CASE_CLAMP_Z,
ROOT_AXIS,
THIGH_CASE_CLAMP_Z,
)
def make_split_case_clamp_rsolid(
*, center: tuple[float, float, float], z_center: float, tag: str
) -> scad.Solid:
"""Create one machinable C-clamp with coaxial pinch-bolt ears."""
z_min = z_center - CASE_CLAMP_WIDTH / 2.0
outer = scad.make_cylinder_rsolid(
radius=CASE_CLAMP_OUTER_RADIUS,
height=CASE_CLAMP_WIDTH,
bottom_face_center=(center[0], center[1], z_min),
axis=(0.0, 0.0, 1.0),
)
bore = scad.make_cylinder_rsolid(
radius=CASE_CLAMP_INNER_RADIUS,
height=CASE_CLAMP_WIDTH + 2.0,
bottom_face_center=(center[0], center[1], z_min - 1.0),
axis=(0.0, 0.0, 1.0),
)
clamp = scad.cut_rsolid(outer, bore, skip_non_intersecting=False)
ear_center_x = center[0] + CASE_CLAMP_PINCH_AXIS_RADIUS - 2.5
ear_center_y = CASE_CLAMP_SLIT_WIDTH / 2.0 + 2.5
ears = [
scad.make_box_rsolid(
width=8.0,
height=5.0,
depth=CASE_CLAMP_WIDTH,
bottom_face_center=(ear_center_x, center[1] + sign * ear_center_y, z_min),
)
for sign in (-1.0, 1.0)
]
clamp = scad.union_rsolid(clamp, ears, glue=False)
slit = scad.make_box_rsolid(
width=15.0,
height=CASE_CLAMP_SLIT_WIDTH,
depth=CASE_CLAMP_WIDTH + 2.0,
bottom_face_center=(center[0] + 31.0, center[1], z_min - 1.0),
)
pinch_hole = scad.make_cylinder_rsolid(
radius=CASE_CLAMP_PINCH_HOLE_RADIUS,
height=CASE_CLAMP_PINCH_HALF_SPAN * 2.0,
bottom_face_center=(
center[0] + CASE_CLAMP_PINCH_AXIS_RADIUS,
center[1] - CASE_CLAMP_PINCH_HALF_SPAN,
z_center,
),
axis=(0.0, 1.0, 0.0),
)
clamp = scad.cut_rsolid(
clamp,
slit,
pinch_hole,
skip_non_intersecting=False,
)
clamp = scad.apply_tag(shape=clamp, tag=tag)
print(
f"{tag}: bore_d={CASE_CLAMP_INNER_RADIUS * 2.0:.2f} "
f"pinch_hole_d={CASE_CLAMP_PINCH_HOLE_RADIUS * 2.0:.2f} "
f"faces={len(clamp.get_faces())} volume={clamp.get_volume():.3f}"
)
return clamp
def make_body_mount_plate_rpart(*, material: scad.Material) -> scad.Part:
"""Build the two-collar body mount for coaxial tandem root actuators."""
lower = make_split_case_clamp_rsolid(
center=ROOT_AXIS,
z_center=THIGH_CASE_CLAMP_Z,
tag="role.thigh_actuator_split_clamp",
)
upper = make_split_case_clamp_rsolid(
center=ROOT_AXIS,
z_center=KNEE_CASE_CLAMP_Z,
tag="role.knee_drive_split_clamp",
)
lower_z = THIGH_CASE_CLAMP_Z - CASE_CLAMP_WIDTH / 2.0
upper_z = KNEE_CASE_CLAMP_Z + CASE_CLAMP_WIDTH / 2.0
post_height = upper_z - lower_z
post_offset_x = 35.0
posts = [
scad.make_cylinder_rsolid(
radius=3.8,
height=post_height,
bottom_face_center=(ROOT_AXIS[0] + sign * post_offset_x, ROOT_AXIS[1], lower_z),
axis=(0.0, 0.0, 1.0),
)
for sign in (-1.0, 1.0)
]
collar_bridges = [
scad.make_box_rsolid(
width=8.0,
height=7.6,
depth=CASE_CLAMP_WIDTH,
bottom_face_center=(
ROOT_AXIS[0] + sign * 33.0,
ROOT_AXIS[1],
clamp_z - CASE_CLAMP_WIDTH / 2.0,
),
)
for sign in (-1.0, 1.0)
for clamp_z in (THIGH_CASE_CLAMP_Z, KNEE_CASE_CLAMP_Z)
]
lug_z_min = BODY_STANDOFF_Z - BODY_STANDOFF_THICKNESS / 2.0
lugs = [
scad.make_box_rsolid(
width=24.0,
height=18.0,
depth=BODY_STANDOFF_THICKNESS,
bottom_face_center=(ROOT_AXIS[0] + sign * 41.0, ROOT_AXIS[1], lug_z_min),
)
for sign in (-1.0, 1.0)
]
mount = scad.union_rsolid(lower, upper, posts, collar_bridges, lugs, glue=False)
torso_holes = [
scad.make_cylinder_rsolid(
radius=2.3,
height=BODY_STANDOFF_THICKNESS + 2.0,
bottom_face_center=(ROOT_AXIS[0] + sign * 45.0, ROOT_AXIS[1], lug_z_min - 1.0),
axis=(0.0, 0.0, 1.0),
)
for sign in (-1.0, 1.0)
]
mount = scad.cut_rsolid(mount, torso_holes, skip_non_intersecting=False)
mount = scad.apply_tag(shape=mount, tag="role.body_mount_plate")
print(
"body_mount_plate: root_actuators=2 tandem=true "
f"clamp_z=({THIGH_CASE_CLAMP_Z:.1f},{KNEE_CASE_CLAMP_Z:.1f}) "
f"torso_holes=2 faces={len(mount.get_faces())} volume={mount.get_volume():.3f}"
)
return make_part_with_connectors_rpart(
part_id="body_mount_plate",
body=mount,
name="Coaxial tandem root actuator split-clamp and body lug bracket",
material=material,
connectors=(
("case_axis", (ROOT_AXIS[0], ROOT_AXIS[1], THIGH_CASE_CLAMP_Z), "z", "Thigh actuator clamp datum"),
(
"knee_drive_case_axis",
(ROOT_AXIS[0], ROOT_AXIS[1], KNEE_CASE_CLAMP_Z),
"z",
"Knee-drive actuator clamp datum opposite the crank",
),
(
"thigh_clamp_bolt_seat",
(
ROOT_AXIS[0] + CASE_CLAMP_PINCH_AXIS_RADIUS,
ROOT_AXIS[1] + CASE_CLAMP_PINCH_HALF_SPAN - 0.9,
THIGH_CASE_CLAMP_Z,
),
"y",
"Thigh collar M4 bolt head seat",
),
(
"knee_clamp_bolt_seat",
(
ROOT_AXIS[0] + CASE_CLAMP_PINCH_AXIS_RADIUS,
ROOT_AXIS[1] + CASE_CLAMP_PINCH_HALF_SPAN - 0.9,
KNEE_CASE_CLAMP_Z,
),
"y",
"Knee-drive collar M4 bolt head seat",
),
(
"body_frame_axis",
(ROOT_AXIS[0], ROOT_AXIS[1], BODY_STANDOFF_Z),
"z",
"Body frame datum",
),
),
)