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

11 KiB
Raw Blame History

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

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

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

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

# 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

# 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

# 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

# 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

-- 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