Echtzeit-Streaming-Beschränkungen für den Batterie-Optimierer

← Learning Hub Lektion 9 · Batterieoptimierungs-Kurs · Geschätzte Dauer: 20 Minuten

In der Produktion läuft der Optimierer nicht mehr ein einziges Mal und verschwindet dann. Du bekommst unregelmäßige Updates aus einem Stream (zum Beispiel Kafka) zu Restriktionen, Derates und neuen Betriebsregeln. Diese Lektion zeigt, wie sich der Optimierer ändert, wenn Echtzeitdaten ein erster klassischer Input werden.

Was ändert sich?

Häufige Echtzeit-Eingaben

Architektur-Update

┌──────────────────────────────┐     ┌──────────────────────┐
│  Kafka-Stream / Events       │──▶  │  Echtzeit-Datenlage  │
│  • Restriktionen             │     │  • Nachrichten       │
│  • Batterie-Status           │     │    normalisieren     │
│  • Marktereignisse           │     │  • aktiver Constraint │
└──────────────────────────────┘     │    Snapshot          │
              │                     └──────────────────────┘
              ▼                             │
   ┌──────────────────┐           ┌─────────────────────────┐
   │ Event-getriggerter │◀──────────│ Rolling-Horizon-Optimierer │
   │ Plan / Reopt     │           │ • zeitvariable Grenzen  │
   └──────────────────┘           └─────────────────────────┘

Von Kafka-Nachricht zu Optimierungsparameter

@dataclass
class RealtimeRestriction:
    quelle: str
    gueltig_von: datetime
    gueltig_bis: datetime
    max_laden_mw: float
    max_entladen_mw: float
    soc_min_pct: float
    soc_max_pct: float
    reservierte_energie_mwh: float
    lade_erlaubt: bool
    entlade_erlaubt: bool
    notiz: str

Der Echtzeit-Feed wird zu einem zeitindizierten Constraint-Bundle. Der Optimierer sieht diese Werte als Parametervektoren und nicht mehr als feste Skalare.

Optimierungsmodell mit Echtzeitgrenzen

model.T = pyo.RangeSet(0, H - 1)
model.p_laden_max = pyo.Param(model.T, initialize=p_max_charge, mutable=True)
model.p_entladen_max = pyo.Param(model.T, initialize=p_max_discharge, mutable=True)
model.soc_min = pyo.Param(model.T, initialize=soc_min, mutable=True)
model.soc_max = pyo.Param(model.T, initialize=soc_max, mutable=True)
model.reserve = pyo.Param(model.T, initialize=reserved_energy, mutable=True)

model.p_c = pyo.Var(model.T, bounds=(0, p.p_max))
model.p_d = pyo.Var(model.T, bounds=(0, p.p_max))
model.soc = pyo.Var(model.T, bounds=(0, p.e_max))

# Echtzeit-Grenzen für Ladeleistung

def lade_grenze(m, t):
    return m.p_c[t] <= m.p_laden_max[t]

model.lade_grenze = pyo.Constraint(model.T, rule=lade_grenze)


def entlade_grenze(m, t):
    return m.p_d[t] <= m.p_entladen_max[t]

model.entlade_grenze = pyo.Constraint(model.T, rule=entlade_grenze)

# Echtzeit-SoC-Reserve und Obergrenze

def soc_min_rule(m, t):
    return m.soc[t] >= m.soc_min[t] * p.e_max + m.reserve[t]

model.soc_min_con = pyo.Constraint(model.T, rule=soc_min_rule)


def soc_max_rule(m, t):
    return m.soc[t] <= m.soc_max[t] * p.e_max

model.soc_max_con = pyo.Constraint(model.T, rule=soc_max_rule)

Wenn eine Restriktion für Stunde 13 das Entladen verbietet, setzt die Datenschicht einfach p_entladen_max[13] = 0. Wenn das BMS für die nächste Stunde eine Derate von 6 MW meldet, wird derselbe Parameter vor dem Lösen angepasst.

Ereignisgesteuerte Reoptimierung

Unregelmäßige Kafka-Updates dürfen nicht bis zum nächsten stündlichen Zyklus ignoriert werden. Dein System braucht eine kleine Event-Schleife, die zwei Dinge macht:

def on_realtime_update(message):
    restriction = parse_message(message)
    data_layer.apply_realtime_restriction(restriction)
    optimizer.update_constraints(data_layer.active_constraints)
    schedule = optimizer.solve(current_time, horizon=next_6_hours)
    execution.submit(schedule)
Harte Wahrheit: Nur ein Fahrplan, der den aktuellsten aktiven Restriktionen entspricht, sollte committed werden. Der Optimierer ist nur dann nützlich, wenn seine Ausgabe unter den aktuellen Echtzeitregeln machbar ist.

Wann der Optimierer nicht lösen sollte

Empfohlenes Architekturpattern

Verwende einen Hybridansatz:

Das hält den Optimierer handhabbar: Er löst weiterhin über einen Horizont, reagiert aber schnell, wenn das System ein bindendes Stream-Ereignis erhält.

📖 Empfohlene Lektüre

Confluent Blog: Data pipelines with Kafka — zeigt, wie Streaming-Ereignisse operative Eingaben für Entscheidungssysteme werden.

Was sollte dein Optimierer tun, wenn eine Kafka-Nachricht eintrifft, die sagt, dass Entladen für die nächsten zwei Stunden verboten ist?

💬 Fragen? Frag nach, wie Kafka in deinen Optimierer eingebunden wird, wie temporäre Restriktionen modelliert werden oder wie die Echtzeit-Entscheidungslogik sicher gehalten werden kann.

Rechtliches: Impressum