Skip to content

Latest commit

 

History

80 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

JTCS — Jalisco Traffic Control System

Guadalahacks 2026 · Adaptive signal control for urban intersections

A camera mounted above an intersection feeds frames into a YOLOv8 vehicle detector. Detected vehicles are counted per lane using configurable polygon regions. The count drives a timing algorithm that allocates green-light time proportionally to real-time demand. A React dashboard displays live phase state, countdown, cycle timeline, and event log — and lets operators draw multi-intersection networks with green-wave coordination.


Table of Contents

  1. System Architecture
  2. Traffic Engineering Theory
  3. Phase 1 — Single Intersection
  4. Phase 2 — Road Network
  5. Car Simulation Model
  6. Computer Vision Pipeline
  7. Project Structure
  8. Prerequisites & Installation
  9. Running the System
  10. API Reference
  11. Configuration Reference
  12. Training Custom Models
  13. Tech Stack
  14. Roadmap

1. System Architecture

┌─────────────────────────────────────────────────────────────────┐
│  VISION (Python)                                                 │
│                                                                  │
│  Camera / video / RTSP  →  YOLOv8 detector  →  lane counter    │
│  (MSMF / DSHOW / ANY)      yolo11n_traffic      polygon zones   │
│                             yolo11n_road                         │
│                          road segmentor                          │
│                                │                                 │
│            POST /intersections/A1/cars   (every 1 s)            │
│            POST /source/progress         (playback telemetry)    │
└────────────────────────────────┬────────────────────────────────┘
                                 │
