FSD-Level5-CoT / fsd_model /visualization.py
Reality123b's picture
Add visualization.py
3e5fb54 verified
"""
Visualization utilities for FSD model outputs.
Generates visual representations of:
- Sensor placement on vehicle
- BEV perception outputs
- Planned trajectory
- Control commands
"""
import torch
import numpy as np
from typing import Dict, Optional, Tuple, List
import math
def visualize_sensor_layout_ascii(vehicle_config) -> str:
"""Generate ASCII art of sensor placement on vehicle."""
sc = vehicle_config.sensor_config
half_l = vehicle_config.length / 2
half_w = vehicle_config.width / 2
# Create grid
grid_h, grid_w = 30, 50
grid = [[' '] * grid_w for _ in range(grid_h)]
# Scale factors
scale_x = (grid_w - 10) / (vehicle_config.length + 2)
scale_y = (grid_h - 6) / (vehicle_config.width + 2)
cx, cy = grid_w // 2, grid_h // 2
# Draw vehicle outline
vw = int(vehicle_config.length * scale_x / 2)
vh = int(vehicle_config.width * scale_y / 2)
for x in range(cx - vw, cx + vw + 1):
if 0 <= x < grid_w:
if 0 <= cy - vh < grid_h:
grid[cy - vh][x] = '─'
if 0 <= cy + vh < grid_h:
grid[cy + vh][x] = '─'
for y in range(cy - vh, cy + vh + 1):
if 0 <= y < grid_h:
if 0 <= cx - vw < grid_w:
grid[y][cx - vw] = 'β”‚'
if 0 <= cx + vw < grid_w:
grid[y][cx + vw] = 'β”‚'
# Corners
for (dy, dx), ch in [
((-vh, -vw), 'β”Œ'), ((-vh, vw), '┐'),
((vh, -vw), 'β””'), ((vh, vw), 'β”˜')
]:
gy, gx = cy + dy, cx + dx
if 0 <= gy < grid_h and 0 <= gx < grid_w:
grid[gy][gx] = ch
# Direction arrow
if 0 <= cy < grid_h and cx + vw + 1 < grid_w:
grid[cy][cx + vw + 1] = 'β–Ί'
grid[cy][cx] = '+' # center
# Place cameras
for i, cam in enumerate(sc.cameras):
gx = cx + int(cam.placement.x * scale_x)
gy = cy - int(cam.placement.y * scale_y)
if 0 <= gy < grid_h and 0 <= gx < grid_w:
grid[gy][gx] = 'C'
# Place ultrasonics
for i, us in enumerate(sc.ultrasonics):
gx = cx + int(us.placement.x * scale_x)
gy = cy - int(us.placement.y * scale_y)
if 0 <= gy < grid_h and 0 <= gx < grid_w:
if grid[gy][gx] == ' ' or grid[gy][gx] in '─│':
grid[gy][gx] = 'U'
# Add labels
result = "Vehicle Sensor Layout (Top View)\n"
result += "C = Camera, U = Ultrasonic, + = Center, β–Ί = Forward\n"
result += "=" * grid_w + "\n"
for row in grid:
result += ''.join(row) + "\n"
result += "=" * grid_w + "\n"
return result
def format_model_output(output: Dict[str, torch.Tensor]) -> str:
"""Format model output as human-readable text."""
lines = []
lines.append("╔══════════════════════════════════════════╗")
lines.append("β•‘ FSD Model Output Summary β•‘")
lines.append("╠══════════════════════════════════════════╣")
# Control outputs
if "control/steering_deg" in output:
steer = output["control/steering_deg"].mean().item()
throttle = output["control/throttle"].mean().item()
brake = output["control/brake"].mean().item()
lines.append(f"β•‘ Steering: {steer:+.2f}Β°")
lines.append(f"β•‘ Throttle: {throttle:.3f}")
lines.append(f"β•‘ Brake: {brake:.3f}")
# Safety
if "planning/collision_risk" in output:
risk = output["planning/collision_risk"].mean().item()
emergency = output["planning/emergency_brake"].mean().item()
lines.append(f"β•‘ Collision Risk: {risk:.3f}")
lines.append(f"β•‘ Emergency Brake: {emergency:.3f}")
# Behavior
if "planning/behavior_logits" in output:
behaviors = [
"keep_lane", "turn_left", "turn_right", "change_left",
"change_right", "stop", "yield", "park", "reverse", "emergency"
]
probs = torch.softmax(output["planning/behavior_logits"], dim=-1)
top_prob, top_idx = probs.mean(0).topk(3)
lines.append("β•‘ Top behaviors:")
for p, i in zip(top_prob, top_idx):
lines.append(f"β•‘ {behaviors[i.item()]}: {p.item():.3f}")
# Waypoints
if "planning/safe_waypoints" in output:
wp = output["planning/safe_waypoints"]
lines.append(f"β•‘ Planned waypoints: {wp.shape[1]}")
lines.append(f"β•‘ First: ({wp[0,0,0]:.2f}, {wp[0,0,1]:.2f})")
lines.append(f"β•‘ Last: ({wp[0,-1,0]:.2f}, {wp[0,-1,1]:.2f})")
# Controller weights
if "control/controller_weights" in output:
w = output["control/controller_weights"].mean(0)
lines.append(f"β•‘ Controller mix: Neural={w[0]:.2f} Stanley={w[1]:.2f} PID={w[2]:.2f}")
lines.append("β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•")
return "\n".join(lines)
def format_parameter_count(counts: Dict[str, int]) -> str:
"""Format parameter counts as a nice table."""
lines = []
lines.append("β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”")
lines.append("β”‚ Module β”‚ Parameters β”‚ Size (MB) β”‚")
lines.append("β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€")
for name, count in counts.items():
if name in ["total", "total_trainable"]:
continue
size_mb = count * 4 / (1024 * 1024) # float32
lines.append(f"β”‚ {name:<15} β”‚ {count:>12,} β”‚ {size_mb:>8.2f} β”‚")
lines.append("β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€")
total = counts.get("total", 0)
trainable = counts.get("total_trainable", 0)
total_mb = total * 4 / (1024 * 1024)
lines.append(f"β”‚ {'TOTAL':<15} β”‚ {total:>12,} β”‚ {total_mb:>8.2f} β”‚")
lines.append(f"β”‚ {'Trainable':<15} β”‚ {trainable:>12,} β”‚ β”‚")
lines.append("β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜")
return "\n".join(lines)