Contexte et Défis Nouvelles Caractéristiques des Réseaux de Distribution Intelligents Avec l'intégration de grandes quantités d'énergies renouvelables (photovoltaïque, éolien) dans les réseaux de distribution, le système opérationnel fait face à une double incertitude: la variabilité aléatoire de la prdouction renouvelable et les fluctuations de la demande électrique. Les méthodes de planification déterministes traditionnelles ne parviennent pas à répondre à ces défis, nécessitant l'intégration de la théorie d'optimisation stochastique.
Nécessité d'une Planification à Double Échelle de Temps Les ressources de planification présentent des caractéristiques de réponse différentes:
- Échelle de temps lente (jour-avant, niveau horaire): planification de charge/décharge du stockage, démarrage/arrêt des unités, commutation des condensateurs
- Échelle de temps rapide (jour-courant, niveau minute): régulation réactive des onduleurs photovoltaïques, réponse à la demande, régulation rapide du stockage
Cadre de Planification à Double Échelle de Temps Structure du Cadre de Base Un système de planification à double échelle de temps comprend généralement deux phases:
| Échelle de temps | Période de planification | Résolution temporelle | Contenu principal des décisions | Gestion de l'incertitude |
|---|---|---|---|---|
| Planification jour-avant | 24 heures | 1 heure | Démarrage/arrêt des unités, planification du stockage, transactions sur le marché | Méthode des scénarios / Optimisation robuste |
| Planification jour-courant/temps réel | 4 heures | 15 minutes | Régulation réactive PV, réponse rapide du stockage | Optimisation glissante / Correction par retour |
Processus de Planification
Données prévisionnelles jour-avant
Optimisation stochastique jour-avant
Élaboration du plan jour-avant
Optimisation glissante jour-courant
Correction en temps réel
Mise à jour prévisionnelle ultra-courte
Données de mesure en temps réel
Modèle Mathématique d'Optimisation Stochastique Cadre de Programmation Stochastique à Deux Étapes Étape Jour-avant (Première étape) Fonction objectif : Minimisation du coût total attendu
[ \min \mathbb{E}_{\xi} \left[ C^{\text{DA}}(x) + Q(x, \xi) \right] ]
Où :
- (x) : Variables de décision jour-avant (démarrage/arrêt des unités, planification du stockage, etc.)
- (\xi) : Paramètres d'incertitude (production PV, demande électrique)
- (C^{\text{DA}}) : Coût de la planification jour-avant
- (Q(x, \xi)) : Fonction de coût de la planification en temps réel
Contraintes :
- Contrainte d'équilibre de puissance : (\sum P_{G,i} + \sum P_{PV} + \sum P_{ESS} = P_{Load})
- Contraintes de fonctionnement des unités : (P_{G}^{\min} \leq P_G \leq P_{G}^{\max})
- Contraintes de stockage : (SOC^{\min} \leq SOC \leq SOC^{\max})
- Contraintes réseau : (V^{\min} \leq V \leq V^{\max}), (|I| \leq I^{\max})
Étape Temps Réel (Deuxième étape) Fonction objectif : Minimisation du coût d'ajustement en temps réel
[ Q(x, \xi) = \min_{y} \left{ C^{\text{RT}}(y) : T(x, y, \xi) \leq 0 \right} ]
Où (y) représente les variables d'ajustement en temps réel (curtailment PV, réponse rapide du stockage, etc.).
Méthodes de Modélisation de l'Incertitude Méthode des Scénarios
# Exemple de génération et réduction de scénarios
import numpy as np
from scipy.stats import norm
from sklearn.cluster import KMeans
def generer_scenarios(donnees_prevision, nb_scenarios):
"""
Génère des scénarios de production PV basés sur les données historiques
"""
# Paramètres de la distribution
mu = donnees_prevision['moyenne']
sigma = donnees_prevision['ecart_type']
mat_correlation = donnees_prevision['mat_correlation']
# Génération de scénarios avec prise en compte de la corrélation
L = np.linalg.cholesky(mat_correlation)
scenarios = np.zeros((24, nb_scenarios))
for s in range(nb_scenarios):
bruit = np.random.normal(0, 1, 24)
scenarios[:, s] = mu + sigma * (L @ bruit)
# Réduction des scénarios (méthode des K-moyennes)
kmeans = KMeans(n_clusters=10)
kmeans.fit(scenarios.T)
scenarios_reduits = kmeans.cluster_centers_.T
return scenarios_reduits
Optimisation Robuste Distribuée (DRO) Pour l'ensemble d'incertitude de la production PV :
[ \mathcal{U} = \left{ \mathbb{P} \in \mathcal{P} : \mathbb{E}{\mathbb{P}}[\xi] = \mu, \mathbb{E}{\mathbb{P}}[(\xi-\mu)(\xi-\mu)^T] \preceq \Sigma \right} ]
L'objectif devient la minimisation du coût attendu dans le pire des cas.
Programmation avec Contraintes de Chance Contrainte de sécurité de tension :
[ \mathbb{P}\left( V_i^{\min} \leq V_i \leq V_i^{\max} \right) \geq 1 - \epsilon ]
Où (\epsilon) représente le niveau de risque acceptable.
Points Clés d'Implémentation avec Python Structure du Programme Principal
# Programme principal d'optimisation stochastique à double échelle
import numpy as np
import pandas as pd
from scipy.optimize import minimize
import matplotlib.pyplot as plt
def main():
# 1. Préparation des données
system_data = charger_systeme('donnees_reseau.json')
donnees_prevision = charger_previsions('previsions.csv')
# 2. Modélisation de l'incertitude
scenarios_pv = generer_scenarios(donnees_prevision['pv'], 100)
scenarios_reduits = reduire_scenarios(scenarios_pv, 10)
# 3. Optimisation stochastique jour-avant
plan_jour_avant, cout_jour_avant = optimisation_jour_avant(
system_data, scenarios_reduits)
# 4. Optimisation glissante jour-courant
plan_temps_reel = []
for t in range(24):
# Récupération des prévisions en temps réel
prevision_temps_reel = get_prevision_temps_reel(t)
# Fenêtre d'optimisation glissante (4 heures, résolution 15 min)
plan, cout_temps_reel = optimisation_temps_reel(
plan_jour_avant, prevision_temps_reel, t)
plan_temps_reel.append(plan)
# Exécution des commandes du premier intervalle
executer_commande(plan[:, 0])
# 5. Analyse et visualisation des résultats
visualiser_resultats(plan_jour_avant, plan_temps_reel)
if __name__ == "__main__":
main()
Modèle d'Optimisation Jour-avant (Forme MILO)
def optimisation_jour_avant(systeme, scenarios):
"""
Modèle d'optimisation stochastique jour-avant
"""
import cvxpy as cp
import numpy as np
# Paramètres
N_bus = systeme['nombre_noeuds']
N_temps = 24
N_scen = scenarios.shape[1]
# Variables de décision
P_gen = cp.Variable((N_bus, N_temps)) # Puissance active générée
Q_gen = cp.Variable((N_bus, N_temps)) # Puissance réactive générée
SOC = cp.Variable((systeme['nombre_ess'], N_temps)) # État de charge du stockage
u_gen = cp.Variable((N_bus, N_temps), boolean=True) # État des unités
# Fonction objectif : minimisation du coût total attendu
cout_total = 0
for s in range(N_scen):
cout_scenario = 0
for t in range(N_temps):
# Coût de carburant
cout_scenario += cp.sum(
systeme['a'] * P_gen[:, t]**2 +
systeme['b'] * P_gen[:, t] +
systeme['c']
)
# Coût de démarrage
if t > 0:
cout_scenario += cp.sum(
systeme['cout_demarrage'] * cp.pos(u_gen[:, t] - u_gen[:, t-1])
)
# Pénalité de curtailment PV
curtailment = cp.pos(scenarios[t, s] - systeme['pv_max'])
cout_scenario += systeme['penalite_pv'] * cp.sum(curtailment)
cout_total += systeme['probabilite_scen'][s] * cout_scenario
# Contraintes
contraintes = []
# Équilibre de puissance (chaque scénario)
for s in range(N_scen):
for t in range(N_temps):
# Équilibre actif
contraintes.append(
cp.sum(P_gen[:, t]) + scenarios[t, s] - systeme['charge'][t] == 0
)
# Équilibre réactif
contraintes.append(
cp.sum(Q_gen[:, t]) - systeme['charge_reactive'][t] == 0
)
# Contraintes de sécurité des tensions (relaxation SOCP)
for t in range(N_temps):
for i in range(N_bus):
for j in systeme['voisins'][i]:
# Contraintes de flux de puissance sur les branches
P_ij = cp.Variable()
Q_ij = cp.Variable()
l_ij = cp.Variable() # Carré de l'amplitude du courant
# Contrainte du cône secondaire d'ordre
contraintes.append(
cp.norm(cp.hstack([2*P_ij, 2*Q_ij, l_ij - systeme['V_carre'][i, t]]), 2)
<= l_ij + systeme['V_carre'][i, t]
)
# Problème d'optimisation
probleme = cp.Problem(cp.Minimize(cout_total), contraintes)
# Résolution
probleme.solve(solver=cp.GUROBI, verbose=True)
return {
'P_gen': P_gen.value,
'Q_gen': Q_gen.value,
'SOC': SOC.value,
'u_gen': u_gen.value,
'cout': probleme.value
}
Implémentation de l'Optimisation Glissante en Temps Réel
def optimisation_temps_reel(plan_jour_avant, prevision, temps_actuel):
"""
Optimisation glissante en temps réel basée sur le contrôle prédictif
"""
import cvxpy as cp
# Horizon de prédiction : 4 heures (16 intervalles de 15 min)
N_pred = 16
# État actuel
etat_actuel = get_etat_actuel()
# Construction du problème MPC
opti = cp.Problem()
# Variables de décision
P_pv = cp.Variable((systeme['nombre_noeuds'], N_pred)) # Production PV réelle
Q_pv = cp.Variable((systeme['nombre_noeuds'], N_pred)) # Puissance réactive PV
P_ess = cp.Variable((systeme['nombre_ess'], N_pred)) # Puissance du stockage
# Fonction objectif : suivi du plan jour-avant + coût d'ajustement minimal
J = 0
for k in range(N_pred):
# Pénalité de suivi de trajectoire
J += cp.norm(P_pv[:, k] - plan_jour_avant['P_pv'][:, temps_actuel + k - 1], 2)**2
# Coût d'ajustement
J += systeme['cout_ajustement'] * cp.norm(P_ess[:, k], 1)
# Pénalité de qualité de tension
V = calculer_tension(P_pv[:, k], Q_pv[:, k], P_ess[:, k])
J += systeme['cout_tension'] * cp.sum(
cp.pos(V - systeme['V_max'])**2 +
cp.pos(systeme['V_min'] - V)**2
)
opti.objective = cp.Minimize(J)
# Contraintes
for k in range(N_pred):
# Contraintes de production PV
opti.constraints += [
P_pv[:, k] >= 0,
P_pv[:, k] <= prevision['pv_disponible'][:, k],
-systeme['S_pv'] <= Q_pv[:, k] <= systeme['S_pv']
]
# Contraintes de stockage
opti.constraints += [
-systeme['P_ess_max'] <= P_ess[:, k] <= systeme['P_ess_max']
]
# Équilibre de puissance
opti.constraints += [
cp.sum(P_pv[:, k]) + cp.sum(P_ess[:, k]) == prevision['charge'][:, k]
]
# Résolution
opti.solve(solver=cp.ECOS, verbose=False)
return {
'commande': [P_pv.value[:, 0], Q_pv.value[:, 0], P_ess.value[:, 0]],
'cout': opti.value
}
Technologies et Algorithmes Clés Linéarisation du Modèle de Flux de Puissance Le modèle de flux de puissance du réseau de distribution utilise le modèle de flux de puissance linéaire distribué (LDF) :
[\begin{aligned} P_{ij} &= \sum_{k \in \delta(j)} P_{jk} + p_j^L - p_j^G \ Q_{ij} &= \sum_{k \in \delta(j)} Q_{jk} + q_j^L - q_j^G \ v_j &= v_i - 2(r_{ij}P_{ij} + x_{ij}Q_{ij}) \end{aligned}]
Où (v_i = V_i^2) représente le carré de l'amplitude de tension.
Comparaison des Algorithmes de Résolution
| Type d'algorithme | Avantages | Inconvénients | Cas d'utilisation |
|---|---|---|---|
| Programmation Mixte Second Ordre Cône (MISOCP) | Solution globalement optimale, traitement des contraintes non convexes | Complexité de calcul élevée | Réseaux de petite et moyenne taille |
| Programmation Stochastique (méthode des scénarios) | Intuitive, facile à implémenter | Nombre de scénarios affecte la précision et la charge de calcul | Distribution d'incertitude connue |
| Optimisation Robuste Distribuée (DRO) | Robuste aux ensembles de distribution flous | Peut être trop conservateur | Données historiques limitées |
| Programmation avec Contraintes de Chance | Contrôle de risque explicite | Nécessite des informations de distribution | Systèmes avec exigences de sécurité strictes |
| Programmation Dynamique Approchée (ADP) | Traite les espaces d'état de haute dimension | Nécessite une approximation de la fonction de valeur | Problèmes stochastiques multi-étapes |
Accélération par Calcul Parallèle
# Utilisation du calcul parallèle pour traiter plusieurs scénarios
from joblib import Parallel, delayed
def resoudre_scenarios(scenarios):
"""
Résolution parallèle des sous-problèmes pour chaque scénario
"""
def resoudre_sous_probleme(scenario, idx):
# Chaque scénario est résolu indépendamment
cout, solution = resoudre_scenario_individuel(scenario)
return {'scenario': idx, 'cout': cout, 'solution': solution}
# Exécution parallèle avec 4 workers
resultats = Parallel(n_jobs=4)(
delayed(resoudre_sous_probleme)(scenarios[:, s], s)
for s in range(scenarios.shape[1])
)
# Agrégation des résultats
cout_total = sum(r['cout'] * scenarios_proba[r['scenario']]
for r in resultats)
return cout_total, [r['solution'] for r in resultats]
Tendances de Recherche et Défis Points Chauds Actuels de la Recherche
- Méthodes basées sur les données : Génération de scénarios utilisant les réseaux antagonistes génératifs (GAN)
- Optimisation distribuée : Algorithme ADMM pour une planification coordonnée des réseaux de transmission et de distribution
- Intégration de la centrale virtuelle : Optimisation à deux étapes avec transactions sur le marché
- Coordination source-réseau-charge-stockage : Optimisation conjointe de la réponse à la demande et du stockage
Défis d'Application Pratique
- Efficacité de calcul : Résolution en temps réel pour les grands réseaux de distribution
- Précision des prévisions : Erreurs de prévision ultra-courte des énergies renouvelables
- Délai de communication : Exigences de temps réel pour le contrôle distribué
- Interfaces normalisées : Interaction de données entre multiples acteurs
Directions de Développement Futur
- Fusion avec l'intelligence artificielle : Prévision par apprentissage profond + décision par renforcement
- Déploiement en calcul de bord : Réponse locale rapide
- Technologie blockchain : Validation des transactions d'énergie distribuée
- Jumeau numérique : Perception et simulation de l'état complet du réseau de distribution
Conseils Pratiques et Ressources Outils Python Recommandés
- Pandapower : Calcul de flux de puissance et flux optimal
- Pyomo : Interface de modélisation d'optimisation
- CVXPY : Modélisation d'optimisation convexe
- CasADi : Optimisation non linéaire et différentiation automatique
- UQpy : Quantification de l'incertitude
Parcours d'Apprentissage Recommandé
- Niveau de base : Calcul de flux de distribution + optimisation déterministe
- Niveau intermédiaire : Théorie de la programmation stochastique + techniques de génération de scénarios
- Niveau avancé : Optimisation robuste distribuée + optimisation glissante en temps réel
- Niveau pratique : Reproduction de cas d'étude sur le système IEEE 33 nœuds