┌────────────────────────────────▼────────────────────────────────┐
│  BACKEND  (Node.js — Express + WebSocket)                        │
│                                                                  │
│  POST /intersections/:id/cars  →  SignalController.update()     │
│  GET  /intersections/:id       →  SignalController.snapshot()   │
│  POST /source/*                →  source swap + progress ack    │
│                                                                  │
│  SignalController (1 s tick)                                     │
│  NS_GREEN → NS_YELLOW → EW_GREEN → EW_YELLOW → repeat          │
│  green = clamp(15 + cars×2,  15, 50)                            │
│                                                                  │
│  WebSocket broadcast → UPDATE | SOURCE_* messages               │
└────────────────────────────────┬────────────────────────────────┘
                                 │  ws://
┌────────────────────────────────▼────────────────────────────────┐
│  FRONTEND  (React 19 + Vite — http://localhost:5173)            │
│                                                                  │
│  Phase 1 — Single intersection                                   │
│  ├─ Live phase/countdown/cycle bar                              │
│  ├─ Top-down SVG intersection view (car shapes)                  │
│  ├─ VideoSourcePanel: camera / photo / RTSP input               │
│  └─ Export car counts → Phase 2                                  │
│                                                                  │
│  Phase 2 — Network editor (fully client-side, no backend)       │
│  ├─ SVG node/edge graph editor (900×540, 20 px snap)            │
│  ├─ Per-intersection adaptive signal timing                      │
│  ├─ Animated car simulation (microscopic, physics-based)        │
│  └─ Green-wave offset sync (⚡ 30 km/h design speed)            │
└─────────────────────────────────────────────────────────────────┘

Standalone mode: Phase 1 works entirely in the browser — no backend or camera required. Use the +/− controls to simulate traffic. Phase 2 is always standalone.


2. Traffic Engineering Theory

2.1 Signal Timing Formula

JTCS uses a demand-proportional green time model derived from Webster's optimal cycle formula (F.V. Webster, 1958):

C_opt = (1.5 × L + 5) / (1 − Y)

where L = total lost time per cycle and Y = sum of critical volume/saturation-flow ratios. JTCS approximates Y linearly by vehicle count:

green_time = clamp(BASE_GREEN + cars × PER_CAR,  min = BASE_GREEN,  max = MAX_GREEN)
Constant Value Source / Derivation
BASE_GREEN 15 s MUTCD minimum: 12 m crosswalk ÷ 1.2 m/s (slow pedestrian) + 2 s startup ≈ 12 s, rounded up to 15 s for safety
PER_CAR 2 s HCM 6th ed. saturation headway for passenger cars: 1.8–2.1 s/veh — JTCS uses 2.0 s for mixed traffic
MAX_GREEN 50 s MUTCD maximum for isolated intersections; beyond 50 s, driver noncompliance increases sharply
YELLOW 3 s MUTCD: perception-reaction time (1 s) + braking v²/2a where v = 14 m/s, a = 3.0 m/s² ≈ 3 s

Both backend/src/core/timingEngine.js and frontend/src/hooks/useTimingEngine.js use identical values — they must be kept in sync.

2.2 Lane Capacity Adjustment (3:2 Ratio)

When one approach has more lanes, it processes proportionally more vehicles. JTCS redistributes total green time in a 3:2 ratio (60% / 40%) when lane counts differ — an approximation of the HCM capacity ratio for 2-lane vs. 1-lane approaches at equal saturation flow per lane (~1,800 veh/h/lane):

if (nsLanes > ewLanes) {
  nsGreen = Math.round(total × 3/5)   // 60%
  ewGreen = Math.round(total × 2/5)   // 40%
}

2.3 Network Cycle Length (Phase 2)

For multi-intersection coordination, JTCS computes a demand-scaled cycle between MIN_CYCLE and MAX_CYCLE:

t = min(totalQueuedVehicles / 120,  1.0)
cycleLength = MIN_CYCLE + t × (MAX_CYCLE − MIN_CYCLE)

The normalization constant 120 approximates saturation demand for a 60 s cycle: ~2 phases × 2 lanes × 30 veh/phase. Reaching 120 queued vehicles → maximum cycle justified.

Constant Value Reason
MIN_CYCLE 60 s HCM minimum for coordinated arterials; below 60 s pedestrian intervals become impractical
MAX_CYCLE 100 s MUTCD recommends ≤ 120 s; beyond 100 s, delay to side-street traffic grows nonlinearly
MIN_GREEN 8 s Minimum per phase to prevent starvation (~1–2 vehicles clearing the stop line)

2.4 Green Wave (Bandwidth Optimization)

A green wave staggers signal offsets so vehicles at the design speed encounter consecutive greens. JTCS uses 30 km/h (8.33 m/s) — the typical Guadalajara urban arterial operating speed:

offset(A→B) = distance_m / 8.33 m/s   (seconds)

The ⚡ Sync Wave button computes these offsets from the edge lengths (using a canvas scale of ~0.5 m/pixel) and applies them as phaseOffset values on each node, propagating the green front along each arterial.


3. Phase 1 — Single Intersection

Signal State Machine

PAUSED (initial state — starts on page load)
     │
     └── startSimulation(cars, lanes) ──► NS_GREEN  ──► NS_YELLOW (3 s)
                                              ▲                │
                                              │           EW_YELLOW (3 s)
                                              └──── EW_GREEN ◄─┘

Each state is driven by a 1 s tick. When timeLeft reaches 0, the state advances and green times are recomputed from the current car count — timing adapts to traffic in real time.

Dashboard Components

Component Function
IntersectionView 260×260 SVG top-down view. Car shapes (rect + windshield), traffic light bulbs, flow arrows at corners
CycleBar Segmented horizontal bar showing NS-green / yellow / EW-green / yellow timing. Previews pending values before commit
StatsPanel Stat cards: NS green, EW green, cycle length, yellow
EventLog Per-session collapsible event log. Groups events by startSimulation() call
VideoSourcePanel Switch between camera (browser getUserMedia), photo upload, or RTSP source at runtime

Pending vs. Running State

The Phase 1 dashboard keeps two separate car/lane states:

State Owner Used by
pendingCars / pendingLanes Local useState CycleBar (preview)
state.cars / state.lanes useTrafficState hook IntersectionView, StatsPanel, actual timing

This lets operators adjust values and preview the cycle timing before committing with Start Simulation.

Export P1 → P2

Transfers live car counts from a Phase 1 session into a Phase 2 network node:

  1. Requires: Phase 2 has at least one edge AND Phase 1 simulation is running
  2. Click Export → P2 → Phase 2 shows an import banner
  3. Click any node in the Phase 2 map
  4. classifyAndDistribute() classifies each incoming edge as NS or EW by angle (|sin θ| ≥ |cos θ| → NS)
  5. Distributes Phase 1 cars.NS among NS approaches, cars.EW among EW approaches proportionally
  6. Spawns orange cars into the network with retry logic (up to 20 attempts × 250 ms if blocked)

4. Phase 2 — Road Network

A fully client-side multi-intersection network editor — no backend required.

Editor Controls

Input Action
Click canvas Place node (Node mode)
S key Switch to Edge drawing mode
Click node (Edge mode) Start / complete a road segment
Right-click while dragging Cycle path style: straight → H-V → V-H
Esc Return to Select mode
Del / Backspace Delete selected node or edge
Space Play / pause car simulation

Toolbar

Button Function
▶ / ⏸ Play / pause car simulation
⟳ Reset Cars Clear all cars, re-populate from current edges
🏙 Auto City Assign spawn rates by graph topology, start simulation
↑ Load Map Load SVG background image at 35% opacity (for tracing)
↓ Export SVG Download jtcs-network.svg with embedded signal data
⚡ Sync Wave Apply green-wave offsets (30 km/h design speed)
⏱ slider Car lifetime: 5–120 s (default 20 s)

Edge Properties

Property Range Notes
Direction One-way / Two-way One-way disables backward lane
Priority Major / Normal / Minor Used by Auto City for spawn rate defaults
Forward lanes 0–3
Backward lanes 0–3
Spawn enabled On / Off Disable for transit-only edges
Spawn rate 0.5–60 s Interval between spawns per lane per direction

Signal Indicators

  • Colored circles at the stop line of each approach — red/green per signal state
  • Green arrows on each node — one per approach currently in green phase, pointing toward the source neighbor

5. Car Simulation Model

useCarSimulation.js implements a microscopic traffic simulation using an edge-parametric position model.

Position Model

Each car holds a parameter t ∈ [0, 1] along its current edge:

pixel_x = from.x + t × (to.x − from.x)
pixel_y = from.y + t × (to.y − from.y)

When t ≥ 1, the car transitions to the next edge at t = 0 (random walk).

Physics Parameters

Constant Value Real-world analog
BASE_SPEED 20 px/s ~30 km/h at ~0.5 m/px canvas scale
DECEL_PX 26 px ~13 m braking zone before stop line
NODE_R 16 px Intersection radius ~8 m
FOLLOW_PX 18 px ~9 m minimum following distance (~1 s headway)
Speed variation ±18% speed = BASE_SPEED × (0.82 + random × 0.36) — prevents platoon lock-step

Stop Line Logic

const distLeft = (1 − car.t) × edgeLen
if (distLeft < DECEL_PX + NODE_R && signal is RED) {
  if (distLeft ≤ NODE_R + 4) { /* hold */ }
  speed *= (distLeft − holdDist) / DECEL_PX   // smooth deceleration
}

