// ============================================================ // base_bumper.scad — Base Plate Impact Bumper Rev A 2026-03-01 // Agent: sl-mechanical // ============================================================ // Clip-on bumper system for the 270×240 mm SaltyBot base plate. // Protects wheels, FC board, and wiring when robot tips. // // System overview: // corner_bracket() — L-clip, grips plate corner, print 4× // side_bracket() — straight clip for mid-sides, print 4× // tpu_edge_cap() — soft snap-on edge cap, print in TPU // (same profile fits corner and side) // bumper_rail_2d() — 2-D cross-section DXF for straight rails // // Assembly: 4 corner brackets + 4 side brackets clip onto the // plate bottom edge without drilling. Rails (printed or cut from // 25×8 mm aluminium bar) bridge between clips. TPU edge caps // snap onto rail outer face. // // FC status LEDs: FC is front-centre of plate. Bumper is below // and outside the plate footprint — LEDs remain visible. // // RENDER options: // "assembly" 3-D view with phantom plate (default) // "corner" one corner bracket (print 4×) // "side" one side bracket (print 4×) // "tpu_cap" TPU edge cap cross-section extruded (print N×) // "rail_2d" flat rail profile → DXF // ============================================================ RENDER = "assembly"; // ── Base plate (reference only — not printed) ───────────────── PLATE_W = 270.0; // width (motor axle direction, X) PLATE_D = 240.0; // depth (fore-aft, Y) PLATE_H = 6.0; // plate thickness PLATE_R = 10.0; // plate corner radius // ── Bumper geometry ────────────────────────────────────────── BUMP_H = 50.0; // bumper height below plate bottom face BUMP_T = 8.0; // rail/bracket wall thickness BUMP_CR = 22.0; // outer corner radius of bumper frame BUMP_EXTEND = 15.0; // how far bumper extends beyond plate edge // (absorbs impact before plate corner hits) // ── Clip channel (grips plate edge) ────────────────────────── CLIP_SLOT_W = PLATE_H + 0.6; // slot width (6 mm plate + 0.6 clearance) CLIP_SLOT_D = 12.0; // how deep clip grips plate edge CLIP_GRIP_T = 4.0; // clip wall above plate (keeps clip aligned) // ── Corner bracket ──────────────────────────────────────────── CORNER_ARM = 45.0; // length of each L-arm along plate edge CORNER_H = BUMP_H + CLIP_GRIP_T + CLIP_SLOT_W; // ── Side bracket ───────────────────────────────────────────── SIDE_ARM = 30.0; // side bracket total width along plate edge SIDE_H = CORNER_H; // ── Rail (reference dims for procurement / DXF) ─────────────── RAIL_W = BUMP_T; // 8 mm RAIL_H = BUMP_H; // 50 mm // ── TPU edge cap ───────────────────────────────────────────── CAP_W = BUMP_T + 4.0; // wraps around rail outer face CAP_H = 14.0; // cap height (bottom of bumper profile) CAP_R = 8.0; // outer impact radius CAP_CLIP_W = BUMP_T + 0.6; // inner clip width (snaps on rail) CAP_CLIP_D = 5.0; // clip depth // ── Fasteners ───────────────────────────────────────────────── M4_D = 4.5; // M4 clearance hole (rail → bracket) M3_D = 3.3; // M3 set screw (lock bracket on plate) $fn = 48; e = 0.01; // ───────────────────────────────────────────────────────────── // clip_slot_2d() // 2-D profile of the plate-edge gripping channel. // Origin at plate bottom face, +Y toward plate interior. // ───────────────────────────────────────────────────────────── module clip_slot_2d() { // Slot opens from outside (-Y), grips plate edge translate([0, -(BUMP_EXTEND + BUMP_T)]) square([CLIP_SLOT_W, CLIP_SLOT_D + BUMP_EXTEND + BUMP_T]); } // ───────────────────────────────────────────────────────────── // corner_bracket() // L-shaped clip bracket. Internal coordinate: // Corner at origin, arms along +X and +Y. // Plate sits at Z = BUMP_H (above bracket base). // ───────────────────────────────────────────────────────────── module corner_bracket() { arm_w = BUMP_T; // wall thickness arm_h = CORNER_H; // full height difference() { union() { // X arm (along plate width edge) translate([-BUMP_EXTEND, -BUMP_EXTEND, 0]) hull() { cube([CORNER_ARM + BUMP_EXTEND, arm_w, arm_h]); translate([0, arm_w, 0]) cylinder(r = BUMP_CR, h = arm_h); translate([CORNER_ARM + BUMP_EXTEND, arm_w, 0]) cylinder(r = BUMP_CR, h = arm_h); } // Y arm (along plate depth edge) translate([-BUMP_EXTEND, -BUMP_EXTEND, 0]) hull() { cube([arm_w, CORNER_ARM + BUMP_EXTEND, arm_h]); translate([arm_w, 0, 0]) cylinder(r = BUMP_CR, h = arm_h); translate([arm_w, CORNER_ARM + BUMP_EXTEND, 0]) cylinder(r = BUMP_CR, h = arm_h); } } // Clip slot in X arm (grips plate edge) // Slot at Z = BUMP_H (plate bottom face height) translate([CORNER_ARM - CLIP_SLOT_D, -(BUMP_EXTEND + arm_w + e), BUMP_H]) cube([CLIP_SLOT_D + BUMP_EXTEND + e, arm_w + 2*e, CLIP_SLOT_W]); // Clip slot in Y arm translate([-(BUMP_EXTEND + arm_w + e), CORNER_ARM - CLIP_SLOT_D, BUMP_H]) cube([arm_w + 2*e, CLIP_SLOT_D + BUMP_EXTEND + e, CLIP_SLOT_W]); // M3 set screw through clip above slot (X arm, 2 per arm) for (xoff = [CORNER_ARM * 0.3, CORNER_ARM * 0.7]) { translate([xoff - BUMP_EXTEND, -(BUMP_EXTEND + arm_w/2), BUMP_H + CLIP_SLOT_W + CLIP_GRIP_T/2]) rotate([90, 0, 0]) cylinder(d = M3_D, h = arm_w + 2*e); } for (yoff = [CORNER_ARM * 0.3, CORNER_ARM * 0.7]) { translate([-(BUMP_EXTEND + arm_w/2), yoff - BUMP_EXTEND, BUMP_H + CLIP_SLOT_W + CLIP_GRIP_T/2]) rotate([0, 90, 0]) cylinder(d = M3_D, h = arm_w + 2*e); } // M4 holes for rail attachment (outer face, pairs per arm) for (xoff = [15, CORNER_ARM - 10]) { translate([xoff - BUMP_EXTEND, -(BUMP_EXTEND + arm_w + e), BUMP_H * 0.3]) rotate([90, 0, 0]) cylinder(d = M4_D, h = arm_w + 2*e); translate([xoff - BUMP_EXTEND, -(BUMP_EXTEND + arm_w + e), BUMP_H * 0.7]) rotate([90, 0, 0]) cylinder(d = M4_D, h = arm_w + 2*e); } } } // ───────────────────────────────────────────────────────────── // side_bracket() // Straight clip for mid-side. Grips plate edge, M4 holes // for rail, M3 set screws. Placed between corner brackets. // ───────────────────────────────────────────────────────────── module side_bracket() { arm_w = BUMP_T; arm_h = SIDE_H; difference() { // Outer body translate([-SIDE_ARM/2, -(BUMP_EXTEND + arm_w), 0]) hull() { cube([SIDE_ARM, arm_w, arm_h]); translate([0, arm_w, 0]) cylinder(r = BUMP_CR * 0.6, h = arm_h); translate([SIDE_ARM, arm_w, 0]) cylinder(r = BUMP_CR * 0.6, h = arm_h); } // Clip slot translate([-SIDE_ARM/2 + 5, -(BUMP_EXTEND + arm_w + e), BUMP_H]) cube([SIDE_ARM - 10, arm_w + 2*e, CLIP_SLOT_W]); // M3 set screws (2×) for (xoff = [-SIDE_ARM/4, SIDE_ARM/4]) { translate([xoff, -(BUMP_EXTEND + arm_w/2), BUMP_H + CLIP_SLOT_W + CLIP_GRIP_T/2]) rotate([90, 0, 0]) cylinder(d = M3_D, h = arm_w + 2*e); } // M4 rail holes (2 heights × 1 column) for (zh = [BUMP_H * 0.3, BUMP_H * 0.7]) { translate([0, -(BUMP_EXTEND + arm_w + e), zh]) rotate([90, 0, 0]) cylinder(d = M4_D, h = arm_w + 2*e); } } } // ───────────────────────────────────────────────────────────── // tpu_edge_cap() // Snap-on soft edge cap — print in TPU. // Snaps onto outer face of rail (or corner bracket outer face). // Orient: cap opening faces -Y (toward bracket), print flat. // ───────────────────────────────────────────────────────────── module tpu_edge_cap() { // Length — 40 mm segment; print multiple, install end-to-end cap_len = 40.0; difference() { union() { // Rounded outer impact face hull() { translate([-CAP_W/2, 0, 0]) cylinder(r = CAP_R, h = cap_len); translate([ CAP_W/2, 0, 0]) cylinder(r = CAP_R, h = cap_len); translate([-CAP_W/2, CAP_H - CAP_R, 0]) cylinder(r = CAP_R, h = cap_len); translate([ CAP_W/2, CAP_H - CAP_R, 0]) cylinder(r = CAP_R, h = cap_len); } } // Inner clip slot (snaps onto rail outer face) translate([-CAP_CLIP_W/2, -e, -e]) cube([CAP_CLIP_W, CAP_CLIP_D + e, cap_len + 2*e]); } } // ───────────────────────────────────────────────────────────── // bumper_rail_2d() // 2-D profile for straight bumper rail sections. // Extrude to length per side: front/rear = PLATE_W, sides = PLATE_D. // Use "rail_2d" RENDER to export DXF. // ───────────────────────────────────────────────────────────── module bumper_rail_2d() { // Simple rectangular profile 8 × 50 mm square([RAIL_W, RAIL_H]); } // ───────────────────────────────────────────────────────────── // Phantom plate (reference, assembly view only) // ───────────────────────────────────────────────────────────── module phantom_plate() { color("Gray", 0.25) linear_extrude(PLATE_H) offset(r = PLATE_R, $fn = 32) offset(-PLATE_R) square([PLATE_W, PLATE_D], center = true); } // ───────────────────────────────────────────────────────────── // Render selector // ───────────────────────────────────────────────────────────── if (RENDER == "assembly") { // Phantom plate translate([0, 0, BUMP_H]) phantom_plate(); // 4 corner brackets (NW / NE / SW / SE) half_w = PLATE_W/2; half_d = PLATE_D/2; color("SteelBlue", 0.9) { // NW corner (X−, Y+) → rotate 90° translate([-half_w, half_d, 0]) rotate([0,0,180]) corner_bracket(); // NE corner (X+, Y+) → rotate 90° translate([ half_w, half_d, 0]) rotate([0,0,270]) corner_bracket(); // SW corner (X−, Y−) → no rotation translate([-half_w,-half_d, 0]) rotate([0,0, 90]) corner_bracket(); // SE corner (X+, Y−) translate([ half_w,-half_d, 0]) corner_bracket(); } // 4 side brackets (front, rear, left, right midpoints) color("CornflowerBlue", 0.9) { translate([0, half_d, 0]) rotate([0,0,180]) side_bracket(); // front translate([0, -half_d, 0]) side_bracket(); // rear translate([-half_w, 0, 0]) rotate([0,0, 90]) side_bracket(); // left translate([ half_w, 0, 0]) rotate([0,0,270]) side_bracket(); // right } } else if (RENDER == "corner") { corner_bracket(); } else if (RENDER == "side") { side_bracket(); } else if (RENDER == "tpu_cap") { tpu_edge_cap(); } else if (RENDER == "rail_2d") { projection(cut = false) linear_extrude(1) bumper_rail_2d(); }