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>
292 lines
14 KiB
OpenSCAD
292 lines
14 KiB
OpenSCAD
// ============================================================
|
||
// 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();
|
||
}
|