Regelalgoritmes
Kostoptimalisatie
Het prijsgestuurde regelalgoritme: opslaan wanneer energie goedkoop is en gebruiken of injecteren wanneer ze duur is, binnen de grenzen van batterij en aansluiting.
Kostoptimalisatie is het prijsgestuurde regelalgoritme van Voltmasters. Het houdt de energiekost van de installatie zo laag mogelijk door elke regelcyclus te beslissen hoe de batterij laadt en ontlaadt, op basis van voorspelde energieprijzen en voorspellingen van PV-productie en verbruik.
Hoe het werkt
Elke regelcyclus doet het algoritme dit:
- Vooruitkijken over de voorspellingshorizon naar de dynamische day-aheadprijzen, de verwachte PV-productie en het verwachte verbruik.
- De batterij plannen zodat energie opgeslagen wordt wanneer ze goedkoop is (of uit PV-overschot) en gebruikt of geïnjecteerd wordt wanneer ze duur is. Dat vooruitkijkende plan is de batterijplanning.
- Alleen handelen wanneer het loont: laden en ontladen worden alleen gepland wanneer het prijsverschil groot genoeg is om de verliezen heen en terug van de batterij te overtreffen (een instelbaar minimaal prijsverschil).
- De grenzen respecteren: de minimale en maximale laadtoestand van de batterij en de grenzen van de netaansluiting (maximale injectie en afname).
Het gedetailleerde plan, de soorten acties en een uitgewerkt voorbeeld staan op de pagina Batterijplanning. De rest van deze pagina legt uit hoe het algoritme tot die beslissingen komt.
Twee lussen: plannen en handelen
Kostoptimalisatie draait als twee samenwerkende lussen die één ding delen: de batterijplanning.
- De planningslus kijkt naar de hele voorspellingshorizon en berekent het goedkoopste laad- en ontlaadplan. Dat is het "brein". Het plan wordt regelmatig herberekend, bij elk nieuw kwartier en telkens wanneer er een nieuwe voorspelling binnenkomt, zodat het altijd de laatste prijzen, productie en de echte toestand van de batterij weerspiegelt.
- De regellus draait elke regelcyclus (ongeveer één keer per seconde). Ze leest de ene regel uit de planning die nu geldt, zet die om in concrete setpoints voor batterij, PV en EV, en past die aan aan de live metingen en de grenzen. Dat zijn de "handen".
flowchart LR
subgraph inputs["Voorspellingen en live toestand"]
P["Day-aheadprijzen"]
PV["Voorspelling PV-productie"]
C["Voorspelling verbruik"]
B["Laadtoestand batterij"]
end
GEN["Batterijplanning genereren<br/>planningslus"]
SCHED[("Batterijplanning")]
READ["Regel voor nu lezen<br/>regellus, ongeveer 1s"]
SET["Omzetten naar setpoints batterij / PV / EV"]
HW["Batterij, PV, netaansluiting"]
P --> GEN
PV --> GEN
C --> GEN
B --> GEN
GEN --> SCHED
SCHED --> READ
READ --> SET
SET --> HW
HW -. "gemeten waarden" .-> GEN
Door het werk zo te splitsen hoeft de dure vooruitkijkende optimalisatie niet elke seconde te draaien, terwijl de snelle regellus tussen twee plannen door toch op de werkelijkheid kan reageren.
Wat het algoritme nodig heeft
Het plan wordt opgebouwd uit een mix van voorspellingen, live metingen en projectconfiguratie:
| Invoer | Waar ze vandaan komt | Rol in het algoritme |
|---|---|---|
| Day-ahead verbruiks- en injectieprijzen (per kwartier) | Voorspellingsleverancier (dynamische day-aheadmarkt) | Vertelt het plan wanneer energie goedkoop is om te kopen en wanneer ze lucratief is om te verkopen |
| Voorspelling PV-productie | Voorspellingsleverancier (op basis van het weer) | Vertelt het plan wanneer er gratis of goedkope zonne-energie beschikbaar is om op te slaan |
| Voorspelling verbruik | Voorspellingsleverancier | Vertelt het plan wanneer de site energie nodig heeft: de tekorten die het waard zijn om te dekken |
| Laadtoestand van de batterij | Live meting per batterij | Het startpunt van het verwachte SoC-verloop |
| Grenzen en rendement van de batterij | Projectconfiguratie | Minimale en maximale SoC, laad- en ontlaadsnelheid, nominaal vermogen, rendement heen en terug |
| Grenzen van de netaansluiting | Projectconfiguratie | Maximale afname (ruimte om te laden) en maximale injectie (injectieplafond) |
| Minimaal prijsverschil | Projectconfiguratie | Het verschil dat een laad- en ontlaadpaar moet halen om het verlies en de slijtage waard te zijn |
| Verkoop via batterij-injectie | Projectconfiguratie | Of opgeslagen energie überhaupt aan het net verkocht mag worden |
| Netlading voor injectie | Projectconfiguratie | Of de planner ook goedkope netenergie mag kopen puur om ze bij injectiepieken weer te verkopen |
Het kernidee: prijsarbitrage binnen de grenzen
In de kern doet het algoritme aan energiearbitrage: energie opslaan zolang ze goedkoop is (uit PV-overschot of goedkope neturen) en weer vrijgeven zolang ze duur is, ofwel door het eigen verbruik van de site te dekken, ofwel door ze op het net te injecteren voor inkomsten.
Twee dingen houden dat eerlijk:
- Verliezen heen en terug. Energie die verloren gaat op weg de batterij in en uit, betekent dat een opgeslagen kilowattuur er minder dan één aflevert. Het plan berekent elke laad- en ontlaadbeweging met het rendement heen en terug van de batterij.
- Het minimale prijsverschil. Een laad- en ontlaadpaar wordt alleen gepland wanneer het prijsverschil tussen het goedkope en het dure kwartier een instelbare drempel haalt. Dat dekt het verlies heen en terug en de slijtage van de batterij, en het voorkomt dat de batterij cyclet voor een marginale winst.
Alles wat de optimalisator plant moet bovendien passen binnen de grenzen van de laadtoestand en de grenzen van de netaansluiting; die beperkingen lopen door elke stap hieronder.
De planning opbouwen, stap voor stap
De planningslus zet de voorspellingshorizon om in een batterijplanning via een vaste keten. Elke stap voegt iets toe aan of verfijnt wat de vorige opleverde:
flowchart TD
A["Voorspellingshorizon<br/>prijzen · PV · verbruik"] --> S1
S1["1 · Energiebronnen per kwartier<br/>PV-overschot + ruimte op het net"] --> S2
S2["2 · Tekortkwartieren bepalen<br/>verbruik hoger dan PV"] --> S3
S3["3 · Laden plannen voor tekorten<br/>goedkoopste haalbare bron eerst"] --> S35
S35["3.5 · Laden bij negatieve prijs<br/>laden zolang de prijs onder nul staat"] --> S375
S375["3.75 · Ontladen voor injectie<br/>verkopen bij een hoge injectieprijs"] --> S38
S38["3.8 · Netlading voor injectie<br/>goedkoop kopen om bij de pieken te verkopen"] --> S3875
S3875["3.875 · Niet-regelbare PV opslaan<br/>onvermijdelijk PV-overschot opvangen"] --> S46
S46["4 tot 6 · Regels bouwen en SoC vooruitrekenen<br/>laden, ontladen, stilstaan"] --> OUT[("Batterijplanning")]
- Energiebronnen per kwartier. Voor elk kwartier berekent het algoritme hoeveel energie daar opgeslagen zou kunnen worden en tegen welke prijs: het PV-overschot (zon boven het lokale verbruik, energie die anders geïnjecteerd zou worden, geprijsd aan de injectieprijs) en de ruimte op het net (hoeveel er nog afgenomen kan worden onder de netgrens, geprijsd aan de verbruiksprijs). Beide worden begrensd zodat het gecombineerde laadvermogen nooit meer bedraagt dan wat de batterijen kunnen opnemen.
- Tekortkwartieren bepalen. Kwartieren waarin het voorspelde verbruik hoger ligt dan de voorspelde PV zijn tekorten: de site zal energie moeten afnemen. Dat zijn de kwartieren die het waard zijn om uit de batterij te dekken, en de dure zijn de eerste doelwitten.
- Laden plannen voor tekorten. Dit is het hart van de optimalisator. Hij loopt de tekorten af van duur naar goedkoop en zoekt in eerdere kwartieren de goedkoopste laadbronnen waarvan de prijs minstens het minimale prijsverschil onder die van het tekort ligt, zodat alleen paren gepland worden die duidelijk lonen. PV krijgt voorrang op het net (het is goedkoper); netbronnen worden van goedkoop naar duur genomen. Elke toewijzing wordt getoetst aan de verwachte SoC, zodat de batterij nooit voorbij haar capaciteit gepland wordt, en de bijhorende ontlading wordt in het tekortkwartier geboekt. Drie regels bepalen wanneer een tekort überhaupt opgeslagen energie krijgt:
- Prijsbodem. Een tekort dat goedkoper is dan het minimale prijsverschil wordt nooit uit de batterij bediend: zelfs gratis bijladen zou de vereiste marge niet halen, dus neemt het gewoon van het net af.
- Geen nutteloze heen-en-terugbewegingen. Een ontlading die de batterij er meteen daarna weer zou bijladen (goedkoop laden of zoninstroom vlak erna) wordt alleen gepland wanneer de prijs van het tekort ook de bijlaadprijs plus de marge overtreft. Dat voorkomt dat de batterij binnen een goedkoop prijsvenster tussen laden en ontladen blijft wisselen.
- Opgeslagen energie gaat naar haar waardevolste bestemming. Wanneer er meer vraag naar opslag is dan aanbod, laadt een tekort dat voordelig uit een goedkoop venster bijgeladen kan worden eerst uit dat venster, maar alleen wanneer de opgeslagen energie die daardoor vrijkomt een echt betere bestemming heeft: een later tekort dat niet voordelig kan bijladen, of een winstgevende injectieverkoop (die geen goedkoop venster kan bedienen). Anders houdt het tekort de gewone volgorde, met opslag eerst.
- Laden bij negatieve prijs. Zakt de verbruiksprijs onder nul, dan word je betaald om te verbruiken, dus plant het algoritme extra laden in die kwartieren, met ruimte gereserveerd voor PV die niet afgeregeld kan worden, om te vermijden dat je die later moet betalen om te injecteren.
- Ontladen voor injectie. Staat het verkopen van opgeslagen energie aan, dan zoekt het algoritme kwartieren met een aantrekkelijke injectieprijs (positief en boven het minimale prijsverschil) en plant het daar ontladingen, hoogste prijs eerst. Het ontlaadt nooit onder de reserve, en nooit binnen de laatste vier uur van de horizon (te onzeker om er opslag aan vast te leggen). Een injectie kan ook als extra bovenop een bestaande ontlading voor een tekort komen. Opgeslagen energie wordt gekoppeld aan de verkopen die ze echt nodig hebben: kandidaten die een goedkoop laadvenster even goed kan bedienen (doorgaans pieken op een latere dag) wijken voor aankopen in dat venster, zodat de huidige piek, waar niets goedkopers bij kan, eerst en op vol vermogen verkoopt.
- Netlading voor injectie. Staat ook de optie netlading voor injectie aan, dan koopt het algoritme goedkope netenergie puur om ze weer te verkopen: voor elk aantrekkelijk injectiekwartier met nog vrije ontlaadruimte plant het aankopen in eerdere kwartieren waarvan de prijs het break-evenpunt heen en terug haalt, precies groot genoeg om die extra verkoop te leveren (zie Energie verkopen aan het net).
- Niet-regelbare PV opslaan. PV die fysiek niet afgeregeld kan worden en anders goedkoop geïnjecteerd zou worden, wordt opgevangen en toegewezen aan de waardevolste injectiekwartieren in plaats van verloren te gaan.
- Regels bouwen en de SoC vooruitrekenen. De plannen worden regels met een tijdstip (laden, ontladen of stilstaan), elk met een actie en een doel (zie Batterijplanning). De overige kwartieren worden stilstaan, of bij productieoverschot opladen waar er nog gratis zon in de batterij loopt. De laadtoestand wordt over de hele horizon vooruitgerekend zodat het volledige plan binnen de minimale en maximale grenzen blijft, en de regels worden op tijd gesorteerd.
Bij het opslaan van het plan blijven regels voor kwartieren die al begonnen zijn bewaard en worden ze samengevoegd met het nieuwe plan; regels ouder dan 14 dagen worden opgeruimd. Zo blijft er per kwartier een spoor van wat er gepland en uitgevoerd is.
Wat het plan haalbaar houdt
Een aantal mechanismen loopt door de hele keten om de planning fysiek kloppend en stabiel te houden:
- Verwacht SoC-verloop. Het algoritme simuleert het energieniveau van de batterij kwartier per kwartier. Laden wordt nooit gepland boven de maximale SoC, en ontladen brengt de verwachte SoC nooit onder het effectieve minimum (de ingestelde minimale SoC plus een eventuele reserve).
- Laadsegmenten. De horizon wordt bij de grote PV-laadperiodes opgesplitst in onafhankelijke budgetten. Een tekort na een zonnige namiddag kan geen energie uitgeven die bij tekorten ervoor hoort. Dat is wat plannen over meerdere dagen stabiel houdt in plaats van bij elke herberekening te laten verschuiven.
- Netgrenzen overal. Het laadvermogen per kwartier wordt begrensd door de afnameruimte die nog vrij is onder de netgrens; injectie en ontlading worden begrensd door de injectiegrens. PV-injectie en batterij-injectie delen datzelfde injectiebudget.
- Reserves. Energie die de piekafvlakkingsreserve opzijzet, geldt als onaanraakbaar voor gewoon ontladen, dus het plan geeft ze nooit uit. Ze blijft beschikbaar om het net op zijn afnamegrens te houden, en wordt met voorrang aangevuld wanneer ze slinkt.
Van plan naar setpoint: de regelcyclus
De planning drukt een intentie uit; de regellus zet die om in actie. Elke cyclus zoekt het algoritme de regel voor nu op, leest het actietype en het geplande vermogen, en zet dat om in een setpoint, altijd begrensd door de live toestand en de netgrenzen.
flowchart TD
N["Deze regelcyclus"] --> LOOK["Regel voor nu opzoeken"]
LOOK --> AT{"Actietype?"}
AT -->|charge_at_max_power| CMAX["Laden op maximaal vermogen<br/>begrensd door netafname + laadsnelheid"]
AT -->|follow_scheduled_power| CFOL["Gepland vermogen volgen<br/>begrensd door de grenzen"]
AT -->|compensate_pv_surplus| CPV["Alleen laden met het live PV-overschot"]
AT -->|compensate_production_deficit| DDEF["Ontladen om het live tekort te dekken"]
AT -->|idle| IDLE["Geen batterijactie"]
CMAX --> APPLY
CFOL --> APPLY
CPV --> APPLY
DDEF --> APPLY
IDLE --> APPLY
APPLY["Setpoints toepassen<br/>SoC en netgrenzen respecteren"] --> REC["Werkelijke tegenover geplande energie vastleggen"]
Hoe elke actie zich tijdens de werking gedraagt:
| Actietype | Wat de regellus doet |
|---|---|
charge_at_max_power |
Laden op het maximale vermogen dat de afnameruimte op het net en de laadsnelheid toelaten |
follow_scheduled_power |
Exact het geplande vermogen volgen (van het net laden, of ontladen om te injecteren), begrensd door de grenzen |
compensate_pv_surplus |
Alleen laden met het live PV-overschot, dus niet van het net |
compensate_production_deficit |
Precies genoeg ontladen om het live productietekort te dekken |
idle |
De batterij met rust laten |
In dezelfde cyclus stelt het algoritme ook de regelbare verbruikers, de verdeling over de laadpalen en de setpoints van de PV-omvormers in (afregelen alleen wanneer dat nodig is om de injectiegrens te respecteren).
Omdat de cyclus vertrekt van live metingen, vangt ze fouten in de voorspelling vanzelf op: de compensate_*-acties bepalen hun grootte op basis van wat er op dat moment echt gebeurt, terwijl follow_scheduled_power en charge_at_max_power het plan volgen binnen de grenzen.
De compensate_*-acties balanceren de aansluiting richting 0 kW. Een doelwaarde voor het netvermogen verschuift dat setpoint naar een kleine vaste afname, wat gebruikt wordt op sites waar een extern toestel de productie afregelt om injectie te vermijden.
De lus sluiten
Na elk kwartier legt de controller vast hoeveel er echt geladen is (uit PV en van het net) en hoeveel ontladen, en vergelijkt dat met het plan. Is er minder energie opgeslagen dan gepland, dan haalt een tekortcorrectie dat tekort eerst weg bij de duurste doelkwartieren. De volgende herberekening vertrekt dan van de werkelijke toestand van de batterij, zodat de strategie zichzelf voortdurend bijstuurt naarmate de werkelijkheid van de voorspelling afwijkt.
Uitgewerkt voorbeeld: hoge avondprijzen
De grafiek hieronder toont het scenario hoge avondprijzen. Elektriciteit is overdag goedkoop en zonnig, en wordt in de avondpiek veel duurder.

Gelezen tegen de keten: stap 2 markeert de avond als een reeks dure tekortkwartieren; stap 3 plant het laden overdag (grotendeels opgeslagen PV-overschot) om die te dekken, omdat het prijsverschil tussen dag en avond het minimale prijsverschil haalt; en de regellus ontlaadt vervolgens door de piek, met een verwachte SoC die overdag naar haar maximum klimt en 's nachts weer zakt. De volledige opsplitsing per actie van dit scenario staat op de pagina Batterijplanning.
Verhouding tot een extern signaal
Kostoptimalisatie is een lokale strategie. Stuurt een partner via een extern signaal de installatie actief aan, dan heeft die partner voorrang en stuurt kostoptimalisatie de batterij niet aan. Is er geen extern signaal actief, of staat de partner op stand-by, dan volgt de batterij de planning van de kostoptimalisatie.