CCockrum commited on
Commit
8e83950
·
verified ·
1 Parent(s): 081f5f0

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +298 -71
app.py CHANGED
@@ -14,6 +14,8 @@ class F1AerodynamicPredictor:
14
  self.model = RandomForestRegressor(n_estimators=100, random_state=42)
15
  self.scaler = StandardScaler()
16
  self.is_trained = False
 
 
17
  self.feature_names = ['front_wing_angle', 'rear_wing_angle', 'ride_height',
18
  'suspension_stiffness', 'downforce', 'drag_coefficient',
19
  'track_temp', 'wind_speed', 'track_grip']
@@ -68,6 +70,65 @@ class F1AerodynamicPredictor:
68
  'lap_time': lap_time
69
  })
70
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
71
  def train_model(self, data):
72
  """Train the aerodynamic performance model"""
73
  X = data[self.feature_names]
@@ -130,40 +191,6 @@ class F1AerodynamicPredictor:
130
 
131
  return optimal_setup, optimal_lap_time
132
 
133
- def analyze_sensitivity(self, base_setup, track_conditions):
134
- """Analyze sensitivity of lap time to setup changes"""
135
- if not self.is_trained:
136
- return None, None
137
-
138
- # Base lap time
139
- base_params = list(base_setup) + list(track_conditions)
140
- base_lap_time = self.predict_lap_time(base_params)
141
-
142
- # Analyze each parameter
143
- sensitivity_data = []
144
- param_names = ['Front Wing', 'Rear Wing', 'Ride Height', 'Suspension', 'Downforce', 'Drag Coeff']
145
-
146
- for i, param_name in enumerate(param_names):
147
- # Test parameter variations
148
- variations = np.linspace(0.8, 1.2, 21) # ±20% variation
149
- lap_times = []
150
-
151
- for var in variations:
152
- test_setup = base_setup.copy()
153
- test_setup[i] *= var
154
- test_params = list(test_setup) + list(track_conditions)
155
- lap_time = self.predict_lap_time(test_params)
156
- lap_times.append(lap_time)
157
-
158
- sensitivity_data.append({
159
- 'parameter': param_name,
160
- 'variations': variations,
161
- 'lap_times': lap_times,
162
- 'sensitivity': np.std(lap_times)
163
- })
164
-
165
- return sensitivity_data, base_lap_time
166
-
167
  def create_visualizations(self, data):
168
  """Create aerodynamic analysis visualizations"""
169
  fig, axes = plt.subplots(2, 2, figsize=(15, 12))
@@ -202,6 +229,60 @@ class F1AerodynamicPredictor:
202
 
203
  plt.tight_layout()
204
  return fig
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
205
 
206
  # Initialize the predictor
207
  predictor = F1AerodynamicPredictor()
@@ -219,7 +300,7 @@ def analyze_aerodynamics():
219
 
220
  # Generate report
