Initial commit
Made-with: Cursor
This commit is contained in:
285
docs/04-modules/ev-charging.md
Normal file
285
docs/04-modules/ev-charging.md
Normal file
@@ -0,0 +1,285 @@
|
||||
# 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 ~7-11kW) | 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 ~75, Zoe ~52
|
||||
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).
|
||||
|
||||
---
|
||||
|
||||
## 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',
|
||||
75.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',
|
||||
52.0, 7400, -- Zoe max 7.4kW 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
|
||||
Reference in New Issue
Block a user