OpenSCAD parametric model for SaltyBot two-wheeled self-balancing robot chassis: - 600mm wheelbase, 170mm hoverboard hub motor fork dropouts - MAMBA F722S FC mount (30.5x30.5mm M3 pattern, 6mm nylon standoffs) - Battery tray for 24V 4Ah pack (185x72x52mm) with strap slots + vent holes - Jetson Nano B01 mount plate (58x58mm M3 pattern, 8mm standoffs) - Front/rear bumper brackets with 22mm EMT conduit saddle clamps - Longitudinal ribs, lightening holes, cable routing slots - BOM (32 line items) and step-by-step assembly notes Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
341 lines
16 KiB
OpenSCAD
341 lines
16 KiB
OpenSCAD
// =============================================================================
|
||
// SaltyBot — Parametric Chassis Frame
|
||
// Task: bd-1iy5
|
||
// Agent: sl-mechanical
|
||
// Date: 2026-02-28
|
||
//
|
||
// Self-balancing two-wheeled robot chassis
|
||
// Requirements:
|
||
// - 600mm wheelbase
|
||
// - 2x hoverboard hub motors (170mm OD)
|
||
// - STM32 MAMBA F722S FC mount (30.5x30.5mm pattern)
|
||
// - Battery tray (24V 4Ah — ~180x70x50mm pack)
|
||
// - Jetson Nano B01 mount plate (100x80mm, M3 holes)
|
||
// - Front/rear bumper brackets
|
||
// =============================================================================
|
||
|
||
// ─── RENDER QUALITY ──────────────────────────────────────────────────────────
|
||
$fn = 64;
|
||
|
||
// =============================================================================
|
||
// PARAMETERS — edit here to adjust the whole model
|
||
// =============================================================================
|
||
|
||
// ── Wheelbase / overall geometry ─────────────────────────────────────────────
|
||
WHEELBASE = 600; // mm, center-to-center axle distance (lateral)
|
||
FRAME_WIDTH = 220; // mm, front-to-back depth of main deck
|
||
DECK_THICKNESS = 6; // mm, main deck plate thickness
|
||
WALL_T = 4; // mm, general wall thickness for brackets/ribs
|
||
|
||
// ── Hub motor parameters ─────────────────────────────────────────────────────
|
||
MOTOR_OD = 170; // mm, motor housing outer diameter
|
||
MOTOR_AXLE_D = 14; // mm, axle bolt diameter
|
||
MOTOR_AXLE_FLAT = 10; // mm, axle flat-to-flat (for anti-rotation)
|
||
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
|
||
// =============================================================================
|
||
|
||
color("Silver", 0.9) main_deck();
|
||
color("DimGray") motor_fork(side= 1); // right (+X)
|
||
color("DimGray") motor_fork(side=-1); // left (-X)
|
||
color("SteelBlue") battery_tray();
|
||
color("OliveDrab") fc_mount_plate();
|
||
color("DarkOrange") jetson_mount_plate();
|
||
color("Tomato") bumper_bracket(front= 1);
|
||
color("Tomato") bumper_bracket(front=-1);
|
||
color("WhiteSmoke") longitudinal_ribs();
|
||
|
||
// =============================================================================
|
||
// MODULES
|
||
// =============================================================================
|
||
|
||
// ─── Main deck plate ─────────────────────────────────────────────────────────
|
||
module main_deck() {
|
||
difference() {
|
||
// Deck plate
|
||
translate([-WHEELBASE/2 - MOTOR_FORK_T, -FRAME_WIDTH/2, 0])
|
||
cube([WHEELBASE + 2*MOTOR_FORK_T, FRAME_WIDTH, DECK_THICKNESS]);
|
||
|
||
// Lightening holes — 3 rows × 4 cols
|
||
for (x = [-WHEELBASE/3, 0, WHEELBASE/3])
|
||
for (y = [-FRAME_WIDTH/4, FRAME_WIDTH/4])
|
||
translate([x, y, -1])
|
||
cylinder(d=50, h=DECK_THICKNESS+2);
|
||
|
||
// FC mount holes
|
||
fc_mount_holes(z_offset=-1, depth=DECK_THICKNESS+2);
|
||
|
||
// Battery tray floor cutout for wire pass-through
|
||
translate([-BATT_L/2 + 40, -BATT_W/2 + 40, -1])
|
||
cube([BATT_L - 80, BATT_W - 80, DECK_THICKNESS+2]);
|
||
|
||
// Cable routing slots
|
||
for (x = [-60, 60])
|
||
translate([x, -FRAME_WIDTH/2 - 1, -1])
|
||
cube([14, 18, DECK_THICKNESS+2]);
|
||
}
|
||
}
|
||
|
||
// ─── Longitudinal ribs (×2, symmetric F/R) ───────────────────────────────────
|
||
module longitudinal_ribs() {
|
||
for (y = [-FRAME_WIDTH/2 + RIB_W/2 + WALL_T,
|
||
FRAME_WIDTH/2 - RIB_W/2 - WALL_T])
|
||
translate([-WHEELBASE/2, y - RIB_T/2, -RIB_H])
|
||
cube([WHEELBASE, RIB_T, RIB_H]);
|
||
}
|
||
|
||
// ─── Motor fork dropout bracket ──────────────────────────────────────────────
|
||
// Mounts to deck edge; provides a CNC-milled or FDM dropout slot for the axle.
|
||
// `side` = +1 (right/+X) or -1 (left/-X)
|
||
module motor_fork(side = 1) {
|
||
x_pos = side * (WHEELBASE/2);
|
||
|
||
translate([x_pos, 0, 0]) {
|
||
difference() {
|
||
union() {
|
||
// Vertical fork body
|
||
translate([side*(DECK_THICKNESS/2), -MOTOR_FORK_WIDTH/2 - 4, -MOTOR_FORK_H])
|
||
cube([side * MOTOR_FORK_T, MOTOR_FORK_WIDTH + 8, MOTOR_FORK_H + DECK_THICKNESS]);
|
||
|
||
// Gusset triangles
|
||
for (g = [-1, 1])
|
||
translate([side*(DECK_THICKNESS/2), g*(MOTOR_FORK_WIDTH/2 + 2), -MOTOR_FORK_H])
|
||
linear_extrude(height=MOTOR_FORK_T)
|
||
polygon([[0,0],[0, g*20],[side*-30, 0]]);
|
||
}
|
||
|
||
// Axle dropout slot (open at bottom)
|
||
translate([side*(DECK_THICKNESS/2) - 1,
|
||
-MOTOR_AXLE_FLAT/2,
|
||
-MOTOR_FORK_DEPTH - MOTOR_AXLE_D/2])
|
||
cube([MOTOR_FORK_T + 2, MOTOR_AXLE_FLAT, MOTOR_FORK_DEPTH + 1]);
|
||
|
||
// Axle through-hole at slot top
|
||
translate([side*(DECK_THICKNESS/2) - 1, 0,
|
||
-MOTOR_FORK_DEPTH - MOTOR_AXLE_D/2])
|
||
rotate([0, 90, 0])
|
||
cylinder(d=MOTOR_AXLE_D + 1, h=MOTOR_FORK_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
|
||
module battery_tray() {
|
||
z_base = -BATT_FLOOR - BATT_H - 5; // hang below deck
|
||
translate([-BATT_L/2 - BATT_WALL, -BATT_W/2 - BATT_WALL, z_base]) {
|
||
difference() {
|
||
// Outer tray body
|
||
cube([BATT_L + 2*BATT_WALL,
|
||
BATT_W + 2*BATT_WALL,
|
||
BATT_H + BATT_FLOOR]);
|
||
|
||
// Inner cavity
|
||
translate([BATT_WALL, BATT_WALL, BATT_FLOOR])
|
||
cube([BATT_L, BATT_W, BATT_H + 1]);
|
||
|
||
// Strap slots (2× longitudinal)
|
||
for (x = [BATT_L/2 - BATT_STRAP_W*2,
|
||
BATT_L/2 + BATT_STRAP_W])
|
||
translate([x, -1, BATT_FLOOR + BATT_H/2 - BATT_STRAP_W/2])
|
||
cube([BATT_STRAP_W, BATT_W + 2*BATT_WALL + 2, BATT_STRAP_W]);
|
||
|
||
// Ventilation slots bottom (3×)
|
||
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) {
|
||
// 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() {
|
||
jet_x = 60; // offset toward rear
|
||
jet_y = 0;
|
||
plate_z = DECK_THICKNESS + JETSON_STANDOFF_H;
|
||
|
||
translate([jet_x, jet_y, 0]) {
|
||
difference() {
|
||
union() {
|
||
// Mounting plate
|
||
translate([-JETSON_PLATE_L/2, -JETSON_PLATE_W/2, DECK_THICKNESS + JETSON_STANDOFF_H])
|
||
cube([JETSON_PLATE_L, JETSON_PLATE_W, JETSON_PLATE_T]);
|
||
|
||
// Four standoff columns
|
||
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);
|
||
}
|
||
|
||
// B01 M3 hole pattern (58×58 mm)
|
||
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 - 1])
|
||
cylinder(d=JETSON_HOLE_D, h=JETSON_STANDOFF_H + JETSON_PLATE_T + 2);
|
||
|
||
// Ventilation / cable routing slots in plate
|
||
for (i = [-1, 0, 1])
|
||
translate([i*25 - 10, -JETSON_PLATE_W/2 + 10,
|
||
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)
|
||
module bumper_bracket(front = 1) {
|
||
y_pos = front * (FRAME_WIDTH/2);
|
||
|
||
translate([0, y_pos, 0]) {
|
||
difference() {
|
||
union() {
|
||
// Horizontal mounting rail plate
|
||
translate([-BUMPER_L/2, front*(BUMPER_T/2), 0])
|
||
cube([BUMPER_L, BUMPER_T, BUMPER_H]);
|
||
|
||
// Vertical gussets at ends
|
||
for (x = [-BUMPER_L/2 + 5, BUMPER_L/2 - BUMPER_T - 5])
|
||
translate([x, front*(BUMPER_T), 0])
|
||
cube([BUMPER_T, 20, BUMPER_H]);
|
||
|
||
// 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() {
|
||
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);
|
||
}
|
||
}
|
||
|
||
// Deck mounting holes (M5 × 6 along length)
|
||
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);
|
||
// }
|
||
// }
|