// ============================================================ // rover_stem_adapter.scad — SaltyRover Vertical Stem Adapter // Rev A 2026-03-01 sl-mechanical // ============================================================ // Secures the 25 mm OD vertical stem to the rover deck. // // Two-part system: // base_flange() — annular flange plate bolts to deck top // (4× M4 SHCS into deck; deck already has // 25 mm stem bore through its centre collar) // stem_clamp() — split collar clamps on stem above the // deck collar; two M4 clamping bolts // lock stem position / rotation // // Reuses the existing sensor head, RPLIDAR, camera mounts, and // roll cage from SaltyLab — stem OD 25 mm is unchanged. // // Stem length options: // SaltyLab 1000 mm (balance robot — tall for CG) // SaltyRover 550 mm (rover — sensors visible, compact) // The adapter is identical; only the purchased tube differs. // // ⚠ Ensure the deck stem collar (saltyrover_chassis.scad, // STEM_COLLAR_OD=50 mm, H=22 mm) is the primary lateral // support. The flange + clamp provide torque/axial lock. // // RENDER options: // "assembly" flange + clamp + stem stub // "base_flange" flange plate for printing // "clamp_front" clamp front half for printing // "clamp_rear" clamp rear half for printing // "flange_2d" flange projection → DXF // ============================================================ RENDER = "assembly"; // ── Stem ───────────────────────────────────────────────── STEM_OD = 25.0; STEM_BORE = 25.4; // +0.4 clearance (same as sensor mounts) STEM_L_ROVER = 550; // recommended rover stem length (mm) // ── Base flange ─────────────────────────────────────────── // Sits on deck collar top. 4× M4 bolt through flange into deck. FLANGE_OD = 80.0; // outer diameter FLANGE_T = 6.0; // plate thickness FLANGE_BOLT_BC = 65.0; // M4 bolt circle diameter FLANGE_BOLT_D = 4.3; // M4 clearance FLANGE_BOLT_N = 4; // number of bolts (at 90°) // Deck collar height (must match saltyrover_chassis.scad STEM_COLLAR_H) DECK_COLLAR_H = 22.0; // ── Split stem clamp ───────────────────────────────────── // Sits on top of flange; clamped M4 bolts lock stem. COL_OD = 52.0; // clamp outer diameter COL_H = 28.0; // clamp height (above flange) COL_BOLT_X = 19.0; // M4 clamping bolt CL from stem axis COL_BOLT_D = 4.5; // M4 clearance hole COL_NUT_W = 7.0; // M4 hex nut across-flats COL_NUT_H = 3.4; // hex nut height // Set screw for rotation lock (front half) SET_SCREW_D = 4.5; // M4 set screw // ── Fasteners ───────────────────────────────────────────── M4_D = 4.3; $fn = 64; e = 0.01; // ───────────────────────────────────────────────────────── // base_flange() // Sits on top of the deck stem collar. // Z=0 at deck top (collar rises from here). // ───────────────────────────────────────────────────────── module base_flange() { difference() { union() { // Annular flange plate (sits on collar top) translate([0, 0, DECK_COLLAR_H]) cylinder(d=FLANGE_OD, h=FLANGE_T); // Short skirt that drops inside/over collar translate([0, 0, DECK_COLLAR_H - 4]) cylinder(d=FLANGE_OD - 8, h=4); } // Stem bore translate([0, 0, DECK_COLLAR_H - 4 - e]) cylinder(d=STEM_BORE, h=FLANGE_T + 4 + 2*e); // M4 bolts through flange + down into deck (×4) for (ang=[0, 90, 180, 270]) rotate([0, 0, ang]) translate([FLANGE_BOLT_BC/2, 0, DECK_COLLAR_H - e]) cylinder(d=FLANGE_BOLT_D, h=FLANGE_T + 2*e); // Countersink on top face for (ang=[0, 90, 180, 270]) rotate([0, 0, ang]) translate([FLANGE_BOLT_BC/2, 0, DECK_COLLAR_H + FLANGE_T - 3.5]) cylinder(d1=FLANGE_BOLT_D, d2=8.5, h=3.5 + e); } } // ───────────────────────────────────────────────────────── // stem_clamp_half(side) // Split collar clamps on stem above the flange. // Print flat-face-down. side = "front" | "rear" // ───────────────────────────────────────────────────────── module stem_clamp_half(side="front") { y_front = (side == "front"); // Clamp Z origin: on top of flange clamp_z0 = DECK_COLLAR_H + FLANGE_T; difference() { // D-shaped half intersection() { translate([0, 0, clamp_z0]) cylinder(d=COL_OD, h=COL_H); translate([-COL_OD/2, y_front ? 0 : -COL_OD/2, clamp_z0]) cube([COL_OD, COL_OD/2, COL_H]); } // Stem bore translate([0, 0, clamp_z0 - e]) cylinder(d=STEM_BORE, h=COL_H + 2*e); // M4 clamping bolt holes (Y direction, through front half) for (bx=[-COL_BOLT_X, COL_BOLT_X]) translate([bx, y_front ? COL_OD/2 : 0, clamp_z0 + COL_H/2]) rotate([90, 0, 0]) cylinder(d=COL_BOLT_D, h=COL_OD/2 + e); // M4 hex nut pockets in rear half if (!y_front) for (bx=[-COL_BOLT_X, COL_BOLT_X]) translate([bx, -(COL_OD/4 + e), clamp_z0 + COL_H/2]) rotate([90, 0, 0]) cylinder(d=COL_NUT_W/cos(30), h=COL_NUT_H + e, $fn=6); // M4 set screw hole (front half, mid-height, horizontal) if (y_front) translate([0, COL_OD/2, clamp_z0 + COL_H * 0.65]) rotate([90, 0, 0]) cylinder(d=SET_SCREW_D, h=COL_OD/2 - STEM_BORE/2 + e); // Mating face chamfer (0.2 mm, prevents elephant-foot binding) translate([0, 0, clamp_z0 - e]) rotate([0, 0, y_front ? 0 : 180]) translate([-COL_OD/2, -0.2, 0]) cube([COL_OD, 0.2, COL_H + 2*e]); } } // ───────────────────────────────────────────────────────── // Render selector // ───────────────────────────────────────────────────────── if (RENDER == "assembly") { // Phantom deck collar reference color("Gray", 0.15) difference() { cylinder(d=50, h=DECK_COLLAR_H); translate([0,0,-e]) cylinder(d=STEM_BORE, h=DECK_COLLAR_H+2*e); } color("SteelBlue", 0.90) base_flange(); color("CornflowerBlue", 0.90) stem_clamp_half("front"); color("SlateBlue", 0.90) mirror([0,1,0]) stem_clamp_half("rear"); // Phantom stem stub color("Silver", 0.30) translate([0, 0, DECK_COLLAR_H + FLANGE_T + COL_H]) cylinder(d=STEM_OD, h=STEM_L_ROVER); } else if (RENDER == "base_flange") { // Print flat; rotate flange down translate([0, 0, -(DECK_COLLAR_H - 4)]) base_flange(); } else if (RENDER == "clamp_front") { translate([0, 0, -(DECK_COLLAR_H + FLANGE_T)]) stem_clamp_half("front"); } else if (RENDER == "clamp_rear") { translate([0, 0, -(DECK_COLLAR_H + FLANGE_T)]) stem_clamp_half("rear"); } else if (RENDER == "flange_2d") { projection(cut=true) translate([0, 0, -(DECK_COLLAR_H + FLANGE_T/2)]) base_flange(); }