Files
ems/docs/04-modules/ev-charging.md
Dusan Vojacek 8b4af663d8 Initial commit
Made-with: Cursor
2026-03-20 13:27:44 +01:00

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

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

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


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