Files
ems/docs/04-modules/ev-charging.md
Dusan Vojacek 9f4126946d second version
2026-04-03 14:23:16 +02:00

312 lines
11 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Modul: EV Nabíjení
## Přehled vozidel na home-01
| Vozidlo | Nabíječka | Max výkon | Řízení | API |
|---|---|---|---|---|
| Tesla | ev-charger-1 (Teltonika 22kW) | 22 kW | WB proud limit + Tesla API | Zatím nerozhodnuto (Tessie nebo přímé) |
| Renault Zoe | ev-charger-2 (Teltonika 22kW) | 22 kW (Zoe max 22kW) | WB proud limit (Zoe respektuje) | Žádné Zoe jako fixní zátěž při připojení |
---
## Klíčové principy
### 1. Přímé FVE nabíjení preferováno před průchodem přes baterii
Energie která jde FVE → baterie → EV má round-trip ztráty:
```
η_round_trip = η_charge × η_discharge ≈ 0.95 × 0.95 ≈ 0.90
```
Přímé napájení FVE → EV (nebo síť → EV) je ~10 % efektivnější.
Solver to vidí přes vyšší efektivní cenu energie procházející baterií (degradation_cost + round-trip loss).
### 2. Deadline charging
Každé vozidlo může mít nastaven:
- **cílový SoC** (%)
- **deadline** (do kdy musí být dosažen)
Solver garantuje dosažení SoC do deadline jako hard constraint.
Ekonomická optimalizace probíhá v rámci tohoto omezení.
### 3. Zoe řízení přes WB proud limit
Zoe respektuje maximální proud nastavený na WB (Teltonika Modbus).
Solver nastaví `current_limit_a` pro daný slot.
Zoe vždy nabíjí pokud je připojena a proud > 6A.
Scheduler v Zoe se nepoužívá WB proud limit je jediný řídicí prvek.
### 4. Tesla WB + volitelně Tesla API
V první fázi stejný přístup jako Zoe proud limit přes WB.
Tesla API (Tessie nebo přímé) přidáme ve fázi 2 pro:
- čtení aktuálního SoC bez dotazování WB
- čtení stavu připojení
- případné spuštění/zastavení nabíjení přímo v autě
---
## DB rozšíření EV session a deadline
### Tabulka `ems.ev_session`
```sql
CREATE TABLE ems.ev_session (
id SERIAL PRIMARY KEY,
site_id INT NOT NULL REFERENCES ems.site(id),
charger_id INT NOT NULL REFERENCES ems.asset_ev_charger(id),
vehicle_id INT REFERENCES ems.asset_vehicle(id),
session_start TIMESTAMPTZ NOT NULL DEFAULT now(),
session_end TIMESTAMPTZ,
-- Stav při připojení
soc_at_connect_pct NUMERIC(5,2),
-- Deadline požadavek (nastavuje uživatel nebo API)
target_soc_pct NUMERIC(5,2),
target_deadline TIMESTAMPTZ,
-- Výsledek
soc_at_disconnect_pct NUMERIC(5,2),
energy_delivered_kwh NUMERIC(10,3),
cost_czk NUMERIC(10,4)
);
```
### Tabulka `ems.asset_vehicle`
```sql
CREATE TABLE ems.asset_vehicle (
id SERIAL PRIMARY KEY,
site_id INT NOT NULL REFERENCES ems.site(id),
code TEXT NOT NULL,
name TEXT,
make TEXT, -- 'Tesla', 'Renault'
model TEXT, -- 'Model Y', 'Zoe'
battery_capacity_kwh NUMERIC(6,2), -- Tesla ~58, Zoe ~22
max_charge_power_w INT, -- max přijímaný výkon vozidla
default_charger_id INT REFERENCES ems.asset_ev_charger(id),
api_type TEXT, -- 'tesla', 'none'
api_reference TEXT, -- odkaz na credentials v env
default_target_soc_pct NUMERIC(5,2) DEFAULT 80,
default_deadline_hour INT DEFAULT 7 -- 7:00 ráno jako výchozí deadline
);
```
---
## Solver rozšíření EV s round-trip a deadline
### Nové proměnné pro každý slot t a každé EV e
```python
ev_direct[e][t] # W přímé napájení EV z FVE nebo sítě (bez průchodu baterií)
ev_via_bat[e][t] # W napájení EV přes baterii (vyšší efektivní cena)
# Celkový výkon EV (co jde do auta)
ev_charge[e][t] = ev_direct[e][t] + ev_via_bat[e][t]
# Co ev_via_bat stojí energeticky navíc:
# ev_via_bat musí být "nakoupeno" z baterie s round-trip ztrátou
# solver to vidí přes účelovou funkci viz níže
```
### Energetická bilance rozšířená o přímé EV
```python
# Zdroje = Spotřeba
pv_a_net[t] + pv_b[t] + grid_import[t] + batt_discharge[t]
== load_baseline[t]
+ Σ_e ev_direct[e][t] # přímá spotřeba EV
+ Σ_e ev_via_bat[e][t] # EV přes baterii (z discharge)
+ heat_pump[t]
+ batt_charge[t]
+ grid_export[t]
# Vazba: ev_via_bat[e][t] musí pokrýt batt_discharge[t]
# (solver to vyřeší sám discharge jde buď do ev_via_bat nebo do load)
```
### Účelová funkce efektivní cena EV přes baterii
```python
# Nabíjení přes baterii je dražší o round-trip ztrátu a degradaci:
EV_VIA_BAT_COST_FACTOR = 1.0 / (charge_efficiency * discharge_efficiency)
# ≈ 1.0 / (0.95 * 0.95) ≈ 1.108
# V objective function:
+ ev_via_bat[e][t] * buy_price[t] * EV_VIA_BAT_COST_FACTOR * H / 1000
+ ev_direct[e][t] * buy_price[t] * H / 1000 # přímé bez navýšení
# Solver přirozeně preferuje přímé nabíjení kde je to možné
```
### Deadline constraint
```python
# Pro každé EV e s nastaveným deadline:
if ev_session[e].target_deadline is not None:
# Kolik energie ještě potřebujeme dodat
energy_needed_wh = (
(ev_session[e].target_soc_pct - ev_session[e].current_soc_pct)
/ 100.0 * vehicle[e].battery_capacity_kwh * 1000
)
# Deadline slot index
t_deadline = slot_index_for(ev_session[e].target_deadline)
# Hard constraint: součet dodané energie do deadline musí být >= potřebná
prob += pulp.lpSum(
ev_charge[e][t] * H # Wh za 15min slot
for t in range(t_deadline + 1)
if ev_connected[e][t] # jen sloty kdy je auto připojeno
) >= energy_needed_wh
# Zoe má tvrdší deadline (menší baterie, kritičtější)
# Tesla může mít měkčí deadline nebo vyšší flexibility okno
```
### Připojení EV vstupní podmínka
```python
# ev_connected[e][t] = True/False
# Pokud auto není připojeno → ev_charge[e][t] = 0
for t in range(T):
if not ev_connected[e][t]:
prob += ev_charge[e][t] == 0
prob += ev_direct[e][t] == 0
prob += ev_via_bat[e][t] == 0
```
---
## Jak solver rozhoduje (příklady)
### Přebytek FVE přes poledne, Zoe připojena, baterie poloprázdná
```
Solver volí:
ev_direct[zoe][t] = max(min(surplus_w, zoe_max_w), 0) ← přímé z FVE
batt_charge[t] = zbývající surplus ← do baterie až pak
Protože přímé nabíjení Zoe je levnější než FVE → baterie → Zoe.
```
### Noc, Zoe má deadline 7:00 s SoC 20% (potřeba 30 kWh)
```
Solver:
- Rozloží nabíjení do nejlevnějších nočních slotů
- Garantuje dodání 30 kWh do 7:00 (hard constraint)
- Pokud jsou sloty se zápornou cenou → nabíjí naplno v těch slotech
- Vyhýbá se nabíjení přes baterii pokud není přebytek
```
### Tesla připojena, SoC 70%, deadline není nastaven
```
Solver:
- Tesla je "oportunistická" nabíjí jen při přebytku FVE nebo levné ceně
- Bez deadline = měkká optimalizace, ne hard constraint
- Nastavit default_target_soc = 80% s default_deadline = zítra 7:00
(konfigurovatelné v asset_vehicle)
```
---
## Zjištění stavu připojení
### Teltonika WB (oba vozy)
Modbus registr stavu konektoru (status):
- `available` = žádné auto
- `preparing` / `charging` = auto připojeno
Polling každou minutu z `telemetry_ev_charger.status`.
### Tesla API (fáze 2)
Přes Tessie nebo přímé Tesla API:
- SoC baterie auta
- Stav připojení (plugged_in)
- Nabíjecí stav (charging / stopped)
Uložit do `ev_session` při připojení/odpojení.
### Renault Zoe
Žádné API. Stav připojení čteme výhradně z WB Modbus (`status != 'available'`).
SoC Zoe neznáme přesně použijeme energii dodanou v session (kumulativní kWh z WB).
---
## Statistika příjezdů
### Tabulka `ems.ev_arrival_stats`
Agregace podle `site_id`, `charger_id`, `day_of_week` (0 = neděle … 6 = sobota) a `arrival_hour` (023). Čas příjezdu se počítá v **Europe/Prague**. Unikátní klíč `(site_id, charger_id, day_of_week, arrival_hour)`; sloupec `sample_count` roste s každým zaznamenaným příjezdem.
Účel: po několika týdnech dat odhadnout typickou hodinu připojení vozidla na danou wallbox — pro notifikace („obvykle přijíždíš kolem 1718h“) a později jako měkký vstup do plánovače.
### `ems.fn_update_ev_arrival_stats(site_id, charger_id, vehicle_id, arrived_at)`
Inkrementuje statistiku pro příslušný bucket (INSERT nebo `ON CONFLICT` +1). Volá se při **detekci nového příjezdu** v `telemetry_collector`: přechod telemetrie z `available` na stav připojení (`preparing`, `charging`, …).
### `ems.fn_ev_expected_arrival(site_id, charger_id, for_date)`
Vrátí až **3 řádky**: nejčastější hodiny příjezdu pro den v týdnu odpovídající kalendářnímu datu `for_date` (typicky „zítřek“ v časové zóně lokality z backendu). Filtr `sample_count >= 2`; `confidence_pct` = podíl dané hodiny na součtu vzorků pro stejný `day_of_week` u té nabíječky.
### API
`GET /api/v1/sites/{site_id}/ev/arrival-prediction` vrátí pro každou nabíječku (klíč = `asset_ev_charger.code`) pole `tomorrow` s `{ hour, confidence_pct, samples }`. Pokud je na site méně než **5** záznamů v `ev_session` celkem, odpověď má `insufficient_data: true` (predikce se může vracet prázdné nebo řídké).
### Provozní poznámka
Historie v `ev_arrival_stats` se **nemaže** — jde o dlouhodobou agregaci. Po **4+ týdnech** reálných příjezdů má smysl UI notifikace a experimentální zapojení do solveru (soft constraint).
---
## Seed data vozidla home-01
```sql
-- V006__vehicles.sql
INSERT INTO ems.asset_vehicle
(site_id, code, name, make, model, battery_capacity_kwh,
max_charge_power_w, default_charger_id, api_type,
default_target_soc_pct, default_deadline_hour)
SELECT
s.id, 'tesla-my', 'Tesla Model Y', 'Tesla', 'Model Y',
58.0, 11000, -- Tesla Model Y AC max ~11kW
ch.id, 'none', -- Tesla API fáze 2
80, 7
FROM ems.site s
JOIN ems.asset_ev_charger ch ON ch.site_id = s.id AND ch.code = 'ev-charger-1'
WHERE s.code = 'home-01';
INSERT INTO ems.asset_vehicle
(site_id, code, name, make, model, battery_capacity_kwh,
max_charge_power_w, default_charger_id, api_type,
default_target_soc_pct, default_deadline_hour)
SELECT
s.id, 'zoe-r135', 'Renault Zoe R135', 'Renault', 'Zoe R135',
22.0, 22000, -- Zoe max 22kW AC
ch.id, 'none',
90, 7 -- Zoe: vyšší target SoC (menší baterie, kritičtější)
FROM ems.site s
JOIN ems.asset_ev_charger ch ON ch.site_id = s.id AND ch.code = 'ev-charger-2'
WHERE s.code = 'home-01';
```
---
## Otevřené body
- [ ] Tesla API: Tessie vs přímé API rozhodnout ve fázi 2
- [ ] Ověřit Zoe max nabíjecí výkon (7.4 kW nebo méně dle podmínek)
- [ ] Ověřit round-trip efficiency na reálných datech po prvních týdnech provozu
- [ ] UI pro nastavení deadline a target SoC uživatelem (před odjezdem)
- [ ] Notifikace pokud deadline nelze splnit (nedostatek kapacity WB nebo energie)
- [ ] Zoe SoC estimace z kumulativní energie session přesnost ověřit