saltylab-firmware/chassis/base_bumper.scad
sl-mechanical beb0af7ce6 feat: bumper + frame crash protection for SaltyBot
Protects expensive sensors when robot tips over during testing.

Files added:
  chassis/bumper_frame.scad   — roll cage (25 mm stem collar + 4 inclined
                                posts + crown ring + TPU snap pad). Posts at
                                45/135/225/315° between cameras. Crown at
                                Z=148 mm above cage collar, 18 mm above RPLIDAR
                                top. RPLIDAR FOV unobstructed.
  chassis/base_bumper.scad    — clip-on bumper ring for 270×240 mm base plate.
                                50 mm tall, 15 mm standoff, rounded corners.
                                4× corner + 4× side clip brackets. TPU snap
                                edge caps. FC status LEDs remain visible.
  chassis/stem_protector.scad — split TPU sleeve for 25 mm stem. Snap-fit
                                closure (no hardware). Install 3–4 sleeves
                                along stem at ~200 mm intervals.
  chassis/bumper_BOM.md       — full BOM, print settings, install heights,
                                mass estimate (~500 g total, balanced).

All parts print without supports. Fully removable (clip/snap/clamp).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-28 23:30:06 -05:00

292 lines
14 KiB
OpenSCAD
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// ============================================================
// 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();
}