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()