// ============================================================================= // SaltyBot — Battery Stem Clamp (Part B) // Agent: sl-mechanical | 2026-02-28 // // HEIGHT-ADJUSTABLE battery carousel that slides on the central vertical stem. // 2–4 battery packs stand UPRIGHT, arranged radially around the mast. // // HOW IT WORKS // 1. Two collar halves clamp around the stem at the desired height. // 2. Radial arms project outward from the collar (one per battery pack). // 3. Each arm tip has a battery cradle — an upward-open U-channel that // the pack slides into from above. // 4. Velcro straps thread through cradle slots and cinch around the pack. // 5. Loosen the M6 collar bolts → slide up/down for CG tuning. // Tighten → locks in place. // // BATTERY (each pack, standing vertically): // 420 mm tall × 88 mm wide × 56 mm deep (verified) // // ANGULAR LAYOUT // BATT_COUNT = 2 → arms at 90° and 270° (±Y, balanced front/rear) // BATT_COUNT = 4 → arms at 45°, 135°, 225°, 315° (each collar half owns 2) // BATT_COUNT = 3 → arms at 90°, 210°, 330° // // PARTS (set RENDER= to export each) // collar_half — 3D print × 2 (mirror pair, RENDER="collar_half") // arm — laser-cut or print × BATT_COUNT (RENDER="arm_2d" for DXF) // battery_cradle — 3D print × BATT_COUNT (RENDER="cradle") // // STEM // 38.1 mm OD × 1.5 mm wall 6061-T6 aluminium tube (or 1.5" EMT conduit). // Cut to ~1050 mm. Clamp can sit anywhere from 150 mm to 850 mm height. // ============================================================================= $fn = 64; // ============================================================================= // STEM // ============================================================================= STEM_OD = 38.1; STEM_BORE = STEM_OD + 0.5; // collar bore clearance // ============================================================================= // COLLAR // ============================================================================= COLLAR_H = 80.0; // mm taller = more grip / less slip risk COLLAR_OD = 84.0; // mm outer diameter (wall = (84−38.6)/2 ≈ 22.7 mm) // Split plane: Y = 0 (each half is the +Y or −Y side) // Clamping bolts go through both halves at (±COLLAR_BOLT_X, 0, Z) COLLAR_BOLT_X = 24.0; // mm bolt ±X from stem axis COLLAR_BOLT_D = 6.5; // M6 clearance COLLAR_NUT_D = 11.0; // M6 hex nut AF + 0.5 mm tolerance (point-to-point ≈ 10.4, use 11) COLLAR_NUT_H = 5.2; // M6 standard nut height + 0.2 mm // Height-lock / anti-rotation set screw (M6 thread on outer face of each half) SETSCREW_D = 6.1; // through-hole for M6 set screw // Arm attachment pads on collar exterior (flat boss, one per arm) ARM_PAD_W = 32.0; // mm pad width (tangential) ARM_PAD_H = 18.0; // mm pad height ARM_PAD_T = 4.0; // mm pad protrusion from collar surface ARM_BOLT_D = 4.3; // M4 clearance (arm-to-collar bolt) ARM_BOLT_SPAN = 16.0; // mm C/L-to-C/L of two arm attachment bolts // ============================================================================= // ARMS & BATTERIES // ============================================================================= BATT_COUNT = 4; // 2, 3, or 4 BATT_L = 420.0; // mm pack height (vertical) BATT_W = 88.0; // mm pack width (tangential) BATT_D = 56.0; // mm pack depth (radial, into stem) BATT_CL = 0.8; // mm all-round clearance in cradle ARM_REACH = 55.0; // mm collar surface → battery near face ARM_W = 28.0; // mm arm width ARM_THICK = 8.0; // mm arm thickness (3D-print); 4 mm if laser-cut Al ARM_CRADLE_D = 4.3; // M4 clearance (cradle-to-arm bolt) // ============================================================================= // BATTERY CRADLE // ============================================================================= CRADLE_H = 80.0; // mm cradle height (pack extends BATT_L-CRADLE_H above) CRADLE_WALL = 4.5; // mm wall thickness CRADLE_STRAP_W = 25.0; // mm Velcro strap slot width CRADLE_STRAP_T = 6.0; // mm slot height // ============================================================================= // ANGULAR PLACEMENT // ============================================================================= // First arm angle chosen so all arms are clear of the Y=0 split plane ARM_START = (BATT_COUNT == 2) ? 90 : (BATT_COUNT == 4) ? 45 : /* 3 */ 90 ; // Helper: is arm i on the +Y half (side=+1) or −Y half (side=−1)? // side = +1 → sin(angle) >= 0 // side = -1 → sin(angle) < 0 function arm_angle(i) = ARM_START + i * (360 / BATT_COUNT); function arm_on_side(i, side) = (side > 0) ? (sin(arm_angle(i)) >= -0.001) : (sin(arm_angle(i)) <= 0.001); // ============================================================================= // RENDER CONTROL // ============================================================================= // "assembly" — full 3-D preview with ghosts // "collar_half" — single collar half for printing (print 2, one mirrored) // "arm" — single arm for printing or laser-cut // "arm_2d" — 2-D DXF projection of arm // "cradle" — single battery cradle for printing RENDER = "assembly"; if (RENDER == "assembly") { assembly(); } else if (RENDER == "collar_half") { collar_half(side=1); } else if (RENDER == "arm") { arm(); } else if (RENDER == "arm_2d") { projection(cut=true) translate([0, 0, -ARM_THICK/2]) arm(); } else if (RENDER == "cradle") { battery_cradle(); } // ============================================================================= // ASSEMBLY // ============================================================================= module assembly() { // Collar halves color("LightSlateGray", 0.88) collar_half(side= 1); color("SlateGray", 0.88) collar_half(side=-1); // Arms + cradles at each battery position for (i = [0 : BATT_COUNT - 1]) { a = arm_angle(i); rotate([0, 0, a]) { // Arm: originates at collar surface, runs along +X color("DimGray", 0.90) translate([COLLAR_OD/2, 0, (COLLAR_H - ARM_THICK) / 2]) arm(); // Cradle: at arm tip color("SteelBlue", 0.85) translate([COLLAR_OD/2 + ARM_REACH, -(BATT_W/2 + BATT_CL + CRADLE_WALL), (COLLAR_H - CRADLE_H) / 2]) battery_cradle(); // Battery ghost (not for export) %color("DarkGoldenrod", 0.30) translate([COLLAR_OD/2 + ARM_REACH + CRADLE_WALL, -(BATT_W/2 + BATT_CL), (COLLAR_H - CRADLE_H) / 2]) cube([BATT_D + 2*BATT_CL, BATT_W + 2*BATT_CL, BATT_L]); } } // Stem ghost %color("Gray", 0.20) translate([0, 0, -(COLLAR_H * 2)]) cylinder(d=STEM_OD, h=COLLAR_H * 14); } // ============================================================================= // COLLAR HALF // ============================================================================= // Printed flat-side-down (split face = print bed). // Print TWO: one as-is (side=+1), one mirrored in slicer (side=−1). // They are identical; the mirror instruction handles orientation. // // Bolt pattern: // 4× M6 through the flat face (2 per half at ±COLLAR_BOLT_X) // M6 hex nut pockets on the flat face (captured before assembly) // 1× M6 set screw on the outer curved surface (height lock) // // Arm attachment: // Raised pad on outer curved surface at each arm angle for this half. // 2× M4 through-holes per pad; M4 hex nut pocket on inside of collar wall. module collar_half(side = 1) { mid_z = COLLAR_H / 2; wall_t = (COLLAR_OD - STEM_BORE) / 2; // Half of collar: the Y≥0 half (side=+1) or Y≤0 half (side=−1) difference() { union() { // ── Half-cylinder body ────────────────────────────────────── intersection() { cylinder(d=COLLAR_OD, h=COLLAR_H); // Keep only the appropriate half translate([-COLLAR_OD/2 - 1, (side > 0) ? 0 : -COLLAR_OD - 1, -1]) cube([COLLAR_OD + 2, COLLAR_OD + 1, COLLAR_H + 2]); } // ── Arm attachment pads ────────────────────────────────────── for (i = [0 : BATT_COUNT - 1]) { if (arm_on_side(i, side)) { a = arm_angle(i); rotate([0, 0, a]) translate([COLLAR_OD/2, -ARM_PAD_W/2, mid_z - ARM_PAD_H/2]) cube([ARM_PAD_T, ARM_PAD_W, ARM_PAD_H]); } } } // ── Stem bore ──────────────────────────────────────────────────── translate([0, 0, -1]) cylinder(d=STEM_BORE, h=COLLAR_H + 2); // ── Clamping bolt holes (2× through flat split face) ───────────── // Bolt axis: along Y (perpendicular to split plane) // Holes at (±COLLAR_BOLT_X, 0, COLLAR_H/3) and (±COLLAR_BOLT_X, 0, 2*COLLAR_H/3) for (bx = [-COLLAR_BOLT_X, COLLAR_BOLT_X]) for (bz = [COLLAR_H/3, 2*COLLAR_H/3]) translate([bx, -1, bz]) rotate([-90, 0, 0]) cylinder(d=COLLAR_BOLT_D, h=COLLAR_OD/2 + 2); // ── M6 nut pockets on outer flat face of each half ─────────────── // Pocket depth = COLLAR_NUT_H from the far curved side inward. // This allows pre-installing the nuts before bolting the halves together. for (bx = [-COLLAR_BOLT_X, COLLAR_BOLT_X]) for (bz = [COLLAR_H/3, 2*COLLAR_H/3]) translate([bx, side * (COLLAR_OD/2 - COLLAR_NUT_H), bz]) rotate([-90, 0, 0]) cylinder(d=COLLAR_NUT_D, h=COLLAR_NUT_H + 1, $fn=6); // ── Set screw hole (M6, on curved outer surface at mid-height) ──── translate([0, side * (COLLAR_OD/2 + 1), COLLAR_H/2]) rotate([90, 0, 0]) cylinder(d=SETSCREW_D, h=COLLAR_OD/2 + 2); // ── Arm bolt holes + nut pockets (through collar wall per arm) ──── for (i = [0 : BATT_COUNT - 1]) { if (arm_on_side(i, side)) { a = arm_angle(i); for (dy = [-ARM_BOLT_SPAN/2, ARM_BOLT_SPAN/2]) rotate([0, 0, a]) translate([STEM_BORE/2 - 1, dy, mid_z]) rotate([0, 90, 0]) { // Through-hole (M4 clearance all the way) cylinder(d=ARM_BOLT_D, h=COLLAR_OD/2 - STEM_BORE/2 + ARM_PAD_T + 2); // Nut pocket on bore interior face cylinder(d=10, h=4.5, $fn=6); } } } } } // ============================================================================= // ARM // ============================================================================= // Flat bar, ARM_REACH × ARM_W × ARM_THICK. // Collar end: 2× M4 clearance holes at ±ARM_BOLT_SPAN/2 in Y. // Cradle end: 2× M4 clearance holes at ±(ARM_W/2 - 8) in Y. // Can be laser-cut from 4 mm Al plate (reduce ARM_THICK to 4 in RENDER="arm_2d"). module arm() { difference() { translate([0, -ARM_W/2, 0]) cube([ARM_REACH, ARM_W, ARM_THICK]); // Collar-end bolt holes (M4, match arm pad on collar) for (dy = [-ARM_BOLT_SPAN/2, ARM_BOLT_SPAN/2]) translate([8, dy, -1]) cylinder(d=ARM_BOLT_D, h=ARM_THICK + 2); // Cradle-end bolt holes (M4) for (dy = [-(ARM_W/2 - 8), ARM_W/2 - 8]) translate([ARM_REACH - 10, dy, -1]) cylinder(d=ARM_CRADLE_D, h=ARM_THICK + 2); // Lightening slot in centre (optional — reduces print material) if (ARM_REACH > 40) { slot_l = ARM_REACH - 34; slot_w = ARM_W - 16; translate([17, -slot_w/2, -1]) hull() { translate([slot_w/2, slot_w/2, 0]) cylinder(d=slot_w/2*0.8, h=ARM_THICK+2); translate([slot_l - slot_w/2, slot_w/2, 0]) cylinder(d=slot_w/2*0.8, h=ARM_THICK+2); } } } } // ============================================================================= // BATTERY CRADLE // ============================================================================= // U-channel, open top for pack insertion from above. // Inner pocket: (BATT_D + 2*BATT_CL) radially × (BATT_W + 2*BATT_CL) tangentially. // Cradle height CRADLE_H — battery extends (BATT_L − CRADLE_H) above the cradle. // // Strap slots: 2× horizontal slots through front+rear walls (Velcro through-pass). // Base bolt holes: 2× M4 for arm attachment (arm bolts up through arm into cradle). module battery_cradle() { cw = CRADLE_WALL; id = BATT_D + 2*BATT_CL; // inner depth (radial, +X direction) iw = BATT_W + 2*BATT_CL; // inner width (tangential, Y direction) difference() { // Outer block cube([id + 2*cw, iw + 2*cw, CRADLE_H]); // Battery slot (open top: subtract from cw to top + 1) translate([cw, cw, -1]) cube([id, iw, CRADLE_H + 2]); // Strap slots — through left and right walls (Y faces), 2 heights for (sz = [CRADLE_H * 0.30, CRADLE_H * 0.65]) translate([-1, cw + (iw - CRADLE_STRAP_W) / 2, sz]) cube([id + 2*cw + 2, CRADLE_STRAP_W, CRADLE_STRAP_T]); // Arm attachment holes in floor (2× M4) for (dy = [cw + iw/2 - ARM_BOLT_SPAN/2, cw + iw/2 + ARM_BOLT_SPAN/2]) translate([cw + id/2, dy, -1]) cylinder(d=ARM_CRADLE_D, h=cw + 2); // Corner chamfers (front face — aids pack insertion) chamfer_s = 5; for (cy = [cw - 0.01, cw + iw - chamfer_s + 0.01]) translate([cw - 0.01, cy, CRADLE_H - chamfer_s]) rotate([0, 45, 0]) cube([chamfer_s * 1.42, chamfer_s, chamfer_s * 1.42]); } } // ============================================================================= // DXF / PRINT EXPORT // ============================================================================= // // COLLAR HALF (3D print × 2 — print one as-is, mirror second in slicer): // openscad stem_battery_clamp.scad -D 'RENDER="collar_half"' -o collar_half.stl // Print settings: PETG, 5 perimeters, 40% infill, 0.2 mm layer, no supports needed // (flat split face sits on bed; overhangs ≤ 45°) // // ARM — 3D print or laser-cut × BATT_COUNT: // Print: openscad stem_battery_clamp.scad -D 'RENDER="arm"' -o arm.stl // Laser (DXF): openscad stem_battery_clamp.scad \ // -D 'RENDER="arm_2d"' -D 'ARM_THICK=4' -o arm.dxf // Laser material: 4 mm 5052-H32 aluminium // // BATTERY CRADLE (3D print × BATT_COUNT): // openscad stem_battery_clamp.scad -D 'RENDER="cradle"' -o cradle.stl // Print settings: PETG, 4 perimeters, 30% infill, 0.25 mm layer // // ============================================================================= // // ASSEMBLY SEQUENCE // 1. Print collar halves × 2, cradles × BATT_COUNT. // 2. Laser-cut (or print) arms × BATT_COUNT. // 3. Press M4 hex nuts into collar bore-face pockets. // 4. Wrap collar halves around stem; thread M6 bolts through both halves. // Do not fully tighten yet — position to desired height. // 5. Bolt each arm to its collar pad (M4 × 20 SHCS from arm outward). // 6. Bolt each cradle to its arm tip (M4 × 16 SHCS from below). // 7. Drop battery packs into cradles from above; route Velcro straps. // 8. Tighten M6 collar bolts (≈ 6 N·m each). Use M6 set screw for rotation lock. // // CG TUNING // Loosen M6 collar bolts (do not fully remove). Slide entire carousel up/down. // Re-tighten. Typical balance point: batteries at 400–600 mm above base plate. // =============================================================================