Merge pull request 'feat: Camera gimbal mount for RealSense D435i (Issue #552)' (#556) from sl-mechanical/issue-552-gimbal-mount into main
This commit is contained in:
commit
0dbd64a6f4
599
chassis/gimbal_camera_mount.scad
Normal file
599
chassis/gimbal_camera_mount.scad
Normal file
@ -0,0 +1,599 @@
|
|||||||
|
// ============================================================
|
||||||
|
// 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user