saltylab-firmware/chassis/rover_motor_mount.scad
sl-mechanical 3e4764b3eb feat: SaltyRover 4-wheel chassis design (#73)
Add parametric OpenSCAD designs for the SaltyRover stable 4-wheel
variant. Reuses existing 25mm stem, sensor head, and all SaltyLab
sensor mounts without modification.

Files:
- saltyrover_chassis.scad  480×500mm deck, stem collar, FC+Orin
  standoffs, motor attachment holes, battery tray opening; RENDER
  deck_2d for waterjet/CNC DXF
- rover_motor_mount.scad   L-bracket + axle clamp plate per motor;
  uses caliper-verified axle dims from BOM.md; dropout slot for
  tool-free motor swap; RENDER bracket_2d for CNC DXF
- rover_battery_tray.scad  Slide-out tray for 2-4 × 420×88×56mm
  packs laid flat (low CG); T-slot rails, spring latch
- rover_stem_adapter.scad  Flange + split clamp locks 25mm stem to
  deck collar; 550mm stem option for rover height
- rover_BOM.md             Assembly sequence, fastener table, mass
  estimate (~13.4kg), height stack diagram

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-01 01:21:46 -05:00

241 lines
9.8 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.

// ============================================================
// rover_motor_mount.scad — SaltyRover Drive Motor Brackets
// Rev A 2026-03-01 sl-mechanical
// ============================================================
// L-bracket that bolts to the deck edge and positions the drive
// motor axle at the correct height. Print or CNC 4×.
//
// Default values from BOM.md (caliper-verified hoverboard motor):
// Axle base OD: 16.11 mm
// D-cut OD: 15.95 mm (flat chord 13.00 mm)
// Bearing seat OD: 37.80 mm
// Axle CL above gnd: 127 mm
// Tire OD: 254 mm (10×2.125")
//
// Bracket geometry (all dims in mm):
// Vertical flange — bolts to deck edge (2× M5 SHCS)
// Horizontal arm — extends BRKT_REACH beyond deck edge
// to position axle CL at TRACK_W/2
// Axle channel — open-bottom dropout slot for easy
// motor install/removal; retained by
// a separate clamping plate (×1 per bracket)
// Bearing recess — 37.8 mm recess on inboard face
//
// ⚠ VERIFY before printing / CNC:
// AXLE_D, AXLE_FLAT, BEARING_OD from caliper measurement.
// DECK_BOT_H = GND_CLR + BATT_FLOOR_T + BATT_PACK_H.
// Adjust BRKT_REACH so TRACK_W matches chassis layout.
//
// Print orientation: flange face flat on bed.
// Material: PETG or PC, 5 perims, 60% infill.
// Alternative: CNC-routed 10mm 6061-T6 aluminium plate.
//
// RENDER options:
// "assembly" bracket + clamp plate + phantom motor axle
// "bracket" main L-bracket for printing/CNC
// "clamp_plate" axle retention clamp plate (print 4×)
// "bracket_2d" bracket floor projection → DXF
// ============================================================
RENDER = "assembly";
// ── Axle / motor (BOM.md caliper-verified) ────────────────
AXLE_D = 16.11; // axle base section OD (round)
AXLE_FLAT = 13.00; // D-cut flat chord width
AXLE_D_DCUT = 15.95; // D-cut section OD
BEARING_OD = 37.80; // bearing seat collar OD
BEARING_D = BEARING_OD + 1.0; // bore clearance for bearing seat
BEARING_H = 8.0; // bearing recess depth on inboard face
AXLE_H = 127.0; // axle CL above ground
TIRE_OD = 254.0; // 10×2.125" tire outer diameter
// Dropout slot dims
DROP_W = AXLE_D + 2.0; // slot width (axle + 1 mm each side)
DROP_D = AXLE_H; // slot depth from bracket bottom (full height)
// ── Bracket geometry ─────────────────────────────────────
// Deck geometry (must match saltyrover_chassis.scad)
GND_CLR = 50.0;
BATT_FLOOR_T = 3.0;
BATT_PACK_H = 56.0;
DECK_T = 8.0;
DECK_BOT_H = GND_CLR + BATT_FLOOR_T + BATT_PACK_H; // 109 mm
DECK_TOP_H = DECK_BOT_H + DECK_T; // 117 mm
// Axle height relative to deck top:
AXLE_ABOVE_DECK = AXLE_H - DECK_TOP_H; // ~10 mm
// Bracket reach beyond deck edge to motor axle CL
BRKT_REACH = 40.0; // motor CL is BRKT_REACH mm outside deck edge
// (TRACK_W/2 - ROVER_W/2 = 270 - 240 = 30 mm +
// bearing housing clearance margin = 40 mm)
// Bracket plate dimensions
BRKT_T = 10.0; // bracket plate thickness
BRKT_H_ABOVE = AXLE_ABOVE_DECK + BEARING_OD/2 + 8.0; // above deck top
BRKT_H_BELOW = DECK_T + 12.0; // below deck bottom
BRKT_TOTAL_H = BRKT_H_ABOVE + BRKT_H_BELOW; // full height
// Width of bracket arm (fore-aft direction)
BRKT_W = 60.0; // covers motor fore-aft attachment bolts
// Flange for deck attachment
FLANGE_T = 8.0; // flange plate thickness
FLANGE_DEPTH = 20.0; // how deep flange sits on deck face (Y direction)
// M5 bolt holes: 2× through flange (deck attachment)
M5_D = 5.3;
MOT_BOLT_SPC = 30.0; // matches saltyrover_chassis.scad MOT_BOLT_SPC
// Clamping plate (retains axle in dropout slot)
CLAMP_T = 6.0; // clamp plate thickness
CLAMP_W = DROP_W + 12.0;
CLAMP_H = BEARING_D + 12.0;
CLAMP_BOLT_D= 4.3; // M4 clearance
CLAMP_BOLT_SPC = CLAMP_W - 10.0;
// Gusset (triangular fillet between flange and arm)
GUSSET_T = 8.0;
$fn = 64;
e = 0.01;
// ─────────────────────────────────────────────────────────
// motor_bracket()
// Main L-bracket. Coordinate: Z=0 at deck top.
// Bracket outboard face at X=0 (deck edge).
// Bracket arm extends +X toward motor.
// ─────────────────────────────────────────────────────────
module motor_bracket() {
axle_z = AXLE_ABOVE_DECK; // axle CL in bracket coords
difference() {
union() {
// ── Vertical arm (extends +X from deck edge) ──
translate([0, -BRKT_W/2, -BRKT_H_BELOW])
cube([BRKT_REACH + BRKT_T, BRKT_W, BRKT_TOTAL_H]);
// ── Vertical flange (sits against deck edge, -X side) ──
translate([-FLANGE_DEPTH, -BRKT_W/2, -BRKT_H_BELOW])
cube([FLANGE_DEPTH, BRKT_W, BRKT_TOTAL_H]);
// ── Gusset (arm to flange transition) ──
translate([0, -BRKT_W/2, axle_z - GUSSET_T/2])
linear_extrude(GUSSET_T)
polygon([
[0, 0],
[BRKT_REACH/2, 0],
[0, BRKT_W]
]);
}
// ── Axle dropout slot (open at bottom, centered on arm tip) ──
translate([BRKT_REACH, -DROP_W/2, -BRKT_H_BELOW - e])
cube([BRKT_T + e, DROP_W, AXLE_H + 2*e]);
// ── Axle round bore at slot top ──
translate([BRKT_REACH, 0, axle_z])
rotate([0, 90, 0])
cylinder(d=AXLE_D + 1.0, h=BRKT_T + 2*e);
// ── D-cut anti-rotation flat (matches axle flat chord) ──
translate([BRKT_REACH - e, -AXLE_FLAT/2, axle_z - AXLE_D/2 - e])
cube([BRKT_T + 2*e, AXLE_FLAT, AXLE_D/2 + e]);
// ── Bearing seat recess (inboard face, X=BRKT_T) ──
translate([BRKT_REACH + BRKT_T - BEARING_H, 0, axle_z])
rotate([0, 90, 0])
cylinder(d=BEARING_D, h=BEARING_H + e);
// ── Clamping plate bolt holes (×2, retain axle) ──
for (dz=[-CLAMP_BOLT_SPC/2, CLAMP_BOLT_SPC/2])
translate([BRKT_REACH - e, 0, axle_z + dz])
rotate([0, 90, 0])
cylinder(d=CLAMP_BOLT_D, h=BRKT_T + 2*e);
// ── Deck attachment holes through flange (M5 × 2) ──
for (bz=[-MOT_BOLT_SPC/2, MOT_BOLT_SPC/2])
translate([-FLANGE_DEPTH - e, 0, bz])
rotate([0, 90, 0])
cylinder(d=M5_D, h=FLANGE_DEPTH + 2*e);
// ── Lightening pocket on arm (inboard face) ──
translate([BRKT_T, -BRKT_W/2 + 8, -BRKT_H_BELOW/2])
cube([BRKT_REACH - BRKT_T - 8,
BRKT_W - 16,
BRKT_H_BELOW/2 - 4]);
}
}
// ─────────────────────────────────────────────────────────
// axle_clamp_plate()
// Bolts to bracket outboard face to close dropout slot.
// Print flat face down. 1× per bracket.
// ─────────────────────────────────────────────────────────
module axle_clamp_plate() {
axle_z = AXLE_ABOVE_DECK;
difference() {
translate([-CLAMP_W/2, 0, axle_z - CLAMP_H/2])
cube([CLAMP_W, CLAMP_T, CLAMP_H]);
// Axle bore
translate([0, -e, axle_z])
rotate([-90, 0, 0])
cylinder(d=AXLE_D + 0.5, h=CLAMP_T + 2*e);
// Bearing seat relief
translate([0, CLAMP_T - BEARING_H + e, axle_z])
rotate([-90, 0, 0])
cylinder(d=BEARING_D, h=BEARING_H + e);
// M4 bolt holes (×2)
for (dz=[-CLAMP_BOLT_SPC/2, CLAMP_BOLT_SPC/2])
translate([0, -e, axle_z + dz])
rotate([-90, 0, 0])
cylinder(d=CLAMP_BOLT_D, h=CLAMP_T + 2*e);
}
}
// ─────────────────────────────────────────────────────────
// Render selector
// ─────────────────────────────────────────────────────────
if (RENDER == "assembly") {
color("DimGray", 0.90) motor_bracket();
color("SteelBlue", 0.85)
translate([BRKT_REACH + BRKT_T, 0, 0])
axle_clamp_plate();
// Phantom axle stub
color("Silver", 0.4)
translate([BRKT_REACH, 0, AXLE_ABOVE_DECK])
rotate([0, 90, 0])
cylinder(d=AXLE_D, h=80);
// Phantom tire (semi-transparent outline)
color("Black", 0.12)
translate([BRKT_REACH + 40, 0, AXLE_ABOVE_DECK])
rotate([0, 90, 0])
cylinder(d=TIRE_OD, h=54);
// Reference deck edge
color("Gold", 0.25)
translate([-FLANGE_DEPTH - 20, -BRKT_W/2, -BRKT_H_BELOW])
cube([20, BRKT_W, BRKT_TOTAL_H]);
} else if (RENDER == "bracket") {
motor_bracket();
} else if (RENDER == "clamp_plate") {
// Orient for printing: lay flat, rotate upright
translate([0, CLAMP_T, -AXLE_ABOVE_DECK])
rotate([90, 0, 0])
axle_clamp_plate();
} else if (RENDER == "bracket_2d") {
projection(cut=true)
translate([0, 0, -BRKT_H_BELOW - BRKT_T/2])
motor_bracket();
}