|
clear all;
|
|
clc;
|
|
|
|
|
|
% Chargement des données depuis un fichier Excel
|
|
opts = detectImportOptions('Log6.xlsx');
|
|
opts.VariableNamingRule = 'preserve';
|
|
data = readtable('Log6.xlsx', opts);
|
|
|
|
params.R0 = 0.023 ;
|
|
params.R1 = 0.05985;
|
|
params.C1 = 7345.99;
|
|
params.R2 = 0.0068;
|
|
params.C2 = 1073.25;
|
|
|
|
%params.R0 = 0.0232 ;
|
|
%params.R1 = 0.0482;
|
|
%params.C1 = 7345.99;
|
|
%params.R2 = 0.0032 ;
|
|
%params.C2 = 1099.9982;
|
|
|
|
% Définition des paramètres de la batterie
|
|
%params.R0 = 0.023;
|
|
%params.R1 = 0.007296;
|
|
%params.C1 = 3814.045;
|
|
%params.R2 = 0.002747;
|
|
%params.C2 = 1265.502;
|
|
|
|
params.C_b = 18000; %capacité de la batterie
|
|
dt = 0.2; %temps d'échantillonage
|
|
soc_init = 0.872;
|
|
|
|
% Initialisation de l'état et de la covariance
|
|
x = [soc_init; 0; 0]; % SoC initial, V_RC1 et V_RC2 initialisés à 0
|
|
P = eye(3)*1e-6; % Matrice de covariance initiale avec régularisation
|
|
|
|
% Covariance des bruits
|
|
Q = eye(3)*1e-9; % Bruit de processus ajusté
|
|
R = 0.1; % Bruit de mesure ajusté
|
|
|
|
% Simulation du système avec données de courant et de tension observées
|
|
current = data.Current; %courant de décharge
|
|
voltage_measured = data.Voltage; %tension de décharge
|
|
soc_real = data.SOC; % SoC réel depuis le tableau Logpoly3, soc de la methode de notre client
|
|
n_steps = length(current);
|
|
|
|
% Initialisation des matrices pour stocker les SoC estimés par UKF et Coulomb Counting
|
|
SoC_UKF_estimates = zeros(n_steps, 1);
|
|
P_UKF_estimates = zeros(size(P, 1), size(P, 2), n_steps);
|
|
|
|
|
|
for k = 1:n_steps
|
|
|
|
|
|
% Filtre UKF : Appel de la fonction de Prédiction et mise à jour,
|
|
[x, P] = ukf(@(x) f_battery(x, current(k), params, dt), ...
|
|
@(x) h_battery(x, current(k), params), ...
|
|
x, P, Q, R, voltage_measured(k));
|
|
|
|
|
|
% Contrainte pour s'assurer que le SoC reste entre 0 et 1
|
|
%x(1) = max(0, min(1, x(1))); %soc reste entre 0 et 1
|
|
|
|
% Stockage de l'estimation UKF du SoC
|
|
SoC_UKF_estimates(k) = x(1);
|
|
% Stockage de la matrice de covariance P
|
|
P_UKF_estimates(:, :, k) = P;
|
|
|
|
|
|
end
|
|
|
|
% Conversion en pourcentage des estimations de l'UKF (entre 0 et 100 %)
|
|
SoC_UKF_estimates = SoC_UKF_estimates * 100; % Conversion en pourcentage
|
|
|
|
% Méthode Coulomb Counting
|
|
SoC_Coulomb = Coulomb_Counting(current, params.C_b, soc_init*100);
|
|
|
|
|
|
% Affichage des résultats complets avec SoC Coulomb après calcul
|
|
for k = 1:n_steps
|
|
% Print details about the current, SoC UKF estimate, and Coulomb estimate
|
|
fprintf('Étape %d: Courant = %.4f A, SoC estimé UKF = %.4f%%, SoC estimé Coulomb = %.4f%%\n', ...
|
|
k, current(k), SoC_UKF_estimates(k), SoC_Coulomb(k));
|
|
|
|
|
|
end
|
|
|
|
|
|
% Calcul du RMSE entre l'UKF et Coulomb Counting
|
|
RMSE = sqrt(mean((SoC_UKF_estimates - SoC_Coulomb').^2)); % Vérifier les dimensions correctes
|
|
fprintf('RMSE entre UKF et Coulomb Counting: %.6f\n', RMSE);
|
|
|
|
% Calcul du RMSE entre le SoC estimé par UKF et le SoC réel calculé par la
|
|
% méthode de notre client
|
|
RMSE_UKF_SOC = sqrt(mean((SoC_UKF_estimates - soc_real).^2));
|
|
fprintf('RMSE entre SoC estimé par UKF et SoC réel calculé par la méthode de notre client: %.6f\n', RMSE_UKF_SOC);
|
|
|
|
|
|
|
|
|
|
% Tracé des estimations de SoC des deux méthodes
|
|
figure;
|
|
hold on;
|
|
plot(SoC_UKF_estimates, 'b', 'LineWidth', 1.5);
|
|
plot(SoC_Coulomb, 'r--', 'LineWidth', 1.5);
|
|
ylim([0 100]); % Limite les valeurs entre 0 et 100 %
|
|
xlabel('Time Step');
|
|
ylabel('SoC Estimate (%)');
|
|
title('SoC Estimation: UKF vs Coulomb Counting');
|
|
legend('UKF Estimation', 'Coulomb Counting');
|
|
grid on;
|
|
hold off;
|
|
|
|
|
|
% Tracé des estimations de SoC par UKF et du SoC réel
|
|
figure;
|
|
hold on;
|
|
plot(SoC_UKF_estimates, 'b', 'LineWidth', 1.5);
|
|
plot(soc_real, 'g--', 'LineWidth', 1.5);
|
|
ylim([0 100]); % Limite les valeurs entre 0 et 100 %
|
|
xlabel('Time Step');
|
|
ylabel('SoC Estimate (%)');
|
|
title('SoC Estimation: UKF vs Real SoC');
|
|
legend('UKF Estimation', 'Real SoC');
|
|
grid on;
|
|
hold off;
|
|
|
|
|
|
%%%%%%%%%%%%%%%%%%% Modèles et fonctions UKF %%%%%%%%%%%%%%%%%%%
|
|
|
|
% Modèle de transition d'état de la batterie avec bruit réduit
|
|
function x_next = f_battery(x, I, params, dt)
|
|
SoC = x(1); % État de charge de la batterie
|
|
V_RC1 = x(2); % Tension de la première branche du modèle 2RC de la batterie
|
|
V_RC2 = x(3); % Tension de la deuxième branche du modèle 2RC de la batterie
|
|
|
|
|
|
SoC_next = SoC + (I / params.C_b) * dt; % Mise à jour de l'état de charge (SoC)
|
|
SoC_next = max(0, min(1, SoC_next));
|
|
|
|
V_RC1_next = exp(-dt / (params.R1 * params.C1)) * V_RC1 + params.R1 * (1 - exp(-dt / (params.R1 * params.C1))) * I; % Mise à jour de la tension V_RC1 en utilisant une équation exponentielle
|
|
V_RC2_next = exp(-dt / (params.R2 * params.C2)) * V_RC2 + params.R2 * (1 - exp(-dt / (params.R2 * params.C2))) * I; % Mise à jour de la tension V_RC2 en utilisant une équation exponentielle
|
|
|
|
x_next = [SoC_next; V_RC1_next; V_RC2_next]; %Construction du vecteur d'état mis à jour contenant SoC, V_RC1, et V_RC2
|
|
end
|
|
|
|
% Modèle de mesure de la batterie avec bruit réduit
|
|
function V = h_battery(x, I, params)
|
|
SoC = x(1); % État de charge de la batterie
|
|
V_RC1 = x(2); % Tension de la première branche du modèle 2RC de la batterie
|
|
V_RC2 = x(3); % Tension de la deuxième branche du modèle 2RC de la batterie
|
|
|
|
OCV = OCV_from_SOC(SoC); % Calculer la tension en circuit ouvert (OCV) en fonction de l'état de charge (SoC)
|
|
V = OCV - V_RC1 - V_RC2 - I * params.R0; %la tension estimé utilisé pour calculer l'erreur
|
|
|
|
end
|
|
|
|
|
|
% Fonction pour calculer l'OCV en fonction du SoC
|
|
function OCV = OCV_from_SOC(SoC)
|
|
% Courbe de SoC vs V_OC (données fournies)
|
|
soc_values = [0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100] / 100; % SoC en fraction
|
|
ocv_values = [2.7668, 3.2943, 3.4666, 3.5866, 3.6586, 3.7473, 3.854, 3.9416, 4.051, 4.0824, 4.1257]; % Tensions en Volts
|
|
|
|
% Interpolation pour calculer l'OCV en fonction du SoC
|
|
OCV = interp1(soc_values, ocv_values, SoC, 'linear', 'extrap');
|
|
end
|
|
|
|
% definition de la Fonction du filtre UKF : prédiction et mise a jour
|
|
function [x_hat, P] = ukf(f, h, x, P, Q, R, z)
|
|
n = numel(x); %la dimension du vecteur x
|
|
kappa = 0;
|
|
alpha = 0.002;
|
|
beta = 2;
|
|
|
|
lambda = alpha^2 * (n + kappa) - n; %ce paramétre est utilisé pour déterminer la dispersion des points sigma
|
|
gamma = sqrt(n + lambda);
|
|
|
|
X = sigmaPoints(x, P, gamma); %Point sigma
|
|
|
|
X_pred = zeros(n, 2*n+1); %initialiser une matrice vide appelée X_pred qui va stocker les points sigma prédits
|
|
for i = 1:2*n+1
|
|
X_pred(:,i) = f(X(:,i)); % la prédiction de chaque point sigma à travers le modéle de la batterie, propagation du point sigma à l'aide de la fonction non linéaire f,
|
|
end
|
|
|
|
Wm = [lambda / (n + lambda), repmat(1 / (2 * (n + lambda)), 1, 2 * n)]; %Le poid moyen pour calculer la moyenne pondérée des points sigma
|
|
Wc = Wm; %initialement, les poids de covariance sont les mêmes que les poids de moyenne.
|
|
Wc(1) = Wc(1) + (1 - alpha^2 + beta); %poid de covariance pour calculer la covariance pondérée des points sigma
|
|
|
|
x_pred = X_pred * Wm'; %l'estimation de l'état moyen après la propagation des points sigma à travers la fonction de transition d'état non linéaire f
|
|
P_pred = Q; %initialisation de la covariance avec le bruit de processus
|
|
for i = 1:2*n+1
|
|
P_pred = P_pred + Wc(i) * (X_pred(:,i) - x_pred) * (X_pred(:,i) - x_pred)'; %calcul de la covariance
|
|
end
|
|
|
|
Z_pred = zeros(numel(z), 2*n+1); %Initialiser une matrice vide Z_pred qui va contenir les valeurs prédictives des mesures pour chaque point sigma
|
|
for i = 1:2*n+1
|
|
Z_pred(:,i) = h(X_pred(:,i)); %Stocke la mesure prédite pour le i-ème point sigma dans la colonne correspondante de Z_pred
|
|
end
|
|
|
|
z_pred = Z_pred * Wm'; %la mesure moyenne prédite, tension prédite
|
|
|
|
Pz = R; %initialisation de la covariance par le bruit de mesure
|
|
for i = 1:2*n+1
|
|
Pz = Pz + Wc(i) * (Z_pred(:,i) - z_pred) * (Z_pred(:,i) - z_pred)'; %calcul de la covariance Pz qui représente l'incertitude associée aux mesures prédites
|
|
end
|
|
|
|
Pxz = zeros(n, numel(z)); %initialisation d'une matrice vide
|
|
for i = 1:2*n+1
|
|
Pxz = Pxz + Wc(i) * (X_pred(:,i) - x_pred) * (Z_pred(:,i) - z_pred)'; %corrélation entre l'incertitude des états et l'incertitude des mesures
|
|
end
|
|
|
|
K = Pxz / Pz; %gain de Kalman
|
|
x_hat = x_pred + K * (z - z_pred); %met à jour l'estimation des états, l'estimation corrigée des états \uD835\uDC65hat
|
|
|
|
P = P_pred - K * Pz * K'; % met à jour la matrice de covariance des états
|
|
|
|
|
|
end
|
|
|
|
% Fonction pour générer les points sigma
|
|
function X = sigmaPoints(x, P, gamma)
|
|
n = numel(x); %dimension de x
|
|
|
|
% Décomposition de Cholesky de la matrice de covariance P, multipliée par gamma
|
|
A = gamma * chol(P)'; % Calcul de la dispersion des points sigma et transposition
|
|
|
|
X = zeros(n, 2*n+1); % Initialisation de la matrice des points sigma
|
|
X(:,1) = x; % Le premier point sigma est la moyenne des états, x
|
|
for i = 1:n
|
|
X(:,i+1) = x + A(:,i); % Point sigma i+1 obtenu en ajoutant A(:,i) à x
|
|
X(:,i+n+1) = x - A(:,i); % Point sigma i+n+1 obtenu en soustrayant A(:,i) de x
|
|
end
|
|
end
|
|
|
|
%%%%%%%%%%%%%%%%%%% Coulomb Counting Method %%%%%%%%%%%%%%%%%%%
|
|
function SOC = Coulomb_Counting(courant_decharge, C_N, SoC0)
|
|
% Number of points
|
|
N_points = length(courant_decharge);
|
|
delta_t = 0.2; % Time step in
|
|
% Initialize the SOC array
|
|
SOC = zeros(1, N_points); % SoC at each time step
|
|
SOC(1) = SoC0 ; % Initial SoC in percentage
|
|
|
|
% Calculate the state of charge (SOC) for each point with the Coulomb counting method
|
|
for i = 2:N_points
|
|
% Calculate the consumed or added charge in Ah (current * time in seconds)
|
|
charge_consomme = courant_decharge(i) * delta_t; % Current in A, delta_t in seconds
|
|
|
|
% Update the SOC according to the Coulomb counting method
|
|
SOC(i) = SOC(i-1) + ((charge_consomme / C_N) * 100); % Discharge is negative
|
|
if SOC(i) > 100
|
|
SOC(i) = 100;
|
|
elseif SOC(i) < 0
|
|
SOC(i) = 0;
|
|
end
|
|
|
|
|
|
end
|
|
end
|