CCockrum's picture
Update app.py
8e83950 verified
import gradio as gr
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from sklearn.ensemble import RandomForestRegressor
from sklearn.preprocessing import StandardScaler
from sklearn.metrics import mean_squared_error, r2_score
from scipy.optimize import differential_evolution
import warnings
warnings.filterwarnings('ignore')
class F1AerodynamicPredictor:
def __init__(self):
self.model = RandomForestRegressor(n_estimators=100, random_state=42)
self.scaler = StandardScaler()
self.is_trained = False
self.current_data = None
self.data_source = "None"
self.feature_names = ['front_wing_angle', 'rear_wing_angle', 'ride_height',
'suspension_stiffness', 'downforce', 'drag_coefficient',
'track_temp', 'wind_speed', 'track_grip']
def generate_aero_data(self, num_samples=2000):
"""Generate realistic aerodynamic and setup data"""
np.random.seed(42)
# Car setup parameters
front_wing_angle = np.random.uniform(5, 25, num_samples) # degrees
rear_wing_angle = np.random.uniform(8, 35, num_samples) # degrees
ride_height = np.random.uniform(20, 80, num_samples) # mm
suspension_stiffness = np.random.uniform(50, 150, num_samples) # N/mm
# Aerodynamic parameters (derived from setup)
downforce = 800 + (front_wing_angle * 15) + (rear_wing_angle * 20) + np.random.normal(0, 50, num_samples)
drag_coefficient = 0.8 + (front_wing_angle * 0.02) + (rear_wing_angle * 0.025) + np.random.normal(0, 0.1, num_samples)
drag_coefficient = np.clip(drag_coefficient, 0.5, 2.0)
# Environmental conditions
track_temp = np.random.uniform(25, 45, num_samples) # °C
wind_speed = np.random.uniform(0, 15, num_samples) # m/s
track_grip = np.random.uniform(0.7, 1.0, num_samples) # coefficient
# Calculate lap time based on aerodynamic efficiency and setup
# Base lap time around 90 seconds, modified by aero efficiency
aero_efficiency = downforce / drag_coefficient
base_lap_time = 90
# Lap time calculation with realistic physics
lap_time = (base_lap_time -
(aero_efficiency - 800) * 0.01 + # Aero efficiency impact
(ride_height - 50) * 0.05 + # Ride height impact
(track_temp - 35) * 0.1 + # Temperature impact
wind_speed * 0.2 + # Wind resistance
(1 - track_grip) * 10 + # Grip impact
np.random.normal(0, 1, num_samples)) # Random variation
# Ensure realistic lap times
lap_time = np.clip(lap_time, 75, 110)
return pd.DataFrame({
'front_wing_angle': front_wing_angle,
'rear_wing_angle': rear_wing_angle,
'ride_height': ride_height,
'suspension_stiffness': suspension_stiffness,
'downforce': downforce,
'drag_coefficient': drag_coefficient,
'track_temp': track_temp,
'wind_speed': wind_speed,
'track_grip': track_grip,
'lap_time': lap_time
})
def validate_uploaded_data(self, df):
"""Validate uploaded data format and content"""
required_columns = self.feature_names + ['lap_time']
missing_columns = [col for col in required_columns if col not in df.columns]
if missing_columns:
return False, f"Missing required columns: {missing_columns}"
# Check for reasonable value ranges
validation_ranges = {
'front_wing_angle': (0, 50),
'rear_wing_angle': (0, 50),
'ride_height': (10, 150),
'suspension_stiffness': (10, 300),
'downforce': (200, 2000),
'drag_coefficient': (0.3, 3.0),
'track_temp': (5, 60),
'wind_speed': (0, 30),
'track_grip': (0.3, 1.2),
'lap_time': (60, 180)
}
validation_issues = []
for col, (min_val, max_val) in validation_ranges.items():
if col in df.columns:
out_of_range = df[(df[col] < min_val) | (df[col] > max_val)]
if len(out_of_range) > 0:
validation_issues.append(f"{col}: {len(out_of_range)} values out of range ({min_val}-{max_val})")
if validation_issues:
return False, f"Data validation issues: {'; '.join(validation_issues)}"
return True, "Data validation successful"
def load_user_data(self, file_path):
"""Load and validate user-uploaded data"""
try:
# Try to read the file
if file_path.name.endswith('.csv'):
df = pd.read_csv(file_path.name)
elif file_path.name.endswith(('.xlsx', '.xls')):
df = pd.read_excel(file_path.name)
else:
return None, "Unsupported file format. Please upload CSV or Excel files."
# Validate data
is_valid, message = self.validate_uploaded_data(df)
if not is_valid:
return None, message
# Store the data
self.current_data = df
self.data_source = "User uploaded"
return df, f"Successfully loaded {len(df)} records from uploaded file."
except Exception as e:
return None, f"Error loading file: {str(e)}"
def train_model(self, data):
"""Train the aerodynamic performance model"""
X = data[self.feature_names]
y = data['lap_time']
# Scale features
X_scaled = self.scaler.fit_transform(X)
# Train model
self.model.fit(X_scaled, y)
self.is_trained = True
# Calculate performance metrics
y_pred = self.model.predict(X_scaled)
r2 = r2_score(y, y_pred)
rmse = np.sqrt(mean_squared_error(y, y_pred))
return r2, rmse
def predict_lap_time(self, setup_params):
"""Predict lap time for given setup parameters"""
if not self.is_trained:
return None
# Prepare input
X = np.array([setup_params]).reshape(1, -1)
X_scaled = self.scaler.transform(X)
# Predict
lap_time = self.model.predict(X_scaled)[0]
return lap_time
def optimize_setup(self, track_conditions):
"""Optimize car setup for given track conditions"""
if not self.is_trained:
return None
track_temp, wind_speed, track_grip = track_conditions
def objective(params):
"""Objective function to minimize lap time"""
setup_params = list(params) + [track_temp, wind_speed, track_grip]
return self.predict_lap_time(setup_params)
# Define bounds for optimization (setup parameters only)
bounds = [
(5, 25), # front_wing_angle
(8, 35), # rear_wing_angle
(20, 80), # ride_height
(50, 150), # suspension_stiffness
(600, 1200), # downforce
(0.5, 2.0) # drag_coefficient
]
# Optimize using differential evolution
result = differential_evolution(objective, bounds, maxiter=100, seed=42)
optimal_setup = result.x
optimal_lap_time = result.fun
return optimal_setup, optimal_lap_time
def create_visualizations(self, data):
"""Create aerodynamic analysis visualizations"""
fig, axes = plt.subplots(2, 2, figsize=(15, 12))
# Downforce vs Drag trade-off
aero_efficiency = data['downforce'] / data['drag_coefficient']
scatter = axes[0, 0].scatter(data['drag_coefficient'], data['downforce'],
c=data['lap_time'], cmap='RdYlBu_r', alpha=0.6)
axes[0, 0].set_xlabel('Drag Coefficient')
axes[0, 0].set_ylabel('Downforce (N)')
axes[0, 0].set_title('Downforce vs Drag Trade-off')
plt.colorbar(scatter, ax=axes[0, 0], label='Lap Time (s)')
axes[0, 0].grid(True, alpha=0.3)
# Wing angle correlation
axes[0, 1].scatter(data['front_wing_angle'], data['rear_wing_angle'],
c=data['lap_time'], cmap='RdYlBu_r', alpha=0.6)
axes[0, 1].set_xlabel('Front Wing Angle (°)')
axes[0, 1].set_ylabel('Rear Wing Angle (°)')
axes[0, 1].set_title('Wing Angle Configuration')
axes[0, 1].grid(True, alpha=0.3)
# Aerodynamic efficiency distribution
axes[1, 0].hist(aero_efficiency, bins=30, alpha=0.7, color='skyblue', edgecolor='black')
axes[1, 0].set_xlabel('Aerodynamic Efficiency (Downforce/Drag)')
axes[1, 0].set_ylabel('Frequency')
axes[1, 0].set_title('Aerodynamic Efficiency Distribution')
axes[1, 0].grid(True, alpha=0.3)
# Lap time vs environmental conditions
axes[1, 1].scatter(data['track_temp'], data['lap_time'], alpha=0.6, label='Track Temp')
axes[1, 1].set_xlabel('Track Temperature (°C)')
axes[1, 1].set_ylabel('Lap Time (s)')
axes[1, 1].set_title('Environmental Impact on Performance')
axes[1, 1].grid(True, alpha=0.3)
plt.tight_layout()
return fig
def compare_datasets(self, synthetic_data, user_data):
"""Compare user data with synthetic baseline"""
fig, axes = plt.subplots(2, 2, figsize=(15, 12))
# Lap time distributions
axes[0, 0].hist(synthetic_data['lap_time'], bins=30, alpha=0.7,
label='Synthetic', color='blue', edgecolor='black')
axes[0, 0].hist(user_data['lap_time'], bins=30, alpha=0.7,
label='User Data', color='red', edgecolor='black')
axes[0, 0].set_xlabel('Lap Time (s)')
axes[0, 0].set_ylabel('Frequency')
axes[0, 0].set_title('Lap Time Distribution Comparison')
axes[0, 0].legend()
axes[0, 0].grid(True, alpha=0.3)
# Aerodynamic efficiency comparison
synthetic_eff = synthetic_data['downforce'] / synthetic_data['drag_coefficient']
user_eff = user_data['downforce'] / user_data['drag_coefficient']
axes[0, 1].scatter(synthetic_data['drag_coefficient'], synthetic_data['downforce'],
alpha=0.5, label='Synthetic', color='blue')
axes[0, 1].scatter(user_data['drag_coefficient'], user_data['downforce'],
alpha=0.5, label='User Data', color='red')
axes[0, 1].set_xlabel('Drag Coefficient')
axes[0, 1].set_ylabel('Downforce (N)')
axes[0, 1].set_title('Aerodynamic Trade-off Comparison')
axes[0, 1].legend()
axes[0, 1].grid(True, alpha=0.3)
# Wing angle comparison
axes[1, 0].scatter(synthetic_data['front_wing_angle'], synthetic_data['rear_wing_angle'],
alpha=0.5, label='Synthetic', color='blue')
axes[1, 0].scatter(user_data['front_wing_angle'], user_data['rear_wing_angle'],
alpha=0.5, label='User Data', color='red')
axes[1, 0].set_xlabel('Front Wing Angle (°)')
axes[1, 0].set_ylabel('Rear Wing Angle (°)')
axes[1, 0].set_title('Wing Configuration Comparison')
axes[1, 0].legend()
axes[1, 0].grid(True, alpha=0.3)
# Performance correlation
axes[1, 1].scatter(synthetic_eff, synthetic_data['lap_time'],
alpha=0.5, label='Synthetic', color='blue')
axes[1, 1].scatter(user_eff, user_data['lap_time'],
alpha=0.5, label='User Data', color='red')
axes[1, 1].set_xlabel('Aerodynamic Efficiency')
axes[1, 1].set_ylabel('Lap Time (s)')
axes[1, 1].set_title('Efficiency vs Performance')
axes[1, 1].legend()
axes[1, 1].grid(True, alpha=0.3)
plt.tight_layout()
return fig
# Initialize the predictor
predictor = F1AerodynamicPredictor()
def analyze_aerodynamics():
"""Main aerodynamic analysis function"""
# Generate data
data = predictor.generate_aero_data(2000)
# Train model
r2, rmse = predictor.train_model(data)
# Create visualizations
fig = predictor.create_visualizations(data)
# Generate report
report = f"""
## F1 Aerodynamic Performance Analysis (Synthetic Data)
**Model Performance:**
- R² Score: {r2:.3f}
- RMSE: {rmse:.3f} seconds
**Dataset Statistics:**
- Total configurations analyzed: {len(data)}
- Fastest lap time: {data['lap_time'].min():.2f}s
- Slowest lap time: {data['lap_time'].max():.2f}s
- Average lap time: {data['lap_time'].mean():.2f}s
**Aerodynamic Insights:**
- Average downforce: {data['downforce'].mean():.0f}N
- Average drag coefficient: {data['drag_coefficient'].mean():.3f}
- Best aero efficiency: {(data['downforce'] / data['drag_coefficient']).max():.1f}
**Optimal Ranges:**
- Front wing: {data['front_wing_angle'].quantile(0.1):.1f}° - {data['front_wing_angle'].quantile(0.9):.1f}°
- Rear wing: {data['rear_wing_angle'].quantile(0.1):.1f}° - {data['rear_wing_angle'].quantile(0.9):.1f}°
- Ride height: {data['ride_height'].quantile(0.1):.1f}mm - {data['ride_height'].quantile(0.9):.1f}mm
"""
return fig, report
def upload_and_analyze_data(file):
"""Handle file upload and analysis"""
if file is None:
return None, "Please upload a data file.", None
# Load user data
user_data, message = predictor.load_user_data(file)
if user_data is None:
return None, message, None
# Train model on user data
r2, rmse = predictor.train_model(user_data)
# Create visualizations
fig = predictor.create_visualizations(user_data)
# Generate report
report = f"""
## F1 Aerodynamic Performance Analysis (User Data)
**Data Load Status:** {message}
**Model Performance:**
- R² Score: {r2:.3f}
- RMSE: {rmse:.3f} seconds
**Dataset Statistics:**
- Total configurations analyzed: {len(user_data)}
- Fastest lap time: {user_data['lap_time'].min():.2f}s
- Slowest lap time: {user_data['lap_time'].max():.2f}s
- Average lap time: {user_data['lap_time'].mean():.2f}s
**Aerodynamic Insights:**
- Average downforce: {user_data['downforce'].mean():.0f}N
- Average drag coefficient: {user_data['drag_coefficient'].mean():.3f}
- Best aero efficiency: {(user_data['downforce'] / user_data['drag_coefficient']).max():.1f}
**Data Quality Assessment:**
- Missing values: {user_data.isnull().sum().sum()}
- Duplicate records: {user_data.duplicated().sum()}
- Data range validation: Passed
"""
return fig, report, user_data
def compare_data_sources():
"""Compare user data with synthetic baseline"""
if predictor.current_data is None:
return None, "Please upload data first to enable comparison."
# Generate synthetic data for comparison
synthetic_data = predictor.generate_aero_data(len(predictor.current_data))
# Create comparison visualization
fig = predictor.compare_datasets(synthetic_data, predictor.current_data)
# Generate comparison report
synthetic_avg = synthetic_data['lap_time'].mean()
user_avg = predictor.current_data['lap_time'].mean()
report = f"""
## Data Comparison Report
**Performance Comparison:**
- Synthetic Data Average Lap Time: {synthetic_avg:.2f}s
- User Data Average Lap Time: {user_avg:.2f}s
- Difference: {user_avg - synthetic_avg:.2f}s
**Setup Characteristics:**
- Synthetic wing angles are more conservative
- User data shows {'more aggressive' if user_avg < synthetic_avg else 'more conservative'} setup approach
- Aerodynamic efficiency patterns {'align well' if abs(user_avg - synthetic_avg) < 2 else 'show significant differences'}
**Recommendations:**
- {'Your data suggests more aggressive setups than baseline' if user_avg < synthetic_avg else 'Consider more aggressive aerodynamic configurations'}
- Validate setup ranges against your specific track conditions
- Use comparison insights to refine optimization parameters
"""
return fig, report
def predict_performance(front_wing, rear_wing, ride_height, suspension, downforce, drag_coeff, track_temp, wind_speed, track_grip):
"""Predict lap time for given setup"""
if not predictor.is_trained:
return "Please run the analysis first to train the model!"
setup_params = [front_wing, rear_wing, ride_height, suspension, downforce, drag_coeff, track_temp, wind_speed, track_grip]
lap_time = predictor.predict_lap_time(setup_params)
data_source_note = f"(Model trained on: {predictor.data_source})"
return f"Predicted Lap Time: {lap_time:.3f} seconds {data_source_note}"
def optimize_car_setup(track_temp, wind_speed, track_grip):
"""Optimize car setup for given conditions"""
if not predictor.is_trained:
return "Please run the analysis first to train the model!"
track_conditions = [track_temp, wind_speed, track_grip]
result = predictor.optimize_setup(track_conditions)
if result is None:
return "Optimization failed"
optimal_setup, optimal_lap_time = result
setup_report = f"""
## Optimal Car Setup
**Predicted Lap Time: {optimal_lap_time:.3f} seconds**
*(Model trained on: {predictor.data_source})*
**Optimal Configuration:**
- Front Wing Angle: {optimal_setup[0]:.1f}°
- Rear Wing Angle: {optimal_setup[1]:.1f}°
- Ride Height: {optimal_setup[2]:.1f}mm
- Suspension Stiffness: {optimal_setup[3]:.1f} N/mm
- Target Downforce: {optimal_setup[4]:.0f}N
- Target Drag Coefficient: {optimal_setup[5]:.3f}
**Track Conditions:**
- Track Temperature: {track_temp}°C
- Wind Speed: {wind_speed} m/s
- Track Grip: {track_grip}
"""
return setup_report
def create_sample_data():
"""Create sample data file for download"""
sample_data = predictor.generate_aero_data(100)
return sample_data.to_csv(index=False)
# Create Gradio interface
with gr.Blocks(title="F1 Aerodynamic Performance Predictor", theme=gr.themes.Soft()) as demo:
gr.Markdown("# F1 Aerodynamic Performance Predictor")
gr.Markdown("AI-powered aerodynamic analysis and setup optimization for Formula 1 racing.")
with gr.Tab("Synthetic Data Analysis"):
gr.Markdown("### Analyze synthetic aerodynamic performance data")
analyze_btn = gr.Button("🔍 Analyze Synthetic Data", variant="primary")
with gr.Row():
with gr.Column(scale=2):
aero_plot = gr.Plot(label="Aerodynamic Analysis")
with gr.Column(scale=1):
aero_report = gr.Markdown(label="Analysis Report")
analyze_btn.click(
analyze_aerodynamics,
outputs=[aero_plot, aero_report]
)
with gr.Tab("Upload & Analyze Data"):
gr.Markdown("### Upload your own F1 data for analysis")
gr.Markdown("**Required columns:** front_wing_angle, rear_wing_angle, ride_height, suspension_stiffness, downforce, drag_coefficient, track_temp, wind_speed, track_grip, lap_time")
with gr.Row():
with gr.Column():
file_upload = gr.File(
label="Upload CSV or Excel file",
file_types=[".csv", ".xlsx", ".xls"]
)
sample_btn = gr.Button("Download Sample Data Format", variant="secondary")
sample_file = gr.File(label="Sample Data", visible=False)
upload_btn = gr.Button("Analyze Uploaded Data", variant="primary")
with gr.Column():
upload_status = gr.Markdown("No file uploaded yet.")
with gr.Row():
with gr.Column(scale=2):
upload_plot = gr.Plot(label="Data Analysis")
with gr.Column(scale=1):
upload_report = gr.Markdown(label="Analysis Report")
# Comparison section
gr.Markdown("### Compare with Synthetic Baseline")
compare_btn = gr.Button("Compare Data Sources", variant="secondary")
with gr.Row():
with gr.Column(scale=2):
comparison_plot = gr.Plot(label="Data Comparison")
with gr.Column(scale=1):
comparison_report = gr.Markdown(label="Comparison Report")
# Event handlers
sample_btn.click(
create_sample_data,
outputs=[sample_file]
)
upload_btn.click(
upload_and_analyze_data,
inputs=[file_upload],
outputs=[upload_plot, upload_report, upload_status]
)
compare_btn.click(
compare_data_sources,
outputs=[comparison_plot, comparison_report]
)
with gr.Tab("Performance Prediction"):
gr.Markdown("### Predict lap time for specific setup")
gr.Markdown("*Note: Train the model first using synthetic or uploaded data*")
with gr.Row():
with gr.Column():
gr.Markdown("**Car Setup Parameters:**")
front_wing_input = gr.Slider(5, 25, value=15, label="Front Wing Angle (°)")
rear_wing_input = gr.Slider(8, 35, value=20, label="Rear Wing Angle (°)")
ride_height_input = gr.Slider(20, 80, value=50, label="Ride Height (mm)")
suspension_input = gr.Slider(50, 150, value=100, label="Suspension Stiffness (N/mm)")
downforce_input = gr.Slider(600, 1200, value=900, label="Downforce (N)")
drag_input = gr.Slider(0.5, 2.0, value=1.0, label="Drag Coefficient")
with gr.Column():
gr.Markdown("**Track Conditions:**")
track_temp_input = gr.Slider(25, 45, value=35, label="Track Temperature (°C)")
wind_speed_input = gr.Slider(0, 15, value=5, label="Wind Speed (m/s)")
track_grip_input = gr.Slider(0.7, 1.0, value=0.85, label="Track Grip")
predict_btn = gr.Button("🎯 Predict Lap Time", variant="secondary")
lap_time_output = gr.Textbox(label="Lap Time Prediction", interactive=False)
predict_btn.click(
predict_performance,
inputs=[front_wing_input, rear_wing_input, ride_height_input, suspension_input,
downforce_input, drag_input, track_temp_input, wind_speed_input, track_grip_input],
outputs=[lap_time_output]
)
with gr.Tab("Setup Optimization"):
gr.Markdown("### Optimize car setup for track conditions")
gr.Markdown("*Uses genetic algorithm to find optimal aerodynamic configuration*")
with gr.Row():
with gr.Column():
gr.Markdown("**Track Conditions:**")
opt_track_temp = gr.Slider(25, 45, value=35, label="Track Temperature (°C)")
opt_wind_speed = gr.Slider(0, 15, value=5, label="Wind Speed (m/s)")
opt_track_grip = gr.Slider(0.7, 1.0, value=0.85, label="Track Grip")
optimize_btn = gr.Button("🔧 Optimize Setup", variant="primary")
with gr.Column():
optimization_output = gr.Markdown(label="Optimization Results")
optimize_btn.click(
optimize_car_setup,
inputs=[opt_track_temp, opt_wind_speed, opt_track_grip],
outputs=[optimization_output]
)
with gr.Tab("About"):
gr.Markdown("""
## About This Enhanced Tool
This F1 Aerodynamic Performance Predictor now supports both synthetic and real data analysis:
**Dual Data Sources:**
- **Synthetic Data**: Realistic simulated F1 aerodynamic data for learning and experimentation
- **User Data**: Upload your own telemetry, test results, or historical performance data
**Data Upload Features:**
- CSV and Excel file support
- Automatic data validation and quality checks
- Sample data template download
- Comparison analysis between your data and synthetic baseline
**Enhanced Analysis:**
- Model training on real or synthetic data
- Data quality assessment and validation
- Performance comparison between different datasets
- Track which data source was used for predictions
**Setup Optimization:**
- Uses Differential Evolution algorithm for global optimization
- Adapts to patterns in your specific data
- Provides data-source-aware recommendations
**Required Data Format:**
Your uploaded data should include these columns:
- front_wing_angle, rear_wing_angle, ride_height
- suspension_stiffness, downforce, drag_coefficient
- track_temp, wind_speed, track_grip, lap_time
**Professional Applications:**
- Validate simulation models against real telemetry
- Identify setup trends and patterns in your data
- Optimize configurations for specific track conditions
- Compare your team's approach with industry baselines
""")
if __name__ == "__main__":
demo.launch()