A high-performance race strategy engine developed for the World Solar Challenge (WSC) and Sasol Solar Challenge. This system synchronizes multi-day energy optimization (Dynamic Programming) with real-time tactical velocity control (MPC) and a self-learning Digital Twin (PINN).
-
Physics-Informed Neural Network (PINN): Real-time estimation of
$C_{rr}$ and$C_{dA}$ gated by EKF innovation residuals. - Adversarial Game Theory Agent: A Graph Neural Network (GNN) that models competitor positions to determine tactical risk multipliers.
-
Async Orchestration: Non-blocking 1Hz telemetry ingestion on Raspberry Pi 5 using
asyncioand thread-offloading for heavy solvers. - Solcast Time-Distance Mapping: Predictive irradiance interpolation based on estimated arrival times at 500m route segments.
🛰️ ArchitectureTelemetry Layer: XBee 900HP
- OS: Raspberry Pi OS (64-bit) / Linux.
- Hardware: XBee 900HP S3B connected to
/dev/ttyAMA0. - Python: 3.10+
git clone [https://github.com/buddywhitman/strategy.git](https://github.com/your-repo/solar-strategy.git)
cd strategy
python3 -m venv venv
source venv/bin/activate
pip install -r requirements.txt
# Pre-race: Generate the Global SoC Envelope
python3 scripts/generate_global_plan.py
# Race Start: Launch Orchestrator
python3 main.py| Data Type | Source | Resolution | Format |
|---|---|---|---|
| Elevation | ALOS PALSAR | 12.5m | .tif (GeoTIFF) |
| Weather | Solcast | 15 min | JSON / Time-series |
| Aero Maps | OpenFOAM CFD | 1° Yaw Increments | .csv / .h5 |
| Shading | Ray-Tracing (pvlib) | Route-specific | Bivariate Splines |
- Safety First: The Optimizer can suggest aggressive speeds. Always keep a 5% SoC "Hard Buffer" that the model cannot touch.
- Validation: Regularly compare the PINN's predicted power consumption vs. actual Shunt/BMS readings. If error > 3%, recalibrate the Digital Twin.
This is the spatial backbone of the system. The DP solver uses this to calculate energy potential and segment types.
- Primary Source: ALOS PALSAR 12.5m DEM (Elevation) fused with Google Earth/KML traces.
- Preparation: Use
scipy.interpolateto ensure a consistent 100m or 500m resolution.
| Column | Type | Description |
|---|---|---|
dist |
float |
Cumulative distance from race start (meters). |
lat |
float |
Decimal Latitude (WGS84). |
lon |
float |
Decimal Longitude (WGS84). |
elev |
float |
Elevation in meters (used to calculate slope). |
speed_limit |
int |
Maximum legal speed (km/h) for that segment. |
is_loop_zone |
bool |
True if segment is a Sasol Loop (Triggers Adaptive DP). |
The SolcastClient handles the API fetch, but the resulting DataFrame must be maintained for the Time-Distance Mapping logic.
- Primary Source: Solcast Rooftop Site API.
- Update Frequency: 15 minutes (Race regulation standard).
| Column | Type | Description |
|---|---|---|
period_end |
datetime |
The end of the 15-minute observation window (UTC). |
ghi |
int |
Global Horizontal Irradiance (). |
dni |
int |
Direct Normal Irradiance (used for panel angle calcs). |
air_temp |
float |
Ambient air temperature (°C). |
wind_speed |
float |
Predicted wind speed at 10m height (). |
wind_dir |
int |
Wind direction in degrees (0–360). |
Used by physics.py to calculate the "Sailing Effect" and side-wind drag penalties.
- Primary Source: CFD Analysis (Ansys Fluent/StarCCM+) or Scale Wind Tunnel.
| Column | Type | Description |
|---|---|---|
yaw_angle |
int |
Relative wind angle in degrees (-45 to 45). |
cda_mult |
float |
Multiplier for nominal (e.g., 0.92 at 15° for sailing). |
Used for high-fidelity energy modeling beyond simple fixed percentages.
- Primary Source: Dynamometer testing of your specific hub motor (e.g., Mitsuba/Marand).
| Column | Type | Description |
|---|---|---|
rpm |
int |
Motor revolutions per minute. |
torque |
float |
Measured torque (). |
efficiency |
float |
Efficiency coefficient (0.0–1.0). |
Do not rely on GPS-reported elevation for the GlobalDP.
- Take your
lat/lontrace. - Sample the ALOS PALSAR 12.5m DEM.
- Calculate .
- Apply a Savitzky-Golay filter to remove "jitter" from the elevation data.
For the scripts/simulate_race.py to work, your historic log files must match the XBee binary format:
# historic_run_01.csv
timestamp,speed,current,voltage,soc,temp_cell,solar_irr
1703412000,72.5,-12.4,104.2,0.85,42.1,840
1703412001,72.6,-12.5,104.1,0.849,42.2,838
Before going live on the Pi 5, run the following to ensure the data folder is accessible:
python3 -c "import pandas as pd; print(pd.read_csv('data/maps/route.csv').head())"