Files
cadSet/text-to-cad/models/robots/juno/juno_parts/pelvis.py
T
2026-07-22 19:38:36 +08:00

261 lines
7.8 KiB
Python

"""juno pelvis: waist-yaw ring, graphite chassis, hip pods, shell fairings.
Local frame: origin = waist-yaw joint center at the TOP of the pelvis.
Geometry spans z -120..+2. Robot faces +X, +Y robot-left, +Z up.
Locked interfaces (do not move):
- Waist ring: aluminum cylinder dia 96, z -2..+2, centered on Z.
- Hip pods: flat round mounting face dia 80 at z=-120 centered (0, +-90, -120);
everything below z=-120 stays clear for the leg hip-yaw actuator (dia 76).
- Envelope: x -65..+65, |y| <= 115 (pod faces at y=+-90 r40 reach 122 by the
locked interface itself).
"""
from __future__ import annotations
import sys
from pathlib import Path
import math
from build123d import (
Axis,
Box,
Cylinder,
Plane,
Pos,
RectangleRounded,
Rot,
chamfer,
extrude,
fillet,
)
from .juno_lib import (
ALU_COLOR,
SHELL_COLOR,
STRUCT_COLOR,
part_compound,
styled,
)
def _safe(op, solid):
"""Run a fillet/chamfer op; fall back to the input solid on any failure."""
try:
out = op(solid)
if out is not None and out.volume > 1.0:
return out
except Exception:
pass
return solid
def _zband(solid, lo, hi):
return solid.edges().filter_by_position(Axis.Z, lo, hi)
# ---------------------------------------------------------------- chassis ---
def _waist_deck():
deck = Pos(0, 0, -8) * Box(104, 118, 12)
deck = _safe(lambda s: fillet(s.edges().filter_by(Axis.Z), 14), deck)
deck = _safe(lambda s: chamfer(_zband(s, -2.01, -1.99), 2.0), deck)
deck = _safe(lambda s: chamfer(_zband(s, -14.01, -13.99), 2.0), deck)
return deck
def _abdomen_column():
try:
sk = Plane(
origin=(0, 0, -14), x_dir=(1, 0, 0), z_dir=(0, 0, -1)
) * RectangleRounded(86, 96, 13)
col = extrude(sk, amount=58, taper=6)
if col.volume > 1.0:
return col
except Exception:
pass
return Pos(0, 0, -43) * Box(80, 90, 58)
def _hip_yoke():
yoke = Pos(0, 0, -86) * Box(76, 164, 36)
yoke = _safe(lambda s: chamfer(s.edges().filter_by(Axis.Y), 5.0), yoke)
return yoke
def _pod(side):
return Pos(0, side * 90, -86) * Cylinder(radius=40, height=56)
def _chassis():
chassis = _waist_deck() + _abdomen_column() + _hip_yoke()
chassis = chassis + _pod(+1) + _pod(-1)
# Recessed service pockets on yoke front/back faces.
for sx in (+1, -1):
for sy in (+1, -1):
chassis -= Pos(sx * 38, sy * 48, -86) * Box(4, 36, 22)
# Panel seam groove around each pod above the aluminum rim.
for sy in (+1, -1):
ring = Pos(0, sy * 90, -100) * (
Cylinder(radius=45, height=1.8) - Cylinder(radius=37.5, height=1.8)
)
chassis -= ring
return styled(chassis, "pelvis_chassis", STRUCT_COLOR)
# ----------------------------------------------------------------- accents ---
def _waist_ring():
ring = Cylinder(radius=48, height=4)
ring = _safe(lambda s: chamfer(s.edges(), 0.8), ring)
return styled(ring, "pelvis_waist_ring", ALU_COLOR)
def _pod_rim(side, tag):
rim = Pos(0, side * 90, -117) * Cylinder(radius=40, height=6)
rim = _safe(lambda s: chamfer(_zband(s, -120.01, -119.9), 1.0), rim)
rim = _safe(lambda s: chamfer(_zband(s, -114.1, -113.9), 0.8), rim)
# machined witness groove
rim -= Pos(0, side * 90, -116) * (
Cylinder(radius=41, height=1.0) - Cylinder(radius=39, height=1.0)
)
return styled(rim, f"pelvis_pod_rim_{tag}", ALU_COLOR)
def _pod_collar(side, tag):
collar = Pos(0, side * 90, -83) * (
Cylinder(radius=42.5, height=4) - Cylinder(radius=40, height=4)
)
collar = _safe(lambda s: chamfer(_zband(s, -85.01, -84.9), 0.7), collar)
return styled(collar, f"pelvis_pod_collar_{tag}", ALU_COLOR)
# ---------------------------------------------------------------- fairings ---
def _fairing(side, tag):
s = side
# Volumetric upper hip shell: y 56..112, z -76..-12, x -58..58.
blank = Pos(0, s * 84, -44) * Box(116, 56, 64)
blank = _safe(lambda b: fillet(b.edges().filter_by(Axis.Z), 14), blank)
# Plan-view 45 deg facets at the outer-front / outer-rear corners.
blank -= (
Pos(50, s * 100, -44) * Rot(0, 0, -s * 45.0) * Pos(16, 0, 0) * Box(32, 64, 84)
)
blank -= (
Pos(-50, s * 100, -44) * Rot(0, 0, s * 45.0) * Pos(-16, 0, 0) * Box(32, 64, 84)
)
blank = _safe(lambda b: fillet(b.edges().filter_by(Axis.Z), 5.0), blank)
# Gentle top-outer slope (hip shoulder).
blank -= (
Pos(0, s * 72, -12)
* Rot(-18.0 * s, 0, 0)
* Pos(0, 0, 36)
* Box(150, 170, 72)
)
# Gentle front-top and rear-top slopes.
blank -= Pos(38, s * 84, -13) * Rot(0, 20, 0) * Pos(0, 0, 36) * Box(110, 150, 72)
blank -= Pos(-38, s * 84, -13) * Rot(0, -20, 0) * Pos(0, 0, 36) * Box(110, 150, 72)
# Mild bottom tucks: lower edge rises toward front and rear.
blank -= (
Pos(28, s * 84, -76) * Rot(0, -14, 0) * Pos(0, 0, -36) * Box(130, 150, 72)
)
blank -= (
Pos(-28, s * 84, -76) * Rot(0, 14, 0) * Pos(0, 0, -36) * Box(130, 150, 72)
)
# Angled under-edge: bottom rises inboard so the shell lower line meets
# the yoke clearance (-64 at y=56) flush, no stepped notch.
blank -= (
Pos(0, s * 56, -64)
* Rot(-12.0 * s, 0, 0)
* Pos(0, 0, -36)
* Box(150, 170, 72)
)
# NOTE: no post-cut fillets here. OCCT's fillet on the sloped-cut edge
# network can throw inside ChFi3d and the unwind wedges the process under
# Rosetta, so the sculpted creases stay crisp by design.
# Clearance cuts: ride over the chassis with a visible seam gap.
blank -= Pos(0, s * 90, -88) * Cylinder(radius=43.5, height=70) # pod dome
blank -= Pos(0, s * 60, -86) * Box(84, 120, 44) # yoke
blank -= Pos(0, 0, -7) * Box(110, 126, 14) # waist deck
# Angled vent grooves on the front 45 deg facet.
ux, uy = math.cos(math.radians(45)), math.sin(math.radians(45))
for d in (-8.0, 0.0, 8.0):
cx = 50.0 + d * ux + 2.5 * ux
cy = s * (100.0 - d * uy + 2.5 * uy)
blank -= Pos(cx, cy, -48) * Rot(0, 0, -s * 45.0) * Box(3.0, 8.0, 20.0)
# Horizontal panel-line groove across the outer face.
blank -= Pos(0, s * 112.8, -42) * Box(60.0, 4.0, 2.2)
return styled(blank, f"pelvis_fairing_{tag}", SHELL_COLOR)
# ------------------------------------------------------------- crotch guard --
def _crotch_guard():
# Faceted plate built in a local frame, then tilted and pushed to the
# front of the yoke.
p = Box(8, 46, 40)
front_vert = (
lambda g: chamfer(
g.edges().filter_by(Axis.Z).filter_by_position(Axis.X, 3.9, 4.1),
6.0,
)
)
p = _safe(front_vert, p)
p = _safe(
lambda g: chamfer(
g.edges()
.filter_by(Axis.Y)
.filter_by_position(Axis.Z, -20.1, -19.9)
.filter_by_position(Axis.X, 0.0, 4.2),
5.0,
),
p,
)
p = _safe(
lambda g: chamfer(
g.edges()
.filter_by(Axis.Y)
.filter_by_position(Axis.Z, 19.9, 20.1)
.filter_by_position(Axis.X, 0.0, 4.2),
3.0,
),
p,
)
guard = Pos(50, 0, -86) * Rot(0, 4, 0) * p
# Base wedge filling the gap back to the yoke front face (x=38).
guard += Pos(42.75, 0, -88) * Box(9.5, 40, 32)
return styled(guard, "pelvis_crotch_guard", STRUCT_COLOR)
# ------------------------------------------------------------------- part ---
def build_pelvis():
children = [
_chassis(),
_waist_ring(),
_pod_rim(+1, "left"),
_pod_rim(-1, "right"),
_pod_collar(+1, "left"),
_pod_collar(-1, "right"),
_fairing(+1, "left"),
_fairing(-1, "right"),
_crotch_guard(),
]
return part_compound("pelvis", children)
def gen_step():
return build_pelvis()