File size: 5,475 Bytes
2adb41a | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 | """
ml/scripts/final_demo.py
TrackChain Capstone End-to-End Demo Walkthrough (Phase 2.7 tc.v1 SOTA).
Demonstrates the full 5-model multi-modal intelligence stack running in real-time across a 100-meter railway inspection run.
"""
import sys
import time
import json
from pathlib import Path
import numpy as np
# Add repo root to path
repo_root = Path(__file__).resolve().parent.parent.parent
if str(repo_root) not in sys.path:
sys.path.insert(0, str(repo_root))
from ml.core.schema import (
TrackSegment,
DecisionType,
SeverityLevel,
SignalType,
DefectClass,
)
from ml.inference.pipeline import TrackChainMLPipeline
from ml.fusion.rules import TrackChainFusionEngine
def run_capstone_demo(n_segments: int = 5):
print("=" * 80)
print("🚂 TrackChain Multi-Modal Edge Intelligence Stack — Live Capstone Demo")
print(" Architecture: 5-Model Triad (YOLOv8 + PatchCore + EN13848 + Bi-LSTM + Seq-VAE)")
print(" Contract: tc.v1 Unified [0.0, 1.0] Calibration & Cross-Modal Rule Engine")
print("=" * 80)
print("\n[1/4] Initializing ML Pipeline and loading models...")
start_init = time.time()
pipeline = TrackChainMLPipeline(
fusion_engine=TrackChainFusionEngine(persistence_window=2, known_threshold=0.50, novel_threshold=0.50),
conditional_typing=False,
)
print(f" Pipeline initialized in {(time.time() - start_init):.2f}s")
print("\n[2/4] Generating synthetic 100m railway inspection run (5 segments)...")
segments = []
scenarios = [
("Clean Track", False, 0.0, "mainline_standard"),
("Missing Fastener (Visual Known)", True, 0.0, "mainline_standard"),
("Clean Track (Post-Repair)", False, 0.0, "mainline_standard"),
("Track Twist Exceedance (Geometry Known)", False, 9.5, "mainline_standard"),
("Compound Defect: Crack + Gauge Spread (Dual Stream)", True, 14.0, "mainline_high_speed"),
]
for idx, (scenario_name, has_visual_defect, vert_dip_mm, sec_type) in enumerate(scenarios):
start_m = idx * 20.0
end_m = (idx + 1) * 20.0
n_bins = 80
# Frame
frame = np.zeros((480, 640, 3), dtype=np.uint8)
frame[200:280, :] = 120
if has_visual_defect:
frame[220:260, 300:340] = 255 # Defect
# Telemetry
vertical = np.random.normal(0.0, 0.2, n_bins)
if vert_dip_mm > 0:
vertical[30:50] = vert_dip_mm
gauge = np.full(n_bins, 1676.0)
if idx == 4:
gauge[35:45] = 1684.0 # Gauge widening +8mm
telemetry = {
"roll_rad": np.zeros(n_bins),
"lateral_pos_mm": np.random.normal(0.0, 0.3, n_bins),
"vertical_pos_mm": vertical,
"gauge_mm": gauge,
}
seg = TrackSegment(
segment_id=f"seg-{int(start_m):04d}-{int(end_m):04d}",
chainage_start_m=start_m,
chainage_end_m=end_m,
frames=[frame],
telemetry=telemetry,
section_type=sec_type,
)
segments.append((scenario_name, seg))
print(f" Created {len(segments)} test segments.")
print("\n[3/4] Processing live inspection stream across all 5 models & fusion...")
print("-" * 80)
print(f"{'Chainage (m)':<15} | {'Scenario':<25} | {'Decision':<14} | {'Severity':<9} | {'Latency':<8}")
print("-" * 80)
demo_results = []
total_time = 0.0
for scenario_name, seg in segments:
t0 = time.perf_counter()
decision, signals = pipeline.process_segment(seg)
dt_ms = (time.perf_counter() - t0) * 1000.0
total_time += dt_ms
chainage_str = f"{seg.chainage_start_m:.1f}m - {seg.chainage_end_m:.1f}m"
print(f"{chainage_str:<15} | {scenario_name:<25} | {decision.decision.value:<14} | {decision.severity.value:<9} | {dt_ms:>5.1f} ms")
prim_def = decision.primary_defect
prim_def_str = prim_def.value if hasattr(prim_def, "value") else str(prim_def or "none")
demo_results.append({
"chainage": chainage_str,
"scenario": scenario_name,
"decision": decision.decision.value if hasattr(decision.decision, "value") else str(decision.decision),
"severity": decision.severity.value if hasattr(decision.severity, "value") else str(decision.severity),
"confidence": round(float(decision.confidence), 4),
"primary_defect": prim_def_str,
"action": getattr(decision, "action", "Routine inspection cycle maintained."),
"latency_ms": round(dt_ms, 2),
})
avg_ms = total_time / len(segments)
fps = 1000.0 / avg_ms if avg_ms > 0 else 0.0
print("-" * 80)
print(f"[4/4] Demo Summary: Average Pipeline Latency = {avg_ms:.1f} ms ({fps:.1f} FPS, Target >= 15 FPS)")
print("=" * 80)
# Save summary
out_dir = repo_root / "artifacts"
out_dir.mkdir(parents=True, exist_ok=True)
summary_path = out_dir / "demo_run_summary.json"
with open(summary_path, "w", encoding="utf-8") as f:
json.dump({
"schema_version": "tc.v1",
"average_latency_ms": round(avg_ms, 2),
"fps": round(fps, 1),
"segments": demo_results,
"status": "PASSED",
}, f, indent=2)
print(f"\n[OK] Capstone Demo completed successfully. Report saved to: {summary_path}")
return 0
if __name__ == "__main__":
sys.exit(run_capstone_demo())
|