feat: Parametric chassis frame design (bd-1iy5) #7

Merged
sl-jetson merged 1 commits from sl-mechanical/bd-1iy5-chassis-frame into main 2026-02-28 13:06:24 -05:00
3 changed files with 563 additions and 0 deletions
Showing only changes of commit cd9299ded8 - Show all commits

121
chassis/ASSEMBLY.md Normal file
View File

@ -0,0 +1,121 @@
# SaltyBot Chassis — Assembly Notes
**Task:** bd-1iy5 — Rev A — 2026-02-28
---
## Overview
```
[Front bumper rail — 22mm EMT]
├─ bumper_bracket(front=+1) ──────────────────────┐
│ │
┌───────┴──────────── Main Deck (640×220×6mm Al) ─────────┴───────┐
│ ← Jetson mount plate (rear/+X) FC mount (front/X) → │
│ [Battery tray hanging below centre] │
└───┬──────────────────────────────────────────────────────────┬───┘
│ │
Motor fork (L) Motor fork (R)
Hub motor CL: ±300mm from deck centre │
Axle height: 310mm above ground │
├─ bumper_bracket(front=-1) ──────────────────────┘
[Rear bumper rail — 22mm EMT]
```
---
## Step-by-step Assembly
### 1 Fabricate / print parts
- Send deck plate DXF (export from OpenSCAD → DXF) to CNC router or waterjet cutter.
- Print motor fork brackets in PETG 5 perimeters / 40% gyroid for prototype, or send STEP to machine shop.
- Print battery tray, FC pad, Jetson plate, bumper brackets in PETG.
- Export STEP: OpenSCAD → Render (F6) → Export as STL, then convert with FreeCAD for STEP.
### 2 Verify motor axle dimensions
- Measure actual hoverboard motor axle: diameter and flat-to-flat.
- Adjust `MOTOR_AXLE_D` and `MOTOR_AXLE_FLAT` in `chassis_frame.scad`.
- Re-export fork dropout slot.
### 3 Motor forks → deck
1. Thread M5 T-nuts into underside of deck edge slots (or use M5 rivet nuts).
2. Align fork bracket to deck edge; fasten with 4× M5×16 SHCS + flat washer each side.
3. Apply Loctite 243 to threads. Torque to 4 N·m.
### 4 Motors into forks
1. Slide hub motor axle into dropout slot (flat side aligns with slot).
2. Fit flat washer then flanged M14 axle nut; torque to 3540 N·m.
3. Feed motor phase wires and hall-sensor cable through deck cable slot.
### 5 Longitudinal ribs
1. Align ribs with edge grooves on deck underside.
2. Secure with M4×12 SHCS through pre-drilled holes; torque 2.5 N·m.
### 6 Battery tray
1. Pass any wiring harness through deck wire-pass hole before mounting tray.
2. Align tray mounting ears to deck M4 threaded inserts (or rivet nuts).
3. Fasten 4× M4×12 SHCS. Torque 2.5 N·m.
4. Insert battery pack; route Velcro straps through slots and cinch.
### 7 FC mount (MAMBA F722S)
1. Place silicone anti-vibration grommets onto nylon M3 standoffs.
2. Lower FC onto standoffs; secure with M3×6 BHCS. Snug only — do not over-torque.
3. Orient USB-C port toward front of robot for cable access.
### 8 Jetson Nano mount plate
1. Press or thread M3 nylon standoffs (8mm) into plate holes.
2. Bolt plate to deck: 4× M3×10 SHCS at deck corners.
3. Set Jetson Nano B01 carrier onto plate standoffs; fasten M3×6 BHCS.
### 9 Bumper brackets
1. Slide 22mm EMT conduit through saddle clamp openings.
2. Bolt bracket to deck edge: 4× M5×16 SHCS per bracket.
3. Position bumper rail flush with motor OD outer edge; tighten saddle clamp bolts.
### 10 Cable routing
- Route motor phase cables along longitudinal ribs; secure with cable clips.
- FC ↔ ESC/VESC harness exits through front cable slot.
- Jetson USB/UART ribbon exits through rear cable slot.
- Power harness (battery XT60 → BMS → 24V bus) runs under deck along centreline.
---
## Critical Dimensions (verify before machining)
| Dimension | Nominal | Tolerance |
|-----------|---------|-----------|
| Wheelbase (axle C/L to C/L) | 600 mm | ±1 mm |
| Motor fork slot width | 24 mm | +0.5 / 0 |
| Motor fork dropout depth | 60 mm | ±0.5 mm |
| FC hole pattern | 30.5 × 30.5 mm | ±0.2 mm |
| Jetson hole pattern | 58 × 58 mm | ±0.2 mm |
| Battery tray inner | 185 × 72 × 52 mm | +2 / 0 mm |
---
## OpenSCAD Rendering Notes
```bash
# Render full assembly preview (F5 in GUI)
openscad chassis_frame.scad
# Export individual part STL for slicing
openscad chassis_frame.scad -D "PART=\"motor_fork\"" -o motor_fork_right.stl
# Export deck DXF (set DECK_THICKNESS=0.01 for 2D projection)
openscad chassis_frame.scad -D "RENDER_2D=true" -o deck_plate.dxf
```
**Slicing profile (PETG, structural parts):**
- Nozzle: 0.4mm | Layer: 0.2mm
- Perimeters: 5 | Infill: 40% gyroid
- Supports: Yes (motor fork dropout slot)
- Orientation: Fork bracket printed vertically (load axis = layer direction)
---
## Safety Notes
- Verify axle nut torque after first 10-minute ride — hub motors vibrate and may back off.
- Battery tray is rated for packs ≤185×72×52mm; confirm dimensions before ordering cells.
- Ground all aluminium chassis parts to power-common to avoid RF interference with FC.
- Do **not** run robot without bumpers fitted — uncontrolled runaway risk.

