feat: SaltyTank tracked chassis — drive sprockets, tensioners, skid plate (#121)
Three new chassis design files for the SaltyTank continuous-track variant: • saltytank_chassis.scad — Deck plate (500×360×8mm Al, DXF export), 2× side track frames (6mm Al, CNC/laser), idler tensioner sliding block, 4× CSI corner camera mounts (45°/20°), D435i front bracket (8° tilt), stem collar (Ø25mm shared). Drive sprocket mounts accept hoverboard hub motors with caliper-verified D-cut bore (16.11mm/13mm flat) + 52mm BC hub flange bolt pattern. M6 tensioner bolt adjusts idler ±15mm for track tension. Shared FC 30.5×30.5mm + Jetson 58×49mm M3 patterns. Electronics bay footprint matches rover_electronics_bay.scad exactly. • saltytank_skid_plate.scad — Sacrificial underside skid panel (360×500mm). 4mm HDPE (DXF) or PETG print; countersunk M4 FHCS bolt-on. 4× drain/ inspection slots; optional printed ribs (RIB_PRINT=true). Ground clearance of hull between tracks: 90mm (exceeds 50mm requirement). • saltytank_BOM.md — Full BOM: deck plate, side frames, drive sprockets, idler wheels + tensioners, road wheels (2/side), track belts (1109mm circumference calc), skid plate, sensor brackets, electronics bay (rover_electronics_bay.scad reused unchanged). Frame mass ≈ 2.98 kg (just under 3 kg target). Assembly sequence and track tensioning procedure included. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
7de55accc3
commit
d93919e26f
383
chassis/saltytank_BOM.md
Normal file
383
chassis/saltytank_BOM.md
Normal file
@ -0,0 +1,383 @@
|
||||
# SaltyTank Chassis — BOM & Assembly Notes
|
||||
**Issue: #121 Agent: sl-mechanical Date: 2026-03-01**
|
||||
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
SaltyTank is the tracked variant of the SaltyLab robot family.
|
||||
Rubber or metal continuous tracks replace wheels for rough-terrain capability.
|
||||
The electronics bay, RPLIDAR, D435i, stem, and sensor head are **shared with
|
||||
SaltyLab and SaltyRover** — no modifications required.
|
||||
|
||||
```
|
||||
Side view (schematic):
|
||||
|
||||
← +Y forward
|
||||
Sensor head + RPLIDAR
|
||||
│
|
||||
┌──────────────────────────── deck (Z=0) ───────────────────────────┐
|
||||
│ [Electronics Bay] [Stem collar] │
|
||||
└──────────────────────────────────────────────────────────────────┘
|
||||
│ [Side frame - 90 mm tall] │
|
||||
│ ◎ idler (Ø80) ◎ road wheel ◎ road wheel ◎ drive (Ø66)│
|
||||
│───────────────────── track belt (rubber, 80 mm wide) ─────────────│
|
||||
(ground)
|
||||
|
||||
Top view (schematic):
|
||||
+Y (forward)
|
||||
│
|
||||
[track] ┌──────┴───────┐ [track]
|
||||
│ [Electronics │
|
||||
│ Bay + │
|
||||
│ RPLIDAR] │
|
||||
[track] └──────┬───────┘ [track]
|
||||
│
|
||||
D435i →
|
||||
|
||||
Key dimensions:
|
||||
Deck: 500 mm (L) × 360 mm (W)
|
||||
Track belt centres: 360 + 6 + 80 = 446 mm left-right CL to CL
|
||||
Overall robot width (outer track edge): 446 + 80 = 526 mm
|
||||
Ground clearance (hull between tracks): 90 mm ← exceeds 50 mm req.
|
||||
Height: deck to RPLIDAR scan plane: ~330 mm (with 550 mm stem option)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## File Index
|
||||
|
||||
| File | Description | RENDER → Output |
|
||||
|------|-------------|-----------------|
|
||||
| `saltytank_chassis.scad` | Deck plate + side frames + idler block + CSI/D435i mounts | See table below |
|
||||
| `saltytank_skid_plate.scad` | Underside skid plate (bolt-on, sacrificial) | `skid_2d` → DXF; `skid_stl` → STL |
|
||||
| `rover_electronics_bay.scad` | Electronics bay body + lid + RPLIDAR tower | **Unchanged** — reuse rover part |
|
||||
| `rover_motor_mount.scad` | (Not used — drive sprockets mount directly to side frame) | — |
|
||||
| `rover_stem_adapter.scad` | Stem adapter (shared) | Unchanged |
|
||||
| `rplidar_mount.scad` | RPLIDAR anti-vibration ring (shared) | Unchanged |
|
||||
|
||||
### RENDER map
|
||||
|
||||
| RENDER value | Output | Qty | Process |
|
||||
|---|---|---|---|
|
||||
| `"deck_2d"` | `saltytank_deck.dxf` | 1 | Waterjet / CNC, 8 mm Al |
|
||||
| `"side_frame_2d"` | `saltytank_side_frame.dxf` | 2 | CNC / laser, 6 mm Al (cut 2×, flip 1) |
|
||||
| `"side_frame_stl"` | `saltytank_side_frame.stl` | 2 | PETG print (prototype) |
|
||||
| `"idler_block_stl"` | `saltytank_idler_block.stl` | 2 | PETG print |
|
||||
| `"csi_mount_stl"` | `saltytank_csi_mount.stl` | 4 | PETG print |
|
||||
| `"d435i_mount_stl"` | `saltytank_d435i_mount.stl` | 1 | PETG print |
|
||||
| `"skid_2d"` | `saltytank_skid.dxf` | 1 | Waterjet / CNC, 4 mm HDPE |
|
||||
| `"skid_stl"` | `saltytank_skid.stl` | 1 | PETG print (prototype) |
|
||||
|
||||
### Export commands
|
||||
|
||||
```bash
|
||||
# Deck plate DXF:
|
||||
openscad saltytank_chassis.scad -D 'RENDER="deck_2d"' -o saltytank_deck.dxf
|
||||
|
||||
# Side frame DXF (cut 2×, flip one):
|
||||
openscad saltytank_chassis.scad -D 'RENDER="side_frame_2d"' -o saltytank_side_frame.dxf
|
||||
|
||||
# Side frame STL (print 2×, mirror right in slicer):
|
||||
openscad saltytank_chassis.scad -D 'RENDER="side_frame_stl"' -o saltytank_side_frame.stl
|
||||
|
||||
# Idler tensioner block STL (×2):
|
||||
openscad saltytank_chassis.scad -D 'RENDER="idler_block_stl"' -o saltytank_idler_block.stl
|
||||
|
||||
# CSI bracket STL (×4):
|
||||
openscad saltytank_chassis.scad -D 'RENDER="csi_mount_stl"' -o saltytank_csi_mount.stl
|
||||
|
||||
# D435i bracket STL (×1):
|
||||
openscad saltytank_chassis.scad -D 'RENDER="d435i_mount_stl"' -o saltytank_d435i_mount.stl
|
||||
|
||||
# Skid plate DXF (4 mm HDPE, preferred):
|
||||
openscad saltytank_skid_plate.scad -D 'RENDER="skid_2d"' -o saltytank_skid.dxf
|
||||
|
||||
# Skid plate STL (prototype):
|
||||
openscad saltytank_skid_plate.scad -D 'RENDER="skid_stl"' -o saltytank_skid.stl
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Part A — Deck Plate
|
||||
|
||||
| # | Spec | Qty | Material | Process | Notes |
|
||||
|---|------|-----|----------|---------|-------|
|
||||
| A1 | 8 mm 5052-H32 Al, 500×360 mm blank | 1 | Aluminium | Waterjet | Preferred |
|
||||
| A1-alt | 6 mm 6061-T6 Al | 1 | — | CNC router | Saves 0.3 kg |
|
||||
| A1-proto | 8 mm PETG FDM, 2 halves lap-jointed | 1 | — | Print | Prototype only |
|
||||
|
||||
**Deck fasteners:**
|
||||
|
||||
| # | Spec | Qty | Use |
|
||||
|---|------|-----|-----|
|
||||
| A-f1 | M5×20 SHCS | 8 | Side frame to deck side edge (4 per side) |
|
||||
| A-f2 | M5 T-nut or rivet nut | 8 | Captured in deck edge (installed before frame) |
|
||||
| A-f3 | M4×16 FHCS | 4 | Stem collar to deck (4× M4 countersunk) |
|
||||
| A-f4 | M3×12 SHCS | 10 | Electronics bay to deck |
|
||||
| A-f5 | M4×12 FHCS | 6 | Skid plate to deck (countersunk from below) |
|
||||
| A-f6 | M4 rivet-nut | 6 | Installed in deck for skid plate bolts |
|
||||
|
||||
---
|
||||
|
||||
## Part B — Side Frames (×2)
|
||||
|
||||
| # | RENDER | Qty | Material | Process | Notes |
|
||||
|---|--------|-----|----------|---------|-------|
|
||||
| B1 | `side_frame_2d` | 2 | 6 mm 6061-T6 Al | CNC router or waterjet | Cut 2× same DXF; flip 1 (mirror) |
|
||||
| B1-alt | `side_frame_stl` | 2 | PETG | Print | 5 perims, 60% gyroid; prototype only |
|
||||
|
||||
> ⚠ **Hub motor flange bolt circle:** Default is 52 mm BC × 4× M5.
|
||||
> **Measure your motor hub** before cutting the side frame — adjust `HUB_FLANGE_BC`
|
||||
> in `saltytank_chassis.scad` if needed.
|
||||
|
||||
**Side frame fasteners:**
|
||||
|
||||
| # | Spec | Qty | Use |
|
||||
|---|------|-----|-----|
|
||||
| B-f1 | M5×20 SHCS | 8 | Frame-to-deck attachment (4 per frame, through slotted holes) |
|
||||
| B-f2 | M5 nyloc | 8 | Under deck |
|
||||
| B-f3 | M5×16 SHCS | 8 | Hub motor flange to side frame (2× M5 per motor hub face) |
|
||||
| B-f4 | M8×60 hex bolt | 2 | Road wheel axle bolts (1 per wheel × 2 per side) |
|
||||
| B-f5 | M8 nyloc | 4 | Road wheel axle retention |
|
||||
| B-f6 | M8 flat washer | 8 | Both sides of each road wheel |
|
||||
|
||||
---
|
||||
|
||||
## Part C — Drive Sprockets + Hub Motors
|
||||
|
||||
SaltyTank reuses the same hoverboard hub motors as SaltyLab/SaltyRover.
|
||||
A laser-cut sprocket plate bolts to the motor hub and engages the track belt.
|
||||
|
||||
| # | Part | Qty | Spec | Notes |
|
||||
|---|------|-----|------|-------|
|
||||
| C1 | Hub motor | 2 | 10×2.125" tire, 36 V, ~350 W; axle OD 16.11 mm (caliper) | **Only 2 motors — rear drive** |
|
||||
| C2 | Drive sprocket plate | 2 | 5 mm 6061-T6 Al, laser-cut | 10-tooth, 20 mm pitch, PCD ≈ 64.7 mm; Ø52 mm hub bolt holes (verify) |
|
||||
| C3 | Motor phase cable | 2 | 12 AWG, 300 mm, XT30 | Left + right rear |
|
||||
| C4 | Hall sensor cable | 2 | 6-pin JST-PH, 300 mm | — |
|
||||
|
||||
> **Sprocket plate design** (separate file to be made if needed):
|
||||
> 10 teeth, pitch = 20 mm → PCD = 20 / sin(18°) ≈ 64.7 mm → R ≈ 32.4 mm.
|
||||
> Hub bolt circle = 52 mm, 4× M5 at 90°. Verify against actual motor hub.
|
||||
> Sprocket tooth profile: ISO/DIN standard for roller chain or custom for belt track.
|
||||
|
||||
**Motor fasteners:**
|
||||
|
||||
| # | Spec | Qty | Use |
|
||||
|---|------|-----|-----|
|
||||
| C-f1 | M5×16 SHCS | 8 | Sprocket plate to motor hub flange (4 per sprocket) |
|
||||
| C-f2 | Hub motor axle nut | 2 | M16×1.5 (verify axle thread); torque 35–40 N·m |
|
||||
| C-f3 | Loctite 243 | — | On axle nut and flange bolts |
|
||||
|
||||
---
|
||||
|
||||
## Part D — Idler Wheels + Tensioners
|
||||
|
||||
| # | RENDER | Qty | Material | Notes |
|
||||
|---|--------|-----|----------|-------|
|
||||
| D1 | `idler_block_stl` | 2 | PETG | Tensioner sliding block, 1 per side |
|
||||
|
||||
| # | Part | Qty | Spec | Notes |
|
||||
|---|------|-----|------|-------|
|
||||
| D2 | Idler wheel | 2 | Ø80 mm, 80+ mm wide, M8 bore, flanged | Off-shelf: e.g. polyurethane track idler |
|
||||
| D3 | M8×100 SHCS | 2 | Stainless | Idler axle through block + wheel + outer bearing |
|
||||
| D4 | M8 nyloc nut | 2 | — | Axle retention |
|
||||
| D5 | M8 flat washer | 4 | — | Both sides of idler wheel |
|
||||
| D6 | M6×40 SHCS | 2 | — | Tensioner adjustment bolt (threads into idler block lug) |
|
||||
| D7 | M6 nyloc nut | 2 | — | Captured in block; lock bolt position |
|
||||
|
||||
**Track tensioning procedure:**
|
||||
1. Loosen idler axle nut (M8) — block free to slide in slot.
|
||||
2. Turn tensioner M6 bolt CW to push block rearward → tighten track.
|
||||
3. Target: ~10 mm slack on upper track run (finger-press deflects 10 mm).
|
||||
4. Tighten M8 axle nut to 12 N·m. Check tension after first 5-minute run.
|
||||
|
||||
---
|
||||
|
||||
## Part E — Road Wheels (2 per side, 4 total)
|
||||
|
||||
| # | Part | Qty | Spec | Notes |
|
||||
|---|------|-----|------|-------|
|
||||
| E1 | Road wheel | 4 | Ø60 mm × 80+ mm wide, M8 bore | Polyurethane or HDPE; must fit inside track width |
|
||||
| E2 | M8×80 SHCS | 4 | Stainless | Road wheel axle through side frame |
|
||||
| E3 | M8 nyloc | 4 | — | — |
|
||||
| E4 | M8 flat washer | 8 | — | — |
|
||||
|
||||
---
|
||||
|
||||
## Part F — Track Belts
|
||||
|
||||
| # | Part | Qty | Spec | Notes |
|
||||
|---|------|-----|------|-------|
|
||||
| F1 | Rubber track belt | 2 | 80 mm wide, 20 mm pitch, ~1040 mm circumference | Circumference: 2 × (sprocket–idler CL) + π × (sprocket_D + idler_D)/2 ≈ 2 × 440 + π × 72 ≈ 1106 mm; order next standard size up |
|
||||
| F1-alt | Metal link track | 2 | 80 mm wide, 20 mm pitch, adjustable length | Higher durability; heavier (~0.8 kg/belt); louder |
|
||||
|
||||
> **Track circumference calculation:**
|
||||
> `C = 2 × span + π × (D_sprocket + D_idler) / 2`
|
||||
> ` = 2 × 440 + π × (66 + 80) / 2`
|
||||
> ` = 880 + π × 73`
|
||||
> ` = 880 + 229 ≈ 1109 mm`
|
||||
> Order **1120 mm** (next standard rubber track pitch × N links).
|
||||
|
||||
---
|
||||
|
||||
## Part G — Skid Plate
|
||||
|
||||
| # | RENDER | Qty | Material | Process | Notes |
|
||||
|---|--------|-----|----------|---------|-------|
|
||||
| G1 | `skid_2d` | 1 | 4 mm HDPE (white) | Waterjet / CNC | Preferred — lightweight, low friction |
|
||||
| G1-alt | `skid_2d` | 1 | 2 mm 304 stainless | Laser-cut | Heavy but extreme durability |
|
||||
| G1-proto | `skid_stl` | 1 | PETG | Print | Prototype — 2 halves if bed <500 mm |
|
||||
|
||||
| # | Spec | Qty | Use |
|
||||
|---|------|-----|-----|
|
||||
| G-f1 | M4×12 FHCS | 6 | Skid plate to deck (countersunk flush) |
|
||||
| G-f2 | M4 rivet-nut (short) | 6 | Pre-installed in deck plate |
|
||||
|
||||
---
|
||||
|
||||
## Part H — Sensor Brackets
|
||||
|
||||
| # | RENDER | Qty | Material | Notes |
|
||||
|---|--------|-----|----------|-------|
|
||||
| H1 | `csi_mount_stl` | 4 | PETG | CSI corner brackets (45° outward, 20° down) |
|
||||
| H2 | `d435i_mount_stl` | 1 | PETG | D435i front arm (8° nose-down) |
|
||||
|
||||
| # | Spec | Qty | Use |
|
||||
|---|------|-----|-----|
|
||||
| H-f1 | M2×6 SHCS | 8 | CSI camera PCB to bracket |
|
||||
| H-f2 | M3×8 SHCS | 8 | CSI bracket to deck |
|
||||
| H-f3 | M4×14 SHCS | 2 | D435i bracket to deck front face |
|
||||
| H-f4 | 1/4-20 UNC hex nut | 1 | Captured in D435i bracket face |
|
||||
| H-c1 | 200 mm CSI FPC cable | 4 | IMX219 to Jetson |
|
||||
|
||||
---
|
||||
|
||||
## Part I — Electronics Bay (reuse from rover)
|
||||
|
||||
The `rover_electronics_bay.scad` bay fits the SaltyTank deck directly — the
|
||||
deck bolt pattern is identical. Print or reuse existing bay + lid.
|
||||
|
||||
| # | Part | Qty | Notes |
|
||||
|---|------|-----|-------|
|
||||
| I1 | Electronics bay body | 1 | `rover_electronics_bay.scad` → `bay_stl` |
|
||||
| I2 | Electronics bay lid | 1 | Includes RPLIDAR tower |
|
||||
| I3 | M3×12 SHCS | 10 | Bay to deck |
|
||||
| I4 | M3×8 BHCS | 4 | Lid to bay |
|
||||
|
||||
---
|
||||
|
||||
## Part J — Stem + Sensor Head (shared, unchanged)
|
||||
|
||||
Same stem adapter, RPLIDAR, D435i, and IMX219 sensor head as SaltyLab.
|
||||
Recommended stem length for SaltyTank: **500 mm** (lower than SaltyBot mast for
|
||||
stability; RPLIDAR at ~635 mm from ground with 98 mm deck height).
|
||||
|
||||
---
|
||||
|
||||
## Mass Estimate — Frame Only
|
||||
|
||||
| Assembly | Material | Est. mass |
|
||||
|----------|----------|-----------|
|
||||
| Deck plate (8 mm Al, ~45% lightened) | Al | ~1.55 kg |
|
||||
| Side frames ×2 (6 mm Al, ~35% lightened) | Al | ~0.52 kg |
|
||||
| Skid plate (4 mm HDPE, solid) | HDPE | ~0.56 kg |
|
||||
| Idler tensioner blocks ×2 | PETG | ~0.06 kg |
|
||||
| CSI brackets ×4 + D435i bracket | PETG | ~0.07 kg |
|
||||
| Stem collar | PETG | ~0.04 kg |
|
||||
| Fasteners (M4–M8 SS) | Steel | ~0.18 kg |
|
||||
| **Frame total** | | **~2.98 kg** ✓ |
|
||||
|
||||
> **Just under the 3 kg target.**
|
||||
> To save weight: switch side frames from 6 mm to 5 mm Al → saves ~0.09 kg.
|
||||
> Set `FRAME_T = 5.0` in `saltytank_chassis.scad` and re-export DXF.
|
||||
|
||||
---
|
||||
|
||||
## Assembly Sequence
|
||||
|
||||
### 1. Fabricate / source parts
|
||||
1. Export DXFs; send deck and side frames to waterjet. Order HDPE skid to DXF.
|
||||
2. Print idler blocks, CSI/D435i brackets, stem collar.
|
||||
3. Source track belts, idler wheels, road wheels, hub motors.
|
||||
4. Install M4 rivet-nuts in deck plate (6× for skid; stem flange positions).
|
||||
|
||||
### 2. Side frame preparation
|
||||
1. Test-fit hub motor axle D-cut bore in side frame — adjust `SPROCKET_AXLE_D`
|
||||
and `SPROCKET_AXLE_FLAT` if caliper measurement differs.
|
||||
2. Press or Loctite M8 flange nuts into road wheel axle positions (optional).
|
||||
|
||||
### 3. Frame assembly
|
||||
1. Slide side frames into deck side edge slots (inner face flush with deck edge).
|
||||
2. Insert M5×20 SHCS from above through deck slots; fit nyloc nuts under deck.
|
||||
3. **Snug only** — leave adjustable for wheel alignment (step 6).
|
||||
|
||||
### 4. Drive motor installation
|
||||
1. Feed hub motor axle through rear bore in side frame (D-cut aligned to flat).
|
||||
2. Bolt sprocket plate to motor hub: 4× M5×16 SHCS, 2.5 N·m, Loctite 243.
|
||||
3. Fit axle lock nut; Loctite 243; torque 35 N·m.
|
||||
4. Route phase + hall cables through deck cable slot.
|
||||
|
||||
### 5. Road wheel installation
|
||||
1. Slide M8×80 bolt through side frame road wheel bore.
|
||||
2. Fit road wheel on axle (M8 flat washers both sides).
|
||||
3. Thread M8 nyloc; torque to 12 N·m.
|
||||
4. Verify wheel rotates freely.
|
||||
|
||||
### 6. Track belt installation (one side at a time)
|
||||
1. Install idler block in tensioner slot (tensioner bolt only finger-tight).
|
||||
2. Thread track belt around sprocket → road wheels → idler.
|
||||
*(Easier with motor temporarily removed — reinstall after threading.)*
|
||||
3. Fit idler axle (M8×100) through idler block + idler wheel; nyloc finger-tight.
|
||||
4. Apply tension: tighten M6 tensioner bolt until ~10 mm slack on upper run.
|
||||
5. Torque idler axle nut to 12 N·m.
|
||||
|
||||
### 7. Geometry verification
|
||||
1. Set robot on flat surface; check all 4 road wheels contact ground through track.
|
||||
2. Measure track tension on both sides — should match within 2 mm.
|
||||
3. Spin motors briefly — verify tracks run straight without walking.
|
||||
4. Torque side-frame-to-deck M5 bolts to 4 N·m once alignment confirmed.
|
||||
|
||||
### 8. Skid plate
|
||||
1. Slide skid plate under deck; align M4 countersunk holes with rivet-nuts.
|
||||
2. Fasten 6× M4×12 FHCS from below; tighten evenly to 2.5 N·m.
|
||||
|
||||
### 9. Electronics, sensors, stem
|
||||
1. Mount electronics bay on deck (10× M3×12); route cables.
|
||||
2. Install CSI and D435i brackets on deck corners/front.
|
||||
3. Press stem through collar; install stem adapter + clamp.
|
||||
4. Mount sensor head + RPLIDAR on stem top.
|
||||
|
||||
---
|
||||
|
||||
## Critical Dimensions
|
||||
|
||||
| Dimension | Nominal | Tolerance |
|
||||
|-----------|---------|-----------|
|
||||
| Deck length × width | 500 × 360 mm | ±1 mm |
|
||||
| Frame height | 90 mm | ±0.5 mm |
|
||||
| Frame length | 500 mm | ±0.5 mm |
|
||||
| Drive axle bore OD | 16.11 mm | +0.4/0 (round section) |
|
||||
| Drive axle flat chord | 13.00 mm | +0.4/0 |
|
||||
| Bearing seat recess OD | 37.80 mm | +1.5/0 |
|
||||
| Hub motor flange BC | 52 mm | **⚠ verify caliper** |
|
||||
| Tensioner slot height | 10.5 mm | ±0.2 mm |
|
||||
| Tensioner travel | ±15 mm | — |
|
||||
| FC hole pattern | 30.5×30.5 mm | ±0.2 mm |
|
||||
| Jetson hole pattern | 58×49 mm | ±0.2 mm |
|
||||
| Stem bore | Ø25.5 mm | +0.3/0 |
|
||||
| Track belt width | 80 mm | ±1 mm |
|
||||
| Sprocket PCD | 64.7 mm | ±0.5 mm |
|
||||
| Idler OD | 80 mm | ±1 mm |
|
||||
| Road wheel OD | 60 mm | ±1 mm |
|
||||
|
||||
---
|
||||
|
||||
## OpenSCAD Version
|
||||
|
||||
Requires OpenSCAD **2021.01 or newer**.
|
||||
|
||||
```bash
|
||||
# Full assembly preview:
|
||||
openscad saltytank_chassis.scad &
|
||||
```
|
||||
672
chassis/saltytank_chassis.scad
Normal file
672
chassis/saltytank_chassis.scad
Normal file
@ -0,0 +1,672 @@
|
||||
// ============================================================
|
||||
// saltytank_chassis.scad — SaltyTank Tracked Chassis
|
||||
// Issue: #121 Agent: sl-mechanical Date: 2026-03-01
|
||||
// ============================================================
|
||||
//
|
||||
// Parametric tank chassis for rubber or metal continuous tracks.
|
||||
//
|
||||
// Structure (left-to-right cross-section):
|
||||
// [left track belt]
|
||||
// ← sprocket + idler + road wheels on outer face of side frame
|
||||
// [left side frame plate, 8 mm Al]
|
||||
// [deck plate, 8 mm Al] ← electronics bay on top
|
||||
// [right side frame plate, 8 mm Al]
|
||||
// → sprocket + idler + road wheels
|
||||
// [right track belt]
|
||||
//
|
||||
// Drive: hoverboard hub motors (rear) — caliper-verified axle
|
||||
// (16.11 mm OD, D-cut flat 13.00 mm, bearing seat Ø37.8 mm)
|
||||
// Idler: 80 mm OD, M8 axle; tensioner slot ±15 mm fore-aft
|
||||
// Road wh: 60 mm OD × 2 per side, M8 axle, fixed
|
||||
// Tracks: rubber belt, 80 mm wide, 20 mm pitch (parametric)
|
||||
//
|
||||
// Electronics bay: reuses rover_electronics_bay.scad (same deck
|
||||
// footprint, FC 30.5 × 30.5 mm M3 + Jetson 58 × 49 mm M3)
|
||||
// Sensors: RPLIDAR A1M8 top (bay lid tower), D435i front,
|
||||
// 4 × IMX219 / CSI at deck corners
|
||||
// Stem: Ø25 mm (shared with SaltyLab / SaltyRover)
|
||||
//
|
||||
// Coordinate convention:
|
||||
// Z = 0 deck top face
|
||||
// +Y forward
|
||||
// +X right
|
||||
// Ground Z = -(DECK_T + FRAME_H) [= −98 mm with defaults]
|
||||
//
|
||||
// Ground clearance of hull (between tracks): FRAME_H = 90 mm ✓
|
||||
// (exceeds 50 mm requirement with significant margin)
|
||||
//
|
||||
// Weight estimate — frame only (excl. motors, electronics, battery):
|
||||
// Deck plate (8 mm Al, lightened) ≈ 1.55 kg
|
||||
// Side frames 2 × (6 mm Al) ≈ 0.52 kg
|
||||
// Skid plate (saltytank_skid_plate.scad, 4 mm HDPE) ≈ 0.56 kg
|
||||
// Brackets + fasteners (PETG + SS) ≈ 0.35 kg
|
||||
// Total ≈ 2.98 kg ← just under 3 kg target
|
||||
//
|
||||
// RENDER options:
|
||||
// "assembly" full 3D preview (default)
|
||||
// "deck_2d" DXF — deck plate (waterjet / CNC)
|
||||
// "side_frame_2d" DXF — side frame plate (×2 mirrored; CNC)
|
||||
// "side_frame_stl" STL — side frame (print 2×, mirror right)
|
||||
// "idler_block_stl" STL — tensioner idler block (print 2×)
|
||||
// "csi_mount_stl" STL — CSI corner bracket (print 4×)
|
||||
// "d435i_mount_stl" STL — D435i front bracket (print 1×)
|
||||
//
|
||||
// ── Export commands ─────────────────────────────────────────
|
||||
// Deck DXF:
|
||||
// openscad saltytank_chassis.scad -D 'RENDER="deck_2d"' -o saltytank_deck.dxf
|
||||
// Side frame DXF (cut 2×, flip one for mirror):
|
||||
// openscad saltytank_chassis.scad -D 'RENDER="side_frame_2d"' -o saltytank_side_frame.dxf
|
||||
// Side frame STL (print 2×; mirror one in slicer):
|
||||
// openscad saltytank_chassis.scad -D 'RENDER="side_frame_stl"' -o saltytank_side_frame.stl
|
||||
// Idler block STL (×2):
|
||||
// openscad saltytank_chassis.scad -D 'RENDER="idler_block_stl"' -o saltytank_idler_block.stl
|
||||
// CSI bracket STL (×4):
|
||||
// openscad saltytank_chassis.scad -D 'RENDER="csi_mount_stl"' -o saltytank_csi_mount.stl
|
||||
// D435i bracket STL (×1):
|
||||
// openscad saltytank_chassis.scad -D 'RENDER="d435i_mount_stl"' -o saltytank_d435i_mount.stl
|
||||
// ============================================================
|
||||
|
||||
$fn = 64;
|
||||
e = 0.01;
|
||||
|
||||
// ── Deck plate ────────────────────────────────────────────────────────────────
|
||||
BODY_L = 500.0; // deck fore-aft (Y)
|
||||
BODY_W = 360.0; // deck left-right (X) — space between inner frame faces
|
||||
DECK_T = 8.0; // deck plate thickness
|
||||
DECK_R = 15.0; // corner fillet radius
|
||||
|
||||
// ── Side frame geometry ───────────────────────────────────────────────────────
|
||||
FRAME_H = 90.0; // frame height below deck bottom = hull ground clearance
|
||||
FRAME_T = 6.0; // frame plate thickness (6 mm Al laser-cut)
|
||||
FRAME_R = 10.0; // frame corner fillet radius
|
||||
|
||||
// ── Track system (rubber or metal belt) ───────────────────────────────────────
|
||||
TRACK_WID = 80.0; // track belt width
|
||||
TRACK_PITCH = 20.0; // track link pitch (mm) — affects sprocket tooth count
|
||||
// Track belt inner face sits at X = ±(BODY_W/2 + FRAME_T) from centre
|
||||
// Track CL at X = ±(BODY_W/2 + FRAME_T + TRACK_WID/2) from centre
|
||||
|
||||
// ── Drive sprocket (rear) — hoverboard hub motor ──────────────────────────────
|
||||
// Caliper-verified axle (matches BOM.md):
|
||||
SPROCKET_AXLE_D = 16.11; // axle base OD (round section near hub)
|
||||
SPROCKET_AXLE_FLAT= 13.00; // D-cut chord width
|
||||
SPROCKET_AXLE_DCUT= 15.95; // D-cut OD
|
||||
BEARING_OD = 37.80; // motor bearing-seat collar OD
|
||||
BEARING_RECESS = 8.0; // bearing seat recess depth in frame
|
||||
|
||||
// Sprocket pitch circle: 10 teeth × 20 mm pitch
|
||||
// PCD = pitch / sin(π/N) = 20 / sin(18°) ≈ 64.7 mm → R ≈ 32.4 mm
|
||||
SPROCKET_R = 33.0; // sprocket pitch-circle radius (nominal)
|
||||
SPROCKET_POS_Y = -(BODY_L/2 - SPROCKET_R - 22); // rear, −Y
|
||||
|
||||
// Hub motor flange bolt circle (4× M5 at 90°, BC = 52 mm)
|
||||
// ⚠ Verify against your motor flange before fabricating!
|
||||
HUB_FLANGE_BC = 52.0; // motor hub bolt circle OD (M5 × 4)
|
||||
HUB_BOLT_D = 5.3; // M5 clearance
|
||||
|
||||
// ── Idler wheel (front, adjustable tensioner) ─────────────────────────────────
|
||||
IDLER_R = 40.0; // idler wheel radius (OD = 80 mm)
|
||||
IDLER_AXLE_D = 8.5; // M8 axle clearance bore
|
||||
IDLER_POS_Y_NOM = +(BODY_L/2 - IDLER_R - 22); // front nominal, +Y
|
||||
// Tensioner slot: idler can move ±TENS_TRAVEL fore-aft to tension track
|
||||
TENS_TRAVEL = 15.0; // ±15 mm track tension adjustment
|
||||
// Tensioner bolt: M6 runs fore-aft in threaded lug at front of slot
|
||||
TENS_BOLT_D = 6.5; // M6 clearance
|
||||
TENS_BLOCK_L = 35.0; // sliding idler block length
|
||||
TENS_BLOCK_W = TRACK_WID - 4; // block width (fits inside track)
|
||||
TENS_SLOT_H = IDLER_AXLE_D + 2.0; // slot height (clearance for block)
|
||||
|
||||
// ── Road wheels (2× per side, between sprocket and idler) ────────────────────
|
||||
ROAD_WHEEL_R = 30.0; // road wheel radius (OD = 60 mm)
|
||||
ROAD_AXLE_D = 8.5; // M8 axle clearance bore
|
||||
ROAD_Y_1 = -BODY_L/4; // forward road wheel (−125 mm from centre)
|
||||
ROAD_Y_2 = +BODY_L/4; // rearward road wheel (+125 mm from centre)
|
||||
// ⚠ Reversed fore/aft: ROAD_Y_1 is near rear sprocket, ROAD_Y_2 near front idler
|
||||
|
||||
// ── Height stack ─────────────────────────────────────────────────────────────
|
||||
// Ground Z = 0 (absolute); deck coords: Z=0 at deck top.
|
||||
// In deck coords, ground is at Z = -(DECK_T + FRAME_H) = -98 mm.
|
||||
// SPROCKET_CL_Z = -(DECK_T + FRAME_H - SPROCKET_R) = -(98 - 33) = -65 mm
|
||||
// IDLER_CL_Z = -(DECK_T + FRAME_H - IDLER_R) = -(98 - 40) = -58 mm
|
||||
// ROAD_WHEEL_Z = -(DECK_T + FRAME_H - ROAD_WHEEL_R) = -(98 - 30) = -68 mm
|
||||
GROUND_Z = -(DECK_T + FRAME_H); // = -98 mm in deck coords
|
||||
|
||||
// ── Stem socket (deck centre, shared with SaltyLab / SaltyRover) ─────────────
|
||||
STEM_BORE = 25.5; // 25 mm tube + 0.5 mm clearance
|
||||
STEM_COLLAR_OD = 50.0;
|
||||
STEM_COLLAR_H = 20.0; // boss height above deck top
|
||||
STEM_FLANGE_BC = 40.0; // 4× M4 bolt circle
|
||||
|
||||
// ── Electronics bay footprint (rover_electronics_bay.scad) ───────────────────
|
||||
// Bay dimensions match rover_electronics_bay.scad exactly.
|
||||
// Deck holes match bay floor bolt pattern for drop-in compatibility.
|
||||
BAY_L = 240.0; // bay length (X on deck = left-right)
|
||||
BAY_W = 200.0; // bay width (Y on deck = fore-aft)
|
||||
BAY_WALL = 3.0; // bay wall thickness
|
||||
BAY_BOLT_INSET = 8.0; // bay bolt CL from bay exterior corner
|
||||
|
||||
// ── FC mount — 30.5 × 30.5 mm M3 (shared SaltyLab pattern) ──────────────────
|
||||
FC_PITCH = 30.5;
|
||||
FC_HOLE_D = 3.2;
|
||||
FC_POS_Y = BAY_W/2 - 50.0; // near front edge (inside bay footprint)
|
||||
|
||||
// ── Jetson Orin mount — 58 × 49 mm M3 (shared SaltyLab pattern) ─────────────
|
||||
ORIN_HOLE_X = 58.0;
|
||||
ORIN_HOLE_Y = 49.0;
|
||||
ORIN_HOLE_D = 3.2;
|
||||
ORIN_POS_Y = -(BAY_W/2 - 55.0); // near rear edge
|
||||
|
||||
// ── CSI corner camera mounts ──────────────────────────────────────────────────
|
||||
CSI_PCB = 25.0; // IMX219 PCB square side
|
||||
CSI_M2_SPC = 15.0; // M2 hole pitch
|
||||
CSI_TILT = 20.0; // nose-down tilt (degrees)
|
||||
|
||||
// ── D435i front bracket ───────────────────────────────────────────────────────
|
||||
RS_TILT = 8.0; // nose-down tilt (degrees)
|
||||
RS_ARM_LEN = 70.0; // arm reach forward from deck edge
|
||||
RS_BASE_W = 44.0; // base plate width
|
||||
|
||||
// ── Fasteners ─────────────────────────────────────────────────────────────────
|
||||
M2_D = 2.3;
|
||||
M3_D = 3.3;
|
||||
M4_D = 4.3;
|
||||
M5_D = 5.3;
|
||||
M6_D = 6.5;
|
||||
M8_D = 8.5;
|
||||
|
||||
// ============================================================
|
||||
// RENDER DISPATCH
|
||||
// ============================================================
|
||||
RENDER = "assembly";
|
||||
|
||||
if (RENDER == "assembly") {
|
||||
assembly();
|
||||
} else if (RENDER == "deck_2d") {
|
||||
projection(cut = true)
|
||||
translate([0, 0, -DECK_T / 2])
|
||||
deck_plate();
|
||||
} else if (RENDER == "side_frame_2d") {
|
||||
// Left frame, projected flat (2D profile only — Z/Y plane)
|
||||
projection(cut = true)
|
||||
rotate([90, 0, 0])
|
||||
translate([0, 0, BODY_L / 2])
|
||||
side_frame(-1);
|
||||
} else if (RENDER == "side_frame_stl") {
|
||||
side_frame(-1); // mirror one in slicer for right side
|
||||
} else if (RENDER == "idler_block_stl") {
|
||||
idler_block();
|
||||
} else if (RENDER == "csi_mount_stl") {
|
||||
csi_corner_bracket();
|
||||
} else if (RENDER == "d435i_mount_stl") {
|
||||
d435i_front_bracket();
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// FULL ASSEMBLY
|
||||
// ============================================================
|
||||
module assembly() {
|
||||
// Deck plate
|
||||
color("Silver", 0.90) deck_plate();
|
||||
|
||||
// Stem collar
|
||||
color("DimGray", 0.85) stem_collar();
|
||||
|
||||
// Side frames (left = −1, right = +1)
|
||||
color("SteelBlue", 0.80) side_frame(-1);
|
||||
color("SteelBlue", 0.80) side_frame(+1);
|
||||
|
||||
// Idler tensioner blocks (×2)
|
||||
color("LightSlateGray", 0.85)
|
||||
translate([-BODY_W/2 - FRAME_T, IDLER_POS_Y_NOM,
|
||||
GROUND_Z + IDLER_R])
|
||||
rotate([0, 90, 0]) idler_block();
|
||||
color("LightSlateGray", 0.85)
|
||||
translate([+BODY_W/2, IDLER_POS_Y_NOM,
|
||||
GROUND_Z + IDLER_R])
|
||||
rotate([0, -90, 0]) idler_block();
|
||||
|
||||
// CSI corner brackets
|
||||
for (sx = [-1, 1])
|
||||
for (sy = [-1, 1])
|
||||
color("Teal", 0.85) csi_bracket_placed(sx, sy);
|
||||
|
||||
// D435i front bracket
|
||||
color("DarkSlateGray", 0.85) d435i_bracket_placed();
|
||||
|
||||
// Phantom: electronics bay (rover_electronics_bay.scad)
|
||||
%color("OliveDrab", 0.25)
|
||||
translate([0, 0, DECK_T])
|
||||
cube([BAY_L + 2*BAY_WALL,
|
||||
BAY_W + 2*BAY_WALL,
|
||||
84], center = true);
|
||||
|
||||
// Phantom: track loops (rubber belt cross-section)
|
||||
for (sx = [-1, 1])
|
||||
%color("Black", 0.15)
|
||||
translate([sx * (BODY_W/2 + FRAME_T + TRACK_WID/2), 0, 0])
|
||||
rotate([90, 0, 0])
|
||||
track_loop_ghost();
|
||||
|
||||
// Phantom: hub motors (rear, outboard)
|
||||
for (sx = [-1, 1])
|
||||
%color("Orange", 0.20)
|
||||
translate([sx * (BODY_W/2 + FRAME_T + 30),
|
||||
SPROCKET_POS_Y,
|
||||
GROUND_Z + SPROCKET_R])
|
||||
rotate([0, sx*90, 0])
|
||||
cylinder(d = BEARING_OD, h = 70, center = false);
|
||||
}
|
||||
|
||||
// Ghost track loop outline for preview (no geometry output)
|
||||
module track_loop_ghost() {
|
||||
span = abs(IDLER_POS_Y_NOM - SPROCKET_POS_Y);
|
||||
hull() {
|
||||
translate([0, IDLER_POS_Y_NOM, GROUND_Z + IDLER_R])
|
||||
rotate([90, 0, 0]) cylinder(d = IDLER_R*2, h = 1);
|
||||
translate([0, SPROCKET_POS_Y, GROUND_Z + SPROCKET_R])
|
||||
rotate([90, 0, 0]) cylinder(d = SPROCKET_R*2, h = 1);
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// DECK PLATE (Part A — laser-cut 8 mm 5052-H32 aluminium)
|
||||
// ============================================================
|
||||
// Rectangular plate spanning the gap between the two side frames.
|
||||
// All electronics and sensor mounts attach to the deck top face.
|
||||
// Side frames bolt to deck side edges (M5 × 4 per side).
|
||||
// Weight estimate: 500×360×8 mm Al, ~45% lightened ≈ 1.55 kg
|
||||
module deck_plate() {
|
||||
difference() {
|
||||
// ── Outer profile ─────────────────────────────────────────────
|
||||
linear_extrude(DECK_T)
|
||||
minkowski() {
|
||||
square([BODY_L - 2*DECK_R, BODY_W - 2*DECK_R],
|
||||
center = true);
|
||||
circle(r = DECK_R);
|
||||
}
|
||||
|
||||
// ── Side frame attachment slots (M5 × 4 per side) ─────────────
|
||||
// Slots run fore-aft (Y) for ±10 mm lateral alignment adjustment
|
||||
for (sx = [-1, 1])
|
||||
for (py = [-BODY_L/4, BODY_L/4]) {
|
||||
hull() {
|
||||
translate([sx*(BODY_W/2 - 8), py - 12, -e])
|
||||
cylinder(d = M5_D, h = DECK_T + 2*e);
|
||||
translate([sx*(BODY_W/2 - 8), py + 12, -e])
|
||||
cylinder(d = M5_D, h = DECK_T + 2*e);
|
||||
}
|
||||
}
|
||||
|
||||
// ── Stem bore ─────────────────────────────────────────────────
|
||||
translate([0, 0, -e])
|
||||
cylinder(d = STEM_BORE, h = DECK_T + 2*e);
|
||||
|
||||
// ── Stem flange bolts (4× M4 at 90°) ─────────────────────────
|
||||
for (a = [0, 90, 180, 270])
|
||||
rotate([0, 0, a])
|
||||
translate([STEM_FLANGE_BC/2, 0, -e])
|
||||
cylinder(d = M4_D, h = DECK_T + 2*e);
|
||||
|
||||
// ── Electronics bay footprint bolt holes (10× M3) ─────────────
|
||||
// Matches rover_electronics_bay.scad floor flange pattern exactly
|
||||
for (sx = [-1, 1])
|
||||
for (sy = [-1, 1]) {
|
||||
bx = sx * (BAY_L/2 + BAY_WALL - BAY_BOLT_INSET);
|
||||
by = sy * (BAY_W/2 + BAY_WALL - BAY_BOLT_INSET);
|
||||
translate([bx, by, -e])
|
||||
cylinder(d = M3_D, h = DECK_T + 2*e);
|
||||
}
|
||||
// Centre long-wall bolts (2×)
|
||||
for (sy = [-1, 1])
|
||||
translate([0, sy*(BAY_W/2 + BAY_WALL - BAY_BOLT_INSET), -e])
|
||||
cylinder(d = M3_D, h = DECK_T + 2*e);
|
||||
|
||||
// ── FC mount holes — 30.5×30.5 M3 (shared SaltyLab) ──────────
|
||||
for (dx = [-FC_PITCH/2, FC_PITCH/2])
|
||||
for (dy = [-FC_PITCH/2, FC_PITCH/2])
|
||||
translate([dx, FC_POS_Y + dy, -e])
|
||||
cylinder(d = FC_HOLE_D, h = DECK_T + 2*e);
|
||||
|
||||
// ── Jetson Orin mount holes — 58×49 M3 (shared SaltyLab) ─────
|
||||
for (dx = [-ORIN_HOLE_X/2, ORIN_HOLE_X/2])
|
||||
for (dy = [-ORIN_HOLE_Y/2, ORIN_HOLE_Y/2])
|
||||
translate([dx, ORIN_POS_Y + dy, -e])
|
||||
cylinder(d = ORIN_HOLE_D, h = DECK_T + 2*e);
|
||||
|
||||
// ── Lightening holes (between bay footprint and deck edges) ───
|
||||
for (sx = [-1, 1])
|
||||
for (sy = [-1, 1]) {
|
||||
lx = sx * (BAY_L/2 + 40);
|
||||
ly = sy * (BODY_L/4 + 10);
|
||||
translate([lx, ly, -e])
|
||||
cylinder(d = 50, h = DECK_T + 2*e);
|
||||
}
|
||||
// Centre pair flanking stem
|
||||
for (sx = [-1, 1])
|
||||
translate([sx * 65, 0, -e])
|
||||
cylinder(d = 38, h = DECK_T + 2*e);
|
||||
|
||||
// ── Cable routing slots (motor phase + sensor harness) ─────────
|
||||
// 4× slots near side frame attachment points
|
||||
for (sx = [-1, 1])
|
||||
for (sy = [-1, 1])
|
||||
hull() {
|
||||
translate([sx*(BODY_W/2 - 30), sy*(BODY_L/4 - 8), -e])
|
||||
cylinder(d = 14, h = DECK_T + 2*e);
|
||||
translate([sx*(BODY_W/2 - 30), sy*(BODY_L/4 + 8), -e])
|
||||
cylinder(d = 14, h = DECK_T + 2*e);
|
||||
}
|
||||
|
||||
// ── Skid plate attachment holes (M4 × 8, through deck underside)
|
||||
// These align with saltytank_skid_plate.scad bolt pattern
|
||||
for (sx = [-1, 1])
|
||||
for (sy = [-1, 0, 1]) {
|
||||
bx = sx * (BODY_W/2 - 20);
|
||||
by = sy * (BODY_L/3);
|
||||
translate([bx, by, -e])
|
||||
cylinder(d = M4_D, h = DECK_T + 2*e);
|
||||
}
|
||||
|
||||
// ── CSI corner bracket attachment holes (M3 × 2 per corner) ───
|
||||
for (sx = [-1, 1])
|
||||
for (sy = [-1, 1])
|
||||
for (dd = [-12, 12])
|
||||
translate([sx*(BODY_W/2 - 18),
|
||||
sy*(BODY_L/2 - 18) + dd*sy, -e])
|
||||
cylinder(d = M3_D, h = DECK_T + 2*e);
|
||||
}
|
||||
}
|
||||
|
||||
// ── Deck-top stem collar ─────────────────────────────────────────────────────
|
||||
module stem_collar() {
|
||||
translate([0, 0, DECK_T])
|
||||
difference() {
|
||||
cylinder(d = STEM_COLLAR_OD, h = STEM_COLLAR_H);
|
||||
translate([0, 0, -e])
|
||||
cylinder(d = STEM_BORE, h = STEM_COLLAR_H + 2*e);
|
||||
for (a = [0, 90, 180, 270])
|
||||
rotate([0, 0, a])
|
||||
translate([STEM_FLANGE_BC/2, 0, -e])
|
||||
cylinder(d = M4_D, h = STEM_COLLAR_H + 2*e);
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// SIDE FRAME (Part B — laser-cut 6 mm 6061-T6 aluminium)
|
||||
// ============================================================
|
||||
// Vertical plate running the full body length.
|
||||
// Inner face bolts to deck plate side edge.
|
||||
// Outer face mounts: drive sprocket (rear) + idler slot (front)
|
||||
// + road wheels (middle).
|
||||
//
|
||||
// `side` = -1 (left / −X) or +1 (right / +X)
|
||||
// The right frame is a mirror of the left — cut 2× from same DXF,
|
||||
// flip one face-down before mounting.
|
||||
//
|
||||
// Weight estimate: 500×90×6 mm Al, ~35% lightened ≈ 0.26 kg each
|
||||
// ============================================================
|
||||
module side_frame(side = -1) {
|
||||
sx = side; // −1 = left, +1 = right
|
||||
// Frame inner face at X = sx * BODY_W/2
|
||||
// Frame outer face at X = sx * (BODY_W/2 + FRAME_T)
|
||||
frame_x = sx * BODY_W/2; // inner face X position
|
||||
|
||||
// In deck coords: sprocket / idler CL Z values
|
||||
spr_z = GROUND_Z + SPROCKET_R; // = -(DECK_T + FRAME_H) + SPROCKET_R
|
||||
idl_z = GROUND_Z + IDLER_R;
|
||||
rw_z = GROUND_Z + ROAD_WHEEL_R;
|
||||
|
||||
translate([frame_x, 0, 0])
|
||||
rotate([0, sx > 0 ? 180 : 0, 0]) // mirror right frame in X
|
||||
translate([0, 0, 0]) {
|
||||
difference() {
|
||||
// ── Outer profile of side frame ───────────────────────────
|
||||
// Frame plate in the Y-Z plane, FRAME_T thick in X
|
||||
translate([0, -BODY_L/2, GROUND_Z])
|
||||
linear_extrude(FRAME_T)
|
||||
minkowski() {
|
||||
square([BODY_L - 2*FRAME_R,
|
||||
FRAME_H + DECK_T - 2*FRAME_R],
|
||||
center = false);
|
||||
circle(r = FRAME_R);
|
||||
}
|
||||
|
||||
// ── Deck attachment slots (2× M5 per side, front + rear) ──
|
||||
// Slots allow ±10 mm vertical adjustment for frame height
|
||||
for (py = [-BODY_L/4, BODY_L/4])
|
||||
hull() {
|
||||
translate([e, py - 12, -DECK_T / 2])
|
||||
rotate([0, 90, 0])
|
||||
cylinder(d = M5_D, h = FRAME_T + 2*e);
|
||||
translate([e, py + 12, -DECK_T / 2])
|
||||
rotate([0, 90, 0])
|
||||
cylinder(d = M5_D, h = FRAME_T + 2*e);
|
||||
}
|
||||
|
||||
// ── Drive sprocket bore — D-cut (rear) ────────────────────
|
||||
// Round section (base, near hub)
|
||||
translate([e, SPROCKET_POS_Y, spr_z])
|
||||
rotate([0, 90, 0])
|
||||
cylinder(d = SPROCKET_AXLE_D + 0.4, h = FRAME_T + 2*e);
|
||||
|
||||
// D-cut flat (anti-rotation) — removes chord to AXLE_FLAT
|
||||
dcut_h = sqrt(pow((SPROCKET_AXLE_DCUT + 0.4)/2, 2)
|
||||
- pow((SPROCKET_AXLE_FLAT + 0.4)/2, 2));
|
||||
translate([e, SPROCKET_POS_Y, spr_z])
|
||||
rotate([0, 90, 0])
|
||||
translate([0, (SPROCKET_AXLE_FLAT + 0.4)/2, 0])
|
||||
cube([(SPROCKET_AXLE_DCUT + 0.4)/2,
|
||||
FRAME_T + 2*e,
|
||||
FRAME_T + 2*e],
|
||||
center = false);
|
||||
|
||||
// ── Bearing seat recess (outboard face — inboard of track) ─
|
||||
// Prevents Ø37.8 mm collar binding on frame face
|
||||
translate([e - BEARING_RECESS, SPROCKET_POS_Y, spr_z])
|
||||
rotate([0, 90, 0])
|
||||
cylinder(d = BEARING_OD + 1.5,
|
||||
h = BEARING_RECESS + e);
|
||||
|
||||
// ── Hub motor flange bolt holes (4× M5, 52 mm BC) ─────────
|
||||
for (a = [45, 135, 225, 315])
|
||||
translate([e,
|
||||
SPROCKET_POS_Y + HUB_FLANGE_BC/2 * sin(a),
|
||||
spr_z + HUB_FLANGE_BC/2 * cos(a)])
|
||||
rotate([0, 90, 0])
|
||||
cylinder(d = HUB_BOLT_D, h = FRAME_T + 2*e);
|
||||
|
||||
// ── Idler tensioner slot (front) ──────────────────────────
|
||||
// Horizontal slot in Y direction; idler block slides in it
|
||||
slot_y_ctr = IDLER_POS_Y_NOM;
|
||||
slot_y_min = slot_y_ctr - TENS_TRAVEL;
|
||||
slot_y_max = slot_y_ctr + TENS_TRAVEL;
|
||||
translate([e, slot_y_min, idl_z - TENS_SLOT_H/2])
|
||||
cube([FRAME_T + 2*e,
|
||||
slot_y_max - slot_y_min,
|
||||
TENS_SLOT_H]);
|
||||
// Rounded ends
|
||||
translate([e, slot_y_min, idl_z])
|
||||
rotate([0, 90, 0])
|
||||
cylinder(d = TENS_SLOT_H, h = FRAME_T + 2*e);
|
||||
translate([e, slot_y_max, idl_z])
|
||||
rotate([0, 90, 0])
|
||||
cylinder(d = TENS_SLOT_H, h = FRAME_T + 2*e);
|
||||
|
||||
// ── Tensioner bolt bore (M6, at front end of slot) ────────
|
||||
// M6 bolt threads into lug at front of slot; pushes block rearward
|
||||
translate([e,
|
||||
slot_y_max + 8,
|
||||
idl_z])
|
||||
rotate([0, 90, 0])
|
||||
cylinder(d = M6_D, h = FRAME_T + 2*e);
|
||||
|
||||
// ── Road wheel bores (2× M8, fore/aft of centre) ──────────
|
||||
for (ry = [ROAD_Y_1, ROAD_Y_2])
|
||||
translate([e, ry, rw_z])
|
||||
rotate([0, 90, 0])
|
||||
cylinder(d = ROAD_AXLE_D, h = FRAME_T + 2*e);
|
||||
|
||||
// ── Lightening holes (between bore positions) ──────────────
|
||||
// Row 1: between road wheel 1 and sprocket
|
||||
translate([e,
|
||||
(SPROCKET_POS_Y + ROAD_Y_1) / 2,
|
||||
GROUND_Z + FRAME_H/2])
|
||||
rotate([0, 90, 0])
|
||||
cylinder(d = 45, h = FRAME_T + 2*e);
|
||||
|
||||
// Row 2: between road wheel 2 and idler
|
||||
translate([e,
|
||||
(IDLER_POS_Y_NOM + ROAD_Y_2) / 2,
|
||||
GROUND_Z + FRAME_H/2])
|
||||
rotate([0, 90, 0])
|
||||
cylinder(d = 45, h = FRAME_T + 2*e);
|
||||
|
||||
// Row 3: between the two road wheels
|
||||
translate([e, (ROAD_Y_1 + ROAD_Y_2) / 2, GROUND_Z + FRAME_H/2])
|
||||
rotate([0, 90, 0])
|
||||
cylinder(d = 35, h = FRAME_T + 2*e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// IDLER TENSIONER BLOCK (Part C — 3D print PETG, × 2)
|
||||
// ============================================================
|
||||
// Slides in the frame's tensioner slot.
|
||||
// M8 bore holds the idler axle.
|
||||
// A flat face at the front receives the tensioner M6 bolt.
|
||||
// Lock nut (M6 nyloc) clamps block at desired track tension.
|
||||
// Print orientation: flat face on bed; no supports needed.
|
||||
// ============================================================
|
||||
module idler_block() {
|
||||
block_h = TENS_SLOT_H - 0.6; // height with slot clearance
|
||||
block_l = TENS_BLOCK_L;
|
||||
block_w = FRAME_T - 0.4; // thickness with clearance
|
||||
|
||||
difference() {
|
||||
union() {
|
||||
// Main block body
|
||||
cube([block_w, block_l, block_h], center = true);
|
||||
|
||||
// Tensioner bolt lug (extends from +Y face)
|
||||
translate([0, block_l/2, 0])
|
||||
cube([block_w, 10, block_h], center = true);
|
||||
}
|
||||
|
||||
// M8 axle bore through full width
|
||||
translate([0, 0, 0])
|
||||
rotate([0, 90, 0])
|
||||
cylinder(d = IDLER_AXLE_D, h = block_w + 2*e, center = true);
|
||||
|
||||
// M6 tensioner bolt bore (fore-aft, through lug)
|
||||
translate([0, block_l/2 + 5, 0])
|
||||
rotate([90, 0, 0])
|
||||
cylinder(d = M6_D, h = 16, center = true);
|
||||
|
||||
// M6 nyloc nut pocket (rear face of lug — captured nut)
|
||||
translate([0, block_l/2 - 1, 0])
|
||||
rotate([90, 0, 0])
|
||||
cylinder(d = 11.5, h = 5, $fn = 6, center = false);
|
||||
|
||||
// ── Lightening slot (centre, removes material not in load path)
|
||||
cube([block_w + 2*e, block_l/2 - IDLER_AXLE_D/2 - 4, block_h - 4],
|
||||
center = true);
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// CSI CORNER BRACKET (Part D — 3D print PETG, × 4)
|
||||
// ============================================================
|
||||
// Same design as saltyrover_chassis_r2.scad.
|
||||
// Mounts IMX219 / Arducam CSI camera at each deck corner,
|
||||
// angled 45° outward + CSI_TILT downward.
|
||||
// ============================================================
|
||||
module csi_corner_bracket() {
|
||||
base_l = 42;
|
||||
base_w = 32;
|
||||
base_t = 5;
|
||||
|
||||
difference() {
|
||||
union() {
|
||||
cube([base_l, base_w, base_t]);
|
||||
translate([base_l / 2, base_w / 2, base_t])
|
||||
rotate([0, CSI_TILT, 0])
|
||||
translate([-CSI_PCB/2 - 3, -CSI_PCB/2 - 3, 0])
|
||||
cube([CSI_PCB + 6, CSI_PCB + 6, base_t]);
|
||||
}
|
||||
// 2× M3 base attachment holes
|
||||
for (dx = [8, base_l - 8])
|
||||
translate([dx, base_w / 2, -e])
|
||||
cylinder(d = M3_D, h = base_t + 2*e);
|
||||
// CSI M2 holes (15×15 mm pattern)
|
||||
translate([base_l / 2, base_w / 2, base_t])
|
||||
rotate([0, CSI_TILT, 0])
|
||||
for (cx = [-CSI_M2_SPC/2, CSI_M2_SPC/2])
|
||||
for (cy = [-CSI_M2_SPC/2, CSI_M2_SPC/2])
|
||||
translate([cx, cy, -e])
|
||||
cylinder(d = M2_D, h = base_t + 2*e);
|
||||
// CSI ribbon slot
|
||||
translate([base_l/2 - 6, base_w/2 - 1.5, -e])
|
||||
cube([12, 3, base_t + 2*e]);
|
||||
}
|
||||
}
|
||||
|
||||
module csi_bracket_placed(sx, sy) {
|
||||
cx = sx * (BODY_W/2 - 22);
|
||||
cy = sy * (BODY_L/2 - 22);
|
||||
rot = atan2(sy, sx) * 180 / 3.14159 - 45;
|
||||
translate([cx, cy, DECK_T])
|
||||
rotate([0, 0, rot])
|
||||
translate([-21, -16, 0])
|
||||
csi_corner_bracket();
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// D435i FRONT BRACKET (Part E — 3D print PETG, × 1)
|
||||
// ============================================================
|
||||
// Same design as saltyrover_chassis_r2.scad.
|
||||
// Arm extends forward from deck front edge.
|
||||
// RS_TILT degrees nose-down. 1/4-20 captured nut for D435i.
|
||||
// ============================================================
|
||||
module d435i_front_bracket() {
|
||||
base_d = 24;
|
||||
base_h = 8;
|
||||
arm_len = RS_ARM_LEN;
|
||||
|
||||
nut14_af = 11.1;
|
||||
nut14_h = 5.6;
|
||||
nut14_cl = 6.5;
|
||||
|
||||
difference() {
|
||||
union() {
|
||||
// Base plate (bolts to deck front face)
|
||||
translate([-RS_BASE_W/2, 0, 0])
|
||||
cube([RS_BASE_W, base_d, base_h]);
|
||||
// Forward arm
|
||||
translate([-13, base_d, 0])
|
||||
cube([26, arm_len, base_h]);
|
||||
// Tilted face plate
|
||||
translate([0, base_d + arm_len, base_h/2])
|
||||
rotate([0, RS_TILT, 0])
|
||||
translate([-16, 0, -base_h/2])
|
||||
cube([32, 14, base_h]);
|
||||
}
|
||||
// 2× M4 base attachment
|
||||
for (dx = [-RS_BASE_W/2 + 10, RS_BASE_W/2 - 10])
|
||||
translate([dx, base_d/2, -e])
|
||||
cylinder(d = M4_D, h = base_h + 2*e);
|
||||
// 1/4-20 UNC captured nut
|
||||
translate([0, base_d + arm_len + 12, base_h/2])
|
||||
rotate([0, 90, 0]) {
|
||||
translate([0, 0, -nut14_h - 1])
|
||||
cylinder(d = nut14_af/cos(30), h = nut14_h + 1, $fn = 6);
|
||||
cylinder(d = nut14_cl, h = 20);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module d435i_bracket_placed() {
|
||||
translate([0, BODY_L/2 + 12, DECK_T])
|
||||
rotate([0, 0, 180])
|
||||
d435i_front_bracket();
|
||||
}
|
||||
214
chassis/saltytank_skid_plate.scad
Normal file
214
chassis/saltytank_skid_plate.scad
Normal file
@ -0,0 +1,214 @@
|
||||
// ============================================================
|
||||
// saltytank_skid_plate.scad — SaltyTank Underside Skid Plate
|
||||
// Issue: #121 Agent: sl-mechanical Date: 2026-03-01
|
||||
// ============================================================
|
||||
//
|
||||
// Bolt-on underside protection panel for rough terrain.
|
||||
// Mounts to the underside of the deck plate, covering the hull
|
||||
// floor between the two track frames.
|
||||
//
|
||||
// Purpose:
|
||||
// • Protects electronics bay wiring + deck underside from rock strikes
|
||||
// • Sacrificial layer — replace without reworking deck plate
|
||||
// • Provides a smooth low-drag hull bottom for terrain sliding
|
||||
// • Four drain / inspection slots (water / mud egress)
|
||||
//
|
||||
// Material options:
|
||||
// Primary : 4 mm HDPE (high-density polyethylene)
|
||||
// — lightweight, impact resistant, low friction surface
|
||||
// Alternative: 2 mm 304 stainless steel
|
||||
// — heavier (+0.5 kg), extremely wear resistant
|
||||
// Prototype : 6 mm plywood (quick, cheap; not weatherproof)
|
||||
//
|
||||
// Dimensions: BODY_W × BODY_L × SKID_T
|
||||
// = 360 × 500 × 4 mm (fits between track inner faces)
|
||||
//
|
||||
// Mounting:
|
||||
// 8× M4 SHCS from below; nuts captured in deck plate M4 rivet-nuts
|
||||
// (see saltytank_chassis.scad skid plate attachment holes)
|
||||
// Countersunk (FHCS) variant available — set COUNTERSINK = true
|
||||
//
|
||||
// Coordinate: Z=0 at skid top face (= deck plate underside)
|
||||
// Ground contact surface at Z = -(FRAME_H) = -90 mm (in deck coords)
|
||||
// Skid sits immediately below deck: deck_bottom → skid_top
|
||||
//
|
||||
// RENDER options:
|
||||
// "skid_stl" STL for 3D printing (4 mm PETG, prototype)
|
||||
// "skid_2d" DXF for waterjet / CNC (HDPE or steel plate)
|
||||
// "assembly" preview with deck ghost
|
||||
//
|
||||
// Export commands:
|
||||
// DXF (HDPE / steel — recommended):
|
||||
// openscad saltytank_skid_plate.scad -D 'RENDER="skid_2d"' -o saltytank_skid.dxf
|
||||
// STL (3D print prototype):
|
||||
// openscad saltytank_skid_plate.scad -D 'RENDER="skid_stl"' -o saltytank_skid.stl
|
||||
// ============================================================
|
||||
|
||||
$fn = 64;
|
||||
e = 0.01;
|
||||
|
||||
// ── Skid plate dimensions ────────────────────────────────────────────────────
|
||||
// Must match saltytank_chassis.scad BODY_W and BODY_L exactly.
|
||||
BODY_L = 500.0; // fore-aft — copy from saltytank_chassis.scad
|
||||
BODY_W = 360.0; // left-right
|
||||
SKID_T = 4.0; // skid plate thickness (4 mm HDPE)
|
||||
SKID_R = 12.0; // corner radius (matches deck corners, ease of cleaning)
|
||||
|
||||
FRAME_H = 90.0; // side frame height (from saltytank_chassis.scad)
|
||||
|
||||
// ── Skid reinforcement ribs (under-surface, printed variant only) ─────────────
|
||||
// Ribs add stiffness to the printed skid without adding flat-surface area.
|
||||
// Ribs are omitted on the CNC/laser version (material stiffness is sufficient).
|
||||
RIB_H = 8.0; // rib height below skid top face
|
||||
RIB_T = 3.0; // rib wall thickness
|
||||
RIB_PRINT = false; // set true to add ribs (print only; skip for DXF)
|
||||
|
||||
// ── Attachment bolt holes (M4) ───────────────────────────────────────────────
|
||||
// Pattern must match saltytank_chassis.scad skid plate holes exactly.
|
||||
// 2 rows × 3 columns = 6 holes + 2 corners = 8 total
|
||||
BOLT_D = 4.3; // M4 clearance
|
||||
BOLT_INSET_X = 20.0; // CL inset from left/right edge
|
||||
BOLT_INSET_Y = 25.0; // CL inset from front/rear edge
|
||||
|
||||
// Countersinking (flat-head bolts flush with skid surface — preferred for terrain)
|
||||
COUNTERSINK = true;
|
||||
CSINK_ANGLE = 82; // M4 FHCS countersink angle (82° for DIN 7991)
|
||||
CSINK_OD = 8.0; // M4 FHCS head diameter
|
||||
|
||||
// ── Drain / inspection slots ─────────────────────────────────────────────────
|
||||
DRAIN_W = 20.0; // slot width
|
||||
DRAIN_L = 60.0; // slot length
|
||||
DRAIN_R = 10.0; // slot corner radius
|
||||
|
||||
// ── Road-wheel axle pass-through slots ───────────────────────────────────────
|
||||
// The road wheel axles sit OUTSIDE the skid plate (they're in the side frames),
|
||||
// so no axle holes are needed in the skid plate.
|
||||
// The skid plate is a solid panel between the track frames.
|
||||
|
||||
// ── Fasteners ─────────────────────────────────────────────────────────────────
|
||||
M4_D = 4.3;
|
||||
|
||||
// ============================================================
|
||||
// RENDER DISPATCH
|
||||
// ============================================================
|
||||
RENDER = "assembly";
|
||||
|
||||
if (RENDER == "skid_stl") {
|
||||
skid_plate();
|
||||
} else if (RENDER == "skid_2d") {
|
||||
projection(cut = true)
|
||||
translate([0, 0, -SKID_T / 2])
|
||||
skid_plate_flat_only();
|
||||
} else if (RENDER == "assembly") {
|
||||
assembly();
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// ASSEMBLY PREVIEW
|
||||
// ============================================================
|
||||
module assembly() {
|
||||
color("Sienna", 0.85) skid_plate();
|
||||
|
||||
// Ghost deck plate above
|
||||
%color("Silver", 0.25)
|
||||
translate([0, 0, SKID_T])
|
||||
linear_extrude(8)
|
||||
minkowski() {
|
||||
square([BODY_L - 24, BODY_W - 24], center = true);
|
||||
circle(r = 12);
|
||||
}
|
||||
|
||||
// Ghost side frames (simplified)
|
||||
for (sx = [-1, 1])
|
||||
%color("SteelBlue", 0.20)
|
||||
translate([sx * (BODY_W/2 + 3), 0, SKID_T])
|
||||
cube([6, BODY_L, FRAME_H + 8], center = true);
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// SKID PLATE (3D print — includes optional ribs)
|
||||
// ============================================================
|
||||
module skid_plate() {
|
||||
difference() {
|
||||
union() {
|
||||
// ── Base plate ─────────────────────────────────────────────
|
||||
linear_extrude(SKID_T)
|
||||
skid_profile_2d();
|
||||
|
||||
// ── Reinforcement ribs (printed version only) ──────────────
|
||||
if (RIB_PRINT) {
|
||||
// Longitudinal centre rib
|
||||
translate([0, 0, SKID_T])
|
||||
cube([RIB_T, BODY_L - 40, RIB_H], center = true);
|
||||
// Lateral ribs (3× fore-aft)
|
||||
for (ry = [-BODY_L/4, 0, BODY_L/4])
|
||||
translate([0, ry, SKID_T])
|
||||
cube([BODY_W - 40, RIB_T, RIB_H], center = true);
|
||||
}
|
||||
}
|
||||
|
||||
// ── Attachment bolt holes ──────────────────────────────────────
|
||||
for (bpos = bolt_positions())
|
||||
translate([bpos[0], bpos[1], -e]) {
|
||||
cylinder(d = BOLT_D, h = SKID_T + 2*e);
|
||||
if (COUNTERSINK)
|
||||
// Countersink cone from top face
|
||||
translate([0, 0, SKID_T + e])
|
||||
cylinder(d1 = CSINK_OD, d2 = BOLT_D,
|
||||
h = (CSINK_OD - BOLT_D) / (2 * tan(CSINK_ANGLE/2)));
|
||||
}
|
||||
|
||||
// ── Drain / inspection slots (4×, one per quadrant) ───────────
|
||||
for (sx = [-1, 1])
|
||||
for (sy = [-1, 1]) {
|
||||
dx = sx * (BODY_W/4 + 15);
|
||||
dy = sy * (BODY_L/4 + 20);
|
||||
translate([dx - DRAIN_W/2 + DRAIN_R, dy - DRAIN_L/2 + DRAIN_R, -e])
|
||||
linear_extrude(SKID_T + 2*e)
|
||||
minkowski() {
|
||||
square([DRAIN_W - 2*DRAIN_R, DRAIN_L - 2*DRAIN_R]);
|
||||
circle(r = DRAIN_R);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 2D profile of skid plate (shared by skid_plate and DXF export)
|
||||
module skid_profile_2d() {
|
||||
minkowski() {
|
||||
square([BODY_L - 2*SKID_R, BODY_W - 2*SKID_R], center = true);
|
||||
circle(r = SKID_R);
|
||||
}
|
||||
}
|
||||
|
||||
// Flat-only version for DXF projection (no ribs)
|
||||
module skid_plate_flat_only() {
|
||||
difference() {
|
||||
linear_extrude(SKID_T) skid_profile_2d();
|
||||
for (bpos = bolt_positions())
|
||||
translate([bpos[0], bpos[1], -e])
|
||||
cylinder(d = BOLT_D, h = SKID_T + 2*e);
|
||||
for (sx = [-1, 1])
|
||||
for (sy = [-1, 1]) {
|
||||
dx = sx * (BODY_W/4 + 15);
|
||||
dy = sy * (BODY_L/4 + 20);
|
||||
translate([dx - DRAIN_W/2 + DRAIN_R, dy - DRAIN_L/2 + DRAIN_R, -e])
|
||||
linear_extrude(SKID_T + 2*e)
|
||||
minkowski() {
|
||||
square([DRAIN_W - 2*DRAIN_R, DRAIN_L - 2*DRAIN_R]);
|
||||
circle(r = DRAIN_R);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ── Bolt position list (8 positions matching saltytank_chassis.scad) ──────────
|
||||
function bolt_positions() = [
|
||||
// From saltytank_chassis.scad skid plate holes (sx × (BODY_W/2 - 20), sy × BODY_L/3)
|
||||
[-BODY_W/2 + BOLT_INSET_X, -BODY_L/3],
|
||||
[-BODY_W/2 + BOLT_INSET_X, 0],
|
||||
[-BODY_W/2 + BOLT_INSET_X, +BODY_L/3],
|
||||
[+BODY_W/2 - BOLT_INSET_X, -BODY_L/3],
|
||||
[+BODY_W/2 - BOLT_INSET_X, 0],
|
||||
[+BODY_W/2 - BOLT_INSET_X, +BODY_L/3]
|
||||
];
|
||||
Loading…
x
Reference in New Issue
Block a user