Car Following (Gipps Model)

Cars on the same lane (same edgeId + fwd) are sorted by t. Each car checks the gap to the car ahead:

if (gap < FOLLOW_T) {
  speed = gap < FOLLOW_T × 0.5 ? 0 : speed × ((gap / FOLLOW_T − 0.5) / 0.5)
}

Speed drops linearly to zero as the gap closes to half the minimum following distance.

Random Walk Routing

At each node, the car picks a connected edge at random. At dead ends, it U-turns.

Spawning

Type Color Entry point Blocked behavior
Interval (regular) Blue hsl(188–240, 78%, 62%) t = 0 Skip if red light or gap too small
Burst (P1→P2 export) Orange hsl(30–55, 90%, 62%) t = random ∈ [0, 0.7] Retry up to 20× with 250 ms delay

Red-light blocking: regular spawns are skipped when the destination node's signal is red for that approach — only burst (exported) cars bypass this check.


6. Computer Vision Pipeline

Architecture

Frame (640×480)
     │
     ├─► VehicleDetector (yolo11n_traffic.pt)
     │     Classes: bus(0) car(1) motorcycle(2) truck(3)
     │     Filter: bbox area < 45% of frame (anti-overfit)
     │
     └─► RoadSegmentor (yolo11n_road.pt)
           Class: road(0)
           Output: road mask → lane polygon zones
                │
                ▼
         LaneCounter (polygon zone containment)
                │
                ▼
         { NS: n, EW: m }   →   POST /intersections/A1/cars (every 1 s)

Models

Model File Dataset Classes mAP@50
Vehicle detector weights/yolo11n_traffic.pt Roboflow traffic bus, car, motorcycle, truck ~72%
Road segmentor weights/yolo11n_road.pt CamVid (Kaggle, 469 imgs) road ~85%

The n (nano) variant is required for Raspberry Pi 4 deployment (~30 fps). The s (small) variant gives higher mAP for PC/server use.

Overfitting Filter

VehicleDetector.max_area_fraction = 0.45