221
  report = f"""
222
- ## F1 Aerodynamic Performance Analysis
223
 
224
  **Model Performance:**
225
  - R² Score: {r2:.3f}
@@ -244,6 +325,87 @@ def analyze_aerodynamics():
244
 
245
  return fig, report
246
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
247
  def predict_performance(front_wing, rear_wing, ride_height, suspension, downforce, drag_coeff, track_temp, wind_speed, track_grip):
248
  """Predict lap time for given setup"""
249
  if not predictor.is_trained:
@@ -252,7 +414,9 @@ def predict_performance(front_wing, rear_wing, ride_height, suspension, downforc
252
  setup_params = [front_wing, rear_wing, ride_height, suspension, downforce, drag_coeff, track_temp, wind_speed, track_grip]
253
  lap_time = predictor.predict_lap_time(setup_params)
254
 
255
- return f"🏁 Predicted Lap Time: {lap_time:.3f} seconds"
 
 
256
 
257
  def optimize_car_setup(track_temp, wind_speed, track_grip):
258
  """Optimize car setup for given conditions"""
@@ -271,6 +435,7 @@ def optimize_car_setup(track_temp, wind_speed, track_grip):
271
  ## Optimal Car Setup
272
 
273
  **Predicted Lap Time: {optimal_lap_time:.3f} seconds**
 
274
 
275
  **Optimal Configuration:**
276
  - Front Wing Angle: {optimal_setup[0]:.1f}°
@@ -288,14 +453,19 @@ def optimize_car_setup(track_temp, wind_speed, track_grip):
288
 
289
  return setup_report
290
 
 
 
 
 
 
291
  # Create Gradio interface
292
  with gr.Blocks(title="F1 Aerodynamic Performance Predictor", theme=gr.themes.Soft()) as demo:
293
  gr.Markdown("# F1 Aerodynamic Performance Predictor")
294
  gr.Markdown("AI-powered aerodynamic analysis and setup optimization for Formula 1 racing.")
295
 
296
- with gr.Tab("Aerodynamic Analysis"):
297
- gr.Markdown("### Analyze aerodynamic performance data")
298
- analyze_btn = gr.Button("🔍 Analyze Aerodynamics", variant="primary")
299
 
300
  with gr.Row():
301
  with gr.Column(scale=2):
@@ -308,9 +478,61 @@ with gr.Blocks(title="F1 Aerodynamic Performance Predictor", theme=gr.themes.Sof
308
  outputs=[aero_plot, aero_report]
309
  )
310
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
311
  with gr.Tab("Performance Prediction"):
312
  gr.Markdown("### Predict lap time for specific setup")
313
- gr.Markdown("*Note: Run the analysis first to train the model*")
314
 
315
  with gr.Row():
316
  with gr.Column():
@@ -363,37 +585,42 @@ with gr.Blocks(title="F1 Aerodynamic Performance Predictor", theme=gr.themes.Sof
363
 
364
  with gr.Tab("About"):
365
  gr.Markdown("""
366
- ## About This Tool
367
-
368
- This F1 Aerodynamic Performance Predictor uses advanced machine learning and optimization techniques:
369
-
370
- **Performance Prediction:**
371
- - Random Forest model predicts lap times based on car setup and track conditions
372
- - Considers aerodynamic efficiency, wing configurations, and environmental factors
373
- - Trained on realistic F1 aerodynamic data
374
-
375
- **🔧 Setup Optimization:**
376
- - Uses Differential Evolution algorithm to find optimal car configurations
377
- - Balances downforce vs drag for maximum performance
378
- - Considers track-specific conditions for tailored setups
379
-
380
- **Key Features:**
381
- - Aerodynamic efficiency analysis (downforce/drag ratio)
382
- - Wing angle optimization for different track types
383
- - Environmental impact assessment (temperature, wind, grip)
384
- - Sensitivity analysis for setup parameters
385
-
386
- **Technical Implementation:**
387
- - Random Forest Regressor for non-linear relationships
388
- - Differential Evolution for global optimization
389
- - StandardScaler for feature normalization
390
- - Advanced visualization of aerodynamic trade-offs
391
-
392
- **Racing Applications:**
393
- - Pre-race setup optimization
394
- - Strategy planning for different track conditions
395
- - Understanding aerodynamic trade-offs
396
- - Performance prediction for qualifying and race scenarios
 
 
 
 
 
