Guide for using LFS-Ayats as a training and driver performance improvement tool.
Racing School offers personalized training to drivers who want to improve their performance. They need to:
1. Initial Session (Baseline)
↓
2. Data Collection
↓
3. Automatic Analysis
↓
4. Reference Comparison
↓
5. Improvement Areas Identification
↓
6. Driver Feedback
↓
7. Practice Sessions
↓
8. Progress Tracking
coaching_session.py:
"""
Coaching system with LFS-Ayats.
Analyzes performance and provides feedback.
"""
from typing import List, Dict
import numpy as np
from pathlib import Path
from src.connection import InSimClient
from src.telemetry import TelemetryCollector
from src.analysis import TelemetryAnalyzer
from src.visualization import LapComparator, create_track_map
from src.utils import setup_logger
logger = setup_logger("coaching", "INFO")
class CoachingSession:
"""Manages coaching session."""
def __init__(self, student_name: str, coach_name: str = None):
"""
Initialize session.
Args:
student_name: Student's name
coach_name: Coach's name (for comparison)
"""
self.student_name = student_name
self.coach_name = coach_name
self.student_laps = []
self.coach_laps = []
self.analyzer = TelemetryAnalyzer()
def collect_baseline(self, num_laps: int = 10):
"""
Collect student's baseline laps.
Args:
num_laps: Number of laps to collect
"""
logger.info(f"📊 Collecting {num_laps} baseline laps from {self.student_name}...")
client = InSimClient(host="127.0.0.1", port=29999)
client.connect()
client.initialize()
collector = TelemetryCollector(client)
lap_count = 0
def on_lap(lap_data):
nonlocal lap_count
if lap_data['player_name'] == self.student_name:
self.student_laps.append(lap_data)
lap_count += 1
logger.info(f" Lap {lap_count}/{num_laps}: {lap_data['lap_time']:.3f}s")
collector.register_callback('lap', on_lap)
collector.start()
# Wait until all laps are collected
import time
while lap_count < num_laps:
time.sleep(1)
collector.stop()
client.disconnect()
logger.info(f"✓ Baseline completed: {len(self.student_laps)} laps")
def analyze_consistency(self) -> Dict:
"""
Analyze student's consistency.
Returns:
Dictionary with consistency metrics
"""
logger.info("\n=== Consistency Analysis ===")
lap_times = [lap['lap_time'] for lap in self.student_laps]
if not lap_times:
return {}
mean_time = np.mean(lap_times)
std_dev = np.std(lap_times)
best_time = min(lap_times)
worst_time = max(lap_times)
consistency_score = 100 * (1 - std_dev / mean_time)
results = {
'mean': mean_time,
'std_dev': std_dev,
'best': best_time,
'worst': worst_time,
'range': worst_time - best_time,
'consistency_score': consistency_score
}
logger.info(f" Average: {mean_time:.3f}s")
logger.info(f" Std Dev: {std_dev:.3f}s")
logger.info(f" Best: {best_time:.3f}s")
logger.info(f" Worst: {worst_time:.3f}s")
logger.info(f" Consistency: {consistency_score:.1f}/100")
# Feedback
if consistency_score >= 98:
logger.info(" ✓ Excellent consistency!")
elif consistency_score >= 95:
logger.info(" ✓ Good consistency")
elif consistency_score >= 90:
logger.info(" ⚠️ Room for improvement - focus on reproducing best lap")
else:
logger.info(" ❌ Inconsistent - practice more to stabilize")
return results
def compare_with_coach(self):
"""Compare student's best lap with coach."""
if not self.coach_laps:
logger.warning("No coach data available for comparison")
return
logger.info(f"\n=== Comparison with {self.coach_name} ===")
# Best lap from each
student_best = min(self.student_laps, key=lambda l: l['lap_time'])
coach_best = min(self.coach_laps, key=lambda l: l['lap_time'])
time_diff = student_best['lap_time'] - coach_best['lap_time']
logger.info(f" {self.student_name}: {student_best['lap_time']:.3f}s")
logger.info(f" {self.coach_name}: {coach_best['lap_time']:.3f}s")
logger.info(f" Difference: {time_diff:+.3f}s")
# Visual comparison
comparator = LapComparator()
comparator.add_lap(self.student_name, student_best['telemetry'])
comparator.add_lap(f"{self.coach_name} (ref)", coach_best['telemetry'])
fig = comparator.create_comparison_plot()
fig.write_html(f"comparison_{self.student_name}.html")
logger.info(f" 📊 Comparison saved: comparison_{self.student_name}.html")
# Sector-by-sector analysis
self._analyze_sectors(student_best, coach_best)
def _analyze_sectors(self, student_lap, coach_lap, num_sectors=3):
"""Analyze sectors comparing student and coach."""
logger.info(f"\n📍 Sector Analysis ({num_sectors} sectors):")
student_sectors = self._split_into_sectors(
student_lap['telemetry'],
num_sectors
)
coach_sectors = self._split_into_sectors(
coach_lap['telemetry'],
num_sectors
)
weakest_sectors = []
for i in range(num_sectors):
student_time = self._calculate_sector_time(student_sectors[i])
coach_time = self._calculate_sector_time(coach_sectors[i])
diff = student_time - coach_time
logger.info(f"\n Sector {i+1}:")
logger.info(f" Student: {student_time:.3f}s")
logger.info(f" Coach: {coach_time:.3f}s")
logger.info(f" Difference: {diff:+.3f}s")
if diff > 0.1: # Loses more than 0.1s
weakest_sectors.append((i+1, diff))
logger.info(f" ⚠️ IMPROVEMENT AREA")
return weakest_sectors
def _split_into_sectors(self, telemetry, num_sectors):
"""Split telemetry into sectors."""
sector_size = len(telemetry) // num_sectors
sectors = []
for i in range(num_sectors):
start = i * sector_size
end = start + sector_size if i < num_sectors - 1 else len(telemetry)
sectors.append(telemetry[start:end])
return sectors
def _calculate_sector_time(self, sector_data):
"""Calculate sector time."""
if len(sector_data) < 2:
return 0
from datetime import datetime
start_time = datetime.fromisoformat(sector_data[0]['timestamp'])
end_time = datetime.fromisoformat(sector_data[-1]['timestamp'])
return (end_time - start_time).total_seconds()
def identify_improvement_areas(self) -> List[str]:
"""
Identify specific areas for improvement.
Returns:
List of recommendations
"""
logger.info("\n=== Improvement Areas ===")
recommendations = []
# Analyze best lap
best_lap = min(self.student_laps, key=lambda l: l['lap_time'])
telemetry = best_lap['telemetry']
# 1. Minimum corner speed
speeds = [t['speed'] for t in telemetry]
min_speed = min(speeds)
if min_speed < 60:
rec = "Low minimum corner speed - improve entry and line"
recommendations.append(rec)
logger.info(f" • {rec}")
# 2. Gear usage
gears = [t.get('gear', 0) for t in telemetry]
gear_changes = sum(1 for i in range(1, len(gears)) if gears[i] != gears[i-1])
if gear_changes > 50: # Too many changes
rec = "Excessive gear changes - smooth out driving"
recommendations.append(rec)
logger.info(f" • {rec}")
# 3. Speed variability
speed_std = np.std(speeds)
if speed_std > 40:
rec = "High speed variability - maintain more consistent pace"
recommendations.append(rec)
logger.info(f" • {rec}")
# 4. Excessive RPM
rpms = [t.get('rpm', 0) for t in telemetry]
max_rpm = max(rpms)
if max_rpm > 7800:
rec = "RPM too high - shift earlier to preserve engine"
recommendations.append(rec)
logger.info(f" • {rec}")
if not recommendations:
logger.info(" ✓ No major issues detected!")
recommendations.append("Continue practicing to improve consistency")
return recommendations
def generate_coaching_report(self, output_file: str = None):
"""Generate complete coaching report."""
if output_file is None:
output_file = f"coaching_report_{self.student_name}.html"
logger.info(f"\n=== Generating Coaching Report ===")
# Collect all analyses
consistency = self.analyze_consistency()
improvements = self.identify_improvement_areas()
# Generate HTML
html_content = f"""
<!DOCTYPE html>
<html>
<head>
<title>Coaching Report - {self.student_name}</title>
<style>
body
h1
h2
.metric
.good
.warning
.bad
ul
li
</style>
</head>
<body>
<h1>Coaching Report: {self.student_name}</h1>
<h2>Consistency</h2>
<div class="metric">
<p><strong>Average time:</strong> {consistency.get('mean', 0):.3f}s</p>
<p><strong>Best lap:</strong> {consistency.get('best', 0):.3f}s</p>
<p><strong>Worst lap:</strong> {consistency.get('worst', 0):.3f}s</p>
<p><strong>Consistency score:</strong>
<span class="{'good' if consistency.get('consistency_score', 0) >= 95 else 'warning'}">
{consistency.get('consistency_score', 0):.1f}/100
</span>
</p>
</div>
<h2>Improvement Areas</h2>
<ul>
{''.join(f'<li>• {rec}</li>' for rec in improvements)}
</ul>
<h2>Next Steps</h2>
<ol>
<li>Focus on identified weak sectors</li>
<li>Practice 10-15 more laps focusing on consistency</li>
<li>Review comparison with reference lap</li>
<li>Next session: Progress review</li>
</ol>
</body>
</html>
"""
with open(output_file, 'w') as f:
f.write(html_content)
logger.info(f"✓ Report saved: {output_file}")
logger.info(f" Open with browser to view full details")
def main():
"""Example usage of coaching system."""
# Create session
session = CoachingSession(
student_name="Student1",
coach_name="Coach_Pro"
)
# Collect data
logger.info("Start driving on the track...")
session.collect_baseline(num_laps=10)
# Analysis
session.analyze_consistency()
session.identify_improvement_areas()
# If you have coach data, compare
# session.compare_with_coach()
# Generate report
session.generate_coaching_report()
logger.info("\n✓ Coaching session completed!")
if __name__ == "__main__":
main()
track_progress.py:
"""
Track a driver's progression over time.
"""
import pandas as pd
import matplotlib.pyplot as plt
from datetime import datetime
def track_progress(student_name: str):
"""Show student's progression."""
# Load historical sessions
sessions = load_student_sessions(student_name)
# Extract best lap time per session
progress_data = []
for session in sessions:
date = session['date']
best_lap = min(lap['time'] for lap in session['laps'])
consistency = session['consistency_score']
progress_data.append({
'date': date,
'best_lap': best_lap,
'consistency': consistency
})
df = pd.DataFrame(progress_data)
# Progress chart
fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(12, 8))
# Best lap time
ax1.plot(df['date'], df['best_lap'], marker='o')
ax1.set_title(f'{student_name} Progression')
ax1.set_ylabel('Best Time (s)')
ax1.grid(True)
# Consistency
ax2.plot(df['date'], df['consistency'], marker='o', color='green')
ax2.set_ylabel('Consistency (%)')
ax2.set_xlabel('Date')
ax2.grid(True)
plt.tight_layout()
plt.savefig(f'progress_{student_name}.png')
print(f"✓ Progress chart saved")
Data-driven coaching for maximum improvement! 🏎️📈