Filters detections whose bounding box exceeds 45% of the frame — prevents the model from flagging the vehicle the camera is mounted on, or detecting a close-range bus as multiple vehicles. This was needed after the KITTI dataset (289 imgs) caused severe overfitting; switching to CamVid (469 imgs) and adding this filter resolved it.

Camera Initialization (Windows)

Windows MSMF holds exclusive camera access. open_camera() tries three backends in order:

for backend in [cv2.CAP_MSMF, cv2.CAP_DSHOW, cv2.CAP_ANY]:
    cap = cv2.VideoCapture(index, backend)
    if cap.isOpened(): return cap

The browser camera tab (getUserMedia) is intentionally separate — it avoids the MSMF exclusive-access conflict.


7. Project Structure

.
├── frontend/                        React 19 dashboard (Vite)
│   └── src/
│       ├── App.jsx                  Router + theme toggle (light/dark)
│       ├── App.css                  Global CSS vars: --bg, --surface, --border, --text, --muted, --accent
│       ├── pages/
│       │   ├── Phase1Page.jsx       Single-intersection dashboard
│       │   └── Phase2Page.jsx       SVG network editor
│       ├── hooks/
│       │   ├── useTrafficState.js   Signal FSM — standalone (mirrors backend)
│       │   ├── useTimingEngine.js   Timing formula + exported constants
│       │   ├── useNetworkBuilder.js Phase 2 graph state + signal sim
│       │   └── useCarSimulation.js  Car physics — RAF loop + spawning
│       └── components/
│           ├── Intersection/        IntersectionView (SVG), TrafficLight
│           ├── Dashboard/           CycleBar, StatsPanel, EventLog
│           ├── Camera/              VideoSourcePanel (camera/photo/RTSP)
│           └── Network/             NetworkMap, CarLayer, NetworkControls, NetworkTimingPanel
│
├── backend/                         Node.js / Express
│   └── src/
│       ├── server.js                Entry point: mounts routes, WS broadcast, injectBroadcast()
│       ├── routes/
│       │   ├── intersections.js     GET /:id, POST /:id/cars
│       │   ├── source.js            Vision source management + injectBroadcast export
│       │   └── network.js           Phase 2 registration (stub)
│       └── core/
│           ├── timingEngine.js      SignalController — MUTCD state machine, 1 s tick
│           └── coordinationEngine.js Green-wave algorithm (stub, returns offset=0)
│
├── vision/                          Python CV pipeline
│   ├── streamer.py                  Main loop: camera → detector → counter → API POST
│   ├── detector.py                  YOLOv8 wrapper (yolo11n_traffic.pt)
│   ├── road_segmentor.py            Road segmentation (yolo11n_road.pt) → RoadGeometry
│   ├── counter.py                   Polygon lane counter
│   └── test_detection.py            Smoke test — no backend required
│
├── training/
│   └── training_cars/               Vehicle detection fine-tuning (YOLOv11n/s)
│       ├── download_dataset.py      Downloads Roboflow dataset (ROBOFLOW_API_KEY)
│       └── train.py                 --model n (RPi) | s (PC)
│
├── training/road/                   Road segmentation training (YOLOv11n-seg)
│   ├── download_dataset.py          Downloads CamVid from Kaggle
│   └── train.py
│
├── start.ps1                        One-command launcher (backend + optional vision)
└── weights/
    ├── yolo11n_traffic.pt           Fine-tuned vehicle detector (~5 MB, committed)
    └── yolo11n_road.pt              Fine-tuned road segmentor (~5 MB, committed)

8. Prerequisites & Installation

Requirements

Component Version
Node.js 18+
Python 3.10+
Camera Webcam, USB, or RTSP stream (optional — standalone mode works without)

Frontend

cd frontend
npm install

Backend

cd backend
npm install

Vision pipeline

cd vision
python -m venv .venv

# Windows
.venv\Scripts\activate
# macOS / Linux / Raspberry Pi
source .venv/bin/activate

pip install -r requirements.txt

Model weights (weights/yolo11n_traffic.pt and weights/yolo11n_road.pt) are committed to the repository — no separate download needed.


9. Running the System

One command (recommended)

start.ps1 at the repo root handles everything: builds the frontend if needed, starts the backend (which serves the app on port 3001), optionally starts the vision streamer, and opens the browser automatically.

# Backend + frontend only (no camera)
.\start.ps1

# With vision streamer (default camera index 0)
.\start.ps1 -Vision

