feat: Battery holder bracket (Issue #588) #589
410
chassis/battery_holder.scad
Normal file
410
chassis/battery_holder.scad
Normal file
@ -0,0 +1,410 @@
|
||||
// ============================================================
|
||||
// battery_holder.scad — 6S LiPo Battery Holder for 2020 T-Slot Chassis
|
||||
// Issue: #588 Agent: sl-mechanical Date: 2026-03-14
|
||||
// ============================================================
|
||||
//
|
||||
// Parametric bracket holding a 6S 5000 mAh LiPo pack on 2020 aluminium
|
||||
// T-slot rails. Designed for low centre-of-gravity mounting: pack sits
|
||||
// flat between the two chassis rails, as close to ground as clearance
|
||||
// allows. Quick-release via captive Velcro straps — battery swap in
|
||||
// under 60 s without tools.
|
||||
//
|
||||
// Architecture:
|
||||
// Tray → flat floor + perimeter walls, battery sits inside
|
||||
// Rail saddles→ two T-nut feet drop onto 2020 rails, thumbscrew locks
|
||||
// Strap slots → four pairs of slots for 25 mm Velcro strap loops
|
||||
// XT60 window → cut-out in rear wall for XT60 connector exit
|
||||
// Balance port→ open channel in front wall for balance lead routing
|
||||
// QR tab → front-edge pull tab for one-handed battery extraction
|
||||
//
|
||||
// Part catalogue:
|
||||
// Part 1 — battery_tray() Main tray body (single-piece print)
|
||||
// Part 2 — rail_saddle() T-nut saddle foot (print x2 per tray)
|
||||
// Part 3 — strap_guide() 25 mm Velcro strap guide (print x4)
|
||||
// Part 4 — assembly_preview()
|
||||
//
|
||||
// Hardware BOM:
|
||||
// 2× M3 × 16 mm SHCS + M3 hex nut T-nut rail clamp thumbscrews
|
||||
// 2× 25 mm × 250 mm Velcro strap battery retention (hook + loop)
|
||||
// 1× XT60 female connector (mounted on ESC/PDB harness)
|
||||
// — battery slides in from front, Velcro strap over top —
|
||||
//
|
||||
// 6S LiPo target pack (verify with calipers — packs vary by brand):
|
||||
// BATT_L = 155 mm (length, X axis in tray)
|
||||
// BATT_W = 48 mm (width, Y axis in tray)
|
||||
// BATT_H = 52 mm (height, Z axis in tray)
|
||||
// Clearance 1 mm each side added automatically (BATT_CLEAR)
|
||||
//
|
||||
// Mounting:
|
||||
// Rail span : RAIL_SPAN — distance between 2020 rail centrelines
|
||||
// Default 80 mm; adjust to chassis rail spacing
|
||||
// Saddle height: SADDLE_H — total height of saddle (tray floor above rail)
|
||||
// Keep low for CoG; default 8 mm
|
||||
//
|
||||
// RENDER options:
|
||||
// "assembly" full assembly preview (default)
|
||||
// "tray_stl" Part 1 — battery tray
|
||||
// "saddle_stl" Part 2 — rail saddle (print x2)
|
||||
// "strap_guide_stl" Part 3 — strap guide (print x4)
|
||||
//
|
||||
// Export commands:
|
||||
// openscad battery_holder.scad -D 'RENDER="tray_stl"' -o bh_tray.stl
|
||||
// openscad battery_holder.scad -D 'RENDER="saddle_stl"' -o bh_saddle.stl
|
||||
// openscad battery_holder.scad -D 'RENDER="strap_guide_stl"' -o bh_strap_guide.stl
|
||||
//
|
||||
// Print settings (all parts):
|
||||
// Material : PETG
|
||||
// Perimeters : 5 (tray, saddle), 3 (strap_guide)
|
||||
// Infill : 40 % gyroid (tray floor, saddle), 20 % (strap_guide)
|
||||
// Orientation:
|
||||
// tray — floor flat on bed (no supports needed)
|
||||
// saddle — flat face on bed (no supports)
|
||||
// strap_guide — flat face on bed (no supports)
|
||||
// ============================================================
|
||||
|
||||
$fn = 64;
|
||||
e = 0.01;
|
||||
|
||||
// ── Battery pack dimensions (verify with calipers) ────────────────────────────
|
||||
BATT_L = 155.0; // pack length (X)
|
||||
BATT_W = 48.0; // pack width (Y)
|
||||
BATT_H = 52.0; // pack height (Z)
|
||||
BATT_CLEAR = 1.0; // per-side fit clearance
|
||||
|
||||
// ── Tray geometry ─────────────────────────────────────────────────────────────
|
||||
TRAY_FLOOR_T = 4.0; // tray floor thickness
|
||||
TRAY_WALL_T = 4.0; // tray perimeter wall thickness
|
||||
TRAY_WALL_H = 20.0; // tray wall height (Z) — cradles lower half of pack
|
||||
TRAY_FILLET_R = 3.0; // inner corner radius
|
||||
|
||||
// Inner tray cavity (battery + clearance)
|
||||
TRAY_INN_L = BATT_L + 2*BATT_CLEAR;
|
||||
TRAY_INN_W = BATT_W + 2*BATT_CLEAR;
|
||||
|
||||
// Outer tray footprint
|
||||
TRAY_OUT_L = TRAY_INN_L + 2*TRAY_WALL_T;
|
||||
TRAY_OUT_W = TRAY_INN_W + 2*TRAY_WALL_T;
|
||||
TRAY_TOTAL_H = TRAY_FLOOR_T + TRAY_WALL_H;
|
||||
|
||||
// ── Rail interface ─────────────────────────────────────────────────────────────
|
||||
RAIL_SPAN = 80.0; // distance between 2020 rail centrelines (Y)
|
||||
RAIL_W = 20.0; // 2020 extrusion width
|
||||
SLOT_NECK_H = 3.2; // T-slot neck height
|
||||
SLOT_OPEN = 6.0; // T-slot opening width
|
||||
SLOT_INN_W = 10.2; // T-slot inner width
|
||||
SLOT_INN_H = 5.8; // T-slot inner height
|
||||
|
||||
// ── T-nut / saddle geometry ───────────────────────────────────────────────────
|
||||
TNUT_W = 9.8;
|
||||
TNUT_H = 5.5;
|
||||
TNUT_L = 12.0;
|
||||
TNUT_NUT_AF = 5.5; // M3 hex nut across-flats
|
||||
TNUT_NUT_H = 2.4;
|
||||
TNUT_BOLT_D = 3.3; // M3 clearance
|
||||
|
||||
SADDLE_W = 30.0; // saddle foot width (X, along rail)
|
||||
SADDLE_T = 8.0; // saddle body thickness (Z, above rail top face)
|
||||
SADDLE_PAD_T = 2.0; // rubber-pad recess depth (optional anti-slip)
|
||||
|
||||
// ── Velcro strap slots ────────────────────────────────────────────────────────
|
||||
STRAP_W = 26.0; // 25 mm strap + 1 mm clearance
|
||||
STRAP_T = 4.0; // slot through-thickness (tray wall)
|
||||
// Four slot pairs: one near each end of tray (X), one each side (Y)
|
||||
// Slots run through side walls (Y direction) — strap loops over battery top
|
||||
|
||||
// ── XT60 connector window (rear wall) ─────────────────────────────────────────
|
||||
XT60_W = 14.0; // XT60 body width
|
||||
XT60_H = 18.0; // XT60 body height (with cable exit)
|
||||
XT60_OFFSET_Z = 4.0; // height above tray floor
|
||||
|
||||
// ── Balance lead port (front wall) ────────────────────────────────────────────
|
||||
BAL_W = 40.0; // balance lead bundle width (6S = 7 wires)
|
||||
BAL_H = 6.0; // balance lead channel height
|
||||
BAL_OFFSET_Z = 8.0; // height above tray floor
|
||||
|
||||
// ── Quick-release pull tab (front edge) ──────────────────────────────────────
|
||||
QR_TAB_W = 30.0; // tab width
|
||||
QR_TAB_H = 12.0; // tab height above front wall top
|
||||
QR_TAB_T = 4.0; // tab thickness
|
||||
QR_HOLE_D = 10.0; // finger-loop hole diameter
|
||||
|
||||
// ── Strap guide clip ─────────────────────────────────────────────────────────
|
||||
GUIDE_OD = STRAP_W + 6.0;
|
||||
GUIDE_T = 3.0;
|
||||
GUIDE_BODY_H = 14.0;
|
||||
|
||||
// ── Fasteners ─────────────────────────────────────────────────────────────────
|
||||
M3_D = 3.3;
|
||||
|
||||
// ============================================================
|
||||
// RENDER DISPATCH
|
||||
// ============================================================
|
||||
RENDER = "assembly";
|
||||
|
||||
if (RENDER == "assembly") assembly_preview();
|
||||
else if (RENDER == "tray_stl") battery_tray();
|
||||
else if (RENDER == "saddle_stl") rail_saddle();
|
||||
else if (RENDER == "strap_guide_stl") strap_guide();
|
||||
|
||||
// ============================================================
|
||||
// ASSEMBLY PREVIEW
|
||||
// ============================================================
|
||||
module assembly_preview() {
|
||||
// Ghost 2020 rails (Y direction, RAIL_SPAN apart)
|
||||
for (ry = [-RAIL_SPAN/2, RAIL_SPAN/2])
|
||||
%color("Silver", 0.28)
|
||||
translate([-TRAY_OUT_L/2 - 30, ry - RAIL_W/2, -SADDLE_T - TNUT_H])
|
||||
cube([TRAY_OUT_L + 60, RAIL_W, RAIL_W]);
|
||||
|
||||
// Rail saddles (left and right)
|
||||
for (sy = [-RAIL_SPAN/2, RAIL_SPAN/2])
|
||||
color("DimGray", 0.85)
|
||||
translate([0, sy, -SADDLE_T])
|
||||
rail_saddle();
|
||||
|
||||
// Battery tray (sitting on saddles)
|
||||
color("OliveDrab", 0.85)
|
||||
battery_tray();
|
||||
|
||||
// Battery ghost
|
||||
%color("SaddleBrown", 0.35)
|
||||
translate([-BATT_L/2, -BATT_W/2, TRAY_FLOOR_T])
|
||||
cube([BATT_L, BATT_W, BATT_H]);
|
||||
|
||||
// Strap guides (4×, two each end)
|
||||
for (sx = [-TRAY_OUT_L/2 + STRAP_W/2 + TRAY_WALL_T + 8,
|
||||
TRAY_OUT_L/2 - STRAP_W/2 - TRAY_WALL_T - 8])
|
||||
for (sy = [-1, 1])
|
||||
color("SteelBlue", 0.75)
|
||||
translate([sx, sy*(TRAY_OUT_W/2), TRAY_TOTAL_H + 2])
|
||||
rotate([sy > 0 ? 0 : 180, 0, 0])
|
||||
strap_guide();
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// PART 1 — BATTERY TRAY
|
||||
// ============================================================
|
||||
// Single-piece tray: flat floor, four perimeter walls, T-nut saddle
|
||||
// attachment bosses on underside, Velcro strap slots through side walls,
|
||||
// XT60 window in rear wall, balance lead channel in front wall, and
|
||||
// quick-release pull tab on front edge.
|
||||
//
|
||||
// Battery inserts from the front (−X end) — front wall is lower than
|
||||
// rear wall so the pack slides in and the rear wall stops it.
|
||||
// Velcro straps loop over the top of the pack through the side slots.
|
||||
//
|
||||
// Coordinate convention:
|
||||
// X: along battery length (−X = front/plug-end, +X = rear/balance-end)
|
||||
// Y: across battery width (centred, ±TRAY_OUT_W/2)
|
||||
// Z: vertical (Z=0 = tray floor top face; −Z = underside → saddles)
|
||||
//
|
||||
// Print: floor flat on bed, PETG, 5 perims, 40% gyroid. No supports.
|
||||
module battery_tray() {
|
||||
// Short rear wall height (XT60 connector exits here — full wall height)
|
||||
// Front wall is lower to allow battery slide-in
|
||||
front_wall_h = TRAY_WALL_H * 0.55; // 55% height — battery slides over
|
||||
|
||||
difference() {
|
||||
union() {
|
||||
// ── Floor ────────────────────────────────────────────────────
|
||||
translate([-TRAY_OUT_L/2, -TRAY_OUT_W/2, -TRAY_FLOOR_T])
|
||||
cube([TRAY_OUT_L, TRAY_OUT_W, TRAY_FLOOR_T]);
|
||||
|
||||
// ── Rear wall (+X, full height) ───────────────────────────────
|
||||
translate([TRAY_INN_L/2, -TRAY_OUT_W/2, 0])
|
||||
cube([TRAY_WALL_T, TRAY_OUT_W, TRAY_WALL_H]);
|
||||
|
||||
// ── Front wall (−X, lowered for slide-in) ────────────────────
|
||||
translate([-TRAY_INN_L/2 - TRAY_WALL_T, -TRAY_OUT_W/2, 0])
|
||||
cube([TRAY_WALL_T, TRAY_OUT_W, front_wall_h]);
|
||||
|
||||
// ── Side walls (±Y) ───────────────────────────────────────────
|
||||
for (sy = [-1, 1])
|
||||
translate([-TRAY_OUT_L/2,
|
||||
sy*(TRAY_INN_W/2 + (sy>0 ? 0 : -TRAY_WALL_T)),
|
||||
0])
|
||||
cube([TRAY_OUT_L,
|
||||
TRAY_WALL_T,
|
||||
TRAY_WALL_H]);
|
||||
|
||||
// ── Quick-release pull tab (front wall top edge) ──────────────
|
||||
translate([-TRAY_INN_L/2 - TRAY_WALL_T - e,
|
||||
-QR_TAB_W/2, front_wall_h])
|
||||
cube([QR_TAB_T, QR_TAB_W, QR_TAB_H]);
|
||||
|
||||
// ── Saddle attachment bosses (underside, one per rail) ─────────
|
||||
// Bosses drop into saddle sockets; M3 bolt through floor
|
||||
for (sy = [-RAIL_SPAN/2, RAIL_SPAN/2])
|
||||
translate([-SADDLE_W/2, sy - SADDLE_W/2, -TRAY_FLOOR_T - SADDLE_T/2])
|
||||
cube([SADDLE_W, SADDLE_W, SADDLE_T/2 + e]);
|
||||
}
|
||||
|
||||
// ── Battery cavity (hollow interior) ──────────────────────────────
|
||||
translate([-TRAY_INN_L/2, -TRAY_INN_W/2, -e])
|
||||
cube([TRAY_INN_L, TRAY_INN_W, TRAY_WALL_H + 2*e]);
|
||||
|
||||
// ── XT60 connector window (rear wall) ─────────────────────────────
|
||||
// Centred on rear wall, low position so cable exits cleanly
|
||||
translate([TRAY_INN_L/2 - e, -XT60_W/2, XT60_OFFSET_Z])
|
||||
cube([TRAY_WALL_T + 2*e, XT60_W, XT60_H]);
|
||||
|
||||
// ── Balance lead channel (front wall) ─────────────────────────────
|
||||
// Wide slot for 6S balance lead (7-pin JST-XH ribbon)
|
||||
translate([-TRAY_INN_L/2 - TRAY_WALL_T - e,
|
||||
-BAL_W/2, BAL_OFFSET_Z])
|
||||
cube([TRAY_WALL_T + 2*e, BAL_W, BAL_H]);
|
||||
|
||||
// ── Velcro strap slots (side walls, 2 pairs) ──────────────────────
|
||||
// Pair A: near front end (−X), Pair B: near rear end (+X)
|
||||
// Each slot runs through the wall in Y direction
|
||||
for (sx = [-TRAY_INN_L/2 + STRAP_W*0.5 + 10,
|
||||
TRAY_INN_L/2 - STRAP_W*0.5 - 10])
|
||||
for (sy = [-1, 1]) {
|
||||
translate([sx - STRAP_W/2,
|
||||
sy*(TRAY_INN_W/2) - (sy > 0 ? TRAY_WALL_T + e : -e),
|
||||
TRAY_WALL_H * 0.35])
|
||||
cube([STRAP_W, TRAY_WALL_T + 2*e, STRAP_T]);
|
||||
}
|
||||
|
||||
// ── QR tab finger-loop hole ────────────────────────────────────────
|
||||
translate([-TRAY_INN_L/2 - TRAY_WALL_T/2,
|
||||
0, front_wall_h + QR_TAB_H * 0.55])
|
||||
rotate([0, 90, 0])
|
||||
cylinder(d = QR_HOLE_D, h = QR_TAB_T + 2*e, center = true);
|
||||
|
||||
// ── Saddle bolt holes (M3 through floor into saddle boss) ─────────
|
||||
for (sy = [-RAIL_SPAN/2, RAIL_SPAN/2])
|
||||
translate([0, sy, -TRAY_FLOOR_T - e])
|
||||
cylinder(d = M3_D, h = TRAY_FLOOR_T + 2*e);
|
||||
|
||||
// ── Floor lightening grid (non-structural area) ───────────────────
|
||||
// 2D grid of pockets reduces weight without weakening battery support
|
||||
for (gx = [-40, 0, 40])
|
||||
for (gy = [-12, 12])
|
||||
translate([gx, gy, -TRAY_FLOOR_T - e])
|
||||
cylinder(d = 14, h = TRAY_FLOOR_T - 1.5 + e);
|
||||
|
||||
// ── Inner corner chamfers (battery slide-in guidance) ─────────────
|
||||
// 45° chamfers at bottom-front inner corners
|
||||
translate([-TRAY_INN_L/2, -TRAY_INN_W/2 - e, -e])
|
||||
rotate([0, 0, 45])
|
||||
cube([4, 4, TRAY_WALL_H * 0.3 + e]);
|
||||
translate([-TRAY_INN_L/2, TRAY_INN_W/2 + e, -e])
|
||||
rotate([0, 0, -45])
|
||||
cube([4, 4, TRAY_WALL_H * 0.3 + e]);
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// PART 2 — RAIL SADDLE
|
||||
// ============================================================
|
||||
// T-nut foot that clamps to the top face of a 2020 T-slot rail.
|
||||
// Battery tray boss drops into saddle socket; M3 bolt through tray
|
||||
// floor and saddle body locks everything together.
|
||||
// M3 thumbscrew through side of saddle body grips the rail T-groove
|
||||
// (same thumbscrew interface as all other SaltyLab rail brackets).
|
||||
//
|
||||
// Saddle sits on top of rail (no T-nut tongue needed — saddle clamps
|
||||
// from the top using a T-nut inserted into the rail T-groove from the
|
||||
// end). Low profile keeps battery CoG as low as possible.
|
||||
//
|
||||
// Print: flat base on bed, PETG, 5 perims, 50% gyroid.
|
||||
module rail_saddle() {
|
||||
sock_d = SADDLE_W - 4; // tray boss socket diameter
|
||||
|
||||
difference() {
|
||||
union() {
|
||||
// ── Main saddle body ──────────────────────────────────────────
|
||||
translate([-SADDLE_W/2, -SADDLE_W/2, 0])
|
||||
cube([SADDLE_W, SADDLE_W, SADDLE_T]);
|
||||
|
||||
// ── T-nut tongue (enters rail T-groove from above) ────────────
|
||||
translate([-TNUT_W/2, -TNUT_L/2, -SLOT_NECK_H])
|
||||
cube([TNUT_W, TNUT_L, SLOT_NECK_H + e]);
|
||||
|
||||
// ── T-nut inner body (locks in groove) ────────────────────────
|
||||
translate([-TNUT_W/2, -TNUT_L/2, -SLOT_NECK_H - (TNUT_H - SLOT_NECK_H)])
|
||||
cube([TNUT_W, TNUT_L, TNUT_H - SLOT_NECK_H + e]);
|
||||
}
|
||||
|
||||
// ── Rail channel clearance (bottom of saddle straddles rail) ──────
|
||||
// Saddle body has a channel that sits over the rail top face
|
||||
translate([-RAIL_W/2 - e, -SADDLE_W/2 - e, -e])
|
||||
cube([RAIL_W + 2*e, SADDLE_W + 2*e, 2.0]);
|
||||
|
||||
// ── M3 clamp bolt bore (through saddle body into T-nut) ───────────
|
||||
translate([0, 0, -SLOT_NECK_H - TNUT_H - e])
|
||||
cylinder(d = TNUT_BOLT_D, h = SADDLE_T + TNUT_H + 2*e);
|
||||
|
||||
// ── M3 hex nut pocket (top face of saddle for thumbscrew) ─────────
|
||||
translate([0, 0, SADDLE_T - TNUT_NUT_H - 0.5])
|
||||
cylinder(d = TNUT_NUT_AF / cos(30),
|
||||
h = TNUT_NUT_H + 0.6, $fn = 6);
|
||||
|
||||
// ── Tray boss socket (top face of saddle, tray boss nests here) ───
|
||||
// Cylindrical socket receives tray underside boss; M3 bolt centres
|
||||
translate([0, 0, SADDLE_T - 3])
|
||||
cylinder(d = sock_d + 0.4, h = 3 + e);
|
||||
|
||||
// ── M3 tray bolt bore (vertical, through saddle top) ──────────────
|
||||
translate([0, 0, SADDLE_T - 3 - e])
|
||||
cylinder(d = M3_D, h = SADDLE_T + e);
|
||||
|
||||
// ── Anti-slip pad recess (bottom face, optional rubber adhesive) ──
|
||||
translate([0, 0, -e])
|
||||
cylinder(d = SADDLE_W - 8, h = SADDLE_PAD_T + e);
|
||||
|
||||
// ── Lightening pockets ─────────────────────────────────────────────
|
||||
for (lx = [-1, 1], ly = [-1, 1])
|
||||
translate([lx*8, ly*8, -e])
|
||||
cylinder(d = 5, h = SADDLE_T - 3 - 1 + e);
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// PART 3 — STRAP GUIDE
|
||||
// ============================================================
|
||||
// Snap-on guide that sits on top of tray wall at each strap slot,
|
||||
// directing the 25 mm Velcro strap from the side slot up and over
|
||||
// the battery top. Four per tray, one at each slot exit.
|
||||
// Curved lip prevents strap from cutting into PETG wall edge.
|
||||
// Push-fit onto tray wall top; no fasteners required.
|
||||
//
|
||||
// Print: flat base on bed, PETG, 3 perims, 20% infill.
|
||||
module strap_guide() {
|
||||
strap_w_clr = STRAP_W + 0.5; // strap slot with clearance
|
||||
lip_r = 3.0; // guide lip radius
|
||||
|
||||
difference() {
|
||||
union() {
|
||||
// ── Body (sits on tray wall top edge) ─────────────────────────
|
||||
translate([-GUIDE_OD/2, 0, 0])
|
||||
cube([GUIDE_OD, GUIDE_T, GUIDE_BODY_H]);
|
||||
|
||||
// ── Curved guide lip (top of body, strap bends around this) ───
|
||||
translate([0, GUIDE_T/2, GUIDE_BODY_H])
|
||||
rotate([0, 90, 0])
|
||||
cylinder(r = lip_r, h = GUIDE_OD, center = true);
|
||||
|
||||
// ── Wall engagement tabs (snap over tray wall top) ────────────
|
||||
for (sy = [0, -(TRAY_WALL_T + GUIDE_T)])
|
||||
translate([-strap_w_clr/2 - 3, sy - GUIDE_T, 0])
|
||||
cube([strap_w_clr + 6, GUIDE_T, GUIDE_BODY_H * 0.4]);
|
||||
}
|
||||
|
||||
// ── Strap slot (through body) ──────────────────────────────────────
|
||||
translate([-strap_w_clr/2, -e, -e])
|
||||
cube([strap_w_clr, GUIDE_T + 2*e, GUIDE_BODY_H + 2*e]);
|
||||
|
||||
// ── Wall clearance slot (body slides over tray wall) ──────────────
|
||||
translate([-strap_w_clr/2 - 3 - e,
|
||||
-TRAY_WALL_T - GUIDE_T, -e])
|
||||
cube([strap_w_clr + 6 + 2*e,
|
||||
TRAY_WALL_T, GUIDE_BODY_H * 0.4 + 2*e]);
|
||||
|
||||
// ── Lightening pockets on side faces ──────────────────────────────
|
||||
for (lx = [-GUIDE_OD/4, GUIDE_OD/4])
|
||||
translate([lx, GUIDE_T/2, GUIDE_BODY_H/2])
|
||||
cube([6, GUIDE_T + 2*e, GUIDE_BODY_H * 0.5], center = true);
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user