saltylab-firmware/chassis/prototype_baseplate.scad
sl-mechanical 5bb5c7f863 fix: update baseplate with real battery dimensions (420x88x56mm)
Replaces placeholder 185x72x52mm battery spec with caliper-verified
pack dimensions. 2 packs side-by-side is the default config.

Geometry impact:
- PLATE_DEPTH reduced to 210mm (2x88mm + 17mm margin each side)
- Battery zone: 420x176mm centred between motor forks (fits 600mm wheelbase)
- Mount holes repositioned: 4 per pack x 2 packs = 8 M4 holes
  at (±(BATT_L/2 - 18), ±BATT_W/2)
- Velcro strap slots: 25mm wide, pierce full plate depth at x=±BATT_L/4
- 4-pack expansion: optional M5 shelf bolt holes when BATT_PACKS=4
  (only viable 4-pack layout is 2+2 underdeck — analysed in BOM)
- Battery ghost in assembly preview shows 2-pack deck layout

4-pack analysis (added to BOM):
  in-line 840mm > wheelbase, side-by-side 352mm > plate depth
  → 2+2 underdeck shelf is the only viable 4-pack configuration

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-28 14:46:50 -05:00

425 lines
20 KiB
OpenSCAD
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// =============================================================================
// 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
// =============================================================================