102
chassis/BOM.md Normal file
View File

@ -0,0 +1,102 @@
# SaltyBot Chassis — Bill of Materials
**Task:** bd-1iy5
**Rev:** A — 2026-02-28
**Agent:** sl-mechanical
---
## Structural / Fabricated Parts
| # | Part | Qty | Material | Notes |
|---|------|-----|----------|-------|
| 1 | Main deck plate | 1 | 6mm 5052 aluminium plate, 640×220mm | CNC router or waterjet; all holes per `chassis_frame.scad` |
| 2 | Longitudinal rib (front) | 1 | 4mm 5052 aluminium, 600×40mm | Laser-cut; press-fit into deck slots |
| 3 | Longitudinal rib (rear) | 1 | 4mm 5052 aluminium, 600×40mm | Same file, symmetric |
| 4 | Motor fork bracket (L) | 1 | 8mm 6061 aluminium | CNC mill or FDM PETG @100% infill for prototyping |
| 5 | Motor fork bracket (R) | 1 | 8mm 6061 aluminium | Mirror of item 4 |
| 6 | Battery tray | 1 | 3mm PETG FDM or 3mm aluminium fold | `chassis_frame.scad``battery_tray()` module |
| 7 | FC mount plate / standoffs | 1 set | PETG or nylon FDM | Includes 4× M3 nylon standoffs, 6mm height |
| 8 | Jetson Nano mount plate | 1 | 4mm 5052 aluminium or 4mm PETG FDM | B01 58×58mm hole pattern |
| 9 | Front bumper bracket | 1 | 5mm PETG FDM | Saddle clamps for 22mm EMT conduit |
| 10 | Rear bumper bracket | 1 | 5mm PETG FDM | Mirror of item 9 |
---
## Motors
| # | Part | Qty | Source / Spec | Notes |
|---|------|-----|---------------|-------|
| 11 | Hoverboard hub motor | 2 | Generic 6.5" / 170mm OD, 36V nominal | ~350W each; 14mm flat axle; confirm exact OD before cutting fork slots |
| 12 | Motor hall-sensor cable extension | 2 | 6-pin JST-PH 300mm | Route through deck cable slot |
---
## Electronics Mounts
| # | Part | Qty | Spec | Notes |
|---|------|-----|------|-------|
| 13 | STM32 MAMBA F722S FC | 1 | 36×36mm PCB, 30.5×30.5mm M3 mount | Oriented USB-C port toward front |
| 14 | Nylon M3 standoff 6mm | 4 | F/F nylon | FC vibration isolation |
| 15 | Anti-vibration grommet M3 | 4 | Ø6mm silicone | Under FC mount pads |
| 16 | Jetson Nano B01 module | 1 | 69.6×45mm module + carrier | 58×58mm M3 carrier hole pattern |
| 17 | Nylon M3 standoff 8mm | 4 | F/F nylon | Jetson board standoffs |
---
## Battery
| # | Part | Qty | Spec | Notes |
|---|------|-----|------|-------|
| 18 | LiPo / LiFePO4 pack | 1 | 24V (6S LiPo or 8S LiFePO4), 4Ah, ≤185×72×52mm | Confirm dims before printing tray; add 2mm clearance |
| 19 | Velcro strap 20mm wide | 2 | 300mm length | Route through tray strap slots |
| 20 | BMS board | 1 | Matched to cell chemistry | Mount externally on rear inner face of tray wall |
---
## Bumper Rail
| # | Part | Qty | Spec | Notes |
|---|------|-----|------|-------|
| 21 | 3/4" EMT conduit | 2 | 22mm OD, 660mm length | Front and rear bumper rail; bent or straight |
| 22 | Conduit saddle clamp M4 | 6 | 3D-printed integral to bracket (items 9/10) | Back-up: standard pipe clamp 22mm |
---
## Fasteners
| # | Part | Qty | Spec |
|---|------|-----|------|
| 23 | M5×16 SHCS | 24 | ISO 4762, SS |
| 24 | M5 hex nut | 24 | ISO 4032, SS |
| 25 | M4×12 SHCS | 12 | ISO 4762, SS |
| 26 | M4 hex nut | 12 | ISO 4032, SS |
| 27 | M3×10 SHCS | 20 | ISO 4762, SS |
| 28 | M3 hex nut | 20 | ISO 4032, SS |
| 29 | M3×6 BHCS | 8 | FC + Jetson board bolts |
| 30 | M14×1.5 axle nut | 4 | One each side per motor, flanged | Confirm axle thread pitch on actual motors |
| 31 | Serrated washer M14 | 4 | Axle anti-rotation |
| 32 | Flat washer M5 | 48 | SS |
---
## Tools Required for Assembly
- Torque wrench (M14 axle nuts: 3540 N·m; M5: 4 N·m)
- M2.5 / M3 / M4 / M5 hex drivers
- Thread locker (Loctite 243 blue for all structural fasteners)
- Dial caliper — verify motor OD, axle flat before machining fork slot
---
## Estimated Masses
| Assembly | Est. mass |
|----------|-----------|
| Aluminium deck + ribs | ~1.1 kg |
| Motor forks (×2 Al) | ~0.4 kg |
| Motors (×2) | ~3.6 kg |
| Battery (6S 4Ah LiPo) | ~0.7 kg |
| Electronics (FC + Jetson + wiring) | ~0.5 kg |
| Bumpers + brackets | ~0.3 kg |
| Fasteners | ~0.2 kg |
| **Total estimate** | **~6.8 kg** |

