Compare commits
1 Commits
main
...
serkan/par
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ffda15e3ec |
534
docs/PARKING-MARKING-SPEC.md
Normal file
534
docs/PARKING-MARKING-SPEC.md
Normal file
@ -0,0 +1,534 @@
|
|||||||
|
# Parking Lot Line Marking System — Hardware + Software Spec
|
||||||
|
|
||||||
|
**Platform:** SAUL-TEE Robot
|
||||||
|
**Use case:** Serkan — Autonomous parking lot striping
|
||||||
|
**Author:** mark (parking marking agent)
|
||||||
|
**Date:** 2026-04-07
|
||||||
|
**Status:** Draft v1.0
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Table of Contents
|
||||||
|
|
||||||
|
1. [System Overview](#1-system-overview)
|
||||||
|
2. [Paint Dispensing Hardware](#2-paint-dispensing-hardware)
|
||||||
|
3. [Control Electronics](#3-control-electronics)
|
||||||
|
4. [Positioning Strategy](#4-positioning-strategy)
|
||||||
|
5. [Software Architecture](#5-software-architecture)
|
||||||
|
6. [Parking Lot Templates](#6-parking-lot-templates)
|
||||||
|
7. [Bill of Materials](#7-bill-of-materials)
|
||||||
|
8. [Safety](#8-safety)
|
||||||
|
9. [Open Items](#9-open-items)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. System Overview
|
||||||
|
|
||||||
|
SAUL-TEE autonomously traverses a parking lot surface while a rear-mounted paint dispensing system sprays crisp lines. The Jetson Orin drives RTK-corrected path following via differential VESC drive. The ESP32 IO board triggers a solenoid valve via relay on demand. Lines are generated from a layout file (SVG or JSON) and executed as a sequence of waypoints with paint-on/off events.
|
||||||
|
|
||||||
|
```
|
||||||
|
[Layout File] → [Layout Planner node]
|
||||||
|
↓
|
||||||
|
[Path Follower node] ← RTK/UWB pose
|
||||||
|
↓
|
||||||
|
[Paint Controller node] → GPIO → relay → solenoid → spray
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. Paint Dispensing Hardware
|
||||||
|
|
||||||
|
### 2.1 Spray Mechanism — Selected: Pressurized Airless Canister
|
||||||
|
|
||||||
|
Three options evaluated:
|
||||||
|
|
||||||
|
| Option | Pros | Cons | Verdict |
|
||||||
|
|--------|------|------|---------|
|
||||||
|
| **Pressurized aerosol canister** | Cheap, no pump, self-contained, easy swap | ~500 mL, limited volume, cold-sensitive | **Selected for v1** |
|
||||||
|
| Airless diaphragm pump + reservoir | Large capacity, refillable, consistent | Pump adds complexity, power draw ~3 A | v2 option |
|
||||||
|
| Gravity-fed dip tube | Simple, zero power | Requires height differential, drips on stop | Rejected |
|
||||||
|
|
||||||
|
**v1 approach:** Modified professional marking paint canister (e.g., Rustoleum Marking Chalk, inverted-spray capable) + normally-closed solenoid valve inline with canister outlet tube. Canister pressure (~50 PSI) propels paint through the solenoid to a fan nozzle aimed at the ground.
|
||||||
|
|
||||||
|
### 2.2 Nozzle Selection
|
||||||
|
|
||||||
|
| Line Width | Nozzle | Tip Orifice | Height from Ground | Notes |
|
||||||
|
|------------|--------|-------------|-------------------|-------|
|
||||||
|
| 4" standard parking line | Flat fan 40° | 0.017" | 8–10 cm | Covers standard stripe width at 50 PSI |
|
||||||
|
| 2" detail / curb line | Flat fan 20° | 0.013" | 6–8 cm | Narrower pattern |
|
||||||
|
| 6" fire lane | Flat fan 65° | 0.021" | 10–12 cm | Wide spray, lower pressure preferred |
|
||||||
|
|
||||||
|
**Recommended nozzle:** Graco RAC X 515 (flat fan, 10" fan width at 12" height) for 4" lines. Reversible tip for unclogging.
|
||||||
|
|
||||||
|
**Critical:** Nozzle tip height must be fixed relative to ground. Spring-loaded mount or rigid bracket set to calibrated height.
|
||||||
|
|
||||||
|
### 2.3 Solenoid Valve
|
||||||
|
|
||||||
|
- **Type:** Normally-closed (NC) 12 V DC solenoid valve
|
||||||
|
- **Body:** Brass or stainless, 1/4" NPT female ports
|
||||||
|
- **Pressure rating:** ≥ 100 PSI (canister runs at ~50 PSI)
|
||||||
|
- **Response time:** < 20 ms open/close (critical for line start/stop accuracy)
|
||||||
|
- **Current draw:** ~500 mA @ 12 V when open
|
||||||
|
- **Recommended part:** U.S. Solid USS-MSV00006 1/4" NC 12 V (≈ $18) or equivalent
|
||||||
|
|
||||||
|
**Flyback diode:** 1N4007 across solenoid coil terminals (cathode to +12 V).
|
||||||
|
|
||||||
|
### 2.4 Paint Reservoir / Canister Mount
|
||||||
|
|
||||||
|
**v1 — Inverted aerosol canister:**
|
||||||
|
- Standard 17 oz / 500 mL marking paint canister (inverted nozzle type)
|
||||||
|
- Custom bracket: 3 mm aluminum plate, two 3" U-bolt clamps for canister body
|
||||||
|
- Mount behind rear castor, centered on robot longitudinal axis
|
||||||
|
- Canister outlet → 1/4" OD PTFE tube → solenoid → nozzle
|
||||||
|
|
||||||
|
**v2 — Refillable reservoir:**
|
||||||
|
- 2 L HDPE pressure pot (Harbor Freight or similar, ~$35)
|
||||||
|
- External 12 V mini air compressor regulating to 30–60 PSI
|
||||||
|
- Float-level sensor (reed switch) for paint level monitoring
|
||||||
|
|
||||||
|
### 2.5 Mounting Bracket
|
||||||
|
|
||||||
|
```
|
||||||
|
Top rail (80/20 T-slot or robot frame)
|
||||||
|
│
|
||||||
|
├─── Canister bracket (rear, vertical, above castor)
|
||||||
|
│ U-bolts × 2 clamping canister body
|
||||||
|
│ QD fitting for tube swap
|
||||||
|
│
|
||||||
|
└─── Nozzle arm (extends aft ~200 mm, 50 mm above ground target height)
|
||||||
|
Adjustable height slot (bolt + lock nut)
|
||||||
|
Solenoid mounted inline, protected by splash shield
|
||||||
|
```
|
||||||
|
|
||||||
|
**Key dimension:** Nozzle center is 150 mm behind rear axle center. This offset is compensated in software — spray-on is triggered when the nozzle position (not robot center) reaches the line start point.
|
||||||
|
|
||||||
|
### 2.6 Power Requirements
|
||||||
|
|
||||||
|
| Component | Voltage | Current | Source |
|
||||||
|
|-----------|---------|---------|--------|
|
||||||
|
| Solenoid valve | 12 V | 0.5 A peak (intermittent) | Robot 12 V rail |
|
||||||
|
| Relay module | 5 V logic, 12 V switched | < 50 mA control | 5 V rail via IO GPIO |
|
||||||
|
| RTK GPS module | 5 V | 100–200 mA | 5 V rail |
|
||||||
|
| (v2) Mini compressor | 12 V | 3–5 A | Direct from 36 V via 12 V DC-DC |
|
||||||
|
|
||||||
|
Total paint system power budget: **< 1 A continuous** (solenoid duty cycle ~20%).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Control Electronics
|
||||||
|
|
||||||
|
### 3.1 Signal Chain: Orin → Spray
|
||||||
|
|
||||||
|
```
|
||||||
|
Jetson Orin (ROS2 node)
|
||||||
|
│ publishes /paint/cmd (Bool)
|
||||||
|
│
|
||||||
|
[CAN or MQTT] ← preferred: direct GPIO on Orin Jetson 40-pin header
|
||||||
|
│
|
||||||
|
Orin GPIO pin (3.3 V logic)
|
||||||
|
│
|
||||||
|
Level shifter (3.3 V → 5 V) [optional, if relay module needs 5 V]
|
||||||
|
│
|
||||||
|
5 V relay module (optocoupler isolated, SPDT, 10 A contacts)
|
||||||
|
│ NO contact → +12 V
|
||||||
|
│ COM → solenoid +
|
||||||
|
│ solenoid − → GND
|
||||||
|
│
|
||||||
|
Solenoid valve (NC, 12 V)
|
||||||
|
│
|
||||||
|
Spray nozzle
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3.2 Jetson Orin GPIO
|
||||||
|
|
||||||
|
The Jetson Orin Nano / AGX Orin 40-pin expansion header exposes GPIOs via `Jetson.GPIO` Python library or `/sys/class/gpio`.
|
||||||
|
|
||||||
|
| Signal | Orin Pin | GPIO # | Notes |
|
||||||
|
|--------|----------|--------|-------|
|
||||||
|
| SPRAY_EN | Pin 11 | GPIO09 (Tegra SOC) | Active high → relay ON → solenoid opens |
|
||||||
|
| ESTOP_IN | Pin 12 | GPIO08 | Monitor hardware kill switch state |
|
||||||
|
|
||||||
|
Use `Jetson.GPIO` in ROS2 node:
|
||||||
|
```python
|
||||||
|
import Jetson.GPIO as GPIO
|
||||||
|
SPRAY_PIN = 11
|
||||||
|
GPIO.setmode(GPIO.BOARD)
|
||||||
|
GPIO.setup(SPRAY_PIN, GPIO.OUT, initial=GPIO.LOW)
|
||||||
|
GPIO.output(SPRAY_PIN, GPIO.HIGH) # spray on
|
||||||
|
GPIO.output(SPRAY_PIN, GPIO.LOW) # spray off
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3.3 ESP32 IO Board — Alternative / Backup Path
|
||||||
|
|
||||||
|
If direct Orin GPIO is not preferred, route through the IO board.
|
||||||
|
|
||||||
|
**Available IO pins (from SAUL-TEE-SYSTEM-REFERENCE.md):**
|
||||||
|
|
||||||
|
| GPIO | Current Use | Available? |
|
||||||
|
|------|-------------|-----------|
|
||||||
|
| IO14 | Horn/buzzer | Yes (shared, low duty) |
|
||||||
|
| IO15 | Headlight | Yes (can multiplex) |
|
||||||
|
| **IO16** | Fan (if ELRS not fitted) | **Yes — recommended for spray relay** |
|
||||||
|
|
||||||
|
**Recommended:** IO16 on ESP32 IO board → relay → solenoid.
|
||||||
|
|
||||||
|
Add new UART message type for spray control:
|
||||||
|
|
||||||
|
```
|
||||||
|
IO Board extension — TYPE 0x04: SPRAY_CMD
|
||||||
|
Payload: uint8 enable (0=off, 1=on), uint16 duration_ms (0=indefinite)
|
||||||
|
```
|
||||||
|
|
||||||
|
Orin sends via MQTT → ESP32 MQTT bridge → UART to IO board → GPIO16.
|
||||||
|
|
||||||
|
### 3.4 Safety Interlocks
|
||||||
|
|
||||||
|
| Condition | Action |
|
||||||
|
|-----------|--------|
|
||||||
|
| E-STOP (RC CH6 > 1500 µs) | IO board immediately pulls IO16 LOW (spray off) before forwarding FAULT to BALANCE |
|
||||||
|
| Tilt > ±25° | BALANCE sends FAULT → Orin paint controller kills spray |
|
||||||
|
| Robot velocity = 0 (stationary) | paint_controller node holds spray off (prevents puddles) |
|
||||||
|
| RC loss > 100 ms | FAULT propagated → spray off |
|
||||||
|
| MQTT disconnected > 5 s | paint_controller enters SAFE mode, spray off |
|
||||||
|
|
||||||
|
**IO board firmware addition:** In the FAULT frame handler (TYPE 0x03), ensure IO16 is driven LOW before sending any FAULT frame upward. Zero-latency interlock at MCU level.
|
||||||
|
|
||||||
|
### 3.5 Flow Rate Sensor (Optional — v2)
|
||||||
|
|
||||||
|
- **Type:** YF-S201 Hall-effect flow sensor, 1/2" inline
|
||||||
|
- **Signal:** Pulse output, 7.5 pulses per mL
|
||||||
|
- **Connected to:** IO board I2C bus interrupt pin or free GPIO
|
||||||
|
- **Purpose:** Detect clog (rate drops to zero during spray command), estimate paint consumed
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. Positioning Strategy
|
||||||
|
|
||||||
|
### 4.1 Accuracy Requirements
|
||||||
|
|
||||||
|
| Parameter | Requirement | Rationale |
|
||||||
|
|-----------|-------------|-----------|
|
||||||
|
| Lateral position error | < ±2 cm | 4" (10 cm) line — 2 cm error leaves 3 cm margin |
|
||||||
|
| Heading error | < ±0.5° | Over 20 m line, 0.5° heading error = 17 cm drift — marginal but acceptable with frequent corrections |
|
||||||
|
| Longitudinal (along-line) error | < ±5 cm | Controls spray on/off timing |
|
||||||
|
| Update rate | ≥ 5 Hz | At 0.3 m/s robot speed, 5 Hz gives 6 cm per update |
|
||||||
|
|
||||||
|
**Conclusion:** Phone GPS (±5 m) alone is **insufficient**. RTK GPS (±2 cm) is required for production use.
|
||||||
|
|
||||||
|
### 4.2 RTK GPS Module
|
||||||
|
|
||||||
|
**Recommended:** u-blox ZED-F9P (standalone RTK receiver)
|
||||||
|
|
||||||
|
| Spec | Value |
|
||||||
|
|------|-------|
|
||||||
|
| RTK accuracy (fixed) | 1 cm + 1 ppm horizontal |
|
||||||
|
| Heading (dual antenna) | 0.3° RMS |
|
||||||
|
| Update rate | 10 Hz (RTK), 20 Hz raw |
|
||||||
|
| Interface | UART (NMEA + UBX), USB |
|
||||||
|
| Power | 5 V, ~150 mA |
|
||||||
|
| Module board | SparkFun GPS-RTK2 (ZED-F9P) ~$250, or ArduSimple simpleRTK2B ~$200 |
|
||||||
|
|
||||||
|
**NTRIP correction source:** Use a local CORS network or set up a second ZED-F9P base station at a known point on the parking lot boundary. Base → Orin NTRIP client → RTK corrections to rover.
|
||||||
|
|
||||||
|
**Antenna placement:** Center of robot, clear sky view. Keep > 30 cm from metal frame edges.
|
||||||
|
|
||||||
|
### 4.3 Dual Antenna Heading (Moving Baseline RTK)
|
||||||
|
|
||||||
|
Use two ZED-F9P modules (or one ZED-F9P + ZED-F9H) with antennas at robot front and rear, separated by ≥ 50 cm. This gives direct heading from GPS — no magnetometer drift issues.
|
||||||
|
|
||||||
|
| Antenna separation | Heading accuracy |
|
||||||
|
|-------------------|-----------------|
|
||||||
|
| 50 cm | ~1.1° RMS |
|
||||||
|
| 100 cm | ~0.57° RMS |
|
||||||
|
| 150 cm | ~0.38° RMS |
|
||||||
|
|
||||||
|
**Recommended:** 100 cm baseline along robot longitudinal axis → heading accuracy 0.57° RMS.
|
||||||
|
|
||||||
|
### 4.4 Sensor Fusion Architecture
|
||||||
|
|
||||||
|
```
|
||||||
|
[ZED-F9P RTK rover] ── UART ──→ robot_localization EKF
|
||||||
|
[Phone GPS MQTT bridge] ──────→ (fallback, coarse)
|
||||||
|
[UWB tag] ────────────────────→ (near base station, ±2 cm augment)
|
||||||
|
[IMU - QMI8658 via CAN 0x400]──→ dead reckoning between GPS updates
|
||||||
|
↓
|
||||||
|
/saltybot/pose (geometry_msgs/PoseWithCovarianceStamped)
|
||||||
|
```
|
||||||
|
|
||||||
|
- **robot_localization** (ROS2 EKF node) fuses GPS + IMU
|
||||||
|
- RTK GPS is primary when fixed; covariance set high when float/no fix
|
||||||
|
- UWB active near parking lot entry/exit points (anchor infrastructure needed)
|
||||||
|
- Phone GPS used for coarse positioning only, filtered out when RTK fixed
|
||||||
|
|
||||||
|
### 4.5 Wheel Odometry
|
||||||
|
|
||||||
|
At 0.3 m/s nominal painting speed, the VESC RPM telemetry (CAN 0x401) provides ~1 cm odometry resolution. Combine with IMU heading for dead-reckoning between RTK updates.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. Software Architecture
|
||||||
|
|
||||||
|
### 5.1 ROS2 Node Graph
|
||||||
|
|
||||||
|
```
|
||||||
|
/layout_planner
|
||||||
|
Reads: layout JSON file
|
||||||
|
Publishes: /paint/path (nav_msgs/Path), /paint/zones (PaintZone[])
|
||||||
|
|
||||||
|
/path_follower
|
||||||
|
Subscribes: /paint/path, /saltybot/pose
|
||||||
|
Publishes: /cmd_vel (geometry_msgs/Twist) → CAN 0x300 bridge
|
||||||
|
Algorithm: Pure Pursuit with adaptive lookahead (0.3 m at low speed)
|
||||||
|
|
||||||
|
/paint_controller
|
||||||
|
Subscribes: /saltybot/pose, /paint/zones
|
||||||
|
Publishes: /paint/spray_cmd (std_msgs/Bool) → GPIO
|
||||||
|
Logic: nozzle-offset compensation, edge start/stop, velocity interlock
|
||||||
|
|
||||||
|
/rtk_bridge
|
||||||
|
Reads: ZED-F9P UART (NMEA GGA, UBX-NAV-PVT)
|
||||||
|
Publishes: /gps/fix (sensor_msgs/NavSatFix), /gps/heading (Float64)
|
||||||
|
|
||||||
|
/paint_monitor (MQTT bridge)
|
||||||
|
Bridges: /paint/status → MQTT saltybot/mark/status
|
||||||
|
Bridges: MQTT saltybot/mark/cmd → /paint/cmd
|
||||||
|
```
|
||||||
|
|
||||||
|
### 5.2 MQTT Topics
|
||||||
|
|
||||||
|
| Topic | Direction | Payload | Description |
|
||||||
|
|-------|-----------|---------|-------------|
|
||||||
|
| `saltybot/mark/status` | Orin → broker | JSON | Current state: pose, spray_state, progress_pct, paint_remaining |
|
||||||
|
| `saltybot/mark/cmd` | broker → Orin | JSON | Commands: start, stop, pause, load_layout |
|
||||||
|
| `saltybot/mark/layout` | broker → Orin | JSON | Full layout upload |
|
||||||
|
| `saltybot/mark/fault` | Orin → broker | JSON | Fault events: e-stop, gps_lost, clog_detected |
|
||||||
|
| `saltybot/mark/spray` | Orin → ESP32 IO | `{"en":1}` | Direct spray relay command (fallback path) |
|
||||||
|
|
||||||
|
### 5.3 Layout File Format (JSON)
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"version": 1,
|
||||||
|
"origin": {"lat": 37.4219983, "lon": -122.084},
|
||||||
|
"lines": [
|
||||||
|
{
|
||||||
|
"id": "row_A_space_1_left",
|
||||||
|
"type": "stripe",
|
||||||
|
"width_mm": 100,
|
||||||
|
"start": {"x_m": 0.0, "y_m": 0.0},
|
||||||
|
"end": {"x_m": 5.5, "y_m": 0.0}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"symbols": [
|
||||||
|
{
|
||||||
|
"id": "hc_symbol_1",
|
||||||
|
"type": "handicap_iso",
|
||||||
|
"center": {"x_m": 12.0, "y_m": 2.3},
|
||||||
|
"heading_deg": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Coordinates are in a local ENU (East-North-Up) frame anchored at `origin`.
|
||||||
|
|
||||||
|
### 5.4 path_follower — Pure Pursuit
|
||||||
|
|
||||||
|
```python
|
||||||
|
class PathFollower(Node):
|
||||||
|
LOOKAHEAD = 0.35 # m — tunable
|
||||||
|
MAX_SPEED = 0.3 # m/s during painting
|
||||||
|
NOZZLE_OFFSET = -0.15 # m aft of rear axle
|
||||||
|
|
||||||
|
def compute_cmd_vel(self, pose, path):
|
||||||
|
# Find lookahead point on path
|
||||||
|
# Compute curvature κ = 2*sin(α) / L
|
||||||
|
# angular_vel = κ * linear_vel
|
||||||
|
...
|
||||||
|
```
|
||||||
|
|
||||||
|
**Speed:** 0.3 m/s nominal (≈ 1 ft/s). At this speed, 100 m line ≈ 5.5 min.
|
||||||
|
|
||||||
|
### 5.5 paint_controller — Spray Logic
|
||||||
|
|
||||||
|
```python
|
||||||
|
NOZZLE_OFFSET_M = -0.15 # nozzle is 15 cm behind robot center (rear)
|
||||||
|
|
||||||
|
def nozzle_pose(robot_pose):
|
||||||
|
# Project robot pose backward by offset along heading
|
||||||
|
...
|
||||||
|
|
||||||
|
def on_pose_update(self, pose):
|
||||||
|
nozzle = nozzle_pose(pose)
|
||||||
|
# Check if nozzle is within any active paint zone
|
||||||
|
in_zone = any(zone.contains(nozzle) for zone in self.active_zones)
|
||||||
|
# Velocity interlock: must be moving > 0.05 m/s
|
||||||
|
moving = self.current_speed > 0.05
|
||||||
|
spray = in_zone and moving and not self.fault
|
||||||
|
self.set_spray(spray)
|
||||||
|
```
|
||||||
|
|
||||||
|
### 5.6 Integration with Existing Nodes
|
||||||
|
|
||||||
|
| Existing node | Integration point |
|
||||||
|
|---------------|------------------|
|
||||||
|
| `ios_gps_bridge` (Issue #681) | Provides fallback /gps/fix when RTK not fixed |
|
||||||
|
| VESC CAN bridge | path_follower publishes to /cmd_vel → existing bridge forwards to CAN 0x300 |
|
||||||
|
| `saul_tee_driver` | Subscribes /cmd_vel — no changes needed |
|
||||||
|
| UWB tag node | robot_localization already fuses UWB range — paint system uses same /pose topic |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. Parking Lot Templates
|
||||||
|
|
||||||
|
All templates follow **MUTCD (Manual on Uniform Traffic Control Devices)** and **ADA** standards.
|
||||||
|
|
||||||
|
### 6.1 Standard Dimensions
|
||||||
|
|
||||||
|
| Space Type | Width | Depth | Line Width | Notes |
|
||||||
|
|------------|-------|-------|-----------|-------|
|
||||||
|
| Standard | 9 ft (2.74 m) | 18 ft (5.49 m) | 4 in (102 mm) | Most common |
|
||||||
|
| Compact | 8 ft (2.44 m) | 16 ft (4.88 m) | 4 in | Must be labeled |
|
||||||
|
| ADA accessible | 13 ft (3.96 m) | 18 ft | 4 in | Includes 5' access aisle |
|
||||||
|
| Van accessible | 16 ft (4.88 m) | 18 ft | 4 in | 8' space + 8' aisle |
|
||||||
|
| Fire lane | varies | — | 6 in (152 mm) | Red or yellow, MUTCD |
|
||||||
|
|
||||||
|
### 6.2 Stall Angle Templates
|
||||||
|
|
||||||
|
| Angle | Module width | Drive aisle |
|
||||||
|
|-------|-------------|------------|
|
||||||
|
| 90° (perpendicular) | 9.0 ft stall + 9.0 ft module | 24 ft two-way |
|
||||||
|
| 60° angled | 10.4 ft module | 18 ft one-way |
|
||||||
|
| 45° angled | 12.7 ft module | 13 ft one-way |
|
||||||
|
| Parallel | 23 ft length | 12 ft one-way |
|
||||||
|
|
||||||
|
### 6.3 Symbol Templates
|
||||||
|
|
||||||
|
#### Handicap Symbol (ISA — International Symbol of Access)
|
||||||
|
|
||||||
|
Standard 60" × 60" (1.52 m × 1.52 m) wheelchair figure, blue paint. Consists of:
|
||||||
|
- Head circle: 6" diameter
|
||||||
|
- Body and wheel segments: 4–6" strokes
|
||||||
|
|
||||||
|
Robot approach: rasterize ISA symbol into a grid of parallel stripe passes at 2" nozzle width, ~30 passes per symbol.
|
||||||
|
|
||||||
|
#### Arrow Templates
|
||||||
|
|
||||||
|
| Type | Dimensions | Use |
|
||||||
|
|------|-----------|-----|
|
||||||
|
| Straight arrow | 6" wide × 36" long shaft + 18" head | One-way lane |
|
||||||
|
| Curved arrow | 6" wide, 10 ft radius | Turn lane |
|
||||||
|
| Double-headed | 6" wide | Two-way reminder |
|
||||||
|
|
||||||
|
#### Fire Lane Markings
|
||||||
|
|
||||||
|
- "FIRE LANE — NO PARKING" text: 12" tall letters, 4" stroke
|
||||||
|
- Red curb paint: continuous stripe, 6" wide on curb face (robot with angle-mounted nozzle)
|
||||||
|
- MUTCD crosshatch: 6" lines at 45°, 18" spacing
|
||||||
|
|
||||||
|
### 6.4 Layout Design Tool (CLI)
|
||||||
|
|
||||||
|
A Python script `tools/parking_layout.py` will generate a layout JSON from simple parameters:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Generate a 10-space 90° lot section
|
||||||
|
python tools/parking_layout.py \
|
||||||
|
--type 90deg \
|
||||||
|
--spaces 10 \
|
||||||
|
--rows 2 \
|
||||||
|
--origin 37.4219983,-122.084 \
|
||||||
|
--output lot_A.json
|
||||||
|
|
||||||
|
# Preview as SVG
|
||||||
|
python tools/parking_layout.py --input lot_A.json --svg lot_A.svg
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7. Bill of Materials
|
||||||
|
|
||||||
|
### 7.1 Paint Dispensing System
|
||||||
|
|
||||||
|
| Item | Part / Source | Qty | Unit Cost | Total |
|
||||||
|
|------|--------------|-----|-----------|-------|
|
||||||
|
| Inverted marking paint canister (Rustoleum Professional) | Home Depot / Rustoleum | 4 | $8 | $32 |
|
||||||
|
| NC solenoid valve 12 V 1/4" NPT | U.S. Solid / Amazon | 1 | $18 | $18 |
|
||||||
|
| 5 V single-channel relay module | Amazon | 1 | $5 | $5 |
|
||||||
|
| Graco RAC X 515 reversible tip | Paint supply | 1 | $25 | $25 |
|
||||||
|
| 1/4" OD PTFE tubing (1 m) | Amazon | 1 | $8 | $8 |
|
||||||
|
| 1/4" NPT push-to-connect fittings | Amazon | 4 | $3 | $12 |
|
||||||
|
| 1N4007 flyback diode | electronics | 2 | $0.10 | $1 |
|
||||||
|
| 3 mm aluminum plate (bracket stock) | Metal supermarket | 1 | $15 | $15 |
|
||||||
|
| M5 hardware (bolts, nuts, standoffs) | — | lot | $5 | $5 |
|
||||||
|
| 80/20 T-slot bracket 10-series | 80/20 Inc. | 2 | $8 | $16 |
|
||||||
|
| PTFE thread tape | — | 1 | $2 | $2 |
|
||||||
|
| Splash shield (acrylic 3 mm) | — | 1 | $5 | $5 |
|
||||||
|
| **Subtotal — paint system** | | | | **$144** |
|
||||||
|
|
||||||
|
### 7.2 Positioning (RTK GPS)
|
||||||
|
|
||||||
|
| Item | Part / Source | Qty | Unit Cost | Total |
|
||||||
|
|------|--------------|-----|-----------|-------|
|
||||||
|
| u-blox ZED-F9P RTK board (rover) | SparkFun GPS-RTK2 | 1 | $250 | $250 |
|
||||||
|
| u-blox ZED-F9P RTK board (base) | SparkFun GPS-RTK2 | 1 | $250 | $250 |
|
||||||
|
| Survey GNSS antenna (L1/L2) | SparkFun / u-blox | 2 | $65 | $130 |
|
||||||
|
| SMA cable (3 m) | Amazon | 2 | $12 | $24 |
|
||||||
|
| USB-A to USB-C cable (Orin) | — | 1 | $8 | $8 |
|
||||||
|
| **Subtotal — RTK GPS** | | | | **$662** |
|
||||||
|
|
||||||
|
### 7.3 Software / Labor
|
||||||
|
|
||||||
|
All software is open-source / in-house. No licensing cost.
|
||||||
|
|
||||||
|
### 7.4 Total Estimated BOM
|
||||||
|
|
||||||
|
| Category | Cost |
|
||||||
|
|----------|------|
|
||||||
|
| Paint dispensing system | $144 |
|
||||||
|
| RTK GPS system | $662 |
|
||||||
|
| Contingency (10%) | $81 |
|
||||||
|
| **Total** | **$887** |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 8. Safety
|
||||||
|
|
||||||
|
### 8.1 Paint Hazard
|
||||||
|
|
||||||
|
- Aerosol paint is flammable. Keep robot battery terminals isolated from paint canister.
|
||||||
|
- Ensure ventilation if operating in enclosed areas.
|
||||||
|
- Overspray: operate only in calm wind conditions (< 10 mph / 16 km/h).
|
||||||
|
|
||||||
|
### 8.2 Electrical Safety
|
||||||
|
|
||||||
|
- Relay module must be rated for 12 V / 1 A minimum (solenoid inrush).
|
||||||
|
- Flyback diode mandatory across solenoid coil — unclamped kickback can damage relay contacts and IO GPIO.
|
||||||
|
- Fuse solenoid 12 V line with a 2 A polyfuse.
|
||||||
|
|
||||||
|
### 8.3 Operational Safety
|
||||||
|
|
||||||
|
- Never enable autonomous movement unless lot is clear.
|
||||||
|
- Use RC ESTOP (CH6) as immediate abort — confirm IO board firmware pulls spray pin LOW in FAULT handler.
|
||||||
|
- Run at max 0.5 m/s with human observer for initial field trials.
|
||||||
|
- Paint-on while stationary is locked out in software (puddle prevention).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 9. Open Items
|
||||||
|
|
||||||
|
| # | Item | Owner | Priority |
|
||||||
|
|---|------|-------|----------|
|
||||||
|
| 1 | Confirm Orin GPIO pin accessibility (40-pin header on Jetson Orin Nano vs AGX) | mark / hal | High |
|
||||||
|
| 2 | Source RTK CORS network credentials for target parking lot area | Serkan | High |
|
||||||
|
| 3 | Determine parking lot GPS coordinates / anchor point for origin | Serkan | High |
|
||||||
|
| 4 | Fabricate aluminum canister bracket + nozzle arm | Serkan / mech | Medium |
|
||||||
|
| 5 | Write IO board firmware extension (TYPE 0x04 SPRAY_CMD) | mark | Medium |
|
||||||
|
| 6 | Implement `tools/parking_layout.py` layout designer | mark | Medium |
|
||||||
|
| 7 | Field test RTK fix acquisition time in target lot (tree/building multipath) | mark | Medium |
|
||||||
|
| 8 | Calibrate nozzle offset constant (15 cm assumed — measure actual) | mark | Low |
|
||||||
|
| 9 | Evaluate YF-S201 flow sensor for clog detection (v2) | mark | Low |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
*Spec written by mark — parking lot marking agent.*
|
||||||
|
*Questions/corrections → MQTT: `saltybot/mark/status` or open GitHub issue in saltylab-firmware.*
|
||||||
Loading…
x
Reference in New Issue
Block a user