3× MaUWB ESP32-S3 follow-me UWB system: 1 wearable tag, 2 robot anchors. chassis/uwb_tag_enclosure.scad Belt-clip enclosure for MaUWB PCB (~50×25×10 mm) + TP4056 micro-USB charger + 18650 cell. Snap-fit PETG shell + TPU 95A bumper sleeve. IP44-ish 4 mm overlap + 2-turn labyrinth seam. Open antenna window in lid (no PLA within 10 mm of UWB antenna). Power switch cutout (Y− face), micro-USB port (X− face), LED window hole (Y+ face). Belt clip integrated (PETG spring arm, 42 mm belt slot). RENDER: body/lid/tpu_bumper/assembly. chassis/uwb_anchor_mount.scad Stem-mounted anchor bracket for 25 mm OD stem. Split D-collar with M4 thumbscrews (tool-free), M4 hex nut pockets, M4 set screw height lock. Anti-rotation flat tab on front half prevents axial rotation without stem modification. USB cable routing channel in rear half. Module bracket tilted 10° outward — antenna faces horizon, clears stem metal. Back-wall cutout behind antenna section (10 mm clearance). 250 mm anchor spacing (RENDER "pair" shows both on stem section). RENDER: collar_front/collar_rear/ bracket/assembly/pair. chassis/uwb_assembly.md Full assembly notes: antenna clearance rules, IP44 seam description, stem positioning diagram (anchors at 450 mm + 700 mm), USB cable routing, complete BOM (~300 g total, tag ~130 g). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
276 lines
12 KiB
OpenSCAD
276 lines
12 KiB
OpenSCAD
// ============================================================
|
||
// uwb_anchor_mount.scad — Stem-Mounted UWB Anchor Rev A
|
||
// Agent: sl-mechanical 2026-03-01
|
||
// Closes issues #57, #62
|
||
// ============================================================
|
||
// Clamp-on bracket for 2× MaUWB ESP32-S3 anchor modules on
|
||
// SaltyBot 25 mm OD vertical stem.
|
||
// Anchors spaced ANCHOR_SPACING = 250 mm apart.
|
||
//
|
||
// Features:
|
||
// • Split D-collar with M4 clamping bolts + M4 set screw
|
||
// • Anti-rotation flat tab that keys against a small pin
|
||
// OR printed key tab that registers on the stem flat (if stem
|
||
// has a ground flat) — see ANTI_ROT_MODE parameter
|
||
// • Module bracket: faces outward, tilted 10° from vertical
|
||
// so antenna clears stem and faces horizon
|
||
// • USB cable channel (power from Orin via USB-A) on collar
|
||
// • Tool-free capture: M4 thumbscrews (slot-head, hand-tighten)
|
||
// • UWB antenna area: NO material within 10 mm of PCB top face
|
||
//
|
||
// Components per mount:
|
||
// 2× collar_half print in PLA/PETG, flat-face-down
|
||
// 1× module_bracket print in PLA/PETG, flat-face-down
|
||
//
|
||
// RENDER options:
|
||
// "assembly" single mount assembled (default)
|
||
// "collar_front" front collar half for slicing (×2 per mount × 2 mounts = 4)
|
||
// "collar_rear" rear collar half
|
||
// "bracket" module bracket (×2 mounts)
|
||
// "pair" both mounts on 350 mm stem section
|
||
// ============================================================
|
||
|
||
RENDER = "assembly";
|
||
|
||
// ── ⚠ Verify with calipers ───────────────────────────────────
|
||
MAWB_L = 50.0; // PCB length
|
||
MAWB_W = 25.0; // PCB width
|
||
MAWB_H = 10.0; // PCB + components
|
||
MAWB_HOLE_X = 43.0; // M2 mounting hole X span
|
||
MAWB_HOLE_Y = 20.0; // M2 mounting hole Y span
|
||
M2_D = 2.2; // M2 clearance
|
||
|
||
// ── Stem ─────────────────────────────────────────────────────
|
||
STEM_OD = 25.0;
|
||
STEM_BORE = 25.4; // +0.4 clearance
|
||
WALL = 2.0; // wall thickness (used in thumbscrew recess)
|
||
|
||
// ── Collar ───────────────────────────────────────────────────
|
||
COL_OD = 52.0;
|
||
COL_H = 30.0; // taller than sensor-head collar for rigidity
|
||
COL_BOLT_X = 19.0; // M4 bolt CL from stem axis
|
||
COL_BOLT_D = 4.5; // M4 clearance
|
||
THUMB_HEAD_D= 8.0; // M4 thumbscrew head OD (slot for access)
|
||
COL_NUT_W = 7.0; // M4 hex nut A/F
|
||
COL_NUT_H = 3.4;
|
||
|
||
// Anti-rotation flat tab: a 3 mm wall tab that protrudes radially
|
||
// and bears against the bracket arm, preventing axial rotation
|
||
// without needing a stem flat.
|
||
ANTI_ROT_T = 3.0; // tab thickness (radial)
|
||
ANTI_ROT_W = 8.0; // tab width (tangential)
|
||
ANTI_ROT_Z = 4.0; // distance from collar base
|
||
|
||
// USB cable channel: groove on collar outer surface, runs Z direction
|
||
// Cable routes from anchor module down to base
|
||
USB_CHAN_W = 9.0; // channel width (fits USB-A cable Ø6 mm)
|
||
USB_CHAN_D = 5.0; // channel depth
|
||
|
||
// ── Module bracket ───────────────────────────────────────────
|
||
ARM_L = 20.0; // arm length from collar OD to bracket face
|
||
ARM_W = MAWB_W + 6.0; // bracket width (Y, includes side walls)
|
||
ARM_H = 6.0; // arm thickness (Z)
|
||
BRKT_TILT = 10.0; // tilt outward from vertical (antenna faces horizon)
|
||
|
||
BRKT_BACK_T = 3.0; // bracket back wall (module sits against this)
|
||
BRKT_SIDE_T = 2.0; // bracket side walls
|
||
|
||
M2_STNDFF = 3.0; // M2 standoff height
|
||
M2_STNDFF_OD= 4.5;
|
||
|
||
// USB port access notch in bracket side wall (8×5 mm)
|
||
USB_NOTCH_W = 10.0;
|
||
USB_NOTCH_H = 7.0;
|
||
|
||
// ── Spacing ───────────────────────────────────────────────────
|
||
ANCHOR_SPACING = 250.0; // centre-to-centre Z separation
|
||
|
||
$fn = 64;
|
||
e = 0.01;
|
||
|
||
// ─────────────────────────────────────────────────────────────
|
||
// collar_half(side)
|
||
// split at Y=0 plane. Bracket arm on front (+Y) half.
|
||
// Print flat-face-down.
|
||
// ─────────────────────────────────────────────────────────────
|
||
module collar_half(side = "front") {
|
||
y_front = (side == "front");
|
||
|
||
difference() {
|
||
union() {
|
||
// D-shaped body
|
||
intersection() {
|
||
cylinder(d=COL_OD, h=COL_H);
|
||
translate([-COL_OD/2, y_front ? 0 : -COL_OD/2, 0])
|
||
cube([COL_OD, COL_OD/2, COL_H]);
|
||
}
|
||
|
||
// Anti-rotation tab (front half only, at +X side)
|
||
if (y_front) {
|
||
translate([COL_OD/2, -ANTI_ROT_W/2, ANTI_ROT_Z])
|
||
cube([ANTI_ROT_T, ANTI_ROT_W,
|
||
COL_H - ANTI_ROT_Z - 4]);
|
||
}
|
||
|
||
// Bracket arm attachment boss (front half only, top centre)
|
||
if (y_front) {
|
||
translate([-ARM_W/2, COL_OD/2, COL_H * 0.3])
|
||
cube([ARM_W, ARM_L, COL_H * 0.4]);
|
||
}
|
||
}
|
||
|
||
// ── Stem bore ─────────────────────────────────────────
|
||
translate([0,0,-e])
|
||
cylinder(d=STEM_BORE, h=COL_H + 2*e);
|
||
|
||
// ── M4 clamping bolt holes (Y direction) ──────────────
|
||
for (bx=[-COL_BOLT_X, COL_BOLT_X]) {
|
||
translate([bx, y_front ? COL_OD/2 : 0, COL_H/2])
|
||
rotate([90,0,0])
|
||
cylinder(d=COL_BOLT_D, h=COL_OD/2 + e);
|
||
// Thumbscrew head recess on outer face (front only — access side)
|
||
if (y_front) {
|
||
translate([bx, COL_OD/2 - WALL, COL_H/2])
|
||
rotate([90,0,0])
|
||
cylinder(d=THUMB_HEAD_D, h=8 + e);
|
||
}
|
||
}
|
||
|
||
// ── M4 hex nut pockets (rear half) ────────────────────
|
||
if (!y_front) {
|
||
for (bx=[-COL_BOLT_X, COL_BOLT_X]) {
|
||
translate([bx, -(COL_OD/4 + e), COL_H/2])
|
||
rotate([90,0,0])
|
||
cylinder(d=COL_NUT_W/cos(30), h=COL_NUT_H + e,
|
||
$fn=6);
|
||
}
|
||
}
|
||
|
||
// ── Set screw (height lock, front half) ───────────────
|
||
if (y_front) {
|
||
translate([0, COL_OD/2, COL_H * 0.8])
|
||
rotate([90,0,0])
|
||
cylinder(d=COL_BOLT_D,
|
||
h=COL_OD/2 - STEM_BORE/2 + e);
|
||
}
|
||
|
||
// ── USB cable routing channel (rear half, −X side) ────
|
||
if (!y_front) {
|
||
translate([-COL_OD/2, -USB_CHAN_W/2, -e])
|
||
cube([USB_CHAN_D, USB_CHAN_W, COL_H + 2*e]);
|
||
}
|
||
|
||
// ── M4 hole through arm boss (Z direction, for bracket bolt) ─
|
||
if (y_front) {
|
||
for (dx=[-ARM_W/4, ARM_W/4])
|
||
translate([dx, COL_OD/2 + ARM_L/2, COL_H * 0.35])
|
||
cylinder(d=COL_BOLT_D, h=COL_H * 0.35 + e);
|
||
}
|
||
}
|
||
}
|
||
|
||
// ─────────────────────────────────────────────────────────────
|
||
// module_bracket()
|
||
// Bolts to collar arm boss. Holds MaUWB PCB facing outward.
|
||
// Tilted BRKT_TILT° from vertical — antenna clears stem.
|
||
// Print flat-face-down (back wall on bed).
|
||
// ─────────────────────────────────────────────────────────────
|
||
module module_bracket() {
|
||
bk = BRKT_BACK_T;
|
||
sd = BRKT_SIDE_T;
|
||
|
||
difference() {
|
||
union() {
|
||
// ── Back wall (mounts to collar arm boss) ─────────
|
||
cube([ARM_W, bk, MAWB_H + M2_STNDFF + 6]);
|
||
|
||
// ── Side walls ────────────────────────────────────
|
||
for (sx=[0, ARM_W - sd])
|
||
translate([sx, bk, 0])
|
||
cube([sd, MAWB_L + 2, MAWB_H + M2_STNDFF + 6]);
|
||
|
||
// ── M2 standoff posts (PCB mounts to these) ───────
|
||
for (hx=[0, MAWB_HOLE_X], hy=[0, MAWB_HOLE_Y])
|
||
translate([(ARM_W - MAWB_HOLE_X)/2 + hx,
|
||
bk + (MAWB_L - MAWB_HOLE_Y)/2 + hy,
|
||
0])
|
||
cylinder(d=M2_STNDFF_OD, h=M2_STNDFF);
|
||
}
|
||
|
||
// ── M2 bores through standoffs ────────────────────────
|
||
for (hx=[0, MAWB_HOLE_X], hy=[0, MAWB_HOLE_Y])
|
||
translate([(ARM_W - MAWB_HOLE_X)/2 + hx,
|
||
bk + (MAWB_L - MAWB_HOLE_Y)/2 + hy,
|
||
-e])
|
||
cylinder(d=M2_D, h=M2_STNDFF + e);
|
||
|
||
// ── Antenna clearance cutout in back wall ─────────────
|
||
// Open slot near top of back wall so antenna is unobstructed
|
||
translate([sd, -e, M2_STNDFF + 2])
|
||
cube([ARM_W - 2*sd, bk + 2*e, MAWB_H]);
|
||
|
||
// ── USB port access notch on one side wall ────────────
|
||
translate([-e, bk + 2, M2_STNDFF - 1])
|
||
cube([sd + 2*e, USB_NOTCH_W, USB_NOTCH_H]);
|
||
|
||
// ── Mounting holes to collar arm boss (×2) ────────────
|
||
for (dx=[-ARM_W/4, ARM_W/4])
|
||
translate([ARM_W/2 + dx, bk + ARM_L/2, -e])
|
||
cylinder(d=COL_BOLT_D, h=6 + e);
|
||
}
|
||
}
|
||
|
||
// ─────────────────────────────────────────────────────────────
|
||
// single_anchor_assembly()
|
||
// ─────────────────────────────────────────────────────────────
|
||
module single_anchor_assembly(show_phantom=false) {
|
||
// Collar
|
||
color("SteelBlue", 0.9) collar_half("front");
|
||
color("CornflowerBlue", 0.9) mirror([0,1,0]) collar_half("rear");
|
||
|
||
// Bracket tilted BRKT_TILT° outward from top of arm boss
|
||
color("LightSteelBlue", 0.85)
|
||
translate([0, COL_OD/2 + ARM_L, COL_H * 0.3])
|
||
rotate([BRKT_TILT, 0, 0])
|
||
translate([-ARM_W/2, 0, 0])
|
||
module_bracket();
|
||
|
||
// Phantom UWB PCB
|
||
if (show_phantom)
|
||
color("ForestGreen", 0.4)
|
||
translate([-MAWB_L/2,
|
||
COL_OD/2 + ARM_L + BRKT_BACK_T,
|
||
COL_H * 0.3 + M2_STNDFF])
|
||
cube([MAWB_L, MAWB_W, MAWB_H]);
|
||
}
|
||
|
||
// ─────────────────────────────────────────────────────────────
|
||
// Render selector
|
||
// ─────────────────────────────────────────────────────────────
|
||
if (RENDER == "assembly") {
|
||
single_anchor_assembly(show_phantom=true);
|
||
|
||
} else if (RENDER == "collar_front") {
|
||
collar_half("front");
|
||
|
||
} else if (RENDER == "collar_rear") {
|
||
collar_half("rear");
|
||
|
||
} else if (RENDER == "bracket") {
|
||
module_bracket();
|
||
|
||
} else if (RENDER == "pair") {
|
||
// Both anchors at 250 mm spacing on a stem stub
|
||
color("Silver", 0.2)
|
||
translate([0, 0, -50])
|
||
cylinder(d=STEM_OD, h=ANCHOR_SPACING + COL_H + 100);
|
||
|
||
// Lower anchor (Z = 0)
|
||
single_anchor_assembly(show_phantom=true);
|
||
|
||
// Upper anchor (Z = ANCHOR_SPACING)
|
||
translate([0, 0, ANCHOR_SPACING])
|
||
single_anchor_assembly(show_phantom=true);
|
||
}
|
||
|