From 22d7b546f347c6d69ad73830ef65b2d4c709a7d0 Mon Sep 17 00:00:00 2001 From: sl-mechanical Date: Sat, 28 Feb 2026 14:43:26 -0500 Subject: [PATCH 1/3] feat: prototype base plate with real hub motor measurements MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds prototype_baseplate.scad — a laser-cuttable / CNC-routable flat base plate for the self-balancing robot using caliper-verified axle dimensions from the wiki (replaces placeholder values in PR #7): Axle base dia: 16.11 mm (was 14 mm) D-cut OD: 15.95 mm (new) D-cut flat chord: 13.00 mm (new) Total protrusion: 65.50 mm Bearing seat OD: 37.80 mm Tire OD: 254 mm (10x2.125") Axle CL height: 127 mm (was wrong 310 mm) Design: - Single flat plate (6 mm Al / 8 mm acrylic), 680x220 mm blank - Open fork slots (16.51 mm, semicircular tip) at each axle end - Bearing seat relief cutout prevents Ø37.8 mm collar binding on edge - Two-piece dropout clamp: lower (round bore) + upper (D-cut bore) - D-cut profile computed from chord geometry with 0.3 mm all-round clearance - MAMBA F722S FC holes (30.5x30.5 mm M3), battery mount holes (M4) - Lightening slots, corner radii via minkowski - RENDER param switches between 3-D assembly and 2-D DXF projections for each of the three laser-cut parts Updates BOM.md to Rev B: measurement delta table, prototype BOM section, updated motor entry with verified axle spec. Co-Authored-By: Claude Sonnet 4.6 --- chassis/BOM.md | 63 ++++-- chassis/prototype_baseplate.scad | 371 +++++++++++++++++++++++++++++++ 2 files changed, 420 insertions(+), 14 deletions(-) create mode 100644 chassis/prototype_baseplate.scad diff --git a/chassis/BOM.md b/chassis/BOM.md index 56d4588..9250597 100644 --- a/chassis/BOM.md +++ b/chassis/BOM.md @@ -1,24 +1,58 @@ # SaltyBot Chassis — Bill of Materials -**Task:** bd-1iy5 -**Rev:** A — 2026-02-28 +**Task:** bd-1iy5 / prototype-baseplate +**Rev:** B — 2026-02-28 (updated with caliper-verified axle measurements) **Agent:** sl-mechanical --- +## ⚠ Axle Measurement Update (Rev B) + +PR #7 (`chassis_frame.scad`) used placeholder values. The table below records the +**caliper-verified** hub motor axle dimensions. All new designs must use these. + +| Feature | Measured | PR #7 placeholder | Delta | +|---------|----------|-------------------|-------| +| Axle base diameter (round, near hub) | **16.11 mm** | 14 mm | +2.11 mm | +| D-cut shaft OD | **15.95 mm** | — (not modelled) | new | +| D-cut flat chord | **13.00 mm** | — | new | +| Total axle protrusion | **65.5 mm** | ~60 mm est. | +5.5 mm | +| Base section length | **15.0 mm** | — | new | +| D-cut section length | **43.35 mm** | — | new | +| Tip / shoulder | **3.0 mm** | — | new | +| Bearing seat collar OD | **37.8 mm** | — | new | +| Tire OD | **254 mm** (10×2.125") | 170 mm motor OD only | — | +| Axle CL height above ground | **127 mm** | 310 mm (wrong) | −183 mm | + +`chassis_frame.scad` should be updated in a follow-on task to match Rev B values. + +--- + ## Structural / Fabricated Parts +### Prototype Base Plate (`prototype_baseplate.scad`) — Rev B + +| # | Part | Qty | Material | Cut size | Notes | +|---|------|-----|----------|----------|-------| +| 1 | Prototype base plate | 1 | 6mm 5052-H32 aluminium **or** 8mm clear acrylic | 680×220mm blank | Laser or CNC; export DXF from `RENDER="plate_2d"` | +| 2 | Dropout clamp — lower (L+R) | 2 | 8mm 6061-T6 aluminium | 90×70mm blank each | Round bore Ø16.51mm; `RENDER="clamp_lower_2d"` | +| 3 | Dropout clamp — upper (L+R) | 2 | 8mm 6061-T6 aluminium | 90×70mm blank each | D-cut bore (Ø16.25mm, 13.6mm flat chord); `RENDER="clamp_upper_2d"` | +| 4 | FC standoff M3×6mm nylon | 4 | Nylon | — | MAMBA F722S vibration isolation | +| 5 | Ø4mm alignment pin | 8 | Steel dowel | 16mm length | Clamp-to-plate alignment (2 per clamp stack) | + +### Full Chassis (`chassis_frame.scad`) — Rev A (placeholder values — pending Rev B update) + | # | Part | Qty | Material | Notes | |---|------|-----|----------|-------| -| 1 | Main deck plate | 1 | 6mm 5052 aluminium plate, 640×220mm | CNC router or waterjet; all holes per `chassis_frame.scad` | -| 2 | Longitudinal rib (front) | 1 | 4mm 5052 aluminium, 600×40mm | Laser-cut; press-fit into deck slots | -| 3 | Longitudinal rib (rear) | 1 | 4mm 5052 aluminium, 600×40mm | Same file, symmetric | -| 4 | Motor fork bracket (L) | 1 | 8mm 6061 aluminium | CNC mill or FDM PETG @100% infill for prototyping | -| 5 | Motor fork bracket (R) | 1 | 8mm 6061 aluminium | Mirror of item 4 | -| 6 | Battery tray | 1 | 3mm PETG FDM or 3mm aluminium fold | `chassis_frame.scad` — `battery_tray()` module | -| 7 | FC mount plate / standoffs | 1 set | PETG or nylon FDM | Includes 4× M3 nylon standoffs, 6mm height | -| 8 | Jetson Nano mount plate | 1 | 4mm 5052 aluminium or 4mm PETG FDM | B01 58×58mm hole pattern | -| 9 | Front bumper bracket | 1 | 5mm PETG FDM | Saddle clamps for 22mm EMT conduit | -| 10 | Rear bumper bracket | 1 | 5mm PETG FDM | Mirror of item 9 | +| 6 | Main deck plate | 1 | 6mm 5052 aluminium plate, 640×220mm | CNC router or waterjet; all holes per `chassis_frame.scad` | +| 7 | Longitudinal rib (front) | 1 | 4mm 5052 aluminium, 600×40mm | Laser-cut; press-fit into deck slots | +| 8 | Longitudinal rib (rear) | 1 | 4mm 5052 aluminium, 600×40mm | Same file, symmetric | +| 9 | Motor fork bracket (L) | 1 | 8mm 6061 aluminium | **Update fork slot to Ø16.51mm before cutting** | +| 10 | Motor fork bracket (R) | 1 | 8mm 6061 aluminium | Mirror of item 9 | +| 11 | Battery tray | 1 | 3mm PETG FDM or 3mm aluminium fold | `chassis_frame.scad` — `battery_tray()` module | +| 12 | FC mount plate / standoffs | 1 set | PETG or nylon FDM | Includes 4× M3 nylon standoffs, 6mm height | +| 13 | Jetson Nano mount plate | 1 | 4mm 5052 aluminium or 4mm PETG FDM | B01 58×58mm hole pattern | +| 14 | Front bumper bracket | 1 | 5mm PETG FDM | Saddle clamps for 22mm EMT conduit | +| 15 | Rear bumper bracket | 1 | 5mm PETG FDM | Mirror of item 14 | --- @@ -26,8 +60,9 @@ | # | Part | Qty | Source / Spec | Notes | |---|------|-----|---------------|-------| -| 11 | Hoverboard hub motor | 2 | Generic 6.5" / 170mm OD, 36V nominal | ~350W each; 14mm flat axle; confirm exact OD before cutting fork slots | -| 12 | Motor hall-sensor cable extension | 2 | 6-pin JST-PH 300mm | Route through deck cable slot | +| 16 | Hoverboard hub motor | 2 | 10×2.125" pneumatic tire, 36V nominal, ~350W | Axle: Ø16.11mm base / Ø15.95mm D-cut / 13mm flat; bearing seat Ø37.8mm — **all caliper-verified** | +| 17 | Motor hall-sensor cable extension | 2 | 6-pin JST-PH 300mm | Route through deck cable slot | +| 18 | Axle lock nut | 4 | M16×1.5 or thread-matched to actual axle tip | Confirm thread at tip before ordering; calipers show Ø≈10mm at tip | --- diff --git a/chassis/prototype_baseplate.scad b/chassis/prototype_baseplate.scad new file mode 100644 index 0000000..8f5a2c4 --- /dev/null +++ b/chassis/prototype_baseplate.scad @@ -0,0 +1,371 @@ +// ============================================================================= +// SaltyBot — Prototype Base Plate +// 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). +// +// ── 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 = 220.0; // mm front-to-rear +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 area — 24 V pack footprint (mount holes) +BATT_L = 185.0; +BATT_W = 72.0; + +// 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(); +} + +// ============================================================================= +// 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 tray mount holes (M4 × 4 corners) ────────────────────── + for (x = [-BATT_L/2 + 12, BATT_L/2 - 12]) + for (y = [-BATT_W/2 + 10, BATT_W/2 - 10]) + translate([x, y, -1]) + cylinder(d=M4, h=PLATE_THICK + 2); + + // ── Lightening / cable routing slots (3 centre panels) ───────────── + slot_w = 28; + slot_h = PLATE_DEPTH - 80; + for (x = [-130, 0, 130]) + translate([x - slot_w/2, -slot_h/2, -1]) + // Rounded slot via hull + hull() { + translate([slot_w/2, slot_w/2, 0]) cylinder(d=slot_w, h=PLATE_THICK+2); + translate([slot_w/2, slot_h - slot_w/2, 0]) 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 +// ============================================================================= From 5bb5c7f863931b6ed977f6d3f8dcd80446b35770 Mon Sep 17 00:00:00 2001 From: sl-mechanical Date: Sat, 28 Feb 2026 14:46:50 -0500 Subject: [PATCH 2/3] fix: update baseplate with real battery dimensions (420x88x56mm) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- chassis/BOM.md | 19 +++++-- chassis/prototype_baseplate.scad | 91 +++++++++++++++++++++++++------- 2 files changed, 88 insertions(+), 22 deletions(-) diff --git a/chassis/BOM.md b/chassis/BOM.md index 9250597..cf42569 100644 --- a/chassis/BOM.md +++ b/chassis/BOM.md @@ -80,11 +80,24 @@ PR #7 (`chassis_frame.scad`) used placeholder values. The table below records th ## Battery +> **Rev B update:** previous placeholder (185×72×52 mm) replaced with real pack dimensions. + | # | Part | Qty | Spec | Notes | |---|------|-----|------|-------| -| 18 | LiPo / LiFePO4 pack | 1 | 24V (6S LiPo or 8S LiFePO4), 4Ah, ≤185×72×52mm | Confirm dims before printing tray; add 2mm clearance | -| 19 | Velcro strap 20mm wide | 2 | 300mm length | Route through tray strap slots | -| 20 | BMS board | 1 | Matched to cell chemistry | Mount externally on rear inner face of tray wall | +| 18 | Battery pack | 2 (default) | 24V, 420×88×56 mm each | Sit side-by-side on deck (2-pack = 420×176 mm footprint). Up to 4 packs with underdeck shelf. | +| 19 | Velcro strap 25mm × 500mm | 4 | One per strap slot × 2 slots × 2 packs | Pass through 25mm plate slots at x=±105mm; cinch over pack | +| 20 | BMS board | 1–2 | Matched to cell chemistry | Mount to deck underside near battery centre; one BMS can parallel both packs | +| 21 | 4-pack underdeck shelf plate | 0 (option) | 6mm Al, 440×200mm | Required only for 4-pack config; attaches via M5 shelf bolts; carries 2 more packs below main deck | +| 22 | M5×20 shelf bolt + nut | 8 | SS, for underdeck shelf | Only for 4-pack config; M5 holes in main plate are pre-punched when `BATT_PACKS=4` | + +### Battery footprint analysis + +| Config | Width (Y) | Length (X) | Fits on 600mm wheelbase? | Notes | +|--------|-----------|-----------|--------------------------|-------| +| 2-pack (default) | 176 mm | 420 mm | ✅ Yes | Centred between forks | +| 4-pack (in-line) | 176 mm | 840 mm | ❌ No | 840 > 600 mm wheelbase | +| 4-pack (side-by-side) | 352 mm | 420 mm | ❌ No | 352 > 210 mm plate depth | +| 4-pack (2+2 underdeck) | 176 mm | 420 mm | ✅ Yes | 2 packs below main deck via shelf | --- diff --git a/chassis/prototype_baseplate.scad b/chassis/prototype_baseplate.scad index 8f5a2c4..599e83f 100644 --- a/chassis/prototype_baseplate.scad +++ b/chassis/prototype_baseplate.scad @@ -1,11 +1,18 @@ // ============================================================================= -// SaltyBot — Prototype Base Plate +// 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 // ───────┼──────────────────┼────────────────────┼───────── @@ -43,7 +50,9 @@ AXLE_CL_HEIGHT = TIRE_OD / 2; // 127 mm — axle centre above ground // ============================================================================= WHEELBASE = 600.0; // mm axle C/L to axle C/L -PLATE_DEPTH = 220.0; // mm front-to-rear +// 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) @@ -68,9 +77,22 @@ DCUT_CL = 0.3; // mm all-round clearance on D-cut profile FC_PITCH = 30.5; FC_HOLE_D = 3.2; -// Battery area — 24 V pack footprint (mount holes) -BATT_L = 185.0; -BATT_W = 72.0; +// 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; @@ -136,6 +158,12 @@ module assembly() { 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]); } // ============================================================================= @@ -185,22 +213,47 @@ module base_plate() { translate([x, y, -1]) cylinder(d=FC_HOLE_D, h=PLATE_THICK + 2); - // ── Battery tray mount holes (M4 × 4 corners) ────────────────────── - for (x = [-BATT_L/2 + 12, BATT_L/2 - 12]) - for (y = [-BATT_W/2 + 10, BATT_W/2 - 10]) - translate([x, y, -1]) + // ── 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); - // ── Lightening / cable routing slots (3 centre panels) ───────────── - slot_w = 28; - slot_h = PLATE_DEPTH - 80; - for (x = [-130, 0, 130]) - translate([x - slot_w/2, -slot_h/2, -1]) - // Rounded slot via hull - hull() { - translate([slot_w/2, slot_w/2, 0]) cylinder(d=slot_w, h=PLATE_THICK+2); - translate([slot_w/2, slot_h - slot_w/2, 0]) cylinder(d=slot_w, 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. From 914afbc1ca7dda71abaf07b859d253481d9563df Mon Sep 17 00:00:00 2001 From: sl-mechanical Date: Sat, 28 Feb 2026 14:57:30 -0500 Subject: [PATCH 3/3] =?UTF-8?q?feat:=20vertical=20stem=20architecture=20?= =?UTF-8?q?=E2=80=94=20compact=20baseplate=20+=20battery=20carousel?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- chassis/BOM.md | 92 ++++--- chassis/prototype_baseplate.scad | 458 +++++++++++++------------------ chassis/stem_battery_clamp.scad | 378 +++++++++++++++++++++++++ 3 files changed, 628 insertions(+), 300 deletions(-) create mode 100644 chassis/stem_battery_clamp.scad diff --git a/chassis/BOM.md b/chassis/BOM.md index cf42569..0a92d8f 100644 --- a/chassis/BOM.md +++ b/chassis/BOM.md @@ -1,6 +1,6 @@ # SaltyBot Chassis — Bill of Materials **Task:** bd-1iy5 / prototype-baseplate -**Rev:** B — 2026-02-28 (updated with caliper-verified axle measurements) +**Rev:** C — 2026-02-28 (vertical stem architecture; batteries on mast carousel) **Agent:** sl-mechanical --- @@ -29,15 +29,35 @@ PR #7 (`chassis_frame.scad`) used placeholder values. The table below records th ## Structural / Fabricated Parts -### Prototype Base Plate (`prototype_baseplate.scad`) — Rev B +### Prototype Base Plate (`prototype_baseplate.scad`) — Rev C + +> Batteries are removed from the base plate. Plate is now compact (680×130mm). +> Stem socket replaces battery tray. See Part B (stem clamp) below. | # | Part | Qty | Material | Cut size | Notes | |---|------|-----|----------|----------|-------| -| 1 | Prototype base plate | 1 | 6mm 5052-H32 aluminium **or** 8mm clear acrylic | 680×220mm blank | Laser or CNC; export DXF from `RENDER="plate_2d"` | -| 2 | Dropout clamp — lower (L+R) | 2 | 8mm 6061-T6 aluminium | 90×70mm blank each | Round bore Ø16.51mm; `RENDER="clamp_lower_2d"` | -| 3 | Dropout clamp — upper (L+R) | 2 | 8mm 6061-T6 aluminium | 90×70mm blank each | D-cut bore (Ø16.25mm, 13.6mm flat chord); `RENDER="clamp_upper_2d"` | -| 4 | FC standoff M3×6mm nylon | 4 | Nylon | — | MAMBA F722S vibration isolation | -| 5 | Ø4mm alignment pin | 8 | Steel dowel | 16mm length | Clamp-to-plate alignment (2 per clamp stack) | +| 1 | Base plate | 1 | 6mm 5052-H32 Al **or** 8mm acrylic | 680×130mm blank | `RENDER="plate_2d"` → DXF | +| 2 | Dropout clamp — lower | 2 | 8mm 6061-T6 Al | 90×70mm blank | Round bore Ø16.51mm; `RENDER="clamp_lower_2d"` | +| 3 | Dropout clamp — upper | 2 | 8mm 6061-T6 Al | 90×70mm blank | D-cut bore; `RENDER="clamp_upper_2d"` | +| 4 | Stem flange ring | 2 | 6mm Al or acrylic | Ø82mm disc | One above + one below plate; `RENDER="stem_flange_2d"` | +| 5 | Vertical stem tube | 1 | 38.1mm OD × 1.5mm wall 6061-T6 Al | 1050mm length | 1.5" EMT conduit is a drop-in alternative | +| 6 | FC standoff M3×6mm nylon | 4 | Nylon | — | MAMBA F722S vibration isolation | +| 7 | Ø4mm × 16mm alignment pin | 8 | Steel dowel | — | Dropout clamp-to-plate alignment | + +### Battery Stem Clamp (`stem_battery_clamp.scad`) — Part B + +| # | Part | Qty | Material | Notes | +|---|------|-----|----------|-------| +| 8 | Collar half | 2 | PETG FDM (5 perimeters, 40% infill) | Print flat-side-down; mirror 2nd in slicer. Bore Ø38.6mm | +| 9 | Radial arm | 4 | 4mm 5052-H32 Al (laser-cut) **or** PETG FDM | `RENDER="arm_2d"` for DXF with `ARM_THICK=4`; or print at 8mm | +| 10 | Battery cradle | 4 | PETG FDM (4 perimeters, 30% infill) | U-channel, open top; holds 1 pack per cradle | +| 11 | M6×60 SHCS | 4 | SS | 2 collar clamping bolts × 2 sides | +| 12 | M6 hex nut | 4 | SS | Captured in collar nut pockets | +| 13 | M6×12 set screw | 2 | SS, cup-point | 1 per collar half for height lock | +| 14 | M4×20 SHCS | 8 (16 for 4) | SS | Arm-to-collar bolts (2 per arm) | +| 15 | M4×16 SHCS | 8 (16 for 4) | SS | Cradle-to-arm bolts (2 per cradle) | +| 16 | M4 hex nut | 16 (32 for 4) | SS | Nuts for items 14 + 15 | +| 17 | Velcro strap 25mm × 600mm | 4–8 | — | 1–2 per battery pack through cradle slots | ### Full Chassis (`chassis_frame.scad`) — Rev A (placeholder values — pending Rev B update) @@ -78,26 +98,27 @@ PR #7 (`chassis_frame.scad`) used placeholder values. The table below records th --- -## Battery +## Battery (Vertical Stem Mount — Rev C) -> **Rev B update:** previous placeholder (185×72×52 mm) replaced with real pack dimensions. +> Batteries mount VERTICALLY on the stem carousel, **not** on the base plate. +> Each pack stands upright (420mm tall) in a cradle at the end of a radial arm. | # | Part | Qty | Spec | Notes | |---|------|-----|------|-------| -| 18 | Battery pack | 2 (default) | 24V, 420×88×56 mm each | Sit side-by-side on deck (2-pack = 420×176 mm footprint). Up to 4 packs with underdeck shelf. | -| 19 | Velcro strap 25mm × 500mm | 4 | One per strap slot × 2 slots × 2 packs | Pass through 25mm plate slots at x=±105mm; cinch over pack | -| 20 | BMS board | 1–2 | Matched to cell chemistry | Mount to deck underside near battery centre; one BMS can parallel both packs | -| 21 | 4-pack underdeck shelf plate | 0 (option) | 6mm Al, 440×200mm | Required only for 4-pack config; attaches via M5 shelf bolts; carries 2 more packs below main deck | -| 22 | M5×20 shelf bolt + nut | 8 | SS, for underdeck shelf | Only for 4-pack config; M5 holes in main plate are pre-punched when `BATT_PACKS=4` | +| 18 | Battery pack | 2–4 | 24V, **420×88×56 mm** each (caliper-verified) | Snap into battery cradle from above | +| 19 | BMS board | 1 | Matched to cell chemistry | Mount to stem or base plate underside; run wiring down stem | -### Battery footprint analysis +### Battery stem clamp — configuration guide -| Config | Width (Y) | Length (X) | Fits on 600mm wheelbase? | Notes | -|--------|-----------|-----------|--------------------------|-------| -| 2-pack (default) | 176 mm | 420 mm | ✅ Yes | Centred between forks | -| 4-pack (in-line) | 176 mm | 840 mm | ❌ No | 840 > 600 mm wheelbase | -| 4-pack (side-by-side) | 352 mm | 420 mm | ❌ No | 352 > 210 mm plate depth | -| 4-pack (2+2 underdeck) | 176 mm | 420 mm | ✅ Yes | 2 packs below main deck via shelf | +| Config | Arm count | Carousel weight | Balance | Notes | +|--------|-----------|-----------------|---------|-------| +| 2-pack | 2 arms, 180° apart | ~1.4 kg | ✅ Symmetric | Minimum configuration | +| 4-pack | 4 arms, 90° apart | ~2.8 kg | ✅ Symmetric | Maximum practical config | +| 3-pack | 3 arms, 120° apart | ~2.1 kg | ⚠ Asymmetric mass | Needs counter-balance or avoid | + +### CG height tuning + +Slide entire carousel up/down the stem with M6 collar bolts loosened. Tighten at desired height. Typical balance point (initial estimate): batteries centred at 450–600 mm above base plate. Adjust in ≈50 mm increments during commissioning. --- @@ -112,18 +133,23 @@ PR #7 (`chassis_frame.scad`) used placeholder values. The table below records th ## Fasteners -| # | Part | Qty | Spec | -|---|------|-----|------| -| 23 | M5×16 SHCS | 24 | ISO 4762, SS | -| 24 | M5 hex nut | 24 | ISO 4032, SS | -| 25 | M4×12 SHCS | 12 | ISO 4762, SS | -| 26 | M4 hex nut | 12 | ISO 4032, SS | -| 27 | M3×10 SHCS | 20 | ISO 4762, SS | -| 28 | M3 hex nut | 20 | ISO 4032, SS | -| 29 | M3×6 BHCS | 8 | FC + Jetson board bolts | -| 30 | M14×1.5 axle nut | 4 | One each side per motor, flanged | Confirm axle thread pitch on actual motors | -| 31 | Serrated washer M14 | 4 | Axle anti-rotation | -| 32 | Flat washer M5 | 48 | SS | +| # | Part | Qty | Spec | Use | +|---|------|-----|------|-----| +| 20 | M5×16 SHCS | 16 | ISO 4762, SS | Dropout clamp bolts (4 per clamp × 4) | +| 21 | M5×20 SHCS | 8 | ISO 4762, SS | Stem flange bolts (4 per flange × 2) | +| 22 | M5 hex nut | 24 | ISO 4032, SS | | +| 23 | M4×20 SHCS | 16 | ISO 4762, SS | Arm-to-collar (2 per arm × 4 arms × 2) | +| 24 | M4×16 SHCS | 16 | ISO 4762, SS | Cradle-to-arm (2 per cradle × 4 arms × 2) | +| 25 | M4 hex nut | 32 | ISO 4032, SS | | +| 26 | M6×60 SHCS | 4 | ISO 4762, SS | Collar clamping bolts | +| 27 | M6 hex nut | 4 | ISO 4032, SS | Captured in collar pockets | +| 28 | M6×12 set screw | 2 | ISO 4026, SS cup-point | Stem height lock (1 per collar half) | +| 29 | M3×10 SHCS | 12 | ISO 4762, SS | FC mount + miscellaneous | +| 30 | M3×6 BHCS | 4 | ISO 4762, SS | FC board bolts | +| 31 | Axle lock nut (match axle tip thread) | 4 | Flanged, confirm thread | 2 per motor | +| 32 | Flat washer M5 | 32 | SS | | +| 33 | Flat washer M4 | 32 | SS | | +| 34 | Ø4×16 dowel pin | 8 | Steel | Dropout clamp alignment | --- diff --git a/chassis/prototype_baseplate.scad b/chassis/prototype_baseplate.scad index 599e83f..5ab165f 100644 --- a/chassis/prototype_baseplate.scad +++ b/chassis/prototype_baseplate.scad @@ -1,126 +1,107 @@ // ============================================================================= -// SaltyBot — Prototype Base Plate (Rev B — real battery dimensions) +// SaltyBot — Prototype Base Plate (Rev C — compact, stem-mount) // 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). +// Laser-cut or CNC-routed flat plate (6 mm Al / 8 mm acrylic). +// Uses CALIPER-VERIFIED hub motor axle measurements (see PR #7 / #11). // -// ── 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) +// ARCHITECTURE CHANGE (Rev C): +// Batteries are NO LONGER on the base plate. +// They stand vertically on a central stem via stem_battery_clamp.scad. +// The base plate is now compact — only axle dropouts + stem socket + FC mount. // -// ── VERIFIED AXLE PROFILE (stepped D-cut) ──────────────────────────────────── -// Zone │ Feature │ Diameter / Width │ Length -// ───────┼──────────────────┼────────────────────┼───────── +// ── AXLE PROFILE (stepped D-cut, caliper-verified) ─────────────────────────── +// Zone │ Feature │ Ø / 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) +// │ Flat chord │ 13.00 mm │ +// Tip │ Shoulder/end │ — │ 3.00 mm // 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) +// Bearing seat collar: Ø 37.8 mm +// Tire: 10 × 2.125" pneumatic (Ø 254 mm, 35 PSI) // ============================================================================= $fn = 128; // ============================================================================= -// AXLE PARAMETERS — caliper-verified (edit only when re-measured) +// AXLE PARAMETERS — caliper-verified // ============================================================================= -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 +AXLE_BASE_DIA = 16.11; +AXLE_BASE_LEN = 15.00; +AXLE_DCUT_DIA = 15.95; +AXLE_DCUT_FLAT = 13.00; +AXLE_DCUT_LEN = 43.35; +AXLE_TIP_LEN = 3.00; +AXLE_TOTAL = 65.50; +BEARING_SEAT_OD = 37.80; +TIRE_OD = 254.0; +AXLE_CL_HEIGHT = TIRE_OD / 2; // 127 mm 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) +WHEELBASE = 600.0; // mm axle C/L to axle C/L +// Plate depth now driven only by structural + FC needs (no battery footprint). +PLATE_DEPTH = 130.0; // mm front-to-rear +PLATE_OVERHANG = 40.0; // mm plate past axle C/L each side +PLATE_THICK = 6.0; // mm -// 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 +// Fork slot +FORK_W = AXLE_BASE_DIA + 0.4; // 16.51 mm +FORK_DEPTH = 50.0; -// 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 +// Dropout clamp (two-piece sandwich) +CLAMP_L = 80.0; +CLAMP_H = 60.0; +CLAMP_THICK = 8.0; +CLAMP_BOLT_D = 5.3; // M5 +CLAMP_BOLT_DX = 22.0; +CLAMP_BOLT_DY = 22.0; +CLAMP_ALIGN_D = 4.1; // Ø4 pin -// D-cut bore clearance (applied to both diameter and flat chord) -DCUT_CL = 0.3; // mm all-round clearance on D-cut profile +// D-cut bore clearance +DCUT_CL = 0.3; -// FC mount — MAMBA F722S 30.5 × 30.5 mm M3 stack -FC_PITCH = 30.5; -FC_HOLE_D = 3.2; +// FC mount — MAMBA F722S 30.5 × 30.5 mm M3 +FC_PITCH = 30.5; +FC_HOLE_D = 3.2; +// FC is offset toward front of plate (away from stem) +FC_X_OFFSET = -40.0; // mm from plate centre (negative = front/motor side) -// 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) +// ============================================================================= +// STEM SOCKET PARAMETERS +// ============================================================================= + +STEM_OD = 38.1; // mm 1.5" EMT conduit OD +STEM_BORE = STEM_OD + 0.5; // 38.6 mm with clearance +// Flange ring (laser-cut, bolts above + below plate to grip tube): +STEM_FLANGE_OD = 82.0; // mm flange outer diameter +STEM_FLANGE_BC = 66.0; // mm bolt circle diameter (4× M5 at 90°) +STEM_FLANGE_T = 6.0; // mm = PLATE_THICK (flush-mount) +// Stem position: at plate centre (X=0, Y=0) + +// ============================================================================= +// UTILITIES +// ============================================================================= -// 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 +PLATE_X_HALF = WHEELBASE/2 + PLATE_OVERHANG; // ± 340 mm +DCUT_R = (AXLE_DCUT_DIA + 2*DCUT_CL) / 2; +DCUT_FC = AXLE_DCUT_FLAT + 2*DCUT_CL; // ============================================================================= // RENDER CONTROL // ============================================================================= +// +// "assembly" full 3-D preview +// "plate_2d" DXF — base plate +// "clamp_lower_2d" DXF — lower dropout clamp (× 2) +// "clamp_upper_2d" DXF — upper dropout clamp (× 2) +// "stem_flange_2d" DXF — stem flange ring (× 2, one above + one below plate) -// 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") { @@ -131,6 +112,8 @@ if (RENDER == "assembly") { 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(); +} else if (RENDER == "stem_flange_2d") { + projection(cut=true) translate([0,0,-STEM_FLANGE_T/2]) stem_flange(); } // ============================================================================= @@ -138,287 +121,228 @@ if (RENDER == "assembly") { // ============================================================================= module assembly() { - color("Silver", 0.85) base_plate(); + 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) + color("SteelBlue", 0.80) translate([side * WHEELBASE/2, 0, PLATE_THICK]) clamp_lower(); - - // Upper clamp stacks on top of lower clamp - color("CornflowerBlue", 0.8) + color("CornflowerBlue", 0.80) 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(); + // Stem flange rings (above and below plate) + color("DimGray", 0.70) + translate([0, 0, -STEM_FLANGE_T]) + stem_flange(); + color("DimGray", 0.70) + translate([0, 0, PLATE_THICK]) + stem_flange(); - // 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]); + // Reference ghosts + %color("Orange", 0.25) + translate([0, 0, PLATE_THICK + STEM_FLANGE_T]) + cylinder(d=STEM_OD, h=800); // vertical stem + %for (side = [-1, 1]) + color("Tomato", 0.2) + translate([side * WHEELBASE/2, 0, 0]) + rotate([0, side*90, 0]) + axle_ghost(); } // ============================================================================= -// BASE PLATE +// BASE PLATE (Part A — compact) // ============================================================================= module base_plate() { + R = 12; // corner radius difference() { - // ── Outer profile (rounded corners via minkowski) ────────────────── - R = 12; // corner radius + // ── Outer profile ── 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 + // ── Fork slots (open at ±X edges, semicircular tip) ────────────── 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); - } + translate([side*(PLATE_X_HALF - FORK_DEPTH), -FORK_W/2, -1]) + cube([FORK_DEPTH + 1, FORK_W, PLATE_THICK + 2]); + 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) ──────────────── + // ── Bearing seat relief (prevents Ø37.8 mm collar binding on edge) ─ + for (side = [-1, 1]) + translate([side*PLATE_X_HALF - side*(BEARING_SEAT_OD/2 + 1), + -BEARING_SEAT_OD/2, -1]) + cube([BEARING_SEAT_OD/2 + 2, BEARING_SEAT_OD, PLATE_THICK + 2]); + + // ── Dropout clamp bolt through-holes ───────────────────────────── 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]) + translate([side*WHEELBASE/2 + dx, dy, -1]) cylinder(d=CLAMP_BOLT_D, h=PLATE_THICK + 2); - // ── Alignment pin holes (Ø4, 2 per clamp side) ───────────────────── + // ── Alignment pin holes (Ø4) ────────────────────────────────────── for (side = [-1, 1]) for (dy = [-CLAMP_BOLT_DY + 8, CLAMP_BOLT_DY - 8]) - translate([side * WHEELBASE/2, dy, -1]) + 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]) + // ── Stem socket bore ────────────────────────────────────────────── + translate([0, 0, -1]) + cylinder(d=STEM_BORE, h=PLATE_THICK + 2); + + // ── Stem flange bolt holes (4× M5, 90° pattern on STEM_FLANGE_BC) ─ + for (a = [0, 90, 180, 270]) + rotate([0, 0, a]) + translate([STEM_FLANGE_BC/2, 0, -1]) + cylinder(d=M5, h=PLATE_THICK + 2); + + // ── FC mount (MAMBA F722S 30.5 × 30.5 M3) ──────────────────────── + for (x = [FC_X_OFFSET - FC_PITCH/2, FC_X_OFFSET + 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]) + // ── Wiring / cable pass-through slots (2×, flanking stem) ───────── + for (dy = [-30, 30]) 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); + translate([15, dy, -1]) cylinder(d=14, h=PLATE_THICK + 2); + translate([-15, dy, -1]) cylinder(d=14, 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]); + // ── Lightening slots (between FC zone and dropout zones) ────────── + for (sx = [-1, 1]) { + // One slot each side of stem, in the structural corridor + lx = sx * (WHEELBASE/4); + hull() { + translate([lx, -(PLATE_DEPTH/2 - 22), -1]) cylinder(d=18, h=PLATE_THICK+2); + translate([lx, (PLATE_DEPTH/2 - 22), -1]) cylinder(d=18, h=PLATE_THICK+2); + } + } + } +} + +// ============================================================================= +// STEM FLANGE RING (laser-cut, qty 2 — one above, one below plate) +// ============================================================================= + +module stem_flange() { + difference() { + cylinder(d=STEM_FLANGE_OD, h=STEM_FLANGE_T); + // Stem bore (tight fit — tube presses into flange) + translate([0, 0, -1]) + cylinder(d=STEM_BORE, h=STEM_FLANGE_T + 2); + // 4× M5 flange bolts + for (a = [0, 90, 180, 270]) + rotate([0, 0, a]) + translate([STEM_FLANGE_BC/2, 0, -1]) + cylinder(d=M5, h=STEM_FLANGE_T + 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) + // Round bore (base zone) + translate([0, 0, -1]) cylinder(d=AXLE_BASE_DIA + 0.4, h=CLAMP_THICK + 2); + // Slide-in slot (open toward wheel side) translate([-CLAMP_L/2 - 1, -FORK_W/2, -1]) cube([CLAMP_L/2 + 1, FORK_W, CLAMP_THICK + 2]); - - // Clamp bolt holes + // Clamp bolts 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 + translate([dx, dy, -1]) cylinder(d=CLAMP_BOLT_D, h=CLAMP_THICK + 2); + // Alignment pins for (dy = [-CLAMP_BOLT_DY + 8, CLAMP_BOLT_DY - 8]) - translate([0, dy, -1]) - cylinder(d=CLAMP_ALIGN_D, h=CLAMP_THICK + 2); + 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)); + 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 + // D-cut bore translate([0, 0, -1]) linear_extrude(CLAMP_THICK + 2) dcut_profile_2d(dcut_r, dcut_d); - - // Open entry slot (same as lower clamp) + // Slide-in slot translate([-CLAMP_L/2 - 1, -FORK_W/2, -1]) cube([CLAMP_L/2 + 1, FORK_W, CLAMP_THICK + 2]); - - // Clamp bolt holes + // Clamp bolts 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 + translate([dx, dy, -1]) cylinder(d=CLAMP_BOLT_D, h=CLAMP_THICK + 2); + // Alignment pins 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]) + translate([0, dy, -1]) cylinder(d=CLAMP_ALIGN_D, h=CLAMP_THICK + 2); + // Orientation emboss + translate([0, dcut_d + 1.5, 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]]); + polygon([[0,0],[-3,-5],[3,-5]]); } } -// ── 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) +// ── D-cut 2D profile helper ─────────────────────────────────────────────────── 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]); + square([2*(r+1), r + 1 + flat_d]); } } // ============================================================================= -// AXLE GHOST (for assembly visualisation) +// AXLE GHOST (visualisation only) // ============================================================================= 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); + translate([0,0,12]) cylinder(d=AXLE_BASE_DIA, h=AXLE_BASE_LEN); + translate([0,0,12+AXLE_BASE_LEN]) cylinder(d=AXLE_DCUT_DIA, h=AXLE_DCUT_LEN); } // ============================================================================= -// DXF / FLAT-PATTERN EXPORT GUIDE +// DXF EXPORT // ============================================================================= // -// PART 1 — Base plate -// openscad prototype_baseplate.scad \ -// -D 'RENDER="plate_2d"' \ -// -o baseplate_plate.dxf +// Part 1 — Base plate: +// openscad prototype_baseplate.scad -D 'RENDER="plate_2d"' -o baseplate.dxf // -// PART 2 — Lower clamp (× 2, one per side) -// openscad prototype_baseplate.scad \ -// -D 'RENDER="clamp_lower_2d"' \ -// -o baseplate_clamp_lower.dxf +// Part 2 — Dropout clamp, lower (× 2): +// openscad prototype_baseplate.scad -D 'RENDER="clamp_lower_2d"' -o 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 +// Part 3 — Dropout clamp, upper (× 2): +// openscad prototype_baseplate.scad -D 'RENDER="clamp_upper_2d"' -o 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) +// Part 4 — Stem flange ring (× 2, one each side of plate): +// openscad prototype_baseplate.scad -D 'RENDER="stem_flange_2d"' -o stem_flange.dxf // -// 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 +// Materials: +// Plate + flanges : 6 mm 5052-H32 aluminium (preferred) +// 8 mm clear acrylic (quick proto) +// Dropout clamps : 8 mm 6061-T6 aluminium +// Stem tube : 38.1 mm OD × 1.5 mm wall 6061-T6 (or 1.5" EMT) +// Cut stem to ~1050 mm — allows batteries from ~100 mm to ~950 mm height. // ============================================================================= diff --git a/chassis/stem_battery_clamp.scad b/chassis/stem_battery_clamp.scad new file mode 100644 index 0000000..8e73285 --- /dev/null +++ b/chassis/stem_battery_clamp.scad @@ -0,0 +1,378 @@ +// ============================================================================= +// 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. +// =============================================================================