ARCHITECTURE CHANGE: batteries no longer sit flat on the base plate. They mount VERTICALLY on a central mast via a height-adjustable carousel. CG is tuned by sliding the carousel up/down the stem. Part A — prototype_baseplate.scad (Rev C): - PLATE_DEPTH: 210mm → 130mm (no battery footprint constraint) - Removed all battery tray geometry (holes, strap slots, expansion mounts) - Added central stem socket: Ø38.6mm bore + 4x M5 flange bolt holes on Ø66mm BC - Added stem_flange() module: laser-cut ring (qty 2, one each side of plate) - Wiring pass-through slots flanking stem centre - FC mount relocated to FC_X_OFFSET = -40mm (front of plate, clear of stem) - New RENDER="stem_flange_2d" DXF export option Part B — stem_battery_clamp.scad (new): - Collar: two 3D-printed D-shaped halves, split at Y=0 - Ø38.6mm bore (1.5" EMT / 6061-T6 tube) - 4x M6 clamping bolts + hex nut pockets - 1x M6 set screw per half for height/rotation lock - Arm attachment pads with M4 through-holes + nut pockets - Arms: flat bars, laser-cut or printed, ARM_REACH=55mm - Battery cradles: U-channel, open top, Velcro strap slots at 30% + 65% height - BATT_COUNT param: 2 (180°), 3 (120°), or 4 (90°) radial batteries - ARM_START_ANGLE chosen per BATT_COUNT to keep all arms clear of Y=0 split - Battery ghosts in assembly for visualisation - Full RENDER control: assembly / collar_half / arm / arm_2d / cradle - Assembly sequence + CG tuning notes in file footer BOM.md → Rev C: - Part A table updated (5 laser-cut parts + stem tube) - Part B table added (collar halves, arms, cradles, fasteners) - Battery section: flat-deck layout replaced with vertical stem guide - Fastener table updated to match new architecture Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
379 lines
16 KiB
OpenSCAD
379 lines
16 KiB
OpenSCAD
// =============================================================================
|
||
// 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.
|
||
// =============================================================================
|