Update app.py
Browse files
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 |
-
|
|
|
|
|
|
|
| 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("
|
| 297 |
-
gr.Markdown("### Analyze aerodynamic performance data")
|
| 298 |
-
analyze_btn = gr.Button("🔍 Analyze
|
| 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:
|
| 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
|
| 369 |
-
|
| 370 |
-
**
|
| 371 |
-
-
|
| 372 |
-
-
|
| 373 |
-
|
| 374 |
-
|
| 375 |
-
|
| 376 |
-
-
|
| 377 |
-
-
|
| 378 |
-
-
|
| 379 |
-
|
| 380 |
-
**
|
| 381 |
-
-
|
| 382 |
-
-
|
| 383 |
-
-
|
| 384 |
-
-
|
| 385 |
-
|
| 386 |
-
**
|
| 387 |
-
-
|
| 388 |
-
-
|
| 389 |
-
-
|
| 390 |
-
|
| 391 |
-
|
| 392 |
-
|
| 393 |
-
-
|
| 394 |
-
-
|
| 395 |
-
-
|
| 396 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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__":
|