Reality123b commited on
Commit
3e5fb54
Β·
verified Β·
1 Parent(s): 58174b0

Add visualization.py

Browse files
Files changed (1) hide show
  1. fsd_model/visualization.py +163 -0
fsd_model/visualization.py ADDED
@@ -0,0 +1,163 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Visualization utilities for FSD model outputs.
3
+ Generates visual representations of:
4
+ - Sensor placement on vehicle
5
+ - BEV perception outputs
6
+ - Planned trajectory
7
+ - Control commands
8
+ """
9
+
10
+ import torch
11
+ import numpy as np
12
+ from typing import Dict, Optional, Tuple, List
13
+ import math
14
+
15
+
16
+ def visualize_sensor_layout_ascii(vehicle_config) -> str:
17
+ """Generate ASCII art of sensor placement on vehicle."""
18
+ sc = vehicle_config.sensor_config
19
+ half_l = vehicle_config.length / 2
20
+ half_w = vehicle_config.width / 2
21
+
22
+ # Create grid
23
+ grid_h, grid_w = 30, 50
24
+ grid = [[' '] * grid_w for _ in range(grid_h)]
25
+
26
+ # Scale factors
27
+ scale_x = (grid_w - 10) / (vehicle_config.length + 2)
28
+ scale_y = (grid_h - 6) / (vehicle_config.width + 2)
29
+
30
+ cx, cy = grid_w // 2, grid_h // 2
31
+
32
+ # Draw vehicle outline
33
+ vw = int(vehicle_config.length * scale_x / 2)
34
+ vh = int(vehicle_config.width * scale_y / 2)
35
+
36
+ for x in range(cx - vw, cx + vw + 1):
37
+ if 0 <= x < grid_w:
38
+ if 0 <= cy - vh < grid_h:
39
+ grid[cy - vh][x] = '─'
40
+ if 0 <= cy + vh < grid_h:
41
+ grid[cy + vh][x] = '─'
42
+
43
+ for y in range(cy - vh, cy + vh + 1):
44
+ if 0 <= y < grid_h:
45
+ if 0 <= cx - vw < grid_w:
46
+ grid[y][cx - vw] = 'β”‚'
47
+ if 0 <= cx + vw < grid_w:
48
+ grid[y][cx + vw] = 'β”‚'
49
+
50
+ # Corners
51
+ for (dy, dx), ch in [
52
+ ((-vh, -vw), 'β”Œ'), ((-vh, vw), '┐'),
53
+ ((vh, -vw), 'β””'), ((vh, vw), 'β”˜')
54
+ ]:
55
+ gy, gx = cy + dy, cx + dx
56
+ if 0 <= gy < grid_h and 0 <= gx < grid_w:
57
+ grid[gy][gx] = ch
58
+
59
+ # Direction arrow
60
+ if 0 <= cy < grid_h and cx + vw + 1 < grid_w:
61
+ grid[cy][cx + vw + 1] = 'β–Ί'
62
+ grid[cy][cx] = '+' # center
63
+
64
+ # Place cameras
65
+ for i, cam in enumerate(sc.cameras):
66
+ gx = cx + int(cam.placement.x * scale_x)
67
+ gy = cy - int(cam.placement.y * scale_y)
68
+ if 0 <= gy < grid_h and 0 <= gx < grid_w:
69
+ grid[gy][gx] = 'C'
70
+
71
+ # Place ultrasonics
72
+ for i, us in enumerate(sc.ultrasonics):
73
+ gx = cx + int(us.placement.x * scale_x)
74
+ gy = cy - int(us.placement.y * scale_y)
75
+ if 0 <= gy < grid_h and 0 <= gx < grid_w:
76
+ if grid[gy][gx] == ' ' or grid[gy][gx] in '─│':
77
+ grid[gy][gx] = 'U'
78
+
79
+ # Add labels
80
+ result = "Vehicle Sensor Layout (Top View)\n"
81
+ result += "C = Camera, U = Ultrasonic, + = Center, β–Ί = Forward\n"
82
+ result += "=" * grid_w + "\n"
83
+ for row in grid:
84
+ result += ''.join(row) + "\n"
85
+ result += "=" * grid_w + "\n"
86
+
87
+ return result
88
+
89
+
90
+ def format_model_output(output: Dict[str, torch.Tensor]) -> str:
91
+ """Format model output as human-readable text."""
92
+ lines = []
93
+ lines.append("╔══════════════════════════════════════════╗")
94
+ lines.append("β•‘ FSD Model Output Summary β•‘")
95
+ lines.append("╠══════════════════════════════════════════╣")
96
+
97
+ # Control outputs
98
+ if "control/steering_deg" in output:
99
+ steer = output["control/steering_deg"].mean().item()
100
+ throttle = output["control/throttle"].mean().item()
101
+ brake = output["control/brake"].mean().item()
102
+ lines.append(f"β•‘ Steering: {steer:+.2f}Β°")
103
+ lines.append(f"β•‘ Throttle: {throttle:.3f}")
104
+ lines.append(f"β•‘ Brake: {brake:.3f}")
105
+
106
+ # Safety
107
+ if "planning/collision_risk" in output:
108
+ risk = output["planning/collision_risk"].mean().item()
109
+ emergency = output["planning/emergency_brake"].mean().item()
110
+ lines.append(f"β•‘ Collision Risk: {risk:.3f}")
111
+ lines.append(f"β•‘ Emergency Brake: {emergency:.3f}")
112
+
113
+ # Behavior
114
+ if "planning/behavior_logits" in output:
115
+ behaviors = [
116
+ "keep_lane", "turn_left", "turn_right", "change_left",
117
+ "change_right", "stop", "yield", "park", "reverse", "emergency"
118
+ ]
119
+ probs = torch.softmax(output["planning/behavior_logits"], dim=-1)
120
+ top_prob, top_idx = probs.mean(0).topk(3)
121
+ lines.append("β•‘ Top behaviors:")
122
+ for p, i in zip(top_prob, top_idx):
123
+ lines.append(f"β•‘ {behaviors[i.item()]}: {p.item():.3f}")
124
+
125
+ # Waypoints
126
+ if "planning/safe_waypoints" in output:
127
+ wp = output["planning/safe_waypoints"]
128
+ lines.append(f"β•‘ Planned waypoints: {wp.shape[1]}")
129
+ lines.append(f"β•‘ First: ({wp[0,0,0]:.2f}, {wp[0,0,1]:.2f})")
130
+ lines.append(f"β•‘ Last: ({wp[0,-1,0]:.2f}, {wp[0,-1,1]:.2f})")
131
+
132
+ # Controller weights
133
+ if "control/controller_weights" in output:
134
+ w = output["control/controller_weights"].mean(0)
135
+ lines.append(f"β•‘ Controller mix: Neural={w[0]:.2f} Stanley={w[1]:.2f} PID={w[2]:.2f}")
136
+
137
+ lines.append("β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•")
138
+
139
+ return "\n".join(lines)
140
+
141
+
142
+ def format_parameter_count(counts: Dict[str, int]) -> str:
143
+ """Format parameter counts as a nice table."""
144
+ lines = []
145
+ lines.append("β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”")
146
+ lines.append("β”‚ Module β”‚ Parameters β”‚ Size (MB) β”‚")
147
+ lines.append("β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€")
148
+
149
+ for name, count in counts.items():
150
+ if name in ["total", "total_trainable"]:
151
+ continue
152
+ size_mb = count * 4 / (1024 * 1024) # float32
153
+ lines.append(f"β”‚ {name:<15} β”‚ {count:>12,} β”‚ {size_mb:>8.2f} β”‚")
154
+
155
+ lines.append("β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€")
156
+ total = counts.get("total", 0)
157
+ trainable = counts.get("total_trainable", 0)
158
+ total_mb = total * 4 / (1024 * 1024)
159
+ lines.append(f"β”‚ {'TOTAL':<15} β”‚ {total:>12,} β”‚ {total_mb:>8.2f} β”‚")
160
+ lines.append(f"β”‚ {'Trainable':<15} β”‚ {trainable:>12,} β”‚ β”‚")
161
+ lines.append("β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜")
162
+
163
+ return "\n".join(lines)