// ============================================================ // gimbal_camera_mount.scad — Pan/Tilt Gimbal Mount for RealSense D435i // Issue: #552 Agent: sl-mechanical Date: 2026-03-14 // ============================================================ // // Parametric gimbal bracket system mounting an Intel RealSense D435i // (or similar box camera) on a 2-axis pan/tilt gimbal driven by // ST3215 serial bus servos (25T spline, Feetech/Waveshare). // // Architecture: // Pan axis — base T-nut clamps to 2020 rail; pan servo rotates yoke // Tilt axis — tilt servo horn plate bolts to ST3215 horn; camera cradle // rocks on tilt axis // Camera — D435i captured via 1/4-20 UNC hex nut in cradle floor // Damping — PETG flexure ribs on camera contact faces (or TPU pads) // Wiring — USB-C cable routed through channel in cradle arm // // Part catalogue: // Part 1 — tnut_rail_base() 2020 rail T-nut base + pan servo seat // Part 2 — pan_yoke() U-yoke connecting pan servo to tilt axis // Part 3 — tilt_horn_plate() Plate bolting to ST3215 tilt servo horn // Part 4 — camera_cradle() D435i cradle with 1/4-20 captured nut // Part 5 — vibe_pad() PETG flexure vibration-damping pad (×2) // Part 6 — assembly_preview() Full assembly preview // // Hardware BOM (per gimbal): // 2× ST3215 serial bus servo (pan + tilt) // 2× servo horn (25T spline, ≥Ø36 mm, 4× M3 bolt holes on Ø24 mm BC) // 2× M3 × 8 mm SHCS horn-to-plate bolts (×4 each horn = 8 total) // 1× M3 × 16 mm SHCS + nut T-nut rail clamp thumbscrew // 1× 1/4-20 UNC × 8 mm SHCS camera retention bolt (or existing tripod screw) // 1× 1/4-20 UNC hex nut captured in cradle floor // 4× M3 × 12 mm SHCS yoke-to-tilt-plate pivot axle bolts // 2× M3 × 25 mm SHCS pan yoke attachment to servo body // (optional) 2× vibe_pad printed in TPU 95A // // ST3215 servo interface (caliper-verified Feetech ST3215): // Body footprint : 40.0 × 20.0 mm (W × D), 36.5 mm tall // Shaft centre H : 28.5 mm from mounting face // Shaft spline : 25T, centre Ø5.8 mm, D-cut // Mount holes : 4× M3 on 32 × 10 mm rectangular pattern (18 mm offset) // Horn bolt circle: Ø24 mm, 4× M3 // Horn OD : ~36 mm // // D435i camera interface (caliper-verified): // Body : 90 × 25 × 25 mm (W × D × H) // Tripod thread : 1/4-20 UNC, centred bottom face, 9 mm from front // USB-C connector: right rear, 8 × 5 mm opening, 4 mm from edge // // Parametric camera size (override to adapt to other cameras): // CAM_W, CAM_D, CAM_H — body envelope // CAM_MOUNT_X — tripod hole X offset from camera centre // CAM_MOUNT_Y — tripod hole Y offset from front face // // Coordinate convention: // Camera looks in +Y direction (forward) // Pan axis is Z (vertical); tilt axis is X (lateral) // Rail runs along Z; T-nut base at Z=0 // All parts at assembly origin; translate for assembly_preview // // RENDER options: // "assembly" full assembly preview (default) // "tnut_rail_base_stl" Part 1 // "pan_yoke_stl" Part 2 // "tilt_horn_plate_stl" Part 3 // "camera_cradle_stl" Part 4 // "vibe_pad_stl" Part 5 // // Export commands: // openscad gimbal_camera_mount.scad -D 'RENDER="tnut_rail_base_stl"' -o gcm_tnut_base.stl // openscad gimbal_camera_mount.scad -D 'RENDER="pan_yoke_stl"' -o gcm_pan_yoke.stl // openscad gimbal_camera_mount.scad -D 'RENDER="tilt_horn_plate_stl"' -o gcm_tilt_horn_plate.stl // openscad gimbal_camera_mount.scad -D 'RENDER="camera_cradle_stl"' -o gcm_camera_cradle.stl // openscad gimbal_camera_mount.scad -D 'RENDER="vibe_pad_stl"' -o gcm_vibe_pad.stl // ============================================================ $fn = 64; e = 0.01; // epsilon for boolean clearance // ── Parametric camera envelope ──────────────────────────────────────────────── // Override these for cameras other than D435i CAM_W = 90.0; // camera body width (X) CAM_D = 25.0; // camera body depth (Y) CAM_H = 25.0; // camera body height (Z) CAM_MOUNT_X = 0.0; // tripod hole X offset from camera body centre CAM_MOUNT_Y = 9.0; // tripod hole from front face (Y) [D435i: 9 mm] CAM_USBC_X = CAM_W/2 - 4; // USB-C connector X (right side) CAM_USBC_Z = CAM_H/2; // USB-C connector Z (mid-height rear) CAM_USBC_W = 9.0; // USB-C opening width (X) CAM_USBC_H = 5.0; // USB-C opening height (Z) // ── Rail geometry (matches sensor_rail.scad / sensor_rail_brackets.scad) ───── RAIL_W = 20.0; SLOT_OPEN = 6.0; SLOT_INNER_W = 10.2; SLOT_INNER_H = 5.8; SLOT_NECK_H = 3.2; // ── T-nut geometry (matches sensor_rail_brackets.scad) ─────────────────────── TNUT_W = 9.8; TNUT_H = 5.5; TNUT_L = 12.0; TNUT_M3_NUT_AF = 5.5; TNUT_M3_NUT_H = 2.5; TNUT_BOLT_D = 3.3; // M3 clearance // ── T-nut base plate geometry ───────────────────────────────────────────────── BASE_W = 44.0; // wide enough for pan servo body (40 mm) BASE_H = 40.0; // height along rail (Z) BASE_T = SLOT_NECK_H + 2.0; // plate depth (Y), rail-face side // ── ST3215 servo geometry ───────────────────────────────────────────────────── SERVO_W = 40.0; // servo body width (X) SERVO_D = 20.0; // servo body depth (Y) SERVO_H = 36.5; // servo body height (Z) SERVO_SHAFT_Z = 28.5; // shaft centre height from mounting face SERVO_HOLE_X = 16.0; // mount hole half-span X (32 mm span) SERVO_HOLE_Y = 5.0; // mount hole half-span Y (10 mm span) SERVO_M3_D = 3.3; // M3 clearance // ── Servo horn geometry ─────────────────────────────────────────────────────── HORN_OD = 36.0; // horn outer diameter HORN_SPLINE_D = 5.9; // 25T spline bore clearance (5.8 + 0.1) HORN_BC_D = 24.0; // bolt circle diameter (4× M3) HORN_BOLT_D = 3.3; // M3 clearance through horn plate HORN_PLATE_T = 5.0; // tilt horn plate thickness // ── Yoke geometry ───────────────────────────────────────────────────────────── YOKE_WALL_T = 5.0; // yoke arm wall thickness YOKE_ARM_H = 50.0; // yoke arm height (Z) — clears servo body + camera YOKE_INNER_W = CAM_W + 8.0; // yoke inner span (camera + pad clearance) YOKE_BASE_T = 8.0; // yoke base plate thickness // ── Tilt pivot ──────────────────────────────────────────────────────────────── PIVOT_D = 4.3; // M4 pivot axle bore PIVOT_BOSS_D = 10.0; // boss OD around pivot bore PIVOT_BOSS_L = 6.0; // boss protrusion from yoke wall // ── Camera cradle geometry ──────────────────────────────────────────────────── CRADLE_WALL_T = 4.0; // cradle side wall thickness CRADLE_FLOOR_T = 5.0; // cradle floor thickness (holds 1/4-20 nut) CRADLE_LIP_T = 3.0; // front retaining lip thickness CRADLE_LIP_H = 8.0; // front lip height CABLE_CH_W = 12.0; // USB-C cable channel width CABLE_CH_H = 8.0; // USB-C cable channel height // ── 1/4-20 UNC tripod thread ────────────────────────────────────────────────── QTR20_D = 6.6; // 1/4-20 clearance bore QTR20_NUT_AF = 11.1; // 1/4-20 hex nut across-flats (standard) QTR20_NUT_H = 5.5; // 1/4-20 hex nut height // ── Vibration-damping pad geometry ──────────────────────────────────────────── PAD_W = CAM_W - 2*CRADLE_WALL_T - 2; PAD_H = CAM_H + 4; PAD_T = 2.5; // pad body thickness RIB_H = 1.5; // flexure rib height RIB_W = 1.2; // rib width RIB_PITCH = 5.0; // rib pitch // ── Fastener sizes ──────────────────────────────────────────────────────────── M3_D = 3.3; M4_D = 4.3; M3_NUT_AF = 5.5; M3_NUT_H = 2.4; // ============================================================ // RENDER DISPATCH // ============================================================ RENDER = "assembly"; if (RENDER == "assembly") assembly_preview(); else if (RENDER == "tnut_rail_base_stl") tnut_rail_base(); else if (RENDER == "pan_yoke_stl") pan_yoke(); else if (RENDER == "tilt_horn_plate_stl") tilt_horn_plate(); else if (RENDER == "camera_cradle_stl") camera_cradle(); else if (RENDER == "vibe_pad_stl") vibe_pad(); // ============================================================ // ASSEMBLY PREVIEW // ============================================================ module assembly_preview() { asm_rail_z = 0; // Rail section ghost (200 mm) %color("Silver", 0.25) translate([-RAIL_W/2, -RAIL_W/2, asm_rail_z]) cube([RAIL_W, RAIL_W, 200]); // T-nut rail base color("OliveDrab", 0.85) translate([0, 0, asm_rail_z + 80]) tnut_rail_base(); // Pan servo ghost (sitting in base seat) %color("DimGray", 0.4) translate([-SERVO_W/2, BASE_T, asm_rail_z + 80 + (BASE_H - SERVO_H)/2]) cube([SERVO_W, SERVO_D, SERVO_H]); // Pan yoke rising from servo shaft color("SteelBlue", 0.85) translate([0, BASE_T + SERVO_D, asm_rail_z + 80 + BASE_H/2]) pan_yoke(); // Tilt horn plate (tilt axis — left yoke wall) color("DarkOrange", 0.85) translate([-YOKE_INNER_W/2 - YOKE_WALL_T - HORN_PLATE_T, BASE_T + SERVO_D + YOKE_BASE_T, asm_rail_z + 80 + BASE_H/2 + YOKE_ARM_H/2]) rotate([0, 90, 0]) tilt_horn_plate(); // Camera cradle (centred in yoke) color("DarkSlateGray", 0.85) translate([0, BASE_T + SERVO_D + YOKE_BASE_T + CRADLE_FLOOR_T, asm_rail_z + 80 + BASE_H/2 + YOKE_ARM_H/2 - CAM_H/2]) camera_cradle(); // D435i ghost %color("Black", 0.4) translate([-CAM_W/2, BASE_T + SERVO_D + YOKE_BASE_T + CRADLE_FLOOR_T + PAD_T, asm_rail_z + 80 + Base_H_mid() - CAM_H/2]) cube([CAM_W, CAM_D, CAM_H]); // Vibe pads (front + rear camera face) color("DimGray", 0.80) { translate([-CAM_W/2 + CRADLE_WALL_T + 1, Base_T + SERVO_D + YOKE_BASE_T + CRADLE_FLOOR_T, asm_rail_z + 80 + Base_H_mid() - PAD_H/2]) rotate([90, 0, 0]) vibe_pad(); } } // helper (avoids recomputing same expression) function Base_T() = BASE_T; function Base_H_mid() = BASE_H/2 + YOKE_ARM_H/2; // ============================================================ // PART 1 — T-NUT RAIL BASE (pan servo seat + rail clamp) // ============================================================ // Mounts to 2020 rail via standard T-nut tongue. // Front face (+Y side) provides flat seat for pan ST3215 servo body. // Servo body recessed 1 mm into seat for positive lateral registration. // Pan servo shaft axis = Z (vertical) → pan rotation about Z. // // Print: PETG, 5 perims, 50 % gyroid. Orient face-plate down (flat). module tnut_rail_base() { difference() { union() { // ── Face plate (against rail outer face, -Y side) ──────────── translate([-BASE_W/2, -BASE_T, 0]) cube([BASE_W, BASE_T, BASE_H]); // ── T-nut neck (enters rail slot, +Y side of face plate) ───── translate([-TNUT_W/2, 0, (BASE_H - TNUT_L)/2]) cube([TNUT_W, SLOT_NECK_H + e, TNUT_L]); // ── T-nut inner body (wider, locks inside T-groove) ────────── translate([-TNUT_W/2, SLOT_NECK_H - e, (BASE_H - TNUT_L)/2]) cube([TNUT_W, TNUT_H - SLOT_NECK_H + e, TNUT_L]); // ── Pan servo seat boss (front face, +Y side) ──────────────── // Proud pad that servo body sits on; 1 mm registration recess translate([-BASE_W/2, -BASE_T, 0]) cube([BASE_W, BASE_T + 6, BASE_H]); } // ── Rail clamp bolt bore (M3 through face plate) ───────────────── translate([0, -BASE_T - e, BASE_H/2]) rotate([-90, 0, 0]) cylinder(d = TNUT_BOLT_D, h = BASE_T + TNUT_H + 2*e); // ── M3 hex nut pocket (inside T-nut body) ──────────────────────── translate([0, SLOT_NECK_H + 0.3, BASE_H/2]) rotate([-90, 0, 0]) cylinder(d = TNUT_M3_NUT_AF / cos(30), h = TNUT_M3_NUT_H + 0.3, $fn = 6); // ── Servo body recess (1 mm registration pocket in seat face) ──── translate([-SERVO_W/2 - 0.3, -BASE_T + 6 - 1.0, (BASE_H - SERVO_H)/2 - 0.3]) cube([SERVO_W + 0.6, 1.2, SERVO_H + 0.6]); // ── Pan servo mount holes (4× M3 in rectangular pattern) ───────── for (sx = [-SERVO_HOLE_X, SERVO_HOLE_X]) for (sy = [-SERVO_HOLE_Y, SERVO_HOLE_Y]) translate([sx, -BASE_T + 6 + e, BASE_H/2 + sy]) rotate([90, 0, 0]) cylinder(d = SERVO_M3_D, h = BASE_T + 2*e); // ── Pan servo shaft bore (passes shaft through base if needed) ──── // Centre of shaft at Z = BASE_H/2, no bore needed (shaft exits top) // ── Lightening pockets ──────────────────────────────────────────── translate([0, -BASE_T/2 + 3, BASE_H/2]) cube([BASE_W - 14, BASE_T - 4, BASE_H - 14], center = true); } } // ============================================================ // PART 2 — PAN YOKE // ============================================================ // U-shaped yoke that attaches to pan servo horn (below) and carries // the tilt axis (above). Two vertical arms straddle the camera cradle. // Tilt servo sits on top of one arm; tilt pivot boss on the other. // // Yoke base bolts to pan servo horn (4× M3 on HORN_BC_D bolt circle). // Pan servo horn spline bore passes through yoke base centre. // Tilt axis: M4 pivot axle through boss on each arm (X-axis rotation). // // Print: upright (yoke in final orientation), PETG, 5 perims, 40% gyroid. module pan_yoke() { arm_z_total = YOKE_ARM_H + YOKE_BASE_T; inner_w = YOKE_INNER_W; difference() { union() { // ── Yoke base plate (bolts to pan servo horn) ───────────────── translate([-inner_w/2 - YOKE_WALL_T, 0, 0]) cube([inner_w + 2*YOKE_WALL_T, YOKE_BASE_T, YOKE_BASE_T]); // ── Left arm ────────────────────────────────────────────────── translate([-inner_w/2 - YOKE_WALL_T, 0, 0]) cube([YOKE_WALL_T, YOKE_BASE_T, arm_z_total]); // ── Right arm (tilt servo side) ─────────────────────────────── translate([inner_w/2, 0, 0]) cube([YOKE_WALL_T, YOKE_BASE_T, arm_z_total]); // ── Tilt pivot bosses (both arms, X-axis) ───────────────────── // Left pivot boss (plain pivot — M4 bolt) translate([-inner_w/2 - YOKE_WALL_T - PIVOT_BOSS_L, YOKE_BASE_T/2, YOKE_BASE_T + YOKE_ARM_H/2]) rotate([0, 90, 0]) cylinder(d = PIVOT_BOSS_D, h = PIVOT_BOSS_L + YOKE_WALL_T); // Right pivot boss (tilt servo horn seat) translate([inner_w/2, YOKE_BASE_T/2, YOKE_BASE_T + YOKE_ARM_H/2]) rotate([0, 90, 0]) cylinder(d = PIVOT_BOSS_D + 4, h = PIVOT_BOSS_L + YOKE_WALL_T); // ── Tilt servo body seat on right arm top ───────────────────── translate([inner_w/2, 0, arm_z_total - SERVO_H - 4]) cube([YOKE_WALL_T + SERVO_D + 2, YOKE_BASE_T, SERVO_H + 4]); } // ── Pan horn spline bore (centre of yoke base) ──────────────────── translate([0, YOKE_BASE_T/2, YOKE_BASE_T/2]) rotate([90, 0, 0]) cylinder(d = HORN_SPLINE_D, h = YOKE_BASE_T + 2*e, center = true); // ── Pan horn bolt holes (4× M3 on HORN_BC_D) ───────────────────── for (a = [45, 135, 225, 315]) translate([HORN_BC_D/2 * cos(a), YOKE_BASE_T/2, HORN_BC_D/2 * sin(a) + YOKE_BASE_T/2]) rotate([90, 0, 0]) cylinder(d = HORN_BOLT_D, h = YOKE_BASE_T + 2*e, center = true); // ── Left tilt pivot bore (M4 clearance) ─────────────────────────── translate([-inner_w/2 - YOKE_WALL_T - PIVOT_BOSS_L - e, YOKE_BASE_T/2, YOKE_BASE_T + YOKE_ARM_H/2]) rotate([0, 90, 0]) cylinder(d = PIVOT_D, h = PIVOT_BOSS_L + YOKE_WALL_T + 2*e); // ── Right tilt pivot bore (larger — tilt horn plate seats here) ─── translate([inner_w/2 - e, YOKE_BASE_T/2, YOKE_BASE_T + YOKE_ARM_H/2]) rotate([0, 90, 0]) cylinder(d = HORN_SPLINE_D, h = PIVOT_BOSS_L + YOKE_WALL_T + 2*e); // ── Tilt servo mount holes in right arm seat ────────────────────── for (sz = [-SERVO_HOLE_Y, SERVO_HOLE_Y]) translate([inner_w/2 + YOKE_WALL_T + SERVO_D/2, YOKE_BASE_T/2, arm_z_total - SERVO_H/2 + sz]) rotate([90, 0, 0]) cylinder(d = SERVO_M3_D, h = YOKE_BASE_T + 2*e, center = true); // ── M3 nut pockets (tilt servo mount, rear of arm seat) ────────── for (sz = [-SERVO_HOLE_Y, SERVO_HOLE_Y]) translate([inner_w/2 + YOKE_WALL_T + SERVO_D/2, YOKE_BASE_T - M3_NUT_H - 0.5, arm_z_total - SERVO_H/2 + sz]) rotate([90, 0, 0]) cylinder(d = M3_NUT_AF / cos(30), h = M3_NUT_H + 0.5, $fn = 6); // ── Lightening slots in yoke arms ───────────────────────────────── translate([-inner_w/2 - YOKE_WALL_T/2, YOKE_BASE_T/2, YOKE_BASE_T + YOKE_ARM_H/2 - 10]) cube([YOKE_WALL_T - 2, YOKE_BASE_T - 2, YOKE_ARM_H - 24], center = true); translate([inner_w/2 + YOKE_WALL_T/2, YOKE_BASE_T/2, YOKE_BASE_T + YOKE_ARM_H/2 - 10]) cube([YOKE_WALL_T - 2, YOKE_BASE_T - 2, YOKE_ARM_H - 30], center = true); } } // ============================================================ // PART 3 — TILT HORN PLATE // ============================================================ // Disc plate bolting to tilt ST3215 servo horn on the right yoke arm. // Servo horn spline centres into disc bore (captured, no free rotation). // Camera cradle attaches to opposite face via 2× M3 bolts. // // Tilt range: ±45° limited by yoke arm geometry. // Plate thickness HORN_PLATE_T provides stiffness for cantilevered cradle. // // Print: flat (disc face down), PETG, 5 perims, 50 % infill. module tilt_horn_plate() { plate_od = HORN_OD + 8; // plate OD (4 mm rim outside horn BC) difference() { union() { // ── Main disc ───────────────────────────────────────────────── cylinder(d = plate_od, h = HORN_PLATE_T); // ── Cradle attachment arm (extends to camera cradle) ────────── // Rectangular boss on top of disc toward camera translate([-CAM_W/2, HORN_PLATE_T - e, -CAM_H/2]) cube([CAM_W, HORN_PLATE_T + 4, CAM_H]); } // ── Servo horn spline bore (centre) ─────────────────────────────── translate([0, 0, -e]) cylinder(d = HORN_SPLINE_D, h = HORN_PLATE_T + 2*e); // ── Horn bolt holes (4× M3 on HORN_BC_D) ───────────────────────── for (a = [45, 135, 225, 315]) translate([HORN_BC_D/2 * cos(a), HORN_BC_D/2 * sin(a), -e]) cylinder(d = HORN_BOLT_D, h = HORN_PLATE_T + 2*e); // ── Pivot axle bore (M4, coaxial with horn centre) ──────────────── translate([0, 0, -e]) cylinder(d = PIVOT_D, h = HORN_PLATE_T + 2*e); // ── Cradle attachment bolts (2× M3 in arm boss) ────────────────── for (cz = [-CAM_H/2 + 6, CAM_H/2 - 6]) translate([0, HORN_PLATE_T + 2, cz]) rotate([90, 0, 0]) cylinder(d = M3_D, h = HORN_PLATE_T + 6 + 2*e); // ── M3 hex nut pockets (rear of disc face) ──────────────────────── for (cz = [-CAM_H/2 + 6, CAM_H/2 - 6]) translate([0, M3_NUT_H + 0.5, cz]) rotate([90, 0, 0]) cylinder(d = M3_NUT_AF / cos(30), h = M3_NUT_H + 0.5, $fn = 6); // ── Weight-relief arcs (between horn bolt holes) ────────────────── for (a = [0, 90, 180, 270]) translate([(plate_od/2 - 5) * cos(a), (plate_od/2 - 5) * sin(a), -e]) cylinder(d = 6, h = HORN_PLATE_T + 2*e); } } // ============================================================ // PART 4 — CAMERA CRADLE // ============================================================ // Open-front U-cradle holding D435i via captured 1/4-20 hex nut. // Front lip retains camera from sliding forward (+Y). // Vibration-damping pads seat in recessed pockets on inner faces. // USB-C cable routing channel exits cradle right rear wall. // // 1/4-20 captured nut in cradle floor — tighten with standard // tripod screw or M6→1/4-20 adapter from camera bottom. // // Print: cradle-floor-down (flat), PETG, 5 perims, 40 % gyroid. // No supports needed (overhangs < 45°). module camera_cradle() { outer_w = CAM_W + 2*CRADLE_WALL_T; outer_h = CAM_H + CRADLE_FLOOR_T; difference() { union() { // ── Cradle body ─────────────────────────────────────────────── translate([-outer_w/2, 0, 0]) cube([outer_w, CAM_D + CRADLE_WALL_T, outer_h]); // ── Front retaining lip ─────────────────────────────────────── translate([-outer_w/2, CAM_D + CRADLE_WALL_T - CRADLE_LIP_T, 0]) cube([outer_w, CRADLE_LIP_T, CRADLE_LIP_H]); // ── Cable channel boss (right rear, exits +X side) ──────────── translate([CAM_W/2 + CRADLE_WALL_T - e, 0, CRADLE_FLOOR_T + CAM_H/2 - CABLE_CH_H/2]) cube([CABLE_CH_W + CRADLE_WALL_T, CAM_D * 0.6, CABLE_CH_H]); // ── Tilt horn attachment tabs (left + right, bolt to horn plate)─ for (sx = [-outer_w/2 - 4, outer_w/2]) translate([sx, CAM_D/2, CRADLE_FLOOR_T + CAM_H/2 - 6]) cube([4, 12, 12]); } // ── Camera pocket (hollow interior) ────────────────────────────── translate([-CAM_W/2, 0, CRADLE_FLOOR_T]) cube([CAM_W, CAM_D + CRADLE_WALL_T + e, CAM_H + e]); // ── 1/4-20 UNC clearance bore (camera tripod thread, bottom) ───── translate([CAM_MOUNT_X, CAM_MOUNT_Y, -e]) cylinder(d = QTR20_D, h = CRADLE_FLOOR_T + 2*e); // ── 1/4-20 hex nut pocket (captured in cradle floor) ───────────── translate([CAM_MOUNT_X, CAM_MOUNT_Y, CRADLE_FLOOR_T - QTR20_NUT_H - 0.5]) cylinder(d = QTR20_NUT_AF / cos(30), h = QTR20_NUT_H + 0.6, $fn = 6); // ── USB-C cable channel (exit through right rear wall) ──────────── translate([CAM_W/2 - e, 0, CRADLE_FLOOR_T + CAM_H/2 - CABLE_CH_H/2]) cube([CABLE_CH_W + CRADLE_WALL_T + 2*e, CAM_D * 0.6 + e, CABLE_CH_H]); // ── Vibe pad recesses on inner camera-contact faces ─────────────── // Rear wall recess (camera front face → +Y side of rear wall) translate([-CAM_W/2 + CRADLE_WALL_T, CRADLE_WALL_T, CRADLE_FLOOR_T]) cube([CAM_W, PAD_T, CAM_H]); // ── Tilt horn bolt holes in attachment tabs ─────────────────────── for (sx = [-outer_w/2 - 4 - e, outer_w/2 - e]) translate([sx, CAM_D/2 + 6, CRADLE_FLOOR_T + CAM_H/2]) rotate([0, 90, 0]) cylinder(d = M3_D, h = 6 + 2*e); // ── M3 nut pockets in attachment tabs ───────────────────────────── translate([outer_w/2 + 4 - M3_NUT_H - 0.4, CAM_D/2 + 6, CRADLE_FLOOR_T + CAM_H/2]) rotate([0, 90, 0]) cylinder(d = M3_NUT_AF / cos(30), h = M3_NUT_H + 0.4, $fn = 6); translate([-outer_w/2 - 4 - e, CAM_D/2 + 6, CRADLE_FLOOR_T + CAM_H/2]) rotate([0, 90, 0]) cylinder(d = M3_NUT_AF / cos(30), h = M3_NUT_H + 0.4, $fn = 6); // ── Lightening pockets in cradle walls ──────────────────────────── for (face_x = [-CAM_W/2 - CRADLE_WALL_T - e, CAM_W/2 - e]) translate([face_x, CAM_D * 0.2, CRADLE_FLOOR_T + 3]) cube([CRADLE_WALL_T + 2*e, CAM_D * 0.55, CAM_H - 6]); } } // ============================================================ // PART 5 — VIBRATION-DAMPING PAD // ============================================================ // Flat pad with transverse PETG flexure ribs pressing against camera body. // Rib geometry (thin fins ~1.5 mm tall) deflects under camera vibration, // attenuating high-frequency input from motor/drive-train. // For superior damping: print in TPU 95A (no infill changes needed). // Pads seat in recessed pockets in camera cradle inner wall. // Optional M2 bolt-through at corners or adhesive-back foam tape. // // Print: pad-back-face-down, PETG or TPU 95A, 3 perims, 20 % infill. module vibe_pad() { rib_count = floor((PAD_W - RIB_W) / RIB_PITCH); union() { // ── Base plate ──────────────────────────────────────────────────── translate([-PAD_W/2, -PAD_T, -PAD_H/2]) cube([PAD_W, PAD_T, PAD_H]); // ── Flexure ribs (parallel to Z, spaced RIB_PITCH apart) ───────── for (i = [0 : rib_count - 1]) { rx = -PAD_W/2 + RIB_PITCH/2 + i * RIB_PITCH + RIB_W/2; if (rx <= PAD_W/2 - RIB_W/2) translate([rx, 0, 0]) cube([RIB_W, RIB_H, PAD_H - 6], center = true); } // ── Corner nubs (M2 bolt-through retention, optional) ───────────── for (px = [-PAD_W/2 + 5, PAD_W/2 - 5]) for (pz = [-PAD_H/2 + 5, PAD_H/2 - 5]) translate([px, -PAD_T/2, pz]) difference() { cylinder(d = 5, h = PAD_T, center = true); cylinder(d = 2.4, h = PAD_T + 2*e, center = true); } } }