fix: base plate dimensions 270x240mm per spec (issue #18)

Both chassis files corrected. Previous dimensions were wrong:
  prototype_baseplate.scad: was 680x130mm → now 270x240mm
  chassis_frame.scad:       was 640x220mm → now 270x240mm

Geometry recalculated for 270x240mm envelope:

  PLATE_W = 270mm (axle direction)
  PLATE_D = 240mm (front-to-rear)
  PLATE_X_HALF = 135mm (plate edge / axle entry)
  FORK_SLOT_D = 50mm → AXLE_X = 85mm from plate centre
  Axle-to-axle = 170mm (2 × 85mm)

  Wheels extend beyond plate edges as expected:
  Tire at X = ±(85 ± 27) = ±58–112mm, plate edge at ±135mm ✓

Mount positions recalculated for smaller plate:
  Dropout clamp bolt holes: X = ±(85±22) = ±63 and ±107mm ✓ (all within ±135mm)
  Stem flange bolts: r=33mm BC, at (±33,0) and (0,±33) ✓
  FC mount: X = -40mm offset, holes at ±15mm ✓
  Wiring slots: Y = ±55mm (clear of stem flange bolts at ±33mm)
  Lightening ovals: Y = ±80mm in open front/rear corridors
  Bumper length: 300mm (was 660mm, now PLATE_D + 60)
  Longitudinal ribs: 270mm long (was 600mm)

chassis_frame.scad also updated:
  AXLE_HEIGHT corrected: 310mm → 127mm (TIRE_OD/2)
  Axle dimensions: 14mm → 16.11mm, flat 10mm → 13.00mm
  AXLE_X for fork bracket positioning
  Motor fork bracket logic cleaned up (no more negative cube dimensions)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
sl-mechanical 2026-02-28 17:30:54 -05:00
parent fb3c8c863a
commit 6ea4b56471
2 changed files with 414 additions and 433 deletions

View File

@ -1,340 +1,339 @@
// ============================================================================= // =============================================================================
// SaltyBot Parametric Chassis Frame // SaltyBot Full Chassis Frame (Rev B correct 270 × 240 mm envelope)
// Task: bd-1iy5 // Agent: sl-mechanical | 2026-02-28 | Fixes issue #18
// Agent: sl-mechanical
// Date: 2026-02-28
// //
// Self-balancing two-wheeled robot chassis // Parametric OpenSCAD model. Plate geometry corrected per issue #18:
// Requirements: // Width (axle direction, X): 270 mm was wrong 640 mm
// - 600mm wheelbase // Depth (front-to-rear, Y): 240 mm was wrong 220 mm
// - 2x hoverboard hub motors (170mm OD) // Wheels extend beyond plate edges expected.
// - STM32 MAMBA F722S FC mount (30.5x30.5mm pattern) //
// - Battery tray (24V 4Ah ~180x70x50mm pack) // Motor axle dimensions updated to caliper-verified values (see PR #7 / #11).
// - Jetson Nano B01 mount plate (100x80mm, M3 holes) //
// - Front/rear bumper brackets // NOTE: For the prototype, use prototype_baseplate.scad + stem_battery_clamp.scad.
// This file models the full machined chassis for production reference.
// ============================================================================= // =============================================================================
// RENDER QUALITY
$fn = 64; $fn = 64;
// ============================================================================= // =============================================================================
// PARAMETERS edit here to adjust the whole model // PLATE DIMENSIONS primary parameters fixed for issue #18
// ============================================================================= // =============================================================================
// Wheelbase / overall geometry PLATE_W = 270.0; // mm width (axle / left-right direction)
WHEELBASE = 600; // mm, center-to-center axle distance (lateral) PLATE_D = 240.0; // mm depth (front-to-rear)
FRAME_WIDTH = 220; // mm, front-to-back depth of main deck DECK_THICK = 6.0; // mm deck plate thickness
DECK_THICKNESS = 6; // mm, main deck plate thickness WALL_T = 4.0; // mm general wall / rib thickness
WALL_T = 4; // mm, general wall thickness for brackets/ribs
// Hub motor parameters // Fork slot geometry slot opens at plate edge, axle seats at bottom
MOTOR_OD = 170; // mm, motor housing outer diameter FORK_SLOT_D = 50.0; // mm fork slot depth (inward from plate edge)
MOTOR_AXLE_D = 14; // mm, axle bolt diameter PLATE_X_HALF = PLATE_W / 2; // 135 mm plate half-width / edge
MOTOR_AXLE_FLAT = 10; // mm, axle flat-to-flat (for anti-rotation) AXLE_X = PLATE_X_HALF - FORK_SLOT_D; // 85 mm axle C/L from plate centre
MOTOR_FORK_WIDTH = 24; // mm, dropout slot width (fits 10-14mm axle + spacers)
MOTOR_FORK_DEPTH = 60; // mm, dropout slot depth from fork tip
MOTOR_FORK_H = 80; // mm, total height of motor fork bracket
MOTOR_FORK_T = 8; // mm, fork plate thickness
AXLE_HEIGHT = 310; // mm, axle CL above ground (motor radius + clearance)
// FC mount (MAMBA F722S 30.5 × 30.5 mm M3 pattern)
FC_MOUNT_SPACING = 30.5; // mm, hole pattern pitch
FC_MOUNT_HOLE_D = 3.2; // mm, M3 clearance
FC_STANDOFF_H = 6; // mm, standoff height
FC_PAD_T = 3; // mm, mounting pad thickness
// Battery tray (24V 4Ah LiPo / LiFePO4)
BATT_L = 185; // mm, cell pack length
BATT_W = 72; // mm, cell pack width
BATT_H = 52; // mm, cell pack height
BATT_WALL = 3; // mm, tray wall thickness
BATT_FLOOR = 4; // mm, tray floor thickness
BATT_STRAP_W = 20; // mm, Velcro strap slot width
BATT_STRAP_T = 2; // mm, strap slot depth
// Jetson Nano B01 mount plate
// B01 carrier board hole pattern: 58 x 58 mm M3 (inner) + corner pass-throughs
JETSON_HOLE_PITCH = 58; // mm, M3 mounting hole pattern
JETSON_HOLE_D = 3.2; // mm
JETSON_PLATE_L = 105; // mm, plate length
JETSON_PLATE_W = 90; // mm, plate width
JETSON_PLATE_T = 4; // mm, plate thickness
JETSON_STANDOFF_H = 8; // mm
// Bumper bracket
BUMPER_L = WHEELBASE + 60; // mm, bumper rail length (overhangs wheel CL)
BUMPER_H = 40; // mm, bracket vertical height
BUMPER_T = 5; // mm, bracket plate thickness
BUMPER_TUBE_OD = 22; // mm, 3/4" EMT conduit OD for bumper rail
// Rib / gusset parameters
RIB_W = 20; // mm, longitudinal rib width
RIB_H = 40; // mm, rib height below deck
RIB_T = 4; // mm, rib plate thickness (laser/router cut)
// Fastener helpers
M3_D = 3.2;
M4_D = 4.3;
M5_D = 5.3;
M6_D = 6.5;
// ============================================================================= // =============================================================================
// MAIN ASSEMBLY comment/uncomment parts as needed // HUB MOTOR VERIFIED AXLE DIMENSIONS (caliper)
// ============================================================================= // =============================================================================
color("Silver", 0.9) main_deck(); AXLE_BASE_DIA = 16.11; // mm round section near hub
color("DimGray") motor_fork(side= 1); // right (+X) AXLE_BASE_LEN = 15.00;
color("DimGray") motor_fork(side=-1); // left (-X) AXLE_DCUT_DIA = 15.95; // mm D-cut round OD
color("SteelBlue") battery_tray(); AXLE_DCUT_FLAT = 13.00; // mm flat chord
color("OliveDrab") fc_mount_plate(); AXLE_DCUT_LEN = 43.35;
color("DarkOrange") jetson_mount_plate(); AXLE_TOTAL = 65.50; // mm protrusion from hub face
color("Tomato") bumper_bracket(front= 1); BEARING_SEAT_OD = 37.80; // mm hub centre collar OD
color("Tomato") bumper_bracket(front=-1); TIRE_OD = 254.0; // mm 10 × 2.125" pneumatic
color("WhiteSmoke") longitudinal_ribs(); TIRE_WIDTH = 54.0; // mm
AXLE_HEIGHT = TIRE_OD / 2; // 127 mm axle C/L above ground
// Fork slot width (base section + clearance)
FORK_SLOT_W = AXLE_BASE_DIA + 0.4; // 16.51 mm
// Vertical motor fork bracket
FORK_BKT_H = 100.0; // mm bracket height below deck to axle level
FORK_BKT_T = 8.0; // mm bracket plate thickness
FORK_BKT_W = 28.0; // mm bracket width (Y)
// ============================================================================= // =============================================================================
// MODULES // FC MOUNT MAMBA F722S 30.5 × 30.5 mm M3
// =============================================================================
FC_PITCH = 30.5;
FC_HOLE_D = 3.2;
FC_STANDOFF = 6.0;
FC_X = -40.0; // mm from plate centre (front of plate)
// =============================================================================
// BATTERY TRAY (legacy flat superseded by stem carousel architecture)
// =============================================================================
BATT_L = 185.0;
BATT_W = 72.0;
BATT_H = 52.0;
BATT_WALL = 3.0;
BATT_FLOOR = 4.0;
// =============================================================================
// JETSON NANO B01 MOUNT
// =============================================================================
JETSON_PITCH = 58.0;
JETSON_HOLE_D = 3.2;
JETSON_PL = 100.0;
JETSON_PW = 86.0;
JETSON_PT = 4.0;
JETSON_STOFF = 8.0;
JETSON_X = 50.0; // mm from plate centre (rear)
// =============================================================================
// BUMPER BRACKETS
// =============================================================================
BUMPER_L = PLATE_D + 60; // 300 mm spans plate depth + 30 mm each end
BUMPER_H = 40.0;
BUMPER_T = 5.0;
BUMPER_TUBE_OD = 22.0; // 3/4" EMT
// =============================================================================
// LONGITUDINAL RIBS
// =============================================================================
RIB_T = 4.0;
RIB_H = 40.0;
// =============================================================================
// FASTENERS
// =============================================================================
M3 = 3.2; M4 = 4.3; M5 = 5.3;
// =============================================================================
// ASSEMBLY
// =============================================================================
color("Silver", 0.90) main_deck();
color("DimGray", 1.00) motor_fork_bracket(side= 1);
color("DimGray", 1.00) motor_fork_bracket(side=-1);
color("SteelBlue", 1.00) battery_tray();
color("OliveDrab", 1.00) fc_mount_plate();
color("DarkOrange", 1.00) jetson_mount_plate();
color("Tomato", 1.00) bumper_bracket(front= 1);
color("Tomato", 1.00) bumper_bracket(front=-1);
color("WhiteSmoke", 1.00) longitudinal_ribs();
// =============================================================================
// MAIN DECK (270 × 240 mm)
// ============================================================================= // =============================================================================
// Main deck plate
module main_deck() { module main_deck() {
R = 10;
difference() { difference() {
// Deck plate linear_extrude(DECK_THICK)
translate([-WHEELBASE/2 - MOTOR_FORK_T, -FRAME_WIDTH/2, 0]) minkowski() {
cube([WHEELBASE + 2*MOTOR_FORK_T, FRAME_WIDTH, DECK_THICKNESS]); square([PLATE_W - 2*R, PLATE_D - 2*R], center=true);
circle(r=R);
}
// Lightening holes 3 rows × 4 cols // Fork slots (open at ±X plate edges)
for (x = [-WHEELBASE/3, 0, WHEELBASE/3]) for (side = [-1, 1]) {
for (y = [-FRAME_WIDTH/4, FRAME_WIDTH/4]) translate([side*(PLATE_X_HALF - FORK_SLOT_D), -FORK_SLOT_W/2, -1])
translate([x, y, -1]) cube([FORK_SLOT_D + 1, FORK_SLOT_W, DECK_THICK + 2]);
cylinder(d=50, h=DECK_THICKNESS+2); translate([side*(PLATE_X_HALF - FORK_SLOT_D), 0, -1])
cylinder(d=FORK_SLOT_W, h=DECK_THICK + 2);
}
// Bearing seat relief
for (side = [-1, 1])
translate([side*(PLATE_X_HALF - BEARING_SEAT_OD/2 - 1),
-BEARING_SEAT_OD/2, -1])
cube([BEARING_SEAT_OD/2 + 2, BEARING_SEAT_OD, DECK_THICK + 2]);
// Fork bracket bolt holes (M5 × 4 per side)
for (side = [-1, 1])
for (dx = [-15, 15])
for (dy = [-12, 12])
translate([side * AXLE_X + dx, dy, -1])
cylinder(d=M5, h=DECK_THICK + 2);
// FC mount holes // FC mount holes
fc_mount_holes(z_offset=-1, depth=DECK_THICKNESS+2); fc_holes(z=-1, h=DECK_THICK+2);
// Battery tray floor cutout for wire pass-through // Battery tray anchor holes
translate([-BATT_L/2 + 40, -BATT_W/2 + 40, -1]) for (x = [-BATT_L/2 + 12, BATT_L/2 - 12])
cube([BATT_L - 80, BATT_W - 80, DECK_THICKNESS+2]); for (y = [-BATT_W/2 + 10, BATT_W/2 - 10])
translate([x, y, -1]) cylinder(d=M4, h=DECK_THICK+2);
// Lightening ovals (front/rear open zones)
for (dy = [-80, 80])
hull() {
translate([ 25, dy, -1]) cylinder(d=22, h=DECK_THICK+2);
translate([-25, dy, -1]) cylinder(d=22, h=DECK_THICK+2);
}
// Cable routing slots // Cable routing slots
for (x = [-60, 60]) for (dy = [-55, 55])
translate([x, -FRAME_WIDTH/2 - 1, -1]) hull() {
cube([14, 18, DECK_THICKNESS+2]); translate([ 12, dy, -1]) cylinder(d=12, h=DECK_THICK+2);
translate([-12, dy, -1]) cylinder(d=12, h=DECK_THICK+2);
}
} }
} }
// Longitudinal ribs (×2, symmetric F/R) // =============================================================================
// LONGITUDINAL RIBS (×2)
// =============================================================================
module longitudinal_ribs() { module longitudinal_ribs() {
for (y = [-FRAME_WIDTH/2 + RIB_W/2 + WALL_T, rib_y = PLATE_D/2 - WALL_T - RIB_T/2;
FRAME_WIDTH/2 - RIB_W/2 - WALL_T]) for (y = [-rib_y, rib_y])
translate([-WHEELBASE/2, y - RIB_T/2, -RIB_H]) translate([-PLATE_W/2, y - RIB_T/2, -RIB_H])
cube([WHEELBASE, RIB_T, RIB_H]); cube([PLATE_W, RIB_T, RIB_H]);
} }
// Motor fork dropout bracket // =============================================================================
// Mounts to deck edge; provides a CNC-milled or FDM dropout slot for the axle. // MOTOR FORK BRACKET (vertical, bolts to deck edge at ±PLATE_X_HALF)
// `side` = +1 (right/+X) or -1 (left/-X) // =============================================================================
module motor_fork(side = 1) {
x_pos = side * (WHEELBASE/2);
translate([x_pos, 0, 0]) { module motor_fork_bracket(side = 1) {
translate([side * PLATE_X_HALF, 0, 0]) {
s = side;
difference() { difference() {
union() { union() {
// Vertical fork body // Main bracket plate (extends below deck)
translate([side*(DECK_THICKNESS/2), -MOTOR_FORK_WIDTH/2 - 4, -MOTOR_FORK_H]) translate([s > 0 ? 0 : -FORK_BKT_T,
cube([side * MOTOR_FORK_T, MOTOR_FORK_WIDTH + 8, MOTOR_FORK_H + DECK_THICKNESS]); -FORK_BKT_W/2, -FORK_BKT_H])
cube([FORK_BKT_T, FORK_BKT_W, FORK_BKT_H + DECK_THICK]);
// Gusset triangles // Diagonal gussets
for (g = [-1, 1]) for (gy = [-1, 1])
translate([side*(DECK_THICKNESS/2), g*(MOTOR_FORK_WIDTH/2 + 2), -MOTOR_FORK_H]) translate([s > 0 ? 0 : -FORK_BKT_T,
linear_extrude(height=MOTOR_FORK_T) gy * FORK_BKT_W/2, -FORK_BKT_H/2])
polygon([[0,0],[0, g*20],[side*-30, 0]]); linear_extrude(FORK_BKT_T)
polygon([[0,0],[0, gy*20],[s * -25, 0]]);
} }
// Axle dropout slot (open at bottom) // Axle dropout slot (open at bracket bottom)
translate([side*(DECK_THICKNESS/2) - 1, translate([s > 0 ? -1 : -FORK_BKT_T - 1,
-MOTOR_AXLE_FLAT/2, -AXLE_BASE_DIA/2 - 0.2, -FORK_BKT_H])
-MOTOR_FORK_DEPTH - MOTOR_AXLE_D/2]) cube([FORK_BKT_T + 2, AXLE_BASE_DIA + 0.4, FORK_SLOT_D + 1]);
cube([MOTOR_FORK_T + 2, MOTOR_AXLE_FLAT, MOTOR_FORK_DEPTH + 1]); translate([s > 0 ? -1 : -FORK_BKT_T - 1,
0, -FORK_BKT_H + FORK_SLOT_D])
rotate([-90, 0, 0])
cylinder(d=AXLE_BASE_DIA + 0.4,
h=FORK_BKT_T + 2, center=true);
// Axle through-hole at slot top // Deck attachment bolts (M5 × 4)
translate([side*(DECK_THICKNESS/2) - 1, 0, for (dy = [-12, 12])
-MOTOR_FORK_DEPTH - MOTOR_AXLE_D/2]) for (dz = [DECK_THICK * 0.3, DECK_THICK * 0.7])
translate([s > 0 ? -1 : -FORK_BKT_T - 1, dy, dz])
rotate([0, 90, 0]) rotate([0, 90, 0])
cylinder(d=MOTOR_AXLE_D + 1, h=MOTOR_FORK_T + 2); cylinder(d=M5, h=FORK_BKT_T + 2);
// Bolt holes for deck attachment (M5 × 4)
for (y = [-20, 20])
for (z = [4, 12])
translate([side*(DECK_THICKNESS/2) - 1, y, z - MOTOR_FORK_H + MOTOR_FORK_H/2])
rotate([0, 90, 0])
cylinder(d=M5_D, h=MOTOR_FORK_T + 2);
} }
} }
} }
// Battery tray // =============================================================================
// Positioned in centre of deck, recessed 10mm below deck surface // FC MOUNT HELPERS
// =============================================================================
module fc_holes(z=0, h=10) {
for (x = [FC_X - FC_PITCH/2, FC_X + FC_PITCH/2])
for (y = [-FC_PITCH/2, FC_PITCH/2])
translate([x, y, z]) cylinder(d=FC_HOLE_D, h=h);
}
module fc_mount_plate() {
translate([FC_X, 0, 0]) {
difference() {
union() {
for (x = [-FC_PITCH/2, FC_PITCH/2])
for (y = [-FC_PITCH/2, FC_PITCH/2])
translate([x, y, DECK_THICK]) cylinder(d=8, h=FC_STANDOFF);
translate([-(FC_PITCH/2+8), -(FC_PITCH/2+8), DECK_THICK])
cube([FC_PITCH+16, FC_PITCH+16, 3]);
}
fc_holes(z=DECK_THICK-1, h=FC_STANDOFF+4);
fc_holes(z=-1, h=DECK_THICK+2);
}
}
}
// =============================================================================
// BATTERY TRAY
// =============================================================================
module battery_tray() { module battery_tray() {
z_base = -BATT_FLOOR - BATT_H - 5; // hang below deck z_base = -BATT_FLOOR - BATT_H - 5;
translate([-BATT_L/2 - BATT_WALL, -BATT_W/2 - BATT_WALL, z_base]) { translate([-BATT_L/2 - BATT_WALL, -BATT_W/2 - BATT_WALL, z_base]) {
difference() { difference() {
// Outer tray body cube([BATT_L+2*BATT_WALL, BATT_W+2*BATT_WALL, BATT_H+BATT_FLOOR]);
cube([BATT_L + 2*BATT_WALL,
BATT_W + 2*BATT_WALL,
BATT_H + BATT_FLOOR]);
// Inner cavity
translate([BATT_WALL, BATT_WALL, BATT_FLOOR]) translate([BATT_WALL, BATT_WALL, BATT_FLOOR])
cube([BATT_L, BATT_W, BATT_H + 1]); cube([BATT_L, BATT_W, BATT_H+1]);
for (x = [BATT_L/2-30, BATT_L/2+8])
// Strap slots (2× longitudinal) translate([x, -1, BATT_FLOOR+BATT_H/2-10])
for (x = [BATT_L/2 - BATT_STRAP_W*2, cube([20, BATT_W+2*BATT_WALL+2, 20]);
BATT_L/2 + BATT_STRAP_W]) for (i = [0:1])
translate([x, -1, BATT_FLOOR + BATT_H/2 - BATT_STRAP_W/2]) translate([BATT_WALL+12+i*68, BATT_WALL+8, -1])
cube([BATT_STRAP_W, BATT_W + 2*BATT_WALL + 2, BATT_STRAP_W]); cube([38, BATT_W-16, BATT_FLOOR+2]);
for (x=[10,BATT_L+BATT_WALL-2]) for (y=[10,BATT_W+BATT_WALL-2])
// Ventilation slots bottom (3×) translate([x,y,BATT_H+BATT_FLOOR-1]) cylinder(d=M4,h=BATT_FLOOR+2);
for (i = [0:2])
translate([BATT_WALL + 20 + i*50, BATT_WALL + 10, -1])
cube([30, BATT_W - 20, BATT_FLOOR + 2]);
// Mount holes to deck (M4 × 4 corners)
for (x = [10, BATT_L + BATT_WALL - 2])
for (y = [10, BATT_W + BATT_WALL - 2])
translate([x, y, BATT_H + BATT_FLOOR - 1])
cylinder(d=M4_D, h=BATT_FLOOR + 2);
} }
} }
} }
// FC mount holes helper // =============================================================================
module fc_mount_holes(z_offset=0, depth=10) { // JETSON NANO B01 MOUNT PLATE
// MAMBA F722S: 30.5×30.5 mm M3 pattern, centred at origin // =============================================================================
for (x = [-FC_MOUNT_SPACING/2, FC_MOUNT_SPACING/2])
for (y = [-FC_MOUNT_SPACING/2, FC_MOUNT_SPACING/2])
translate([x, y, z_offset])
cylinder(d=FC_MOUNT_HOLE_D, h=depth);
}
// FC mount plate (raised 40mm above deck centre)
module fc_mount_plate() {
fc_x = -50; // offset toward front from deck centre
fc_y = 0;
plate_z = DECK_THICKNESS + FC_STANDOFF_H;
translate([fc_x, fc_y, 0]) {
difference() {
union() {
// Mount pads with standoffs
for (x = [-FC_MOUNT_SPACING/2, FC_MOUNT_SPACING/2])
for (y = [-FC_MOUNT_SPACING/2, FC_MOUNT_SPACING/2]) {
translate([x, y, DECK_THICKNESS])
cylinder(d=8, h=FC_STANDOFF_H); // standoff column
translate([x - 5, y - 5, DECK_THICKNESS])
cube([10, 10, FC_PAD_T]); // pad base
}
// Base plate
translate([-FC_MOUNT_SPACING/2 - 8, -FC_MOUNT_SPACING/2 - 8, DECK_THICKNESS])
cube([FC_MOUNT_SPACING + 16, FC_MOUNT_SPACING + 16, FC_PAD_T]);
}
// M3 through-holes in standoffs
fc_mount_holes(z_offset=DECK_THICKNESS - 1, depth=FC_STANDOFF_H + FC_PAD_T + 2);
// Deck anchor holes
fc_mount_holes(z_offset=-1, depth=DECK_THICKNESS + 2);
}
}
}
// Jetson Nano B01 mount plate
// Positioned rear of deck, elevated on standoffs
module jetson_mount_plate() { module jetson_mount_plate() {
jet_x = 60; // offset toward rear translate([JETSON_X, 0, 0]) {
jet_y = 0;
plate_z = DECK_THICKNESS + JETSON_STANDOFF_H;
translate([jet_x, jet_y, 0]) {
difference() { difference() {
union() { union() {
// Mounting plate translate([-JETSON_PL/2, -JETSON_PW/2, DECK_THICK+JETSON_STOFF])
translate([-JETSON_PLATE_L/2, -JETSON_PLATE_W/2, DECK_THICKNESS + JETSON_STANDOFF_H]) cube([JETSON_PL, JETSON_PW, JETSON_PT]);
cube([JETSON_PLATE_L, JETSON_PLATE_W, JETSON_PLATE_T]); for (x=[-JETSON_PITCH/2, JETSON_PITCH/2])
for (y=[-JETSON_PITCH/2, JETSON_PITCH/2])
// Four standoff columns translate([x,y,DECK_THICK]) cylinder(d=6, h=JETSON_STOFF);
for (x = [-JETSON_HOLE_PITCH/2, JETSON_HOLE_PITCH/2])
for (y = [-JETSON_HOLE_PITCH/2, JETSON_HOLE_PITCH/2])
translate([x, y, DECK_THICKNESS])
cylinder(d=6, h=JETSON_STANDOFF_H);
} }
for (x=[-JETSON_PITCH/2, JETSON_PITCH/2])
// B01 M3 hole pattern (58×58 mm) for (y=[-JETSON_PITCH/2, JETSON_PITCH/2])
for (x = [-JETSON_HOLE_PITCH/2, JETSON_HOLE_PITCH/2]) translate([x,y,DECK_THICK-1])
for (y = [-JETSON_HOLE_PITCH/2, JETSON_HOLE_PITCH/2]) cylinder(d=JETSON_HOLE_D, h=JETSON_STOFF+JETSON_PT+2);
translate([x, y, DECK_THICKNESS - 1]) for (i=[-1,0,1])
cylinder(d=JETSON_HOLE_D, h=JETSON_STANDOFF_H + JETSON_PLATE_T + 2); translate([i*22-8,-JETSON_PW/2+8,DECK_THICK+JETSON_STOFF-1])
cube([16, JETSON_PW-16, JETSON_PT+2]);
// Ventilation / cable routing slots in plate for (x=[-JETSON_PL/2+8, JETSON_PL/2-8])
for (i = [-1, 0, 1]) for (y=[-JETSON_PW/2+8, JETSON_PW/2-8])
translate([i*25 - 10, -JETSON_PLATE_W/2 + 10, translate([x,y,-1]) cylinder(d=M3, h=DECK_THICK+2);
DECK_THICKNESS + JETSON_STANDOFF_H - 1])
cube([20, JETSON_PLATE_W - 20, JETSON_PLATE_T + 2]);
// Deck anchor holes (M3 × 4 corners of plate)
for (x = [-JETSON_PLATE_L/2 + 8, JETSON_PLATE_L/2 - 8])
for (y = [-JETSON_PLATE_W/2 + 8, JETSON_PLATE_W/2 - 8])
translate([x, y, -1])
cylinder(d=M3_D, h=DECK_THICKNESS + 2);
} }
} }
} }
// Bumper bracket // =============================================================================
// `front` = +1 (front) or -1 (rear) // BUMPER BRACKETS (front / rear)
module bumper_bracket(front = 1) { // =============================================================================
y_pos = front * (FRAME_WIDTH/2);
translate([0, y_pos, 0]) { module bumper_bracket(front = 1) {
yp = front * PLATE_D/2;
translate([0, yp, 0]) {
difference() { difference() {
union() { union() {
// Horizontal mounting rail plate
translate([-BUMPER_L/2, front*(BUMPER_T/2), 0]) translate([-BUMPER_L/2, front*(BUMPER_T/2), 0])
cube([BUMPER_L, BUMPER_T, BUMPER_H]); cube([BUMPER_L, BUMPER_T, BUMPER_H]);
for (x=[-BUMPER_L/2+4, BUMPER_L/2-BUMPER_T-4])
// Vertical gussets at ends translate([x, front*BUMPER_T, 0]) cube([BUMPER_T, 16, BUMPER_H]);
for (x = [-BUMPER_L/2 + 5, BUMPER_L/2 - BUMPER_T - 5]) for (x=[-BUMPER_L/3, 0, BUMPER_L/3])
translate([x, front*(BUMPER_T), 0]) translate([x-BUMPER_TUBE_OD/2-2,
cube([BUMPER_T, 20, BUMPER_H]); front*(BUMPER_T+10),
BUMPER_H-BUMPER_TUBE_OD/2-4])
// Bumper tube saddle clamps (×3 evenly spaced)
for (x = [-BUMPER_L/3, 0, BUMPER_L/3])
translate([x - BUMPER_TUBE_OD/2 - 2,
front*(BUMPER_T + 15),
BUMPER_H - BUMPER_TUBE_OD/2 - 5])
difference() { difference() {
cube([BUMPER_TUBE_OD + 4, BUMPER_TUBE_OD/2 + 4, BUMPER_TUBE_OD + 4]); cube([BUMPER_TUBE_OD+4, BUMPER_TUBE_OD/2+4, BUMPER_TUBE_OD+4]);
translate([BUMPER_TUBE_OD/2 + 2, -1, BUMPER_TUBE_OD/2 + 2]) translate([BUMPER_TUBE_OD/2+2,-1,BUMPER_TUBE_OD/2+2])
rotate([-90, 0, 0]) rotate([-90,0,0])
cylinder(d=BUMPER_TUBE_OD, h=BUMPER_TUBE_OD/2 + 6); cylinder(d=BUMPER_TUBE_OD, h=BUMPER_TUBE_OD/2+6);
} }
} }
for (x=[-BUMPER_L/2+16, -BUMPER_L/6, BUMPER_L/6, BUMPER_L/2-16])
// Deck mounting holes (M5 × 6 along length) translate([x, yp*0.001, -1]) cylinder(d=M5, h=BUMPER_H+2);
for (x = [-BUMPER_L/2 + 20, -BUMPER_L/6, BUMPER_L/6, BUMPER_L/2 - 20])
translate([x, y_pos * 0.001, -1])
cylinder(d=M5_D, h=BUMPER_H + 2);
} }
} }
} }
// =============================================================================
// DIMENSIONS / ANNOTATIONS (2D cross-section reference)
// =============================================================================
// Uncomment to render a side-view dimension guide:
//
// %translate([0, FRAME_WIDTH/2 + 30, 0]) {
// color("Blue") {
// cube([WHEELBASE, 1, 1]); // wheelbase span
// translate([0,0,0]) text(str("Wheelbase: ", WHEELBASE, "mm"), size=12);
// }
// }