397
  """)
398
 
399
  if __name__ == "__main__":
 
14
  self.model = RandomForestRegressor(n_estimators=100, random_state=42)
15
  self.scaler = StandardScaler()
16
  self.is_trained = False
17
+ self.current_data = None
18
+ self.data_source = "None"
19
  self.feature_names = ['front_wing_angle', 'rear_wing_angle', 'ride_height',
20
  'suspension_stiffness', 'downforce', 'drag_coefficient',
21
  'track_temp', 'wind_speed', 'track_grip']
 
70
  'lap_time': lap_time
71
  })
72
 
73
+ def validate_uploaded_data(self, df):
74
+ """Validate uploaded data format and content"""
75
+ required_columns = self.feature_names + ['lap_time']
76
+ missing_columns = [col for col in required_columns if col not in df.columns]
77
+
78
+ if missing_columns:
79
+ return False, f"Missing required columns: {missing_columns}"
80
+
81
+ # Check for reasonable value ranges
82
+ validation_ranges = {
83
+ 'front_wing_angle': (0, 50),
84
+ 'rear_wing_angle': (0, 50),
85
+ 'ride_height': (10, 150),
86
+ 'suspension_stiffness': (10, 300),
87
+ 'downforce': (200, 2000),
88
+ 'drag_coefficient': (0.3, 3.0),
89
+ 'track_temp': (5, 60),
90
+ 'wind_speed': (0, 30),
91
+ 'track_grip': (0.3, 1.2),
92
+ 'lap_time': (60, 180)
93
+ }
94
+
95
+ validation_issues = []
96
+ for col, (min_val, max_val) in validation_ranges.items():
97
+ if col in df.columns:
98
+ out_of_range = df[(df[col] < min_val) | (df[col] > max_val)]
99
+ if len(out_of_range) > 0:
100
+ validation_issues.append(f"{col}: {len(out_of_range)} values out of range ({min_val}-{max_val})")
101
+
102
+ if validation_issues:
103
+ return False, f"Data validation issues: {'; '.join(validation_issues)}"
104
+
105
+ return True, "Data validation successful"
106
+
107
+ def load_user_data(self, file_path):
108
+ """Load and validate user-uploaded data"""
109
+ try:
110
+ # Try to read the file
111
+ if file_path.name.endswith('.csv'):
112
+ df = pd.read_csv(file_path.name)
113
+ elif file_path.name.endswith(('.xlsx', '.xls')):
114
+ df = pd.read_excel(file_path.name)
115
+ else:
116
+ return None, "Unsupported file format. Please upload CSV or Excel files."
117
+
118
+ # Validate data
119
+ is_valid, message = self.validate_uploaded_data(df)
120
+ if not is_valid:
121
+ return None, message
122
+
123
+ # Store the data
124
+ self.current_data = df
125
+ self.data_source = "User uploaded"
126
+
127
+ return df, f"Successfully loaded {len(df)} records from uploaded file."
128
+
129
+ except Exception as e:
130
+ return None, f"Error loading file: {str(e)}"
131
+
132
  def train_model(self, data):
133
  """Train the aerodynamic performance model"""
134
  X = data[self.feature_names]
 
191
 
192
  return optimal_setup, optimal_lap_time
193
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
194
  def create_visualizations(self, data):
195
  """Create aerodynamic analysis visualizations"""
196
  fig, axes = plt.subplots(2, 2, figsize=(15, 12))
 
229
 
230
  plt.tight_layout()
231
  return fig
232
+
233
+ def compare_datasets(self, synthetic_data, user_data):
234
+ """Compare user data with synthetic baseline"""
235
+ fig, axes = plt.subplots(2, 2, figsize=(15, 12))
236
+
237
+ # Lap time distributions
238
+ axes[0, 0].hist(synthetic_data['lap_time'], bins=30, alpha=0.7,
239
+ label='Synthetic', color='blue', edgecolor='black')
240
+ axes[0, 0].hist(user_data['lap_time'], bins=30, alpha=0.7,
241
+ label='User Data', color='red', edgecolor='black')
242
+ axes[0, 0].set_xlabel('Lap Time (s)')
243
+ axes[0, 0].set_ylabel('Frequency')
244
+ axes[0, 0].set_title('Lap Time Distribution Comparison')
245
+ axes[0, 0].legend()
246
+ axes[0, 0].grid(True, alpha=0.3)
247
+
248
+ # Aerodynamic efficiency comparison
249
+ synthetic_eff = synthetic_data['downforce'] / synthetic_data['drag_coefficient']
250
+ user_eff = user_data['downforce'] / user_data['drag_coefficient']
251
+
252
+ axes[0, 1].scatter(synthetic_data['drag_coefficient'], synthetic_data['downforce'],
253
+ alpha=0.5, label='Synthetic', color='blue')
254
+ axes[0, 1].scatter(user_data['drag_coefficient'], user_data['downforce'],
255
+ alpha=0.5, label='User Data', color='red')
256
+ axes[0, 1].set_xlabel('Drag Coefficient')
257
+ axes[0, 1].set_ylabel('Downforce (N)')
258
+ axes[0, 1].set_title('Aerodynamic Trade-off Comparison')
259
+ axes[0, 1].legend()
260
+ axes[0, 1].grid(True, alpha=0.3)
261
+
262
+ # Wing angle comparison
263
+ axes[1, 0].scatter(synthetic_data['front_wing_angle'], synthetic_data['rear_wing_angle'],
264
+ alpha=0.5, label='Synthetic', color='blue')
265
+ axes[1, 0].scatter(user_data['front_wing_angle'], user_data['rear_wing_angle'],
266
+ alpha=0.5, label='User Data', color='red')
267
+ axes[1, 0].set_xlabel('Front Wing Angle (°)')
268
+ axes[1, 0].set_ylabel('Rear Wing Angle (°)')
269
+ axes[1, 0].set_title('Wing Configuration Comparison')
270
+ axes[1, 0].legend()
271
+ axes[1, 0].grid(True, alpha=0.3)
272
+
273
+ # Performance correlation
274
+ axes[1, 1].scatter(synthetic_eff, synthetic_data['lap_time'],
275
+ alpha=0.5, label='Synthetic', color='blue')
276
+ axes[1, 1].scatter(user_eff, user_data['lap_time'],
277
+ alpha=0.5, label='User Data', color='red')
278
+ axes[1, 1].set_xlabel('Aerodynamic Efficiency')
279
+ axes[1, 1].set_ylabel('Lap Time (s)')
280
+ axes[1, 1].set_title('Efficiency vs Performance')
281
+ axes[1, 1].legend()
282
+ axes[1, 1].grid(True, alpha=0.3)
283
+
284
+ plt.tight_layout()
285
+ return fig
286
 
287
  # Initialize the predictor
288
  predictor = F1AerodynamicPredictor()
 
300
 
301
  # Generate report
302
  report = f"""
