second version
This commit is contained in:
@@ -26,21 +26,25 @@ Střídač Deye poskytuje přes Modbus registr `load_power_w` = celková okamži
|
||||
load_power_w (Deye) = bazální_spotřeba + EV_nabíjení + TUV + ostatní flexibilní
|
||||
```
|
||||
|
||||
### Odvození bazální spotřeby
|
||||
### Výpočet bazální spotřeby
|
||||
|
||||
Bazální výkon je to, co zůstane po odečtení řízených zátěží od celkové spotřeby ze střídače:
|
||||
|
||||
```
|
||||
bazální_w = load_power_w - sum(flexibilní zařízení aktuální výkon)
|
||||
bazální_w = load_power_w - ev_power_w - heat_pump_power_w
|
||||
```
|
||||
|
||||
V praxi:
|
||||
```
|
||||
bazální_w = load_power_w
|
||||
- ev_charger_1_power_w
|
||||
- ev_charger_2_power_w
|
||||
- tuv_power_w (pokud je měřitelná zvlášť)
|
||||
```
|
||||
- **`load_power_w`** – telemetrie střídače (`telemetry_inverter`), 1min.
|
||||
- **`ev_power_w`** – v agregaci statistik se bere průměr výkonu ze všech nabíječek site v časovém okně ±30 s kolem měření střídače (`telemetry_ev_charger`).
|
||||
- **`heat_pump_power_w`** – stejně z `telemetry_heat_pump` (TČ včetně kompresoru; slouží jako proxy za měřitelný příkon TČ).
|
||||
|
||||
> **Předpoklad:** TUV výkon není přímo měřen, pouze víme že je ON/OFF (přes Loxone). Pokud je ON, odečítáme `asset_flexible_device.max_power_w`. Toto je zjednodušení – lze zpřesnit později podružným měřením.
|
||||
**Ukládání profilu:** tabulka `consumption_baseline_stats` (unikátní `(site_id, day_of_week, hour_of_day)`). Plní ji **`ems.fn_update_baseline_stats(site_id, lookback_days)`** z minutové telemetrie za posledních *N* dní; agregace po DOW a hodině (Europe/Prague). Do řádku se zapisuje jen bucket s alespoň 4 vzorky (minuty). **EMA 70/30** při `ON CONFLICT`: nový batch má váhu 30 %, existující průměr 70 % (postupné zpřesňování). Denní job v backendu: **00:30** (`fn_update_baseline_stats(..., 30)`).
|
||||
|
||||
**Predikce do horizontu:** **`ems.fn_get_baseline_forecast(site_id, from, to)`** generuje 15min sloty (`generate_series`), pro každý slot najde řádek podle DOW+hodiny v Praze. **`forecast_w`** = uložený průměr; **`confidence_w`** = konzervativní odhad `avg + 0.5 * COALESCE(stddev, 100)`. Pokud pro slot neexistuje statistika, fallback **`forecast_w = 500` W** (málo nebo žádná historie; prakticky odpovídá situaci před ~4 týdny kvalitních dat v jednotlivých hodinách). Směrodatná odchylka je v DB k dispozici pro budoucí použití v solveru (fáze 2).
|
||||
|
||||
**Solver (`planning_engine._load_slots`):** pro každý 15min interval efektivní ceny bere **`avg_power_w` z `consumption_baseline_stats`** podle DOW+hodiny slotu, jinak **500 W** – nečte `consumption_baseline_interval`.
|
||||
|
||||
> **Poznámka:** TUV jako samostatný odečet zůstává otevřený bod, pokud není měřen zvlášť; aktuálně je TČ zahrnut v `heat_pump_power_w`.
|
||||
|
||||
---
|
||||
|
||||
@@ -52,43 +56,16 @@ Celková spotřeba je součástí `telemetry_inverter.load_power_w` (1min zázna
|
||||
EV nabíječky mají vlastní tabulku `telemetry_ev_charger` s přesným výkonem.
|
||||
|
||||
### Agregovaná spotřeba pro plánování
|
||||
Tabulka `consumption_baseline_interval` ukládá 15min průměry bazální spotřeby:
|
||||
|
||||
- `data_type = 'actual'` – historická skutečnost (zpětně dopočítáno z telemetrie)
|
||||
- `data_type = 'forecast'` – predikce pro plánování
|
||||
- **`consumption_baseline_stats`** – primární vstup solveru: hodinový profil (DOW + hodina) z telemetrie, EMA, viz výše.
|
||||
- **`consumption_baseline_interval`** – volitelné 15min řady (`actual` / `forecast`) pro jiné účely; solver z ní bazál nečte.
|
||||
|
||||
---
|
||||
|
||||
## Predikce bazální spotřeby
|
||||
|
||||
### Metoda: historický průměr + denní profil
|
||||
### Metoda: DOW + hodina + EMA
|
||||
|
||||
Jednoduchý model pro začátek:
|
||||
|
||||
```python
|
||||
def forecast_baseline_consumption(site_id: int, target_date: date):
|
||||
"""
|
||||
Predikce bazální spotřeby na základě průměru posledních N podobných dní.
|
||||
Podobnost: stejný den v týdnu, přibližně stejná roční doba.
|
||||
"""
|
||||
lookback_weeks = 4
|
||||
day_of_week = target_date.weekday()
|
||||
|
||||
# Stáhnout historické bazální hodnoty pro stejné dny v týdnu
|
||||
historical = db.query("""
|
||||
SELECT interval_start, power_w
|
||||
FROM consumption_baseline_interval
|
||||
WHERE site_id = %s
|
||||
AND data_type = 'actual'
|
||||
AND EXTRACT(dow FROM interval_start) = %s
|
||||
AND interval_start >= %s
|
||||
ORDER BY interval_start
|
||||
""", site_id, day_of_week, target_date - timedelta(weeks=lookback_weeks))
|
||||
|
||||
# Průměr per 15min slot
|
||||
profile = aggregate_by_time_of_day(historical) # 96 hodnot (15min sloty)
|
||||
return profile
|
||||
```
|
||||
Operativní predikce je v **`fn_get_baseline_forecast`** a v přímém dotazu v `_load_slots` na **`consumption_baseline_stats`**. Doplňkově lze z historie stavět 15min profily v `consumption_baseline_interval`, pokud je k tomu samostatný job – není nutné pro běh LP.
|
||||
|
||||
---
|
||||
|
||||
|
||||
Reference in New Issue
Block a user