# With a specific camera index
.\start.ps1 -Vision -Source 2

# With a video file
.\start.ps1 -Vision -Source "video.mp4"

# With an RTSP stream
.\start.ps1 -Vision -Source "rtsp://192.168.1.10:8554/stream"

The app opens at http://localhost:3001. The backend serves both the REST API and the built frontend from the same port — no separate dev server needed.

First run: start.ps1 automatically runs npm run build inside frontend/ if dist/ doesn't exist yet. Subsequent runs skip the build and start immediately.

Frontend only (standalone — no backend or camera needed)

cd frontend && npm run dev
# → http://localhost:5173

Use the +/− buttons in Phase 1 to simulate traffic. Phase 2 is always standalone.

Manual full stack (separate terminals)

Terminal 1 — Backend:

cd backend && npm run dev
# → http://localhost:3001

Terminal 2 — Vision:

cd vision
source .venv/bin/activate   # or .venv\Scripts\activate on Windows

python streamer.py                          # built-in webcam (index 0)
python streamer.py --source 1               # alternate camera index
python streamer.py --source video.mp4       # video file
python streamer.py --source rtsp://...      # IP camera / RTSP
python streamer.py --preview                # open OpenCV window (calibration)
python streamer.py --no-push                # detection only, no API push
python test_detection.py                    # smoke test without backend

Finding the camera index (Windows)

import cv2
for i in range(6):
    cap = cv2.VideoCapture(i, cv2.CAP_MSMF)
    print(f'{i}: {"OK" if cap.isOpened() else "no"}')
    cap.release()

Calibrating lane polygons

  1. Run in calibration mode: python streamer.py --no-push --preview
  2. The NS polygon appears yellow, EW appears blue in the preview window
  3. Edit the polygon coordinates in vision/streamer.py:
    NS_POLYGON = [[x1,y1], [x2,y2], [x3,y3], [x4,y4]]
    EW_POLYGON = [[x1,y1], [x2,y2], [x3,y3], [x4,y4]]
  4. Re-run with --no-push to verify, then run normally

10. API Reference

Intersection Routes

GET /intersections/:id

Returns current signal state and timing.

{
  "id": "A1",
  "cars": { "NS": 4, "EW": 2 },
  "currentState": {
    "phase": "NS_GREEN",
    "ns_signal": "GREEN",
    "ew_signal": "RED",
    "time_remaining": 18,
    "ns_green_duration": 23,
    "ew_green_duration": 19
  }
}

POST /intersections/:id/cars

Vision streamer pushes vehicle counts. Triggers a WebSocket UPDATE broadcast to all clients.

{ "cars": { "NS": 6, "EW": 2 }, "lanes": { "NS": 2, "EW": 1 } }

Source Routes

GET /source/status

{ "current": "camera:0", "pending": null, "progress": 0.0 }

POST /source/swap

Queue a runtime source change.

{ "source": "rtsp://192.168.1.10:8554/stream" }

POST /source/progress

Streamer reports video file playback progress (0–1). Progress = 1 signals camera-ready ACK.

{ "source": "video.mp4", "progress": 0.42, "loopsCompleted": 1, "isFile": true }

DELETE /source/pending

Cancel a queued source change before the streamer picks it up.

WebSocket Messages (server → client)

type Payload When
UPDATE { phase, timeLeft, cars, lanes, cycleTime } Every 1 s
SOURCE_PENDING { source } Source swap queued
SOURCE_PROGRESS { source, progress, loopsCompleted, isFile } Each frame during video playback
SOURCE_PENDING_CANCELLED { source } Pending swap cancelled

11. Configuration Reference

Timing Constants

Defined in backend/src/core/timingEngine.js and mirrored in frontend/src/hooks/useTimingEngine.js. Both files must stay identical.

Constant Value Meaning
BASE_GREEN 15 s Minimum green per direction
PER_CAR 2 s Additional green per vehicle detected
MAX_GREEN 50 s Maximum green per phase
YELLOW_DURATION 3 s Fixed yellow interval

Formula: green = clamp(15 + cars × 2, min=15, max=50)

Environment Variables

Variable File Default Description
PORT backend/.env 3001 Backend listen port
VITE_API_URL frontend/.env.local http://localhost:3001 REST base URL (created locally, not in repo)
VITE_WS_URL frontend/.env.local ws://localhost:3001 WebSocket URL
ROBOFLOW_API_KEY training/training_cars/.env — Required for vehicle dataset download
KAGGLE_USERNAME training/road/.env luisxavierxd Kaggle credentials for road dataset
KAGGLE_KEY training/road/.env (see file) Kaggle API key