303
+ ## F1 Aerodynamic Performance Analysis (Synthetic Data)
304
 
305
  **Model Performance:**
306
  - R² Score: {r2:.3f}
 
325
 
326
  return fig, report
327
 
328
+ def upload_and_analyze_data(file):
329
+ """Handle file upload and analysis"""
330
+ if file is None:
331
+ return None, "Please upload a data file.", None
332
+
333
+ # Load user data
334
+ user_data, message = predictor.load_user_data(file)
335
+ if user_data is None:
336
+ return None, message, None
337
+
338
+ # Train model on user data
339
+ r2, rmse = predictor.train_model(user_data)
340
+
341
+ # Create visualizations
342
+ fig = predictor.create_visualizations(user_data)
343
+
344
+ # Generate report
345
+ report = f"""
346
+ ## F1 Aerodynamic Performance Analysis (User Data)
347
+
348
+ **Data Load Status:** {message}
349
+
350
+ **Model Performance:**
351
+ - R² Score: {r2:.3f}
352
+ - RMSE: {rmse:.3f} seconds
353
+
354
+ **Dataset Statistics:**
355
+ - Total configurations analyzed: {len(user_data)}
356
+ - Fastest lap time: {user_data['lap_time'].min():.2f}s
357
+ - Slowest lap time: {user_data['lap_time'].max():.2f}s
358
+ - Average lap time: {user_data['lap_time'].mean():.2f}s
359
+
360
+ **Aerodynamic Insights:**
361
+ - Average downforce: {user_data['downforce'].mean():.0f}N
362
+ - Average drag coefficient: {user_data['drag_coefficient'].mean():.3f}
363
+ - Best aero efficiency: {(user_data['downforce'] / user_data['drag_coefficient']).max():.1f}
364
+
365
+ **Data Quality Assessment:**
366
+ - Missing values: {user_data.isnull().sum().sum()}
367
+ - Duplicate records: {user_data.duplicated().sum()}
368
+ - Data range validation: Passed
369
+ """
370
+
371
+ return fig, report, user_data
372
+
373
+ def compare_data_sources():
374
+ """Compare user data with synthetic baseline"""
375
+ if predictor.current_data is None:
376
+ return None, "Please upload data first to enable comparison."
377
+
378
+ # Generate synthetic data for comparison
379
+ synthetic_data = predictor.generate_aero_data(len(predictor.current_data))
380
+
381
+ # Create comparison visualization
382
+ fig = predictor.compare_datasets(synthetic_data, predictor.current_data)
383
+
384
+ # Generate comparison report
385
+ synthetic_avg = synthetic_data['lap_time'].mean()
386
+ user_avg = predictor.current_data['lap_time'].mean()
387
+
388
+ report = f"""
389
+ ## Data Comparison Report
390
+
391
+ **Performance Comparison:**
392
+ - Synthetic Data Average Lap Time: {synthetic_avg:.2f}s
393
+ - User Data Average Lap Time: {user_avg:.2f}s
394
+ - Difference: {user_avg - synthetic_avg:.2f}s
395
+
396
+ **Setup Characteristics:**
397
+ - Synthetic wing angles are more conservative
398
+ - User data shows {'more aggressive' if user_avg < synthetic_avg else 'more conservative'} setup approach
399
+ - Aerodynamic efficiency patterns {'align well' if abs(user_avg - synthetic_avg) < 2 else 'show significant differences'}
400
+
401
+ **Recommendations:**
402
+ - {'Your data suggests more aggressive setups than baseline' if user_avg < synthetic_avg else 'Consider more aggressive aerodynamic configurations'}
403
+ - Validate setup ranges against your specific track conditions
404
+ - Use comparison insights to refine optimization parameters
405
+ """
406
+
407
+ return fig, report
408
+
409
  def predict_performance(front_wing, rear_wing, ride_height, suspension, downforce, drag_coeff, track_temp, wind_speed, track_grip):
