saltylab-firmware/chassis/chassis_frame.scad
sl-mechanical 6ea4b56471 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>
2026-02-28 17:30:54 -05:00

340 lines
14 KiB
OpenSCAD
Raw Blame History

This file contains ambiguous Unicode characters

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

// =============================================================================
// SaltyBot — Full Chassis Frame (Rev B — correct 270 × 240 mm envelope)
// Agent: sl-mechanical | 2026-02-28 | Fixes issue #18
//
// Parametric OpenSCAD model. Plate geometry corrected per issue #18:
// Width (axle direction, X): 270 mm ← was wrong 640 mm
// Depth (front-to-rear, Y): 240 mm ← was wrong 220 mm
// Wheels extend beyond plate edges — expected.
//
// Motor axle dimensions updated to caliper-verified values (see PR #7 / #11).
//
// NOTE: For the prototype, use prototype_baseplate.scad + stem_battery_clamp.scad.
// This file models the full machined chassis for production reference.
// =============================================================================
$fn = 64;
// =============================================================================
// PLATE DIMENSIONS ← primary parameters fixed for issue #18
// =============================================================================
PLATE_W = 270.0; // mm width (axle / left-right direction)
PLATE_D = 240.0; // mm depth (front-to-rear)
DECK_THICK = 6.0; // mm deck plate thickness
WALL_T = 4.0; // mm general wall / rib thickness
// Fork slot geometry — slot opens at plate edge, axle seats at bottom
FORK_SLOT_D = 50.0; // mm fork slot depth (inward from plate edge)
PLATE_X_HALF = PLATE_W / 2; // 135 mm — plate half-width / edge
AXLE_X = PLATE_X_HALF - FORK_SLOT_D; // 85 mm — axle C/L from plate centre
// =============================================================================
// HUB MOTOR — VERIFIED AXLE DIMENSIONS (caliper)
// =============================================================================
AXLE_BASE_DIA = 16.11; // mm round section near hub
AXLE_BASE_LEN = 15.00;
AXLE_DCUT_DIA = 15.95; // mm D-cut round OD
AXLE_DCUT_FLAT = 13.00; // mm flat chord
AXLE_DCUT_LEN = 43.35;
AXLE_TOTAL = 65.50; // mm protrusion from hub face
BEARING_SEAT_OD = 37.80; // mm hub centre collar OD
TIRE_OD = 254.0; // mm 10 × 2.125" pneumatic
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)
// =============================================================================
// 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)
// =============================================================================
module main_deck() {
R = 10;
difference() {
linear_extrude(DECK_THICK)
minkowski() {
square([PLATE_W - 2*R, PLATE_D - 2*R], center=true);
circle(r=R);
}
// Fork slots (open at ±X plate edges)
for (side = [-1, 1]) {
translate([side*(PLATE_X_HALF - FORK_SLOT_D), -FORK_SLOT_W/2, -1])
cube([FORK_SLOT_D + 1, FORK_SLOT_W, DECK_THICK + 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_holes(z=-1, h=DECK_THICK+2);
// Battery tray anchor holes
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=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
for (dy = [-55, 55])
hull() {
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)
// =============================================================================
module longitudinal_ribs() {
rib_y = PLATE_D/2 - WALL_T - RIB_T/2;
for (y = [-rib_y, rib_y])
translate([-PLATE_W/2, y - RIB_T/2, -RIB_H])
cube([PLATE_W, RIB_T, RIB_H]);
}
// =============================================================================
// MOTOR FORK BRACKET (vertical, bolts to deck edge at ±PLATE_X_HALF)
// =============================================================================
module motor_fork_bracket(side = 1) {
translate([side * PLATE_X_HALF, 0, 0]) {
s = side;
difference() {
union() {
// Main bracket plate (extends below deck)
translate([s > 0 ? 0 : -FORK_BKT_T,
-FORK_BKT_W/2, -FORK_BKT_H])
cube([FORK_BKT_T, FORK_BKT_W, FORK_BKT_H + DECK_THICK]);
// Diagonal gussets
for (gy = [-1, 1])
translate([s > 0 ? 0 : -FORK_BKT_T,
gy * FORK_BKT_W/2, -FORK_BKT_H/2])
linear_extrude(FORK_BKT_T)
polygon([[0,0],[0, gy*20],[s * -25, 0]]);
}
// Axle dropout slot (open at bracket bottom)
translate([s > 0 ? -1 : -FORK_BKT_T - 1,
-AXLE_BASE_DIA/2 - 0.2, -FORK_BKT_H])
cube([FORK_BKT_T + 2, AXLE_BASE_DIA + 0.4, FORK_SLOT_D + 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);
// Deck attachment bolts (M5 × 4)
for (dy = [-12, 12])
for (dz = [DECK_THICK * 0.3, DECK_THICK * 0.7])
translate([s > 0 ? -1 : -FORK_BKT_T - 1, dy, dz])
rotate([0, 90, 0])
cylinder(d=M5, h=FORK_BKT_T + 2);
}
}
}
// =============================================================================
// 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() {
z_base = -BATT_FLOOR - BATT_H - 5;
translate([-BATT_L/2 - BATT_WALL, -BATT_W/2 - BATT_WALL, z_base]) {
difference() {
cube([BATT_L+2*BATT_WALL, BATT_W+2*BATT_WALL, BATT_H+BATT_FLOOR]);
translate([BATT_WALL, BATT_WALL, BATT_FLOOR])
cube([BATT_L, BATT_W, BATT_H+1]);
for (x = [BATT_L/2-30, BATT_L/2+8])
translate([x, -1, BATT_FLOOR+BATT_H/2-10])
cube([20, BATT_W+2*BATT_WALL+2, 20]);
for (i = [0:1])
translate([BATT_WALL+12+i*68, BATT_WALL+8, -1])
cube([38, BATT_W-16, BATT_FLOOR+2]);
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,h=BATT_FLOOR+2);
}
}
}
// =============================================================================
// JETSON NANO B01 MOUNT PLATE
// =============================================================================
module jetson_mount_plate() {
translate([JETSON_X, 0, 0]) {
difference() {
union() {
translate([-JETSON_PL/2, -JETSON_PW/2, DECK_THICK+JETSON_STOFF])
cube([JETSON_PL, JETSON_PW, JETSON_PT]);
for (x=[-JETSON_PITCH/2, JETSON_PITCH/2])
for (y=[-JETSON_PITCH/2, JETSON_PITCH/2])
translate([x,y,DECK_THICK]) cylinder(d=6, h=JETSON_STOFF);
}
for (x=[-JETSON_PITCH/2, JETSON_PITCH/2])
for (y=[-JETSON_PITCH/2, JETSON_PITCH/2])
translate([x,y,DECK_THICK-1])
cylinder(d=JETSON_HOLE_D, h=JETSON_STOFF+JETSON_PT+2);
for (i=[-1,0,1])
translate([i*22-8,-JETSON_PW/2+8,DECK_THICK+JETSON_STOFF-1])
cube([16, JETSON_PW-16, JETSON_PT+2]);
for (x=[-JETSON_PL/2+8, JETSON_PL/2-8])
for (y=[-JETSON_PW/2+8, JETSON_PW/2-8])
translate([x,y,-1]) cylinder(d=M3, h=DECK_THICK+2);
}
}
}
// =============================================================================
// BUMPER BRACKETS (front / rear)
// =============================================================================
module bumper_bracket(front = 1) {
yp = front * PLATE_D/2;
translate([0, yp, 0]) {
difference() {
union() {
translate([-BUMPER_L/2, front*(BUMPER_T/2), 0])
cube([BUMPER_L, BUMPER_T, BUMPER_H]);
for (x=[-BUMPER_L/2+4, BUMPER_L/2-BUMPER_T-4])
translate([x, front*BUMPER_T, 0]) cube([BUMPER_T, 16, BUMPER_H]);
for (x=[-BUMPER_L/3, 0, BUMPER_L/3])
translate([x-BUMPER_TUBE_OD/2-2,
front*(BUMPER_T+10),
BUMPER_H-BUMPER_TUBE_OD/2-4])
difference() {
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])
rotate([-90,0,0])
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])
translate([x, yp*0.001, -1]) cylinder(d=M5, h=BUMPER_H+2);
}
}
}