View File

@ -1,96 +1,104 @@
// ============================================================================= // =============================================================================
// SaltyBot Prototype Base Plate (Rev C compact, stem-mount) // SaltyBot Prototype Base Plate (Rev D correct 270 × 240 mm envelope)
// Agent: sl-mechanical | 2026-02-28 // Agent: sl-mechanical | 2026-02-28 | Fixes issue #18
// //
// Laser-cut or CNC-routed flat plate (6 mm Al / 8 mm acrylic). // Laser-cut or CNC-routed flat plate (6 mm Al / 8 mm acrylic).
// Uses CALIPER-VERIFIED hub motor axle measurements (see PR #7 / #11).
// //
// ARCHITECTURE CHANGE (Rev C): // PLATE DIMENSIONS (per spec, issue #18)
// Batteries are NO LONGER on the base plate. // Width (axle direction, X): 270 mm
// They stand vertically on a central stem via stem_battery_clamp.scad. // Depth (front-to-rear, Y): 240 mm
// The base plate is now compact only axle dropouts + stem socket + FC mount. // Wheels extend beyond plate edges expected.
// //
// AXLE PROFILE (stepped D-cut, caliper-verified) // MOTOR AXLE GEOMETRY
// Zone Feature Ø / Width Length // Plate edge = axle entry point (X = ±135 mm from plate centre)
// // Fork slot depth = 50 mm inward axle C/L seated at X = ±85 mm
// Base Round (near hub) Ø 16.11 mm 15.00 mm // Axle-to-axle distance = 170 mm
// D-cut Round OD Ø 15.95 mm 43.35 mm // Tires (Ø254 mm, 54 mm wide) at X = ±(85 ± 27) = ±58 112 mm clear of ±135 edge
// Flat chord 13.00 mm //
// Tip Shoulder/end 3.00 mm // VERIFIED AXLE PROFILE (caliper)
// Total Hub face tip 65.50 mm // Base section: Ø 16.11 mm, 15 mm long
// D-cut zone: Ø 15.95 mm, 13 mm flat chord, 43.35 mm long
// Tip: 3 mm
// Total: 65.5 mm from hub face
// Bearing seat collar: Ø 37.8 mm // Bearing seat collar: Ø 37.8 mm
// Tire: 10 × 2.125" pneumatic (Ø 254 mm, 35 PSI)
// ============================================================================= // =============================================================================
$fn = 128; $fn = 128;
// =============================================================================
// PLATE DIMENSIONS primary parameters for issue #18 fix
// =============================================================================
PLATE_W = 270.0; // mm width (axle / left-right direction)
PLATE_D = 240.0; // mm depth (front-to-rear)
PLATE_THICK = 6.0; // mm stock thickness
// Derived
PLATE_X_HALF = PLATE_W / 2; // 135 mm plate edge
FORK_DEPTH = 50.0; // mm fork slot depth (inward from edge)
AXLE_X = PLATE_X_HALF - FORK_DEPTH; // 85 mm axle C/L from plate centre
// ============================================================================= // =============================================================================
// AXLE PARAMETERS caliper-verified // AXLE PARAMETERS caliper-verified
// ============================================================================= // =============================================================================
AXLE_BASE_DIA = 16.11; AXLE_BASE_DIA = 16.11; AXLE_BASE_LEN = 15.00;
AXLE_BASE_LEN = 15.00; AXLE_DCUT_DIA = 15.95; AXLE_DCUT_FLAT = 13.00; AXLE_DCUT_LEN = 43.35;
AXLE_DCUT_DIA = 15.95; AXLE_TIP_LEN = 3.00; AXLE_TOTAL = 65.50;
AXLE_DCUT_FLAT = 13.00;
AXLE_DCUT_LEN = 43.35;
AXLE_TIP_LEN = 3.00;
AXLE_TOTAL = 65.50;
BEARING_SEAT_OD = 37.80; BEARING_SEAT_OD = 37.80;
TIRE_OD = 254.0; TIRE_OD = 254.0; // 10" × 25.4 mm
AXLE_CL_HEIGHT = TIRE_OD / 2; // 127 mm above ground
// ============================================================================= // =============================================================================
// PLATE PARAMETERS // DROPOUT CLAMP PARAMETERS
// ============================================================================= // =============================================================================
WHEELBASE = 600.0; // mm axle C/L to axle C/L FORK_W = AXLE_BASE_DIA + 0.4; // 16.51 mm fork slot width
// 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 slot CLAMP_L = 80.0; // mm clamp length along axle axis
FORK_W = AXLE_BASE_DIA + 0.4; // 16.51 mm // outer end at AXLE_X + CLAMP_L/2 = 85+40 = 125 mm
FORK_DEPTH = 50.0; // plate edge at 135 mm 10 mm margin
CLAMP_H = 60.0; // mm clamp height (front-to-rear, in Y)
CLAMP_THICK = 8.0; // mm each clamp layer
CLAMP_BOLT_D = 5.3; // M5 clearance
CLAMP_BOLT_DX = 22.0; // mm bolt offset ±X from axle C/L
// inner bolt at AXLE_X - 22 = 63 mm
// outer bolt at AXLE_X + 22 = 107 mm < 135 mm
CLAMP_BOLT_DY = 22.0; // mm bolt offset ±Y from axle C/L
CLAMP_ALIGN_D = 4.1; // Ø4 alignment pin clearance
// Dropout clamp (two-piece sandwich) DCUT_CL = 0.3; // mm D-cut bore clearance (all-round)
CLAMP_L = 80.0; DCUT_R = (AXLE_DCUT_DIA + 2*DCUT_CL) / 2;
CLAMP_H = 60.0; DCUT_FC = AXLE_DCUT_FLAT + 2*DCUT_CL;
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
DCUT_CL = 0.3;
// 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)
// ============================================================================= // =============================================================================
// STEM SOCKET PARAMETERS // STEM SOCKET
// ============================================================================= // =============================================================================
STEM_OD = 38.1; // mm 1.5" EMT conduit OD STEM_OD = 38.1; // mm 1.5" EMT conduit OD
STEM_BORE = STEM_OD + 0.5; // 38.6 mm with clearance STEM_BORE = STEM_OD + 0.5;
// Flange ring (laser-cut, bolts above + below plate to grip tube): STEM_FLANGE_OD = 82.0; // mm flange ring OD
STEM_FLANGE_OD = 82.0; // mm flange outer diameter STEM_FLANGE_BC = 66.0; // mm M5 bolt circle diameter (r = 33 mm)
STEM_FLANGE_BC = 66.0; // mm bolt circle diameter (4× M5 at 90°) STEM_FLANGE_T = 6.0; // mm = PLATE_THICK
STEM_FLANGE_T = 6.0; // mm = PLATE_THICK (flush-mount)
// Stem position: at plate centre (X=0, Y=0) // Verify flange bolts clear all other holes:
// Flange bolts at (±33, 0) and (0, ±33)
// Clamp bolts at (±63, ±22) and (±107, ±22)
// FC holes centred at (-40, 0) spanning X = ±55.25, Y = ±15.25
// All separations are adequate
// =============================================================================
// FC MOUNT MAMBA F722S 30.5 × 30.5 mm M3
// =============================================================================
FC_PITCH = 30.5;
FC_HOLE_D = 3.2;
FC_X_OFFSET = -40.0; // mm front of plate (X); FC at (40, 0) within ±135
// ============================================================================= // =============================================================================
// UTILITIES // UTILITIES
// ============================================================================= // =============================================================================
M3 = 3.2; M4 = 4.3; M5 = 5.3; M3 = 3.2; M4 = 4.3; M5 = 5.3;
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 // RENDER CONTROL
@ -100,21 +108,15 @@ DCUT_FC = AXLE_DCUT_FLAT + 2*DCUT_CL;
// "plate_2d" DXF base plate // "plate_2d" DXF base plate
// "clamp_lower_2d" DXF lower dropout clamp (× 2) // "clamp_lower_2d" DXF lower dropout clamp (× 2)
// "clamp_upper_2d" DXF upper dropout clamp (× 2) // "clamp_upper_2d" DXF upper dropout clamp (× 2)
// "stem_flange_2d" DXF stem flange ring (× 2, one above + one below plate) // "stem_flange_2d" DXF stem flange ring (× 2)
RENDER = "assembly"; RENDER = "assembly";
if (RENDER == "assembly") { if (RENDER == "assembly") assembly();
assembly(); else if (RENDER == "plate_2d") projection(cut=true) translate([0,0,-PLATE_THICK/2]) base_plate();
} else if (RENDER == "plate_2d") { else if (RENDER == "clamp_lower_2d") projection(cut=true) translate([0,0,-CLAMP_THICK/2]) clamp_lower();
projection(cut=true) translate([0,0,-PLATE_THICK/2]) base_plate(); else if (RENDER == "clamp_upper_2d") projection(cut=true) translate([0,0,-CLAMP_THICK/2]) clamp_upper();
} else if (RENDER == "clamp_lower_2d") { else if (RENDER == "stem_flange_2d") projection(cut=true) translate([0,0,-STEM_FLANGE_T/2]) stem_flange();
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();
}
// ============================================================================= // =============================================================================
// ASSEMBLY // ASSEMBLY
@ -125,119 +127,115 @@ module assembly() {
for (side = [-1, 1]) { for (side = [-1, 1]) {
color("SteelBlue", 0.80) color("SteelBlue", 0.80)
translate([side * WHEELBASE/2, 0, PLATE_THICK]) translate([side * AXLE_X, 0, PLATE_THICK])
clamp_lower(); clamp_lower();
color("CornflowerBlue", 0.80) color("CornflowerBlue", 0.80)
translate([side * WHEELBASE/2, 0, PLATE_THICK + CLAMP_THICK]) translate([side * AXLE_X, 0, PLATE_THICK + CLAMP_THICK])
clamp_upper(); clamp_upper();
} }
// Stem flange rings (above and below plate) // Stem flanges (above and below plate)
color("DimGray", 0.70) color("DimGray", 0.70) translate([0, 0, -STEM_FLANGE_T]) stem_flange();
translate([0, 0, -STEM_FLANGE_T]) color("DimGray", 0.70) translate([0, 0, PLATE_THICK]) stem_flange();
stem_flange();
color("DimGray", 0.70)
translate([0, 0, PLATE_THICK])
stem_flange();
// Reference ghosts // Ghosts not exported
%color("Orange", 0.25) %color("Orange", 0.25)
translate([0, 0, PLATE_THICK + STEM_FLANGE_T]) translate([0, 0, PLATE_THICK + STEM_FLANGE_T])
cylinder(d=STEM_OD, h=800); // vertical stem cylinder(d=STEM_OD, h=900);
%for (side = [-1, 1]) %for (side = [-1, 1])
color("Tomato", 0.2) color("Tomato", 0.20)
translate([side * WHEELBASE/2, 0, 0]) translate([side * PLATE_X_HALF, 0, 0])
rotate([0, side*90, 0]) rotate([0, side * (-90), 0]) // axle projects inward from plate edge
axle_ghost(); axle_ghost();
} }
// ============================================================================= // =============================================================================
// BASE PLATE (Part A compact) // BASE PLATE (Part A 270 × 240 mm)
// ============================================================================= // =============================================================================
module base_plate() { module base_plate() {
R = 12; // corner radius R = 10; // corner radius
difference() { difference() {
// Outer profile // Outer profile (minkowski rounded corners)
linear_extrude(PLATE_THICK) linear_extrude(PLATE_THICK)
minkowski() { minkowski() {
square([2*(PLATE_X_HALF - R), PLATE_DEPTH - 2*R], center=true); square([PLATE_W - 2*R, PLATE_D - 2*R], center=true);
circle(r=R); circle(r=R);
} }
// Fork slots (open at ±X edges, semicircular tip) // Fork slots (open at ±X plate edges, semicircular tip)
// Slot width FORK_W, depth FORK_DEPTH inward from plate edge.
// Axle seats in the semicircle at X = ±(PLATE_X_HALF FORK_DEPTH) = ±85 mm.
for (side = [-1, 1]) { for (side = [-1, 1]) {
translate([side*(PLATE_X_HALF - FORK_DEPTH), -FORK_W/2, -1]) // Rectangular slot body
cube([FORK_DEPTH + 1, FORK_W, PLATE_THICK + 2]); translate([side * (PLATE_X_HALF - FORK_DEPTH), -FORK_W/2, -1])
translate([side*(PLATE_X_HALF - FORK_DEPTH), 0, -1]) cube([side > 0 ? FORK_DEPTH + 1 : FORK_DEPTH + 1,
FORK_W, PLATE_THICK + 2]);
// Semicircular slot tip (axle seat)
translate([side * (PLATE_X_HALF - FORK_DEPTH), 0, -1])
cylinder(d=FORK_W, h=PLATE_THICK + 2); cylinder(d=FORK_W, h=PLATE_THICK + 2);
} }
// Bearing seat relief (prevents Ø37.8 mm collar binding on edge) // Bearing seat relief (prevents Ø37.8 mm hub collar contacting edge)
for (side = [-1, 1]) for (side = [-1, 1])
translate([side*PLATE_X_HALF - side*(BEARING_SEAT_OD/2 + 1), translate([side * (PLATE_X_HALF - BEARING_SEAT_OD/2 - 1),
-BEARING_SEAT_OD/2, -1]) -BEARING_SEAT_OD/2, -1])
cube([BEARING_SEAT_OD/2 + 2, BEARING_SEAT_OD, PLATE_THICK + 2]); cube([BEARING_SEAT_OD/2 + 2, BEARING_SEAT_OD, PLATE_THICK + 2]);
// Dropout clamp bolt through-holes // Dropout clamp bolt through-holes
for (side = [-1, 1]) for (side = [-1, 1])
for (dx = [-CLAMP_BOLT_DX, CLAMP_BOLT_DX]) for (dx = [-CLAMP_BOLT_DX, CLAMP_BOLT_DX])
for (dy = [-CLAMP_BOLT_DY, CLAMP_BOLT_DY]) for (dy = [-CLAMP_BOLT_DY, CLAMP_BOLT_DY])
translate([side*WHEELBASE/2 + dx, dy, -1]) translate([side * AXLE_X + dx, dy, -1])
cylinder(d=CLAMP_BOLT_D, h=PLATE_THICK + 2); cylinder(d=CLAMP_BOLT_D, h=PLATE_THICK + 2);
// Alignment pin holes (Ø4) // Alignment pin holes (Ø4 × 2 per clamp)
for (side = [-1, 1]) for (side = [-1, 1])
for (dy = [-CLAMP_BOLT_DY + 8, CLAMP_BOLT_DY - 8]) for (dy = [-CLAMP_BOLT_DY + 8, CLAMP_BOLT_DY - 8])
translate([side*WHEELBASE/2, dy, -1]) translate([side * AXLE_X, dy, -1])
cylinder(d=CLAMP_ALIGN_D, h=PLATE_THICK + 2); cylinder(d=CLAMP_ALIGN_D, h=PLATE_THICK + 2);
// Stem socket bore // Stem socket bore
translate([0, 0, -1]) translate([0, 0, -1]) cylinder(d=STEM_BORE, h=PLATE_THICK + 2);
cylinder(d=STEM_BORE, h=PLATE_THICK + 2);
// Stem flange bolt holes (4× M5, 90° pattern on STEM_FLANGE_BC) // Stem flange bolt holes (4× M5 at 90° on Ø66 BC)
for (a = [0, 90, 180, 270]) for (a = [0, 90, 180, 270])
rotate([0, 0, a]) rotate([0, 0, a])
translate([STEM_FLANGE_BC/2, 0, -1]) translate([STEM_FLANGE_BC/2, 0, -1])
cylinder(d=M5, h=PLATE_THICK + 2); cylinder(d=M5, h=PLATE_THICK + 2);
// FC mount (MAMBA F722S 30.5 × 30.5 M3) // FC mount holes (MAMBA F722S 30.5 × 30.5 M3)
for (x = [FC_X_OFFSET - FC_PITCH/2, FC_X_OFFSET + FC_PITCH/2]) for (x = [FC_X_OFFSET - FC_PITCH/2, FC_X_OFFSET + FC_PITCH/2])
for (y = [-FC_PITCH/2, FC_PITCH/2]) for (y = [-FC_PITCH/2, FC_PITCH/2])
translate([x, y, -1]) translate([x, y, -1]) cylinder(d=FC_HOLE_D, h=PLATE_THICK + 2);
cylinder(d=FC_HOLE_D, h=PLATE_THICK + 2);
// Wiring / cable pass-through slots (2×, flanking stem) // Wiring / cable pass-through slots (flanking stem)
for (dy = [-30, 30]) // Placed at Y = ±55 mm (clear of stem flange bolts at ±33 mm and clamp at ±22 mm)
for (dy = [-55, 55])
hull() { hull() {
translate([15, dy, -1]) cylinder(d=14, h=PLATE_THICK + 2); translate([ 12, dy, -1]) cylinder(d=12, h=PLATE_THICK + 2);
translate([-15, dy, -1]) cylinder(d=14, h=PLATE_THICK + 2); translate([-12, dy, -1]) cylinder(d=12, h=PLATE_THICK + 2);
} }
// Lightening slots (between FC zone and dropout zones) // Lightening holes (2× in open areas between stem and clamp zones)
for (sx = [-1, 1]) { // At X = 0, Y = ±80 mm (front/rear open corridor)
// One slot each side of stem, in the structural corridor for (dy = [-80, 80])
lx = sx * (WHEELBASE/4);
hull() { hull() {
translate([lx, -(PLATE_DEPTH/2 - 22), -1]) cylinder(d=18, h=PLATE_THICK+2); translate([ 20, dy, -1]) cylinder(d=20, h=PLATE_THICK + 2);
translate([lx, (PLATE_DEPTH/2 - 22), -1]) cylinder(d=18, h=PLATE_THICK+2); translate([-20, dy, -1]) cylinder(d=20, h=PLATE_THICK + 2);
}
} }
} }
} }
// ============================================================================= // =============================================================================
// STEM FLANGE RING (laser-cut, qty 2 one above, one below plate) // STEM FLANGE RING (laser-cut, qty 2)
// ============================================================================= // =============================================================================
module stem_flange() { module stem_flange() {
difference() { difference() {
cylinder(d=STEM_FLANGE_OD, h=STEM_FLANGE_T); 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);
translate([0, 0, -1])
cylinder(d=STEM_BORE, h=STEM_FLANGE_T + 2);
// 4× M5 flange bolts
for (a = [0, 90, 180, 270]) for (a = [0, 90, 180, 270])
rotate([0, 0, a]) rotate([0, 0, a])
translate([STEM_FLANGE_BC/2, 0, -1]) translate([STEM_FLANGE_BC/2, 0, -1])
@ -246,7 +244,7 @@ module stem_flange() {
} }
// ============================================================================= // =============================================================================
// DROPOUT CLAMP LOWER (round bore, base-section diameter) // DROPOUT CLAMP LOWER (round bore, base section)
// ============================================================================= // =============================================================================
module clamp_lower() { module clamp_lower() {
@ -255,16 +253,13 @@ module clamp_lower() {
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);
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 zone) translate([0,0,-1]) cylinder(d=AXLE_BASE_DIA + 0.4, h=CLAMP_THICK + 2);
translate([0, 0, -1]) cylinder(d=AXLE_BASE_DIA + 0.4, h=CLAMP_THICK + 2); // Open entry slot (axle slides in from plate edge)
// Slide-in slot (open toward wheel side)
translate([-CLAMP_L/2 - 1, -FORK_W/2, -1]) translate([-CLAMP_L/2 - 1, -FORK_W/2, -1])
cube([CLAMP_L/2 + 1, FORK_W, CLAMP_THICK + 2]); cube([CLAMP_L/2 + 1, FORK_W, CLAMP_THICK + 2]);
// Clamp bolts
for (dx = [-CLAMP_BOLT_DX, CLAMP_BOLT_DX]) for (dx = [-CLAMP_BOLT_DX, CLAMP_BOLT_DX])
for (dy = [-CLAMP_BOLT_DY, CLAMP_BOLT_DY]) for (dy = [-CLAMP_BOLT_DY, CLAMP_BOLT_DY])
translate([dx, dy, -1]) cylinder(d=CLAMP_BOLT_D, h=CLAMP_THICK + 2); 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]) 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);
} }
@ -275,41 +270,31 @@ module clamp_lower() {
// ============================================================================= // =============================================================================
module clamp_upper() { module clamp_upper() {
dcut_r = DCUT_R; 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() { difference() {
hull() { 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);
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 translate([0,0,-1])
translate([0, 0, -1]) linear_extrude(CLAMP_THICK + 2) dcut_profile_2d(DCUT_R, dcut_d);
linear_extrude(CLAMP_THICK + 2)
dcut_profile_2d(dcut_r, dcut_d);
// Slide-in slot
translate([-CLAMP_L/2 - 1, -FORK_W/2, -1]) translate([-CLAMP_L/2 - 1, -FORK_W/2, -1])
cube([CLAMP_L/2 + 1, FORK_W, CLAMP_THICK + 2]); cube([CLAMP_L/2 + 1, FORK_W, CLAMP_THICK + 2]);
// Clamp bolts
for (dx = [-CLAMP_BOLT_DX, CLAMP_BOLT_DX]) for (dx = [-CLAMP_BOLT_DX, CLAMP_BOLT_DX])
for (dy = [-CLAMP_BOLT_DY, CLAMP_BOLT_DY]) for (dy = [-CLAMP_BOLT_DY, CLAMP_BOLT_DY])
translate([dx, dy, -1]) cylinder(d=CLAMP_BOLT_D, h=CLAMP_THICK + 2); 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]) 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);
// Orientation emboss // Orientation marker (triangle pointing toward flat)
translate([0, dcut_d + 1.5, CLAMP_THICK - 0.8]) translate([0, dcut_d + 1.5, CLAMP_THICK - 0.8])
linear_extrude(1) linear_extrude(1) polygon([[0,0],[-3,-5],[3,-5]]);
polygon([[0,0],[-3,-5],[3,-5]]);
} }
} }
// D-cut 2D profile helper
module dcut_profile_2d(r, flat_d) { module dcut_profile_2d(r, flat_d) {
intersection() { intersection() {
circle(r=r); circle(r=r);
translate([-r - 1, -r - 1]) translate([-r-1, -r-1]) square([2*(r+1), r+1+flat_d]);
square([2*(r+1), r + 1 + flat_d]);
} }
} }
@ -324,25 +309,22 @@ module axle_ghost() {
} }
// ============================================================================= // =============================================================================
// DXF EXPORT // DXF EXPORT COMMANDS
// ============================================================================= // =============================================================================
// //
// Part 1 Base plate: // Base plate (270 × 240 mm, 6 mm Al or 8 mm acrylic):
// openscad prototype_baseplate.scad -D 'RENDER="plate_2d"' -o baseplate.dxf // openscad prototype_baseplate.scad -D 'RENDER="plate_2d"' -o baseplate.dxf
// //
// Part 2 Dropout clamp, lower (× 2): // Dropout clamp lower (× 2):
// openscad prototype_baseplate.scad -D 'RENDER="clamp_lower_2d"' -o clamp_lower.dxf // openscad prototype_baseplate.scad -D 'RENDER="clamp_lower_2d"' -o clamp_lower.dxf
// //
// Part 3 Dropout clamp, upper (× 2): // Dropout clamp upper (× 2):
// openscad prototype_baseplate.scad -D 'RENDER="clamp_upper_2d"' -o clamp_upper.dxf // openscad prototype_baseplate.scad -D 'RENDER="clamp_upper_2d"' -o clamp_upper.dxf
// //
// Part 4 Stem flange ring (× 2, one each side of plate): // Stem flange ring (× 2):
// openscad prototype_baseplate.scad -D 'RENDER="stem_flange_2d"' -o stem_flange.dxf // openscad prototype_baseplate.scad -D 'RENDER="stem_flange_2d"' -o stem_flange.dxf
// //
// Materials: // PLATE BLANK: 280 × 250 mm (10 mm oversized each side for datum + clamping)
// Plate + flanges : 6 mm 5052-H32 aluminium (preferred) // CLAMP BLANKS: 90 × 70 mm each
// 8 mm clear acrylic (quick proto) // FLANGE BLANKS: Ø90 mm disc each
// 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.
// ============================================================================= // =============================================================================