410
  """Predict lap time for given setup"""
411
  if not predictor.is_trained:
 
414
  setup_params = [front_wing, rear_wing, ride_height, suspension, downforce, drag_coeff, track_temp, wind_speed, track_grip]
415
  lap_time = predictor.predict_lap_time(setup_params)
416
 
417
+ data_source_note = f"(Model trained on: {predictor.data_source})"
418
+
419
+ return f"Predicted Lap Time: {lap_time:.3f} seconds {data_source_note}"
420
 
421
  def optimize_car_setup(track_temp, wind_speed, track_grip):
422
  """Optimize car setup for given conditions"""
 
435
  ## Optimal Car Setup
436
 
437
  **Predicted Lap Time: {optimal_lap_time:.3f} seconds**
438
+ *(Model trained on: {predictor.data_source})*
439
 
440
  **Optimal Configuration:**
441
  - Front Wing Angle: {optimal_setup[0]:.1f}°
 
453
 
454
  return setup_report
455
 
456
+ def create_sample_data():
457
+ """Create sample data file for download"""
458
+ sample_data = predictor.generate_aero_data(100)
459
+ return sample_data.to_csv(index=False)
460
+
461
  # Create Gradio interface
462
  with gr.Blocks(title="F1 Aerodynamic Performance Predictor", theme=gr.themes.Soft()) as demo:
463
  gr.Markdown("# F1 Aerodynamic Performance Predictor")
464
  gr.Markdown("AI-powered aerodynamic analysis and setup optimization for Formula 1 racing.")
465
 
466
+ with gr.Tab("Synthetic Data Analysis"):
467
+ gr.Markdown("### Analyze synthetic aerodynamic performance data")
468
+ analyze_btn = gr.Button("🔍 Analyze Synthetic Data", variant="primary")
469
 
470
  with gr.Row():
471
  with gr.Column(scale=2):
 
478
  outputs=[aero_plot, aero_report]