Vision Constants (vision/streamer.py)

Constant Description
API_URL Backend base URL (default http://localhost:3001)
INTERSECTION_ID Intersection identifier (default "A1")
PUSH_INTERVAL Seconds between count POSTs (default 1.0)
NS_POLYGON Four-point pixel polygon for the NS lane region
EW_POLYGON Four-point pixel polygon for the EW lane region

12. Training Custom Models

Vehicle Detector

Fine-tunes YOLOv11 on a custom traffic dataset from Roboflow. Classes: bus, car, motorcycle, truck.

cd training/training_cars
cp .env.example .env          # set ROBOFLOW_API_KEY
pip install -r requirements.txt
python download_dataset.py    # ~800 annotated images
python train.py --model n     # yolo11n — optimized for Raspberry Pi 4
python train.py --model s     # yolo11s — higher mAP for PC deployment

Output: runs/detect/train/weights/best.pt → copy to weights/yolo11n_traffic.pt

Road Segmentor

Fine-tunes YOLOv11-seg on CamVid to detect drivable road surfaces, used to auto-generate lane polygon zones.

cd training/road
pip install -r requirements.txt
python download_dataset.py    # CamVid from Kaggle (469 labeled images)
python train.py --model n

Output: runs/segment/train/weights/best.pt → copy to weights/yolo11n_road.pt

Dataset note: CamVid (469 imgs) was chosen over KITTI (289 imgs). KITTI caused severe overfitting due to insufficient dataset size — validation loss diverged after ~20 epochs.

Base model policy: Base YOLO checkpoints (yolo*.pt at repo root) are in .gitignore and auto-downloaded by Ultralytics. Only fine-tuned models in weights/ are committed (~11 MB total).


13. Tech Stack

Layer Technology
Frontend React 19, Vite, plain CSS (CSS custom properties for theming)
Visualization Inline SVG — all charts, maps, and intersection views
Backend Node.js, Express 4, ws (WebSocket)
Vision Python 3.10+, Ultralytics YOLOv11, OpenCV
Training Ultralytics YOLOv11, Roboflow SDK, Kaggle API
Deployment target Raspberry Pi 4 (vision) + any Node.js host (backend)

14. Roadmap

Phase 1 — Single Intersection ✅

Feature Status
Demand-proportional timing formula (Webster-derived) ✅
3:2 lane capacity adjustment ✅
Frontend signal state machine (mirrors backend exactly) ✅
React dashboard: phase display, cycle bar, event log ✅
Backend Express API + WebSocket broadcast ✅
YOLOv11n vehicle detector (fine-tuned) ✅
YOLOv11n-seg road segmentor (fine-tuned) ✅
Per-lane polygon counter ✅
Vision streamer: camera / video / RTSP / headless ✅
Runtime source hot-swap ✅
Browser camera tab via getUserMedia (no backend) ✅
Photo upload + 2-photo NS/EW analysis ✅
Pending preview vs. committed running state ✅
Top-down SVG intersection view with car shapes ✅
Export car counts → Phase 2 ✅
Light / dark mode toggle (persisted) ✅
Wire WebSocket live updates into Phase 1 dashboard ⏳
Field test with real intersection camera ⏳

Phase 2 — Road Network ✅ Frontend / ⏳ Backend

Feature Status
SVG node/edge graph editor (900×540, 20 px snap) ✅
Edge path styles: straight, H-V, V-H ✅
Per-intersection adaptive signal timing ✅
Green-wave offset sync (⚡ Sync Wave) ✅
SVG background overlay (Load Map) ✅
SVG export ✅
Microscopic car simulation: spawning, physics, stop-line, following ✅
Red/green stop indicators per approach ✅
Green phase arrow indicators per node ✅
Auto City mode (topology-based spawn assignment) ✅
Spawn rate per edge: 0.5–60 s ✅
Car lifetime slider: 5–120 s ✅
Import from Phase 1 by intersection (NS/EW classification) ✅
Light / dark mode (canvas + all UI) ✅
Backend network registration API ⏳
Backend green-wave coordination engine ⏳
Wire Phase 2 editor to backend ⏳

About

JTCS is an edge-AI traffic system that uses computer vision to adapt green lights based on live vehicle demand.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages