// ============================================================ // sensor_head.scad — Sensor Head Platform Rev A 2026-02-28 // Agent: sl-mechanical // ============================================================ // Clamps to 25 mm OD vertical stem (top of mast). // Platform hosts: // • RPLIDAR A1M8 — centred, topmost // • RealSense D435i — front arm, 10° down tilt // • 4× IMX219 cameras — radial arms, 90° apart, 10° down // // RENDER options: // "assembly" 3-D exploded-ish view (default) // "collar_front" front collar half for slicing (print 2) // "collar_rear" rear collar half for slicing // "platform_2d" flat projection → DXF // ============================================================ RENDER = "assembly"; // ── Stem ───────────────────────────────────────────────────── STEM_OD = 25.0; // actual stem OD (mm) STEM_BORE = 25.4; // collar bore (+ 0.4 mm clearance for FDM) // ── Collar ─────────────────────────────────────────────────── COL_OD = 52.0; // outer diameter COL_H = 36.0; // height 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 A/F COL_NUT_H = 3.4; // M4 hex nut thickness // ── Platform ───────────────────────────────────────────────── PLAT_OD = 120.0; // octagonal platform, across-flats ≈ OD PLAT_H = 8.0; // plate thickness PLAT_Z = COL_H; // platform bottom sits atop collar // ── RPLIDAR bolt circle ─────────────────────────────────────── RPL_BC = 58.0; // M3 hole bolt circle diameter RPL_HOLE_D = 3.3; // M3 clearance RPL_RECESS_D = 72.0; // alignment lip diameter (body + 2 mm clearance) RPL_RECESS_H = 1.5; // recess depth // ── Sensor arm attachment holes ─────────────────────────────── // RealSense arm: 2× M4 holes at X = +ARM_R, Y = ±ARM_DY_RS ARM_R = 50.0; // radial distance from stem CL ARM_DY_RS = 18.0; // half-spacing for RealSense pair // IMX219 arms: 4 × 2 M4 holes at each 90° cardinal, tangential offset ±ARM_DY_CAM ARM_DY_CAM = 9.0; // half-spacing for IMX219 pair ARM_HOLE_D = 4.5; // M4 clearance $fn = 64; e = 0.01; // ───────────────────────────────────────────────────────────── // collar_half(side) // side = "front" (+Y) | "rear" (-Y) // Print flat-face-down (mating face on build plate). // ───────────────────────────────────────────────────────────── module collar_half(side = "front") { difference() { // ── Body: D-shaped half-cylinder ───────────────────── intersection() { cylinder(d = COL_OD, h = COL_H); translate([-COL_OD/2, (side == "front") ? 0 : -COL_OD/2, 0]) cube([COL_OD, COL_OD/2, COL_H]); } // ── Stem bore ──────────────────────────────────────── translate([0, 0, -e]) cylinder(d = STEM_BORE, h = COL_H + 2*e); // ── M4 clamping bolt clearance holes (Y direction) ─── for (bx = [-COL_BOLT_X, COL_BOLT_X]) { translate([bx, (side == "front") ? COL_OD/2 : 0, COL_H/2]) rotate([90, 0, 0]) cylinder(d = COL_BOLT_D, h = COL_OD/2 + e); } // ── M4 hex nut pockets (rear half only) ────────────── if (side == "rear") { for (bx = [-COL_BOLT_X, COL_BOLT_X]) { translate([bx, -(COL_OD/4 + e), COL_H/2]) rotate([90, 0, 0]) cylinder(d = COL_NUT_W / cos(30), h = COL_NUT_H + e, $fn = 6); } } // ── M4 set screw — height / rotation lock (front half) ─ if (side == "front") { translate([0, COL_OD/2, COL_H * 0.72]) rotate([90, 0, 0]) cylinder(d = COL_BOLT_D, h = COL_OD/2 - STEM_BORE/2 + e); } } } // ───────────────────────────────────────────────────────────── // platform() // ───────────────────────────────────────────────────────────── module platform() { difference() { // ── Octagonal disc ─────────────────────────────────── cylinder(d = PLAT_OD, h = PLAT_H, $fn = 8); // ── Stem passage ───────────────────────────────────── translate([0, 0, -e]) cylinder(d = STEM_BORE, h = PLAT_H + 2*e); // ── RPLIDAR alignment recess (top face) ────────────── translate([0, 0, PLAT_H - RPL_RECESS_H]) cylinder(d = RPL_RECESS_D, h = RPL_RECESS_H + e); // ── RPLIDAR M3 bolt holes — 58 mm BC ───────────────── for (a = [45, 135, 225, 315]) translate([RPL_BC/2 * cos(a), RPL_BC/2 * sin(a), -e]) cylinder(d = RPL_HOLE_D, h = PLAT_H + 2*e); // ── RealSense arm M4 holes — front (+X), ±ARM_DY_RS ── for (dy = [-ARM_DY_RS, ARM_DY_RS]) translate([ARM_R, dy, -e]) cylinder(d = ARM_HOLE_D, h = PLAT_H + 2*e); // ── IMX219 arm M4 holes — 4 × cardinal, ±ARM_DY_CAM ─ // Tangential perpendicular for angle a: (sin(a), -cos(a)) for (a = [0, 90, 180, 270]) for (s = [-1, 1]) translate([ ARM_R * cos(a) + s * ARM_DY_CAM * sin(a), ARM_R * sin(a) - s * ARM_DY_CAM * cos(a), -e ]) cylinder(d = ARM_HOLE_D, h = PLAT_H + 2*e); // ── Lightening pockets — between arm attachment holes ─ for (a = [45, 135, 225, 315]) translate([(PLAT_OD/2 - 22) * cos(a), (PLAT_OD/2 - 22) * sin(a), -e]) cylinder(d = 20, h = PLAT_H + 2*e); } } // ───────────────────────────────────────────────────────────── // Render selector // ───────────────────────────────────────────────────────────── if (RENDER == "assembly") { color("SkyBlue", 0.9) collar_half("front"); color("DodgerBlue", 0.9) mirror([0, 1, 0]) collar_half("rear"); color("Silver", 0.85) translate([0, 0, PLAT_Z]) platform(); } else if (RENDER == "collar_front") { collar_half("front"); } else if (RENDER == "collar_rear") { collar_half("rear"); } else if (RENDER == "platform_2d") { projection(cut = true) translate([0, 0, -(PLAT_Z + PLAT_H / 2)]) platform(); }