479
  )
480
 
481
+ with gr.Tab("Upload & Analyze Data"):
482
+ gr.Markdown("### Upload your own F1 data for analysis")
483
+ 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")
484
+
485
+ with gr.Row():
486
+ with gr.Column():
487
+ file_upload = gr.File(
488
+ label="Upload CSV or Excel file",
489
+ file_types=[".csv", ".xlsx", ".xls"]
490
+ )
491
+
492
+ sample_btn = gr.Button("Download Sample Data Format", variant="secondary")
493
+ sample_file = gr.File(label="Sample Data", visible=False)
494
+
495
+ upload_btn = gr.Button("Analyze Uploaded Data", variant="primary")
496
+
497
+ with gr.Column():
498
+ upload_status = gr.Markdown("No file uploaded yet.")
499
+
500
+ with gr.Row():
501
+ with gr.Column(scale=2):
502
+ upload_plot = gr.Plot(label="Data Analysis")
503
+ with gr.Column(scale=1):
504
+ upload_report = gr.Markdown(label="Analysis Report")
505
+
506
+ # Comparison section
507
+ gr.Markdown("### Compare with Synthetic Baseline")
508
+ compare_btn = gr.Button("Compare Data Sources", variant="secondary")
509
+
510
+ with gr.Row():
511
+ with gr.Column(scale=2):
512
+ comparison_plot = gr.Plot(label="Data Comparison")
513
+ with gr.Column(scale=1):
514
+ comparison_report = gr.Markdown(label="Comparison Report")
515
+
516
+ # Event handlers
517
+ sample_btn.click(
518
+ create_sample_data,
519
+ outputs=[sample_file]
520
+ )
521
+
522
+ upload_btn.click(
523
+ upload_and_analyze_data,
524
+ inputs=[file_upload],
525
+ outputs=[upload_plot, upload_report, upload_status]
526
+ )
527
+
528
+ compare_btn.click(
529
+ compare_data_sources,
530
+ outputs=[comparison_plot, comparison_report]
531
+ )
532
+
533
  with gr.Tab("Performance Prediction"):
534
  gr.Markdown("### Predict lap time for specific setup")
535
+ gr.Markdown("*Note: Train the model first using synthetic or uploaded data*")
536
 
537
  with gr.Row():
538
  with gr.Column():
 
585
 
586
  with gr.Tab("About"):
587
  gr.Markdown("""
588
+ ## About This Enhanced Tool
589
+
590
+ This F1 Aerodynamic Performance Predictor now supports both synthetic and real data analysis:
591
+
592
+ **Dual Data Sources:**
593
+ - **Synthetic Data**: Realistic simulated F1 aerodynamic data for learning and experimentation
594
+ - **User Data**: Upload your own telemetry, test results, or historical performance data
595
+
596
+ **Data Upload Features:**
597
+ - CSV and Excel file support
598
+ - Automatic data validation and quality checks
599
+ - Sample data template download
600
+ - Comparison analysis between your data and synthetic baseline
601
+
602
+ **Enhanced Analysis:**
603
+ - Model training on real or synthetic data
604
+ - Data quality assessment and validation
605
+ - Performance comparison between different datasets
606
+ - Track which data source was used for predictions
607
+
608
+ **Setup Optimization:**
609
+ - Uses Differential Evolution algorithm for global optimization
610
+ - Adapts to patterns in your specific data
611
+ - Provides data-source-aware recommendations
612
+
613
+ **Required Data Format:**
614
+ Your uploaded data should include these columns:
615
+ - front_wing_angle, rear_wing_angle, ride_height
616
+ - suspension_stiffness, downforce, drag_coefficient
617
+ - track_temp, wind_speed, track_grip, lap_time
618
+
619
+ **Professional Applications:**
620
+ - Validate simulation models against real telemetry
621
+ - Identify setup trends and patterns in your data
622
+ - Optimize configurations for specific track conditions
623
+ - Compare your team's approach with industry baselines
624
  """)
625
 
626
  if __name__ == "__main__":