340
chassis/chassis_frame.scad Normal file
View File

@ -0,0 +1,340 @@
// =============================================================================
// SaltyBot Parametric Chassis Frame
// Task: bd-1iy5
// Agent: sl-mechanical
// Date: 2026-02-28
//
// Self-balancing two-wheeled robot chassis
// Requirements:
// - 600mm wheelbase
// - 2x hoverboard hub motors (170mm OD)
// - STM32 MAMBA F722S FC mount (30.5x30.5mm pattern)
// - Battery tray (24V 4Ah ~180x70x50mm pack)
// - Jetson Nano B01 mount plate (100x80mm, M3 holes)
// - Front/rear bumper brackets
// =============================================================================
// RENDER QUALITY
$fn = 64;
// =============================================================================
// PARAMETERS edit here to adjust the whole model
// =============================================================================
// Wheelbase / overall geometry
WHEELBASE = 600; // mm, center-to-center axle distance (lateral)
FRAME_WIDTH = 220; // mm, front-to-back depth of main deck
DECK_THICKNESS = 6; // mm, main deck plate thickness
WALL_T = 4; // mm, general wall thickness for brackets/ribs
// Hub motor parameters
MOTOR_OD = 170; // mm, motor housing outer diameter
MOTOR_AXLE_D = 14; // mm, axle bolt diameter
MOTOR_AXLE_FLAT = 10; // mm, axle flat-to-flat (for anti-rotation)
MOTOR_FORK_WIDTH = 24; // mm, dropout slot width (fits 10-14mm axle + spacers)
MOTOR_FORK_DEPTH = 60; // mm, dropout slot depth from fork tip
MOTOR_FORK_H = 80; // mm, total height of motor fork bracket
MOTOR_FORK_T = 8; // mm, fork plate thickness
AXLE_HEIGHT = 310; // mm, axle CL above ground (motor radius + clearance)
// FC mount (MAMBA F722S 30.5 × 30.5 mm M3 pattern)
FC_MOUNT_SPACING = 30.5; // mm, hole pattern pitch
FC_MOUNT_HOLE_D = 3.2; // mm, M3 clearance
FC_STANDOFF_H = 6; // mm, standoff height
FC_PAD_T = 3; // mm, mounting pad thickness
// Battery tray (24V 4Ah LiPo / LiFePO4)
BATT_L = 185; // mm, cell pack length
BATT_W = 72; // mm, cell pack width
BATT_H = 52; // mm, cell pack height
BATT_WALL = 3; // mm, tray wall thickness
BATT_FLOOR = 4; // mm, tray floor thickness
BATT_STRAP_W = 20; // mm, Velcro strap slot width
BATT_STRAP_T = 2; // mm, strap slot depth
// Jetson Nano B01 mount plate
// B01 carrier board hole pattern: 58 x 58 mm M3 (inner) + corner pass-throughs
JETSON_HOLE_PITCH = 58; // mm, M3 mounting hole pattern
JETSON_HOLE_D = 3.2; // mm
JETSON_PLATE_L = 105; // mm, plate length
JETSON_PLATE_W = 90; // mm, plate width
JETSON_PLATE_T = 4; // mm, plate thickness
JETSON_STANDOFF_H = 8; // mm
// Bumper bracket
BUMPER_L = WHEELBASE + 60; // mm, bumper rail length (overhangs wheel CL)
BUMPER_H = 40; // mm, bracket vertical height
BUMPER_T = 5; // mm, bracket plate thickness
BUMPER_TUBE_OD = 22; // mm, 3/4" EMT conduit OD for bumper rail
// Rib / gusset parameters
RIB_W = 20; // mm, longitudinal rib width
RIB_H = 40; // mm, rib height below deck
RIB_T = 4; // mm, rib plate thickness (laser/router cut)
// Fastener helpers
M3_D = 3.2;
M4_D = 4.3;
M5_D = 5.3;
M6_D = 6.5;
// =============================================================================
// MAIN ASSEMBLY comment/uncomment parts as needed
// =============================================================================
color("Silver", 0.9) main_deck();
color("DimGray") motor_fork(side= 1); // right (+X)
color("DimGray") motor_fork(side=-1); // left (-X)
color("SteelBlue") battery_tray();
color("OliveDrab") fc_mount_plate();
color("DarkOrange") jetson_mount_plate();
color("Tomato") bumper_bracket(front= 1);
color("Tomato") bumper_bracket(front=-1);
color("WhiteSmoke") longitudinal_ribs();
// =============================================================================
// MODULES
// =============================================================================
// Main deck plate
module main_deck() {
difference() {
// Deck plate
translate([-WHEELBASE/2 - MOTOR_FORK_T, -FRAME_WIDTH/2, 0])
cube([WHEELBASE + 2*MOTOR_FORK_T, FRAME_WIDTH, DECK_THICKNESS]);
// Lightening holes 3 rows × 4 cols
for (x = [-WHEELBASE/3, 0, WHEELBASE/3])
for (y = [-FRAME_WIDTH/4, FRAME_WIDTH/4])
translate([x, y, -1])
cylinder(d=50, h=DECK_THICKNESS+2);
// FC mount holes
fc_mount_holes(z_offset=-1, depth=DECK_THICKNESS+2);
// Battery tray floor cutout for wire pass-through
translate([-BATT_L/2 + 40, -BATT_W/2 + 40, -1])
cube([BATT_L - 80, BATT_W - 80, DECK_THICKNESS+2]);
// Cable routing slots
for (x = [-60, 60])
translate([x, -FRAME_WIDTH/2 - 1, -1])
cube([14, 18, DECK_THICKNESS+2]);
}
}
// Longitudinal ribs (×2, symmetric F/R)
module longitudinal_ribs() {
for (y = [-FRAME_WIDTH/2 + RIB_W/2 + WALL_T,
FRAME_WIDTH/2 - RIB_W/2 - WALL_T])
translate([-WHEELBASE/2, y - RIB_T/2, -RIB_H])
cube([WHEELBASE, RIB_T, RIB_H]);
}
// Motor fork dropout bracket
// Mounts to deck edge; provides a CNC-milled or FDM dropout slot for the axle.
// `side` = +1 (right/+X) or -1 (left/-X)
module motor_fork(side = 1) {
x_pos = side * (WHEELBASE/2);
translate([x_pos, 0, 0]) {
difference() {
union() {
// Vertical fork body
translate([side*(DECK_THICKNESS/2), -MOTOR_FORK_WIDTH/2 - 4, -MOTOR_FORK_H])
cube([side * MOTOR_FORK_T, MOTOR_FORK_WIDTH + 8, MOTOR_FORK_H + DECK_THICKNESS]);
// Gusset triangles
for (g = [-1, 1])
translate([side*(DECK_THICKNESS/2), g*(MOTOR_FORK_WIDTH/2 + 2), -MOTOR_FORK_H])
linear_extrude(height=MOTOR_FORK_T)
polygon([[0,0],[0, g*20],[side*-30, 0]]);
}
// Axle dropout slot (open at bottom)
translate([side*(DECK_THICKNESS/2) - 1,
-MOTOR_AXLE_FLAT/2,
-MOTOR_FORK_DEPTH - MOTOR_AXLE_D/2])
cube([MOTOR_FORK_T + 2, MOTOR_AXLE_FLAT, MOTOR_FORK_DEPTH + 1]);
// Axle through-hole at slot top
translate([side*(DECK_THICKNESS/2) - 1, 0,
-MOTOR_FORK_DEPTH - MOTOR_AXLE_D/2])
rotate([0, 90, 0])
cylinder(d=MOTOR_AXLE_D + 1, h=MOTOR_FORK_T + 2);
// Bolt holes for deck attachment (M5 × 4)
for (y = [-20, 20])
for (z = [4, 12])
translate([side*(DECK_THICKNESS/2) - 1, y, z - MOTOR_FORK_H + MOTOR_FORK_H/2])
rotate([0, 90, 0])
cylinder(d=M5_D, h=MOTOR_FORK_T + 2);
}
}
}
// Battery tray
// Positioned in centre of deck, recessed 10mm below deck surface
module battery_tray() {
z_base = -BATT_FLOOR - BATT_H - 5; // hang below deck
translate([-BATT_L/2 - BATT_WALL, -BATT_W/2 - BATT_WALL, z_base]) {
difference() {
// Outer tray body
cube([BATT_L + 2*BATT_WALL,
BATT_W + 2*BATT_WALL,
BATT_H + BATT_FLOOR]);
// Inner cavity
translate([BATT_WALL, BATT_WALL, BATT_FLOOR])
cube([BATT_L, BATT_W, BATT_H + 1]);
// Strap slots (2× longitudinal)
for (x = [BATT_L/2 - BATT_STRAP_W*2,
BATT_L/2 + BATT_STRAP_W])
translate([x, -1, BATT_FLOOR + BATT_H/2 - BATT_STRAP_W/2])
cube([BATT_STRAP_W, BATT_W + 2*BATT_WALL + 2, BATT_STRAP_W]);
// Ventilation slots bottom (3×)
for (i = [0:2])
translate([BATT_WALL + 20 + i*50, BATT_WALL + 10, -1])
cube([30, BATT_W - 20, BATT_FLOOR + 2]);
// Mount holes to deck (M4 × 4 corners)
for (x = [10, BATT_L + BATT_WALL - 2])
for (y = [10, BATT_W + BATT_WALL - 2])
translate([x, y, BATT_H + BATT_FLOOR - 1])
cylinder(d=M4_D, h=BATT_FLOOR + 2);
}
}
}
// FC mount holes helper
module fc_mount_holes(z_offset=0, depth=10) {
// MAMBA F722S: 30.5×30.5 mm M3 pattern, centred at origin
for (x = [-FC_MOUNT_SPACING/2, FC_MOUNT_SPACING/2])
for (y = [-FC_MOUNT_SPACING/2, FC_MOUNT_SPACING/2])
translate([x, y, z_offset])
cylinder(d=FC_MOUNT_HOLE_D, h=depth);
}
// FC mount plate (raised 40mm above deck centre)
module fc_mount_plate() {
fc_x = -50; // offset toward front from deck centre
fc_y = 0;
plate_z = DECK_THICKNESS + FC_STANDOFF_H;
translate([fc_x, fc_y, 0]) {
difference() {
union() {
// Mount pads with standoffs
for (x = [-FC_MOUNT_SPACING/2, FC_MOUNT_SPACING/2])
for (y = [-FC_MOUNT_SPACING/2, FC_MOUNT_SPACING/2]) {
translate([x, y, DECK_THICKNESS])
cylinder(d=8, h=FC_STANDOFF_H); // standoff column
translate([x - 5, y - 5, DECK_THICKNESS])
cube([10, 10, FC_PAD_T]); // pad base
}
// Base plate
translate([-FC_MOUNT_SPACING/2 - 8, -FC_MOUNT_SPACING/2 - 8, DECK_THICKNESS])
cube([FC_MOUNT_SPACING + 16, FC_MOUNT_SPACING + 16, FC_PAD_T]);
}
// M3 through-holes in standoffs
fc_mount_holes(z_offset=DECK_THICKNESS - 1, depth=FC_STANDOFF_H + FC_PAD_T + 2);
// Deck anchor holes
fc_mount_holes(z_offset=-1, depth=DECK_THICKNESS + 2);
}
}
}
// Jetson Nano B01 mount plate
// Positioned rear of deck, elevated on standoffs
module jetson_mount_plate() {
jet_x = 60; // offset toward rear
jet_y = 0;
plate_z = DECK_THICKNESS + JETSON_STANDOFF_H;
translate([jet_x, jet_y, 0]) {
difference() {
union() {
// Mounting plate
translate([-JETSON_PLATE_L/2, -JETSON_PLATE_W/2, DECK_THICKNESS + JETSON_STANDOFF_H])
cube([JETSON_PLATE_L, JETSON_PLATE_W, JETSON_PLATE_T]);
// Four standoff columns
for (x = [-JETSON_HOLE_PITCH/2, JETSON_HOLE_PITCH/2])
for (y = [-JETSON_HOLE_PITCH/2, JETSON_HOLE_PITCH/2])
translate([x, y, DECK_THICKNESS])
cylinder(d=6, h=JETSON_STANDOFF_H);
}
// B01 M3 hole pattern (58×58 mm)
for (x = [-JETSON_HOLE_PITCH/2, JETSON_HOLE_PITCH/2])
for (y = [-JETSON_HOLE_PITCH/2, JETSON_HOLE_PITCH/2])
translate([x, y, DECK_THICKNESS - 1])
cylinder(d=JETSON_HOLE_D, h=JETSON_STANDOFF_H + JETSON_PLATE_T + 2);
// Ventilation / cable routing slots in plate
for (i = [-1, 0, 1])
translate([i*25 - 10, -JETSON_PLATE_W/2 + 10,
DECK_THICKNESS + JETSON_STANDOFF_H - 1])
cube([20, JETSON_PLATE_W - 20, JETSON_PLATE_T + 2]);
// Deck anchor holes (M3 × 4 corners of plate)
for (x = [-JETSON_PLATE_L/2 + 8, JETSON_PLATE_L/2 - 8])
for (y = [-JETSON_PLATE_W/2 + 8, JETSON_PLATE_W/2 - 8])
translate([x, y, -1])
cylinder(d=M3_D, h=DECK_THICKNESS + 2);
}
}
}
// Bumper bracket
// `front` = +1 (front) or -1 (rear)
module bumper_bracket(front = 1) {
y_pos = front * (FRAME_WIDTH/2);
translate([0, y_pos, 0]) {
difference() {
union() {
// Horizontal mounting rail plate
translate([-BUMPER_L/2, front*(BUMPER_T/2), 0])
cube([BUMPER_L, BUMPER_T, BUMPER_H]);
// Vertical gussets at ends
for (x = [-BUMPER_L/2 + 5, BUMPER_L/2 - BUMPER_T - 5])
translate([x, front*(BUMPER_T), 0])
cube([BUMPER_T, 20, BUMPER_H]);
// Bumper tube saddle clamps (×3 evenly spaced)
for (x = [-BUMPER_L/3, 0, BUMPER_L/3])
translate([x - BUMPER_TUBE_OD/2 - 2,
front*(BUMPER_T + 15),
BUMPER_H - BUMPER_TUBE_OD/2 - 5])
difference() {
cube([BUMPER_TUBE_OD + 4, BUMPER_TUBE_OD/2 + 4, BUMPER_TUBE_OD + 4]);
translate([BUMPER_TUBE_OD/2 + 2, -1, BUMPER_TUBE_OD/2 + 2])
rotate([-90, 0, 0])
cylinder(d=BUMPER_TUBE_OD, h=BUMPER_TUBE_OD/2 + 6);
}
}
// Deck mounting holes (M5 × 6 along length)
for (x = [-BUMPER_L/2 + 20, -BUMPER_L/6, BUMPER_L/6, BUMPER_L/2 - 20])
translate([x, y_pos * 0.001, -1])
cylinder(d=M5_D, h=BUMPER_H + 2);
}
}
}
// =============================================================================
// DIMENSIONS / ANNOTATIONS (2D cross-section reference)
// =============================================================================
// Uncomment to render a side-view dimension guide:
//
// %translate([0, FRAME_WIDTH/2 + 30, 0]) {
// color("Blue") {
// cube([WHEELBASE, 1, 1]); // wheelbase span
// translate([0,0,0]) text(str("Wheelbase: ", WHEELBASE, "mm"), size=12);
// }
// }