// ============================================================================= // SaltyBot — Prototype Base Plate (Rev B — real battery dimensions) // Agent: sl-mechanical | 2026-02-28 // // Laser-cut or CNC-routed flat plate (6mm aluminium or 8mm acrylic). // Uses CALIPER-VERIFIED hub motor axle measurements — replaces placeholder // values from chassis_frame.scad (PR #7). // // ── BATTERY LAYOUT (real measurements — NOT placeholder) ───────────────────── // Each pack: 420 mm (L) × 88 mm (W) × 56 mm (H) // 2-pack (default): side-by-side in Y → footprint 420 × 176 mm on deck // 4-pack (expansion): 2 more packs on secondary underdeck tray // (4-wide = 352 mm exceeds robot width; in-line = 840 mm exceeds wheelbase) // Plate depth: 210 mm (176 mm battery + 17 mm margin each side) // // ── VERIFIED AXLE PROFILE (stepped D-cut) ──────────────────────────────────── // Zone │ Feature │ Diameter / Width │ Length // ───────┼──────────────────┼────────────────────┼───────── // Base │ Round (near hub) │ Ø 16.11 mm │ 15.00 mm // D-cut │ Round OD │ Ø 15.95 mm │ 43.35 mm // │ Flat chord │ 13.00 mm across │ // Tip │ Shoulder/end │ — │ 3.00 mm (tip lock nut seat) // Total │ Hub face → tip │ — │ 65.50 mm // ───────┼──────────────────┼────────────────────┼───────── // Bearing seat collar OD: Ø 37.8 mm (hub centre) // Tire: 10 × 2.125" pneumatic (254 mm OD, 54 mm wide, 35 PSI) // ============================================================================= $fn = 128; // ============================================================================= // AXLE PARAMETERS — caliper-verified (edit only when re-measured) // ============================================================================= AXLE_BASE_DIA = 16.11; // mm round section near hub face AXLE_BASE_LEN = 15.00; // mm length of base section AXLE_DCUT_DIA = 15.95; // mm round OD of D-cut zone AXLE_DCUT_FLAT = 13.00; // mm flat chord width (caliper across flat) AXLE_DCUT_LEN = 43.35; // mm D-cut section length AXLE_TIP_LEN = 3.00; // mm tip / shoulder AXLE_TOTAL = 65.50; // mm total protrusion from hub face BEARING_SEAT_OD = 37.80; // mm hub centre collar OD TIRE_OD = 254.0; // mm 10" × 25.4 TIRE_WIDTH = 54.0; // mm 2.125" × 25.4 AXLE_CL_HEIGHT = TIRE_OD / 2; // 127 mm — axle centre above ground // ============================================================================= // PLATE PARAMETERS // ============================================================================= WHEELBASE = 600.0; // mm axle C/L to axle C/L // PLATE_DEPTH driven by 2-pack battery width: 2 × 88 mm = 176 mm + 2×15 mm edge = 206 mm min. // 210 mm gives ~17 mm edge margin each side; increase if structural ribs are added. PLATE_DEPTH = 210.0; // mm front-to-rear (was 220; reduced to match 2-pack footprint) PLATE_OVERHANG = 40.0; // mm plate extends past axle C/L on each side PLATE_THICK = 6.0; // mm stock thickness (6 mm Al / 8 mm acrylic) // Fork (open-ended slot in plate edge) // Width matched to base-section diameter + 0.4 mm running clearance FORK_W = AXLE_BASE_DIA + 0.4; // 16.51 mm FORK_DEPTH = 50.0; // mm slot depth inward from plate edge // Clamp block (two-piece sandwich, bolts through plate) CLAMP_L = 80.0; // mm along axle axis CLAMP_H = 60.0; // mm across axle (front-to-back of plate) CLAMP_THICK = 8.0; // mm each clamp layer — 8 mm Al or 10 mm acrylic CLAMP_BOLT_D = 5.3; // M5 clearance CLAMP_BOLT_DX = 22.0; // mm bolt ±X from axle C/L CLAMP_BOLT_DY = 22.0; // mm bolt ±Y from axle C/L (across plate) CLAMP_ALIGN_D = 4.1; // Ø4 alignment pin clearance // D-cut bore clearance (applied to both diameter and flat chord) DCUT_CL = 0.3; // mm all-round clearance on D-cut profile // FC mount — MAMBA F722S 30.5 × 30.5 mm M3 stack FC_PITCH = 30.5; FC_HOLE_D = 3.2; // Battery — REAL MEASUREMENTS (each cell pack) // ┌──────────────────────────────────────────────────────────────────────────┐ // │ 420 mm (L) × 88 mm (W) × 56 mm (H) per pack │ // │ Default config: 2 packs side-by-side → footprint 420 × 176 mm │ // │ Expansion: 4 packs (2+2) → 4-pack requires underdeck shelf │ // │ — 4-pack side-by-side (352 mm wide) exceeds robot width envelope │ // │ — 4-pack in-line (840 mm long) exceeds wheelbase │ // │ → Only viable 4-pack layout: 2 on deck + 2 on secondary underdeck tray │ // └──────────────────────────────────────────────────────────────────────────┘ BATT_L = 420.0; // mm pack length (runs along X / wheelbase axis) BATT_W = 88.0; // mm pack width (runs along Y / front-rear axis) BATT_H = 56.0; // mm pack height (clearance for under-deck routing) BATT_PACKS = 2; // packs on this plate (2 or 4); 4 adds shelf mounts BATT_INSET = 18.0; // mm mount-hole inset from each pack end BATT_STRAP_W = 25.0; // mm Velcro strap slot width BATT_STRAP_T = 4.0; // mm strap slot depth (through-slot in plate) // Utility M3 = 3.2; M4 = 4.3; M5 = 5.3; // ============================================================================= // COMPUTED GEOMETRY (do not edit directly) // ============================================================================= // D-cut bore geometry (with clearance): // r = (AXLE_DCUT_DIA + 2·DCUT_CL) / 2 // fc = AXLE_DCUT_FLAT + 2·DCUT_CL (chord with clearance on each side) // d = distance from centre to flat = sqrt(r² – (fc/2)²) DCUT_R = (AXLE_DCUT_DIA + 2*DCUT_CL) / 2; // 8.275 mm DCUT_FC = AXLE_DCUT_FLAT + 2*DCUT_CL; // 13.60 mm // DCUT_D computed inside module (OpenSCAD has no sqrt at module-level constant) // Plate outer bounds PLATE_X_HALF = WHEELBASE/2 + PLATE_OVERHANG; // ± 340 mm // ============================================================================= // RENDER CONTROL // ============================================================================= // Set RENDER to one of: // "assembly" full 3-D preview (default) // "plate_2d" 2-D projection of base plate (→ DXF) // "clamp_lower_2d" 2-D projection of lower clamp (→ DXF) // "clamp_upper_2d" 2-D projection of upper clamp (→ DXF) RENDER = "assembly"; if (RENDER == "assembly") { assembly(); } else if (RENDER == "plate_2d") { projection(cut=true) translate([0,0,-PLATE_THICK/2]) base_plate(); } else if (RENDER == "clamp_lower_2d") { projection(cut=true) translate([0,0,-CLAMP_THICK/2]) clamp_lower(); } else if (RENDER == "clamp_upper_2d") { projection(cut=true) translate([0,0,-CLAMP_THICK/2]) clamp_upper(); } // ============================================================================= // ASSEMBLY // ============================================================================= module assembly() { color("Silver", 0.85) base_plate(); for (side = [-1, 1]) { // Lower clamp sits on top of base plate, below fork slot level color("SteelBlue", 0.8) translate([side * WHEELBASE/2, 0, PLATE_THICK]) clamp_lower(); // Upper clamp stacks on top of lower clamp color("CornflowerBlue", 0.8) translate([side * WHEELBASE/2, 0, PLATE_THICK + CLAMP_THICK]) clamp_upper(); } // Reference ghosts (not for export) %for (side = [-1, 1]) color("Orange", 0.25) translate([side * WHEELBASE/2, 0, 0]) rotate([0, side * 90, 0]) axle_ghost(); // Battery pack ghosts — shows 2-pack layout on deck surface %for (pack_y = [BATT_W/2, -BATT_W/2]) color("DarkGoldenrod", 0.35) translate([-BATT_L/2, pack_y - BATT_W/2, PLATE_THICK]) cube([BATT_L, BATT_W, BATT_H]); } // ============================================================================= // BASE PLATE // ============================================================================= module base_plate() { difference() { // ── Outer profile (rounded corners via minkowski) ────────────────── R = 12; // corner radius linear_extrude(PLATE_THICK) minkowski() { square([2*(PLATE_X_HALF - R), PLATE_DEPTH - 2*R], center=true); circle(r=R); } // ── Fork slots (open at ±X edges) ───────────────────────────────── // Slot is FORK_W wide, FORK_DEPTH deep, open at plate edge for (side = [-1, 1]) { // Round the slot tip (half-circle end) union() { translate([side*(PLATE_X_HALF - FORK_DEPTH), -FORK_W/2, -1]) cube([FORK_DEPTH + 1, FORK_W, PLATE_THICK + 2]); // Semicircular slot bottom translate([side*(PLATE_X_HALF - FORK_DEPTH), 0, -1]) cylinder(d=FORK_W, h=PLATE_THICK + 2); } } // ── Clamp bolt through-holes (4 per side × 2 sides) ──────────────── for (side = [-1, 1]) for (dx = [-CLAMP_BOLT_DX, CLAMP_BOLT_DX]) for (dy = [-CLAMP_BOLT_DY, CLAMP_BOLT_DY]) translate([side * WHEELBASE/2 + dx, dy, -1]) cylinder(d=CLAMP_BOLT_D, h=PLATE_THICK + 2); // ── Alignment pin holes (Ø4, 2 per clamp side) ───────────────────── for (side = [-1, 1]) for (dy = [-CLAMP_BOLT_DY + 8, CLAMP_BOLT_DY - 8]) translate([side * WHEELBASE/2, dy, -1]) cylinder(d=CLAMP_ALIGN_D, h=PLATE_THICK + 2); // ── FC mount holes (MAMBA F722S 30.5 × 30.5 M3) ──────────────────── for (x = [-FC_PITCH/2, FC_PITCH/2]) for (y = [-FC_PITCH/2, FC_PITCH/2]) translate([x, y, -1]) cylinder(d=FC_HOLE_D, h=PLATE_THICK + 2); // ── Battery mount holes ────────────────────────────────────────────── // Layout: 2 packs side-by-side, each running lengthwise (420 mm in X). // Pack centres at y = +BATT_W/2 (pack A, front) // y = -BATT_W/2 (pack B, rear) // 4 mount holes per pack (corners, BATT_INSET from each end) = 8 holes total. for (pack_y = [BATT_W/2, -BATT_W/2]) // two packs in Y for (dx = [-BATT_L/2 + BATT_INSET, BATT_L/2 - BATT_INSET]) // two holes in X per pack translate([dx, pack_y, -1]) cylinder(d=M4, h=PLATE_THICK + 2); // ── Velcro strap slots (2 slots per pack, run full Y depth) ───────── // Slots pierce the plate so the strap passes through from below. // Positioned at x = ±BATT_L/4 (quarter-length of battery). for (sx = [-BATT_L/4, BATT_L/4]) translate([sx - BATT_STRAP_W/2, -PLATE_DEPTH/2 + 8, -1]) cube([BATT_STRAP_W, PLATE_DEPTH - 16, PLATE_THICK + 2]); // ── 4-pack expansion shelf attachment holes (M5, 4 per side) ──────── // Only punched when BATT_PACKS >= 4. // These accept M5 bolts that hold a secondary underdeck tray for // two more packs (same 420×88mm dims) mounted below the main plate. if (BATT_PACKS >= 4) for (ex = [-BATT_L/2 - 10, -BATT_L/6, BATT_L/6, BATT_L/2 + 10]) for (ey = [-PLATE_DEPTH/2 + 12, PLATE_DEPTH/2 - 12]) translate([ex, ey, -1]) cylinder(d=M5, h=PLATE_THICK + 2); // ── Lightening / cable routing slots ──────────────────────────────── // Placed between the two battery pack columns and at the centre. // Only if they don't overlap the strap slots (strap slots are at ±BATT_L/4). slot_w = 22; slot_h = BATT_W - 20; // runs between the pack boundaries for (x = [-BATT_L/3 + 10, 0, BATT_L/3 - 10]) hull() { translate([x, -slot_h/2 + slot_w/2, -1]) cylinder(d=slot_w, h=PLATE_THICK + 2); translate([x, slot_h/2 - slot_w/2, -1]) cylinder(d=slot_w, h=PLATE_THICK + 2); } // ── Bearing seat relief (36mm slot lets hub collar clear plate edge) ─ // Prevents Ø37.8mm bearing seat from binding against plate edge. for (side = [-1, 1]) translate([side*PLATE_X_HALF - side*(BEARING_SEAT_OD/2 + 2), -BEARING_SEAT_OD/2, -1]) cube([BEARING_SEAT_OD/2 + 3, BEARING_SEAT_OD, PLATE_THICK + 2]); } } // ============================================================================= // DROPOUT CLAMP — LOWER (round bore, base-section diameter) // ============================================================================= // Sits directly on top of the base plate. // The 16.11 mm round bore captures the base axle section. // The fork slot in the base plate is reproduced here to allow the axle to // slide in from the side. module clamp_lower() { difference() { // Body hull() { translate([-CLAMP_L/2 + CLAMP_H/2, 0, 0]) cylinder(d=CLAMP_H, h=CLAMP_THICK); translate([ CLAMP_L/2 - CLAMP_H/2, 0, 0]) cylinder(d=CLAMP_H, h=CLAMP_THICK); } // Round bore — base axle diameter + clearance translate([0, 0, -1]) cylinder(d = AXLE_BASE_DIA + 0.4, h = CLAMP_THICK + 2); // Open entry slot (matches base plate fork slot — allows slide-in assembly) translate([-CLAMP_L/2 - 1, -FORK_W/2, -1]) cube([CLAMP_L/2 + 1, FORK_W, CLAMP_THICK + 2]); // Clamp bolt holes for (dx = [-CLAMP_BOLT_DX, CLAMP_BOLT_DX]) for (dy = [-CLAMP_BOLT_DY, CLAMP_BOLT_DY]) translate([dx, dy, -1]) cylinder(d=CLAMP_BOLT_D, h=CLAMP_THICK + 2); // Alignment pin holes for (dy = [-CLAMP_BOLT_DY + 8, CLAMP_BOLT_DY - 8]) translate([0, dy, -1]) cylinder(d=CLAMP_ALIGN_D, h=CLAMP_THICK + 2); } } // ============================================================================= // DROPOUT CLAMP — UPPER (D-cut bore, anti-rotation) // ============================================================================= // Stacks on top of lower clamp. // D-cut bore matches the caliper-verified profile: // Ø 15.95 mm round OD, 13.00 mm flat chord // Clearance DCUT_CL applied all-round. // The flat is oriented toward +Y (front of robot) — rotate clamp at assembly // to match actual flat position on your axles. module clamp_upper() { dcut_r = DCUT_R; dcut_fc = DCUT_FC; // Distance from bore CL to flat (chord geometry) dcut_d = sqrt(pow(dcut_r, 2) - pow(dcut_fc/2, 2)); difference() { // Body (same footprint as lower clamp) hull() { translate([-CLAMP_L/2 + CLAMP_H/2, 0, 0]) cylinder(d=CLAMP_H, h=CLAMP_THICK); translate([ CLAMP_L/2 - CLAMP_H/2, 0, 0]) cylinder(d=CLAMP_H, h=CLAMP_THICK); } // D-cut bore: // 1. Full round circle of radius dcut_r // 2. Intersect with a box that removes the top (y > dcut_d) // Net result: D-shape with flat at +Y = dcut_d translate([0, 0, -1]) linear_extrude(CLAMP_THICK + 2) dcut_profile_2d(dcut_r, dcut_d); // Open entry slot (same as lower clamp) translate([-CLAMP_L/2 - 1, -FORK_W/2, -1]) cube([CLAMP_L/2 + 1, FORK_W, CLAMP_THICK + 2]); // Clamp bolt holes for (dx = [-CLAMP_BOLT_DX, CLAMP_BOLT_DX]) for (dy = [-CLAMP_BOLT_DY, CLAMP_BOLT_DY]) translate([dx, dy, -1]) cylinder(d=CLAMP_BOLT_D, h=CLAMP_THICK + 2); // Alignment pin holes for (dy = [-CLAMP_BOLT_DY + 8, CLAMP_BOLT_DY - 8]) translate([0, dy, -1]) cylinder(d=CLAMP_ALIGN_D, h=CLAMP_THICK + 2); // Label emboss (orientation marker — "D-CUT" + flat side arrow) translate([-12, dcut_d + 1, CLAMP_THICK - 0.8]) linear_extrude(1) text("D", size=6, font="Liberation Sans:style=Bold", halign="center"); // Arrow indicating flat direction translate([8, dcut_d - 1, CLAMP_THICK - 0.8]) linear_extrude(1) polygon([[0,0],[4,-3],[4,3]]); } } // ── 2-D D-cut profile helper ────────────────────────────────────────────────── // r = bore radius (with clearance already applied) // flat_d = distance from bore CL to flat (computed from chord geometry) module dcut_profile_2d(r, flat_d) { intersection() { circle(r=r); // Keep everything from y = -(r+1) up to y = flat_d translate([-r - 1, -r - 1]) square([2*(r + 1), r + 1 + flat_d]); } } // ============================================================================= // AXLE GHOST (for assembly visualisation) // ============================================================================= module axle_ghost() { // Bearing seat collar cylinder(d=BEARING_SEAT_OD, h=12); // Base zone — round translate([0, 0, 12]) cylinder(d=AXLE_BASE_DIA, h=AXLE_BASE_LEN); // D-cut zone (approximate cylinder for ghost) translate([0, 0, 12 + AXLE_BASE_LEN]) cylinder(d=AXLE_DCUT_DIA, h=AXLE_DCUT_LEN); // Tip translate([0, 0, 12 + AXLE_BASE_LEN + AXLE_DCUT_LEN]) cylinder(d=10, h=AXLE_TIP_LEN); } // ============================================================================= // DXF / FLAT-PATTERN EXPORT GUIDE // ============================================================================= // // PART 1 — Base plate // openscad prototype_baseplate.scad \ // -D 'RENDER="plate_2d"' \ // -o baseplate_plate.dxf // // PART 2 — Lower clamp (× 2, one per side) // openscad prototype_baseplate.scad \ // -D 'RENDER="clamp_lower_2d"' \ // -o baseplate_clamp_lower.dxf // // PART 3 — Upper clamp (× 2, one per side) // openscad prototype_baseplate.scad \ // -D 'RENDER="clamp_upper_2d"' \ // -o baseplate_clamp_upper.dxf // // MATERIAL NOTES // Base plate: 6 mm 5052-H32 aluminium (preferred) // 8 mm clear acrylic (quick prototype) // Clamp blocks: 8 mm 6061-T6 aluminium (preferred — tighter bore tolerance) // 10 mm PETG FDM (proto only — pre-drill bolt holes) // // LASER PARAMETERS (6 mm Al, fibre laser) // Power: 3 kW | Speed: 1800 mm/min | Assist: N2 // Min bore dia: 6 mm (M5 holes OK) // // DIMENSIONAL NOTES // Fork slot width (FORK_W): 16.51 mm — verify on actual axle before cut // D-cut flat clearance (DCUT_CL): 0.3 mm — adjust if axle spins in clamp // Bearing seat relief: check collar actually clears plate edge before tightening // =============================================================================