BMS, Etat de charge et santé de batterie lithium, banc cyclage (arduino)

Avez-vous vous mis une résistance de pull up R2 de valeurs entre 820Ω à 470Ω entre le PCF8574 et l’optocoupleur ?
Comme sur la figure de simulation suivante
La valeur de la résistance R2 ne doit pas être trop importante, car il faut un courant pour saturer le transistor de l’optocoupleur PS2801.
En effet, suite au data sheet, PCF8574, celui-ci ne peut fournir qu’un courant de 0.1mA, insuffisant pour commander le PS2801.
https://www.ti.com/lit/ds/symlink/pcf8574.pdf?ts=1597942095825&ref_url=https%253A%252F%252Fwww.ti.com%252Fproduct%252FPCF8574
http://jp79dsfr.free.fr/Docs%20et%20infos/Elec%20%20Arduino%20-%20Bus%20I2c.pdf
Par contre, PCF8574 peut supporter un courant rentrant de 25mA (10mA typique)
Donc, voici le schéma électrique et le programme que j’ai utilisé pour vérifier le bon fonctionnement PCF8574 avec la carte MOSFET.
Cela fonctionne, j’ai verifié.

Sur la figure précédente, il est vrai que la simulation ISIS allume la led alors que ce n’est pas possible en réalité.
La simulation d’ISIS a de nombreux petits bugs comme celui-ci.
Le programme qui ecrit toutes les 3 secondes sur les 2 PCF8574 et qui lit l’octet pour vérifier que l’écritrue se fait bien.
La led 13 est allumée et eteinte tous les 3 secondes pour verifier que le programme fonctionne bien

#include <Wire.h>     // // Join i2c bus
#include <LiquidCrystal_I2C.h>
#include "RTClib.h"      // Bibliothèque pour le module RTC

LiquidCrystal_I2C lcd(0x27, 20, 4);     //A0, A1, A2 non shunter
RTC_DS1307 rtc;   //minuscule    minuscule 


#define led13   13 
byte data;



DateTime datetime;

void setup()
{  // initialise l'afficheur LCD
pinMode(led13, OUTPUT); 

 Wire.begin();
lcd.init();  //et pas lcd begin comme cetraine biblio
  lcd.display();     // activer l'affichage
  lcd.backlight();   // allumer retroeclairage
  lcd.setCursor(0,0);
  lcd.print("IUT GENIE elect&info");
  lcd.setCursor(5,1);
  lcd.print("Soissons");
  //Serial.begin(57600);

}




void loop()
{

if (digitalRead(13)== 1 ) 

{digitalWrite(13,LOW);   
Wire.beginTransmission(0x20);   // transmit to device #20  PCF8574  A0=0, A1=0, A2=0
Wire.write(00);                  // sends value byte 
Wire.endTransmission();         // stop transmitting 

Wire.beginTransmission(0x21);   // transmit to device #21  PCF8574  A0=1, A1=0, A2=0
Wire.write(0b10101010);         // sends value byte 
Wire.endTransmission(); 

}


else {digitalWrite(13,HIGH);
Wire.beginTransmission(0x20);   // transmit to device #20  PCF8574  A0=0, A1=0, A2=0
Wire.write(03);                  // sends value byte 
Wire.endTransmission();         // stop transmitting

Wire.beginTransmission(0x21);
Wire.write(0b01010101);         // sends value byte
Wire.endTransmission(); 
}  

 Wire.requestFrom (0x20, 1);   //lecture information
 if (Wire.available ()) {
   data = Wire.read ();  }
  lcd.setCursor(0,2);
  lcd.print(data);
  lcd.print("=adresse20  ");

   Wire.requestFrom (0x21, 1);   //lecture information
 if (Wire.available ()) {
   data = Wire.read ();  }
  lcd.setCursor(0,3);
  lcd.print(data);
  lcd.print("=adresse21  ");
delay(3000); 
}

Les fabricants de shields pourraient mettre cette résistance de pull up sur leur carte PCF8574!
je ne sais pas pourquoi il ne le font pas. Je me serais fait avoir aussi dans un premier temps.

1 Like

Lorsqu’on sait tester et recharger une batterie NiMH, on sait réparer un volet roulant solaire…. En effet, la techno NiMh sont souvent défectueuse au bout de 4 ans ou elle est déchargé complément au milieu de l’hiver dans le cas des systemes rechargé solaire dans la region du nord de la france.[](https://zupimages.net/viewer.php?id=21/04/0v33.jpg)https://zupimages.net/up/21/04/0v33.jpg |500x242

donc, il a été possible de modifier les programme precedent pour tester et recharger la batterie précédente qui est en 10.9V.

Les vendeurs et le SAV exagère sur leurs tarifs….ils echangent directement la batterie sans faire de teste Si on va chez Action, il y en a pour 9euros, mais il faut savoir monter les elements |500x389 Est-ce que ces batteries Ni Mh ne pourraient pas être remplacé par du lithium ? Il aurait été possible certainement de recharger en mettant une alimentation de 15V directement à la place de panneau solaire….mais il y a très peu d’information sur la tension max et le courant max du panneau…..

merci à la transparence des industriels pour le bien de la planète et de notre portefeuille

Bonjour !

Marius et moi-même (Julie) allons reprendre la suite de ce TR 8) Il s'agit de l'élaboration d'un chargeur de batterie, avec toutes les analyses, études et recherches à faire autour.

Les posts suivant rendront compte de notre avancée, contenant le travail fait, sa date, les images nécessaires, mais également nos interrogations et le travail qui sera à faire ensuite. (Nous faisons bien évidemment beaucoup de recherches à côté, qui ne sont pas toutes répertoriées ici. Nous indiquerons celles qui sont vraiment essentielles).

Si vous avez quelques minutes pour nous aider à avancer .. avec plaisir ! :grin:

1 Like

En testant les montages précédents en supprimant l’optocoupleur, les résultats n’avaient aucune cohérence.
Nous avons donc testé notre transistor sur une maquette réelle.
Schéma complet utilisé :

Schéma de test virtuel du transistor, on a réalisé ensuite ce montage :

Voici le schéma du transistor utilisé à peu de choses près :

avec :
R5=4,7kOhms
Résistance de gate = 4.7KOhms
R4= 4.5k
R3=100Ohms
Imax photocoupleur (sortie) = 30mA
R3 < tensionalim/ 30mA = 166 ohms

R3 est le PCB. C’est sur ces valeurs qu’on va se pencher.

On constate que le retard est trop fort avec un PCB de 100 ohms, pourtant on n’est qu’à 2kHz.

On va donc réduire cette résistance : on la remplace par une résistance de 25 ohms, sur le même transistor.
Le résultat n’est pas concluant non plus. Le retard est toujours présent, on n’atteindra pas la fréquence voulue (64kHz dans l’idéal, au pire 32kHz).
La tension de sortie est de 5V, elle devrait être de 10V, ce qui suppose une perte de 5V dans l’optocoupleur :

On prend la décision de changer le transistor : on prend un transistor sans optocoupleur, comme le montre ce montage :

On le valide. Comme on peut le voir sur la courbe ci-dessous, on peut atteindre une fréquence de 200kHz avant que les signaux ne se dégradent.

On oublie le montage précédent même s’il ne fonctionne pas, il ne nous sert plus à rien, et on garde ce montage sans optocoupleur pour notre chargeur de batterie.

2 Likes

[Gestion de la charge - Test Simulink]

On considère le schéma de charge suivant : |500x133

Les valeurs considérées pour cette charge à 1 ampère : |500x455

|500x375

En lançant la simulation : |500x304

On insère sur notre schéma la tension d'alimentation de la batterie (5V, on pourrait mettre 10V), la tension de notre batterie (1.2V), sa résistance (0.5ohms), et notre PWM permettant de gérer le courant (en modifiant la tension qui passe dans la résistance). Constat des différents tests de charge : Notre entrée échelon permet de gérer à partir de quand la charge sera effective (step). Le gain de la fonction de transfert nous permet de choisir si on arrivera rapidement ou non au courant de charge : plus K est grand, plus on arrive vite au courant de charge, ici 1A en plus ou moins 3 secondes (voir graphe).

Augmenter notre condition initiale permettra également de démarrer plus rapidement la charge. On fixe le seuil de saturation à 2 ampères, et on pense à mettre l'échantillonnage à 0.1s pour des courbes d'évolution homogènes.

A l'état initial le courant, mesuré sur la première entrée du mux, est nul. Par ailleurs, il reste nul tant que la tension délivrée dans la batterie est inférieure à 1.2V (tension de la batterie elle-même). Pour une meilleure visibilité, on pensera à adapter la taille de la fenêtre de notre oscilloscope à nos valeurs, c'est un petit détail qui a son importance.

3 Likes

[Correcteur et courbes - PWM et transistor canal P] Le programme sur lequel on s'est basé est rédigé pour un transistor canal N. On l'adapte pour un canal P, notamment dans l'expression de la PWM (inverse : pour une PWM de 255, on a un courant de 0).

On adapte le paramétrage de l'afficheur LCD (pour ajouter le courant de mesure, de consigne et la valeur de la PWM), et on construit une courbe sur Excel pour affiche la relation entre l'évolution du courant et la PWM régulée pour un courant consigne de 1A. Pour avoir une vision globale, on mets les deux courbes sur le même graphe que voici : On créée une PWM relative comprise entre 0 et 1 pour optimiser l'affichage (sinon on ne pourrait pas avoir les deux courbes en même temps). Il nous faut 0.2 secondes pour atteindre le courant de charge d'un ampère.

Les valeurs utilisées sont récupérées de la simulation finale dont voici le schéma, un screen du résultat, et le programme associé :

|500x416 |500x199 https://drive.google.com/file/d/1CBZFe9Ho5kUdWFAD8R-2_20lj8wCrHiw/view?usp=sharing

2 Likes

Il est possible de programmer un code Arduino via Matlab Simulink. Ainsi, MATLAB peut gérer les mesures, et permet également, en temps réel, d'avoir une estimation de l'état de santé du chargeur de batterie.

Nous allons voir comment programmer une régression linéaire avec MATLAB.

Une solution serait, directement sur l'interface de MATLAB, de reprendre les lignes de code et simuler avec ces lignes de code. De ce fait, pour établir une base sur cette régression sur MATLAB, il y a une fonction particulière disponible dans les librairies du logiciel.

Voici ce que l'on obtient, selon des valeurs de a (capacité) et b (tension) lambda.

|500x403

[u]Voici le code MATLAB utilisé pour réaliser cette petite simulation :[/u]

[color=forestgreen]% Donnees originales[/color]
[color=black]a = [0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20];[/color]
[color=black]b =[2 5 3 5 7 9 11 13 15 17 19 20 22 24 25 26 28 30 33 36 39];[/color]
[color=black]plot(a, b, [/color][color=#a020f0]'d'[/color][color=black]);[/color]

[color=forestgreen]% Regression lineaire d'ordre 1 : f(x) = m*x + n[/color]
[color=black]p1 = polyfit(a,b,1);[/color]
[color=black]c = a;[/color]
[color=black]d = p1(2) + a* p1(1);[/color]
[color=black]plot(a, b, [/color][color=#a020f0]'d'[/color][color=black],c, d);[/color]
[color=black]title([/color][color=#a020f0]'Donnees et regressions lineaires'[/color][color=black]);[/color]
[color=black]legend([/color][color=#a020f0]'donnees'[/color][color=black], [/color][color=#a020f0]'ordre 1'[/color][color=black]);[/color]

On retrouve bien le principe de régression linéaire énoncé précédemment dans ce forum Arduino.

Ensuite, via Simulink, j'ai repris le principe de régression linéaire et créer un schéma pour établir les calculs nécessaires à réaliser cette régression.

[u]Voici le schéma Simulink, qui retranscris le code permettant la régression linéaire.[/u]

|500x232

La simulation n’a pas encore été faite pour le moment. Pour la suite, il faudrait tester ce schéma Simulink avec des valeurs simulées sur ISIS par exemple.

Et par la suite, essayer de réaliser des tests physiques sur une vraie batterie, et voir l’évolution de la tension de batterie sur la capacité, pour estimer l’état de santé du stockeur.

3 Likes

Pour donner suite à mon dernier post sur le forum, j’ai décidé de réaliser des tests sur système réel. Les tests que l’on va effectuer seront sur une batterie Lipofer possédant une résistance interne de 0.02 ohms.
Pour cela j’ai repris le modèle d’anciens schémas ISIS pour l’adapter à notre système. Pour cela, la batterie utilisée en simulation sera de 3.5V.

Voici le schéma ISIS utilisé :

Pour ce schéma, on ne va pas utiliser l’extension de broche car ici ce n’est pas utile. Sur cette simulation, on va pouvoir mesurer la tension de la batterie, la tension aux bornes de l’ACS, ainsi que la température.
Pour le programme Arduino, on utilise toujours le Timer1 avec une routine de 1s pour l’affichage sur le LCD. On souhaite également faire une mesure toutes les 5 secondes. Donc on va utiliser cette routine d’1 seconde, et utiliser un compteur et afficher toutes les 5 secondes.

Voici le programme de la simulation de cette batterie :

#include <SoftwareSerial.h>
#include <TimerOne.h>
#include <LiquidCrystal.h>
#include <LiquidCrystal_I2C.h>

//#define led13   13
#define buzz   2  //buzzer si tension en décharge inférieure à 2.7V
LiquidCrystal lcd(8,9,4,5,6,7); // (RS=8, E=9, D4=4, D5=5, D6=6, D7=7)
/******************************************************************/
float tension,tensionACS=0;       // Initialisation
float courant=0;
float sensible=0.185;           // Sensibilité du capteur
float ACSoffset=2.5;           // Valeur de tension de sortie du capteur lorsque le courant=0
float capacite=0; 
float ESR=0.02; 
float R;
float E; // tension électrochimique
//float temperature;
int heure=0,minute=0,seconde=0; // le temps

int i=0;
//int j=1;

//float  n=0;                 //nombre d'echantillons
//float  X=0;    //variable abscisse
//float Y=0;     //ordonnée image de l'abcisse
//float SommeX=0;
//float SommeXsquare=0;  
//float SommeY=0;
//float SommeXY=0;
 
void setup() {
  // put your setup code here, to run once:
  Timer1.initialize(1000000);          // initialize timer1, and set a 0,1 second period =>  100 000  pour 0.01s  10 000
  Timer1.attachInterrupt(callback);   // attaches callback() as a timer overflow interrupt

  pinMode(buzz, OUTPUT);
  Serial.begin(9600);
  lcd.begin(20,4);         //et pas lcd begin comme cetraine biblio
  lcd.clear();      // Effacer le l'écran
  
}
void callback() 
{
  seconde++;
  tension=analogRead(A0); // Lecture de la valeur récupèrée de A0
  tension=tension*(5.0/1023.0); // Conversion de cette valeur en une tension comprise entre 0 et 5V (Batterie)
  tensionACS=analogRead(A1); // Lecture de la valeur récupèrée de A1
  tensionACS=(tensionACS*5.0)/1023; // Conversion de cette valeur en une tension comprise entre 0 et 5V (Capteur ASC)
  courant=(tensionACS-ACSoffset)/sensible; // Calcul du courant
  capacite=(courant/3.6)+capacite; // (I(A)*1000)/(1s/3600)+capacite

  E=tension+ESR*courant;
  //R=E/courant;

  //temperature=((analogRead(A2)*5.0)/1023.0)*(1/0.01); // la tension de sortie est de : 10mV/°C
  if(tension<=2.7) {digitalWrite(buzz,HIGH);} else {digitalWrite(buzz,LOW);}
  //si tension de décharge trop faible => ne pas décharger entièrement
  if(i==5)
  {
    Serial.print(capacite); Serial.print(";");
    Serial.print(tension); Serial.println(";");
    //Serial.print(E); Serial.println(";");
    i=0;
//    j++;
  }
  i++;
}
void temps()
{ if(seconde>=60){seconde=0;
                  minute++;
                  if(minute>=60){minute=0;
                                 heure++;
                                }
                  if(heure>=24){heure=0;
                                lcd.clear();
                               }
                 }          
} // fin void temps

void loop() {
  // put your main code here, to run repeatedly:
 temps();
  /*************Affichage sur l'écran LCD****************/
 lcd.setCursor(0,0);
 lcd.print(tension,2);
 lcd.print("V      ");
 lcd.print(courant,2);
 lcd.print("A");
 lcd.setCursor(0,1);
 lcd.print("C=");
 lcd.print(capacite,0);
 lcd.print("mAh");
 
 //lcd.setCursor(0,2);
 //lcd.print("T=");
 //lcd.print(temperature);
 //lcd.print(" oC");
 
 lcd.setCursor(5,3);
 lcd.print(heure);
 lcd.print("h ");
 lcd.print(minute);
 lcd.print("m ");
 lcd.print(seconde);
 lcd.print("s ");
}

Également j’ai dû modifier la maquette de départ pour l’adapter à notre cahier des charges et ce que l’on souhaite.
Par la suite, on souhaite aussi instaurer un système de buzzer (inclus dans la maquette de l’écran LCD) lorsque la tension de décharge de la batterie est inférieure à 2.7V, pour ne pas décharger totalement la batterie, donc éviter sa destruction.
Avant de valider par système réel, il faut valider par simulation. La valeur de la batterie étant fixe en simulation, il faut donc faire le test avec deux valeurs de batterie choisies.

Si on prend Ubat=3.5V :

Si on prend Ubat=2V (inférieur à 2.7V) :

On remarque bien que la broche numérique du buzzer passera à 1 si la tension est inférieure à 2.7V.
Pour valider ces simulations et établir la courbe de régression de la batterie, ce qu’il reste à faire est de faire les tests sur la batterie Lipofer.

Ci-joint le lien du schéma ISIS utilisé :

3 Likes

Tests réalisés sur un pack de batteries Phylion 60A :

Pour pouvoir exploiter les courbes obtenues par rapport à la tension du pack, il est important de reprendre point par point les résultats obtenus pour les mettre dans Excel. Après avoir saisi tous les points des courbes, on va ainsi faire une estimation de la tension du pack avec l’ajout d’une courbe de tendance à chaque courbe.

Pour faire l’estimation, on va rentrer l’équation obtenue des courbes de tendance, en indiquant x=capacité. Les courbes de tendance seront des régressions polynomiales, appliquées à différents essais réalisés sur le pack, pour 0.2C, 1.5C et 2.5C.

Exploitation des résultats sur le pack de batteries :

Pour la courbe réalisée en 0.2C (orange), on peut remarquer qu’une régression polynomiale de degré 3 est suffisante (voir courbe rouge). De même pour la courbe réalisée en 1.5C (courbes vertes).

En revanche, pour la courbe en 2.5C (bleu clair) apparaît un certain problème. En effet entre 0 et 20% de la capacité utilisée, on peut remarquer qu’il y a une inflexion de tension non désirée. De surcroit, on va utiliser une régression polynomiale de degré 4 voire 5 du fait de cette inflexion.

Si on prend une régression de degré 5 pour la courbe à 2.5C (courbe violette), on peut remarquer que l’estimation est plus proche de la valeur mesurée. Donc si l’on souhaite une certaine précision des résultats de mesure, il est plus judicieux de choisir une régression de degré 5.

Sur ce graphique, on peut noter que l’ESR est bien de 0.02V, et que la tension OCV du pack de batterie est légèrement supérieure à la tension mesurée. Également, les coefficients de régression (en jaune foncé) sont calculés pour chaque courbes grâce à la fonction « DROITEREG » incluse dans Excel.

De plus, on peut indiquer qu’il est plus judicieux de réaliser le calcul dans Excel plutôt que de rentrer les coefficients affichés dans la courbe de tendance car il peut il y avoir des valeurs trop arrondies et donc une courbe qui ne correspond plus à la régression.

Tests réalisés sur batterie GTX Lipofer 10A.h :
Mesure de tensions avec OCV et tension batterie :

Dans cette simulation on éteint le banc résistif tous les 1A.h pendant 15 secondes.

  • On peut observer que quand la batterie est à vide, sa tension est égale à la tension OCV.
  • Ainsi, on aura toujours la tension OCV de la batterie qui sera toujours supérieure à la tension de la batterie. A chaque 1 A.h, on met hors tension le banc résistif pour récupérer les tensions à vide. Ainsi on remarque que la tension de la batterie, à vide, sera égal à peu près à la tension OCV de la batterie (voir les pics de tension sur le schéma).
    On peut donc en déduire que la tension de la batterie à vide sera égale à la tension OCV. Et quand la batterie se décharge, sa tension redeviendra inférieure à la tension OCV.
  • Quant à lui, la tension OCV à chaque fois que le système est mis sous tension, on peut remarquer que sa tension augmente très légèrement, mais cela reste très négligeable si on regarde l’ensemble des résultats sur la courbe.

On va à présent réaliser le test énergétique uniquement sur la tension à l’entrée de la batterie sur la capacité utilisée.

Voici le programme utilisé :

#include <SoftwareSerial.h>
#include <TimerOne.h>
#include <LiquidCrystal.h>
#include <LiquidCrystal_I2C.h>

#define led13   13
#define bpreset  10
#define envoi2   11
//#define buzz   2  //buzzer si tension en décharge inférieure à 2.7V
LiquidCrystal lcd(8,9,4,5,6,7); // (RS=8, E=9, D4=4, D5=5, D6=6, D7=7)
/******************************************************************/
float tension,tensionACS=0;       // Initialisation
float courant=0;
float sensible=0.1;           // Sensibilité du capteur
float ACSoffset=2.5;           // Valeur de tension de sortie du capteur lorsque le courant=0
float capacite=0.0; 
float ESR=0.021; 
float R;
float E; // tension électrochimique
float temperature;
int heure=0,minute=0,seconde=0; // le temps

int  cycles=0,i=0,flag=0;                 //nombre d'echantillons
//float  X=0;    //variable abscisse
//float Y=0;     //ordonnée image de l'abcisse
//float SommeX=0;
//float SommeXsquare=0;  
//float SommeY=0;
//float SommeXY=0;
 
void setup() {
  // put your setup code here, to run once:
  Timer1.initialize(1000000);          // initialize timer1, and set a 0,1 second period =>  100 000  pour 0.01s  10 000
  Timer1.attachInterrupt(callback);   // attaches callback() as a timer overflow interrupt

  //pinMode(buzz, OUTPUT);
  pinMode(bpreset, INPUT);
  pinMode(envoi2, INPUT);
  Serial.begin(9600);
  lcd.begin(20,4);         //et pas lcd begin comme cetraine biblio
  lcd.clear();      // Effacer le l'écran

  Serial.print("Capacite;"); Serial.print("Tension;");
  Serial.print("OCV;"); Serial.print("TensionACS;"); Serial.println("Courant;");
  
}
void callback() 
{
  seconde++;
  tension=analogRead(A0); // Lecture de la valeur récupèrée de A0
  tension=tension*(5.0/1023.0); // Conversion de cette valeur en une tension comprise entre 0 et 5V (Batterie)
  tensionACS=analogRead(A1); // Lecture de la valeur récupèrée de A1
  tensionACS=(tensionACS*5.0)/1023; // Conversion de cette valeur en une tension comprise entre 0 et 5V (Capteur ASC)
  courant=(tensionACS-ACSoffset)/sensible; // Calcul du courant
  capacite=(courant/3.6)+capacite; // (I(A)*1000)/(1s/3600)+capacite

  E=tension-ESR*courant;
  //R=E/courant;

  //temperature=((analogRead(A2)*5.0)/1023.0)*(1/0.01); // la tension de sortie est de : 10mV/°C
  //if(tension<=2.7) {digitalWrite(buzz,HIGH);} else {digitalWrite(buzz,LOW);}
  //si tension de décharge trop faible => ne pas décharger entièrement
  if(i==4)
  {
    Serial.print(-capacite); Serial.print(";");
    Serial.print(tension,3); Serial.println(";");
    //Serial.print(E,3); Serial.println(";");
    //Serial.print(tensionACS); Serial.print(";");
    //Serial.print(courant); Serial.println(";");
    i=0;
    cycles++;
  }
  else{i++;}
}
void temps()
{ if(seconde>=60){seconde=0;
                  minute++;
                  if(minute>=60){minute=0;
                                 heure++;
                                }
                  if(heure>=24){heure=0;
                                lcd.clear();
                               }
                 }          
} // fin void temps

void loop() {
  // put your main code here, to run repeatedly:
  if(digitalRead(bpreset)==1)
  {
    seconde=0; minute=0; heure=0;
    cycles=0; i=0;
    courant=0; capacite=0;
    }
  if(digitalRead(envoi2)==1)
  {
    Serial.print(tension); Serial.print(";");
    Serial.print(-capacite); Serial.println(";");
    delay(400);
    }
 temps();
  /*************Affichage sur l'écran LCD****************/
 lcd.setCursor(0,0);
 lcd.print(tension,3);
 lcd.print("V      ");
 lcd.print(courant,2);
 lcd.print("A   ");
 lcd.setCursor(0,1);
 lcd.print(E,3);
 lcd.print("V   ");
 lcd.setCursor(10,1);
 lcd.print("C=");
 lcd.print(capacite,0);
 lcd.print("mAh   ");
 
 //lcd.setCursor(0,2);
 //lcd.print("T=");
 //lcd.print(temperature);
 //lcd.print(" oC");
 
 lcd.setCursor(0,3);
 lcd.print(heure);
 lcd.print("h ");
 lcd.print(minute);
 lcd.print("m ");
 lcd.print(seconde);
 lcd.print("s ");
 lcd.setCursor(15,3);
 lcd.print("N=");
 lcd.print(cycles);
 lcd.print("  ");
}

Voici les résultats obtenus par Excel :

On peut remarquer que les points mesurés sont très précis, mais il est moins facile d’établir l’estimation du SOH par rapport à la forte chute de tension au début de la décharge.

Pour remédier à ce problème, il faut réaliser une courbe plus linéaire avec moins de points, mais assez pour montrer la forme de la courbe de la batterie selon la capacité.

Ainsi, on va réaliser des mesures de capacité et tensions tous les 0.5A.h (ou tous les 500mA.h) pour avoir à peu près un total de 20 points. Dans ces points, le premier que l’on va prendre est quand la capacité est nulle, pour avoir une forme de courbe qui se rapproche de la courbe de la batterie.

On aura toujours l’OCV qui sera supérieure à la tension de batterie, et une courbe de tendance qui se rapproche de la courbe mesurée. L’estimation est réalisée avec une régression polynomiale de degré 4 pour la précision.

Après les différents tests réalisés via Excel, on a remarqué qu’il est préférable de d’abord réaliser le calcul des coefficients avec Arduino, puis établir l’estimation des tensions et résolution du SOH sur Excel.

Pour mieux comprendre les méthodes de calculs, on va utiliser dans un premier temps mathcad qui est un logiciel de calcul comme Matlab par exemple. Le premier calcul est un exemple de régression polynomiale de degré 2.

Ce que l’on va établir, c’est une relation entre Mathcad et la programmation Arduino. Ainsi, si les résultats des coefficients sont les mêmes pour les deux logiciels, alors on pourra déduire que notre programme sera bon.

On va alors faire le calcul des coefficients pour 20 points avec la batterie, avec une régression polynomiale de degré 3.

Voici le programme Arduino de cette simulation :

#include <SoftwareSerial.h>
#include <TimerOne.h>
#include <LiquidCrystal.h>
#include <LiquidCrystal_I2C.h>
#include <MatrixMath.h>

#define led13   13
#define bpreset  10
#define envoi   2
#define BP     3
#define buzz   A4  //buzzer si tension en décharge inférieure à 2.7V
LiquidCrystal lcd(8,9,4,5,6,7); // (RS=8, E=9, D4=4, D5=5, D6=6, D7=7)
/******************************************************************/
float tension,tensionACS=0;       // Initialisation
float courant=0;
float sensible=0.1;           // Sensibilité du capteur
float ACSoffset=2.5;           // Valeur de tension de sortie du capteur lorsque le courant=0
float capacite=0,capaciteA=0; 
float ESR=0.021; 
float R;
float E; // tension électrochimique
float temperature;
int heure=0,minute=0,seconde=0; // le temps

float val[8],cap[8];

int  cycles=1,i=0,j=0;                 //nombre d'echantillons
#define N (3+1)

float SommeX[2*N],SommeXY[N];
mtx_type S[N][N];
mtx_type v[N];
mtx_type a[N];
float n=0,X=0,Y=0;
 
void setup() {
  // put your setup code here, to run once:
  Timer1.initialize(1000000);          // initialize timer1, and set a 0,1 second period =>  100 000  pour 0.01s  10 000
  Timer1.attachInterrupt(callback);   // attaches callback() as a timer overflow interrupt

  pinMode(bpreset, INPUT);
  pinMode(envoi, INPUT);
  pinMode(BP, INPUT);
  //pinMode(buzz, OUTPUT);
  Serial.begin(9600);
  lcd.begin(20,4);         //et pas lcd begin comme cetraine biblio
  lcd.clear();      // Effacer le l'écran

  Serial.print("Capacite;"); Serial.print("Tension;");
  Serial.print("OCV;"); Serial.print("A0;"); Serial.print("A1;"); Serial.print("A2;"); Serial.print("A3;");
  Serial.println("n;"); 
  
}
void callback() 
{
  seconde++;
  tension=analogRead(A0); // Lecture de la valeur récupèrée de A0
  tension=tension*(5.0/1023.0); // Conversion de cette valeur en une tension comprise entre 0 et 5V (Batterie)
  tensionACS=analogRead(A1); // Lecture de la valeur récupèrée de A1
  tensionACS=(tensionACS*5.0)/1023; // Conversion de cette valeur en une tension comprise entre 0 et 5V (Capteur ASC)
  courant=(tensionACS-ACSoffset)/sensible; // Calcul du courant
  capacite=(courant/3.6)+capacite; // (I(A)*1000)/(1s/3600)+capacite
  capaciteA=capacite/1000;

  E=tension-ESR*courant;
  //R=E/courant;

  if(-capacite>=cycles*500 and -capacite<=cycles*500+5 and tension<=3.7) //si capacité est un multiple de 0.5A.h, alors lecture sur port série des mesures
                                              //on estimera la capacité entre 500 et 505 mA.h car la mesure de la capacité n'est pas souvent pile sur 500 mA.h
  { 
    X=-capaciteA;
    Y=tension;
    for(i=0;i<=(N-1)*2;i++) { SommeX[i]=SommeX[i]+pow(X,i);}
    for(i=0;i<(N);i++) { SommeXY[i]=SommeXY[i]+Y*pow(X,i);}
    n++;
    if(n<4) {
      Serial.print(-capaciteA); Serial.print(";"); Serial.print(tension,3); Serial.print(";");
      Serial.print(E,3); Serial.print(";");
      Serial.print(" ; ; ; ;"); Serial.print(n); Serial.println(";");
    } 
    if(n>=4) {
      for(i=0;i<N;i++) {
        for(j=0;j<N;j++) {S[i][j]=SommeX[j+i]; }
        v[i]=SommeXY[i];
      }
      Matrix.Invert((mtx_type*)S,N);
      Matrix.Multiply((mtx_type*)S, (mtx_type*)v,N,N,1,(mtx_type*)a);
      Serial.print(-capaciteA); Serial.print(";"); Serial.print(tension,3); Serial.print(";");
      Serial.print(E,3); Serial.print(";");
      Serial.print(a[0],8); Serial.print(";"); Serial.print(a[1],8); Serial.print(";"); 
      Serial.print(a[2],8); Serial.print(";"); Serial.print(a[3],8); Serial.print(";"); 
      Serial.print(n); Serial.println(";"); 
    }  
    //cap[cycles]=-capacite;
    //val[cycles]=tension;
    cycles++;
  }


//  //temperature=((analogRead(A2)*5.0)/1023.0)*(1/0.01); // la tension de sortie est de : 10mV/°C
//    if(tension<=2.7) {
  //    tone(buzz,100,1); } //si tension de décharge trop faible => ne pas décharger entièrement
}

void temps()
{ if(seconde>=60){seconde=0;
                  minute++;
                  if(minute>=60){minute=0;
                                 heure++;
                                }
                  if(heure>=24){heure=0;
                                lcd.clear();
                               }
                 }          
} // fin void temps

void loop() {
  // put your main code here, to run repeatedly:
  if(digitalRead(bpreset)==1)
  {
    seconde=0; minute=0; heure=0;
    cycles=0; i=0;
    courant=0; capacite=0;
    }
//  if(digitalRead(envoi)==1)
//  {
//    for(j==0;j<=20;j++)
//    {
//      Serial.print(cap[j]); Serial.print(";");
//      Serial.print(val[j]); Serial.println(";");
//      }
//    }
 temps();
  /*************Affichage sur l'écran LCD****************/
 lcd.setCursor(0,0);
 lcd.print(tension,3);
 lcd.print("V      ");
 lcd.print(courant,2);
 lcd.print("A   ");
 lcd.setCursor(0,1);
 lcd.print(E,3);
 lcd.print("V   ");
 lcd.setCursor(10,1);
 lcd.print("C=");
 lcd.print(capacite,0);
 lcd.print("mAh   ");
 
 //lcd.setCursor(0,2);
 //lcd.print("T=");
 //lcd.print(temperature);
 //lcd.print(" oC");
 
 lcd.setCursor(0,3);
 lcd.print(heure);
 lcd.print("h ");
 lcd.print(minute);
 lcd.print("m ");
 lcd.print(seconde);
 lcd.print("s ");
 lcd.setCursor(15,3);
 lcd.print("N=");
 lcd.print(cycles);
 lcd.print("  ");
}

Par la même occasion, on va changer l’unité de la capacité de mA.h à A.h pour ne pas avoir des coefficients trop petits, et donc des coefficients que l’on peut calculer avec Arduino (Arduino ne peut calculer qu’avec 7 chiffres significatifs).

Voici les premiers coefficients que l’on obtient par simulation Arduino :

Dans cette simulation, on a réalisé une mesure supplémentaire avec une décharge de la batterie à 14A. Ainsi, pour chaque courbe de batterie, les coefficients de régression polynomiale sont directement calculés et cela facilite grandement la résolution du SOH. On peut constater que les coefficients de régression sont bons puisqu’on a calculé les coefficients sur Excel (en bas avec cellules en jaune foncé) et ces coefficients sont les mêmes que pour C=10.5A.h pour une décharge à 8A, et pour C=9.5A.h pour une décharge à 14A.

Pour cette simulation on utilise de nouveau la dichotomie pour le SOH. Mais la tension de seuil va diminuer. Passant ainsi de 3V à 2.7V.

On peut remarquer que la valeur de la régression est aux environ de 2.7V, validant ainsi les valeurs de SOH. Cependant sur cette batterie, on peut observer que l’estimation du SOH est environ à 11A.h, et non à 10A.h comme défini au début. (Passage de 3 à 2.7V en tension de seuil).

Pour donner un avis sur cette décharge plus intense, on peut dire que le temps de décharge est nettement diminué (à peu près 1h20 de décharge pour 8A, et 40min à peu près pour 14A). Toutes fois, même si la décharge est plus rapide, cela ne change pas que la charge de la batterie prend toujours le même temps, quelque soit le courant de décharge.

Pour la suite, on va utiliser un M5Stack avec un ESP32 intégré, pour permettre un meilleur calcul et un meilleur affichage des données.
Le prochain test qui sera réalisé sera une décharge à 20A, permettant ainsi d’observer l’évolution de la tension selon la capacité dans un cas presque critique.

2 Likes

Décharge à 22A :

Tout d’abord, il faut changer le fusible de la batterie, de 20A à 30A.

Pour réaliser cette simulation avec une décharge importante, on ne peut plus utiliser le composant ACS712 20A, puisque le courant utilisé est trop important par rapport au courant maximal du composant. Pour cela on va utiliser un capteur de courant manuel.

Sur le banc résistif, pour cette simulation, 12 lampes servant de résistances sont implantées sur le banc résistif.

Comme expliqué précédemment, plus le courant de décharge est important, plus la batterie se décharge vite. Mais le temps de recharge reste le même, sauf si on augmente le courant de charge.

Pour cette simulation, on va inclure en plus un capteur de température LM35 pour connaître la température de la batterie.

Résultats de simulations :

Après avoir réalisé la première simulation, on s’est rendu compte que le fusible de 30A chauffait, car dès qu’on active le banc résistif, il y a un pic de courant pouvant aller jusqu’à 35A.

Pour remédier à ce problème, on va donc remplacer par un fusible de calibre 40A.

Sur la courbe des mesures (vert) on peut remarquer l’inflexion au départ comme elle a été trouvée précédemment sur le pack de batteries Phylion. Cependant le programme a réalisé le calcul des coefficients de A3 à A0 pour une régression de degré 3. Ainsi, on peut constater que l’estimation du SOH n’est plus très précise, et que la valeur finale du SOH est légèrement élevée par rapport aux autres courbes.

Avec cette inflexion, il serait plus judicieux de refaire un test avec la même intensité, mais en réalisant le calcul de A4 à A0 pour une régression polynomiale de degré 4.

Pour certains points sur les courbes, les estimations du SOH ne sont pas très précises par rapport aux coefficients qui peuvent être imprécis également, ou mal calculés à un instant donné.

A cet instant, on peut estimer le SOH de la batterie à environ 11A.h pour une tension de seuil à 2.7V. On a un peu plus de 10A.h puisque les premiers essais ont été réalisés avec une tension seuil de 3V. On rappelle qu’il ne faut pas instaurer une tension seuil trop faible pour ne pas dégrader la batterie.

Différents essais externes ont été réalisés sur le courant déchargé par la batterie, pour vérifier les données du capteur de courant. En effet, il faut ajuster bien correctement le « zéro » du capteur pour avoir les bonnes données.

Avec des mesures de la tension de la carte Arduino, on a remarqué que la tension d’alimentation de la carte était de 4.66V et non 5V comme indiqué. Cela est dû à la tension que l’ordinateur fourni par le câble USB.

De plus, sur le banc résistif, les lampes reliées entre elles peuvent réaliser une déperdition du courant à travers les différents câbles et dominos installés.

Un problème est survenu par rapport à la mesure et l’affichage du courant du capteur. Effectivement, la tension mesurée était trop basse pour que l’on puisse avoir des valeurs précises et correctes. On ne pouvait passer par exemple que de 0.5 à 1A mesuré, sans autres valeurs entre celles-ci.

Pour remédier à ce problème, on va donc utiliser un amplificateur opérationnel avec un gain de 10. C’est-à-dire qu’à la sortie de cet AOP, qui maintenant est l’entrée analogique mesurée, on va avoir une tension 10 fois plus importante (passage de 10mV à 100mV pour 1A). Pour avoir une valeur qui se rapproche de la valeur mesurée par ampèremètre, il faut ajouter un certain coefficient.

Voici le résultat Excel pour une décharge de 20A :

On peut remarquer que les premières valeurs ne sont pas représentatives du SOH de la batterie. Vers la fin de décharge on peut estimer à peu près à 10A.h la capacité de la batterie que l’on peut utiliser avant recharge. Cependant, vers 9.5A.h, la tension est inférieure à 2.7V, tension de seuil. Cela va donc faire le calcul d’un SOH, passant ainsi de 10A.h à 9A.h à peu près. C’est un souci puisque l’on souhaite déterminer la capacité totale que l’on peut utiliser sur une batterie, et donc avoir des résultats précis.

Une autre chose que l’on a remarqué, c’est la présence d’une tension différente de 5V. Donc deux solutions sont possibles. La première est d’instaurer un coefficient (4.66/5) multiplié par le résultat pour avoir la tension avec une alimentation de 4.66V. Ce coefficient pourrait être instauré sur Excel, et les calculs pourront être réalisés automatiquement via VBA.

Une autre solution serait de directement changer la valeur de tension d’alimentation sur Arduino. Mais ça ne peut être une bonne solution puisque nous n’avons pas tous une tension d’alimentation de 4.66V (cela varie selon les ordinateurs et câbles USB).

Le prochain test qui va être réalisé sera une décharge de 20A avec une tension d’alimentation de 4.66V.

Recherche du coefficient à implanter pour avoir une bonne visualisation et un bon calcul de la capacité :

Pour permettre d’avoir la bonne valeur sur l’écran LCD et donc un bon calcul de la capacité utilisée, il faut modifier le coefficient dans le calcul du courant dans le programme Arduino.

Au départ, le coefficient implanté était une division par 1023 pour avoir le résultat de la conversion de la valeur mesurée en volts. Il va falloir donc ajouter un coefficient pour prendre en compte la présence du capteur, et l’ajuster selon le courant que l’on désire. Il faut tout d’abord multiplier par 1/10 par rapport au capteur de courant dont sa tension est multipliée par 10 par rapport au gain de l’amplificateur.

On a déjà le coefficient d’origine pour pouvoir définir un courant précis à 0A. Il va falloir par la suite également réaliser les mesures de la sortie de l’amplificateur tout d’abord pour un courant de 0 à 12A grâce à une alimentation continue. Par la formule du courant définie sur Arduino, on peut trouver le palier de mesure (résultat de « analogRead() » sur Arduino) pour chaque courant simulé.

Il ne reste plus qu’à simuler une première fois la tension de décharge de la batterie, à regarder le courant affiché sur le LCD et sur le capteur de courant à l’aide d’un voltmètre.

Il y a une différence de courant entre le courant affiché sur écran LCD et le courant mesurée par le capteur de courant.

A partir du courant trouvé sur écran LCD, on peut trouver le palier de mesure d’Arduino par tâtonnement avec une calculatrice. Une fois la valeur du palier trouvée, on peut rechercher la valeur du nouveau coefficient. Pour ma part j’ai inclus le 1023 directement dans mon calcul de coefficient, cela est plus simple. Une fois ensuite, on prend la valeur mesurée par le capteur, ce que l’on est sensé déchargé, et on recherche le nouveau coefficient.

Pour une décharge à 20.22A environ, il faut choisir un coefficient de 90/1023 (ou remplacer 1023 par 90).

Code Arduino pour une décharge à 20A :

#include <SoftwareSerial.h>
#include <TimerOne.h>
#include <LiquidCrystal.h>
#include <LiquidCrystal_I2C.h>
#include <MatrixMath.h>

#define led13   13
#define bpreset  10
#define envoi   2
#define BP     3
//#define buzz   A4  //buzzer si tension en décharge inférieure à 2.7V
LiquidCrystal lcd(8,9,4,5,6,7); // (RS=8, E=9, D4=4, D5=5, D6=6, D7=7)
/******************************************************************/
float tension,tensionCapt=0;       // Initialisation
float courant=0;
float sensible=0.01;           // Sensibilité du capteur
//float ACSoffset=2.5;           // Valeur de tension de sortie du capteur lorsque le courant=0
float capacite=0,capaciteA=0; 
float ESR=0.021; 
float R;
float E; // tension électrochimique
float temperature;
int heure=0,minute=0,seconde=0; // le temps
float U=5; //Tension d'alimentation de la carte (peut varier selon l'USB)

float val[8],cap[8];

int  cycles=1,i=0,j=0;                 //nombre d'echantillons
float m,vm,reg;
float A=1.0, B=15.0;

#define N (4+1)

float SommeX[2*N],SommeXY[N];
mtx_type S[N][N];
mtx_type v[N];
mtx_type a[N];
float n=0,X=0,Y=0;
 
void setup() {
  // put your setup code here, to run once:
  Timer1.initialize(1000000);          // initialize timer1, and set a 0,1 second period =>  100 000  pour 0.01s  10 000
  Timer1.attachInterrupt(callback);   // attaches callback() as a timer overflow interrupt

  pinMode(bpreset, INPUT);
  pinMode(envoi, INPUT);
  pinMode(BP, INPUT);
  pinMode(A0, INPUT);
  pinMode(A1, INPUT);
  pinMode(A2, INPUT);
  //pinMode(buzz, OUTPUT);
  Serial.begin(9600);
  lcd.begin(20,4);         //et pas lcd begin comme cetraine biblio
  lcd.clear();      // Effacer le l'écran

  Serial.print("Capacite;"); Serial.print("Tension;");
  Serial.print("OCV;"); Serial.print("Température (%);"); Serial.println("A4;A3;A2;A1;A0;SOH;"); 
  
}
void callback() 
{
  seconde++;
  tension=analogRead(A0); // Lecture de la valeur récupèrée de A0
  tension=tension*(4.66/1023.0);    // Conversion de cette valeur en une tension comprise entre 0 et 5V (Batterie)
  delayMicroseconds(10);
  tensionCapt=analogRead(A1); // Lecture de la valeur récupèrée de A1
  //delayMicroseconds(10);
  //tensionCapt=analogRead(A3);
  courant=(tensionCapt*4.66)/90; // Conversion de cette valeur en une tension comprise entre 0 et 5V (Capteur ASC)
 // courant=tensionCapt/sensible; // Calcul du courant
  capacite=(courant/3.6)+capacite; // (I(A)*1000)/(1s/3600)+capacite
  capaciteA=capacite/1000;
  temperature=((analogRead(A2)*4.66)/1023.0)*(1/0.01); // la tension de sortie est de : 10mV/°C

  E=tension+ESR*courant;
  //R=E/courant;

  //if (seconde==60) { Serial.print(temperature); Serial.println(";"); }
  if(capacite>=cycles*500 and capacite<=cycles*500+30 and tension<=3.7) //si capacité est un multiple de 0.5A.h, alors lecture sur port série des mesures
                                              //on estimera la capacité entre 500 et 505 mA.h car la mesure de la capacité n'est pas souvent pile sur 500 mA.h
  { 
    X=capaciteA;
    Y=tension;
    for(i=0;i<=(N-1)*2;i++) { SommeX[i]=SommeX[i]+pow(X,i);}
    for(i=0;i<(N);i++) { SommeXY[i]=SommeXY[i]+Y*pow(X,i);}
    n++;
    if(n<4) {
      Serial.print(capaciteA); Serial.print(";"); Serial.print(tension,3); Serial.print(";");
      Serial.print(E,3); Serial.print(";");Serial.print(temperature); Serial.print(";");
      Serial.println(" ; ; ; ; ; ; ;"); //respectivement (a4;a3;a2;a1;a0;SOH;reg)
      //A changer selon l'ordre utilisé
    } 
    if(n>=N) {
      for(i=0;i<N;i++) {
        for(j=0;j<N;j++) {S[i][j]=SommeX[j+i]; }
        v[i]=SommeXY[i];
      }
      Matrix.Invert((mtx_type*)S,N);
      Matrix.Multiply((mtx_type*)S, (mtx_type*)v,N,N,1,(mtx_type*)a);
      //Serial.print(capaciteA); Serial.print(";"); Serial.print(tension,3); Serial.print(";");
      //Serial.print(E,3); Serial.print(";"); Serial.print(temperature); Serial.print(";");
      //Serial.print(a[4],8); Serial.print(";"); Serial.print(a[3],8); Serial.print(";"); 
      //Serial.print(a[2],8); Serial.print(";"); Serial.print(a[1],8); Serial.print(";"); 
      //Serial.print(a[0],8); Serial.print(";");
      //Serial.print(n); Serial.println(";"); 

      //SOH avec coefficients
      while ((B-A) > 0.01) {
        m=(A+B)/2;
        vm = a[4]*pow(m,4)+a[3]*pow(m,3)+a[2]*m*m+a[1]*m+a[0];
        if(vm>2.7) { B=m; }
        else { A=m;}
      }
      //Régression (à changer selon l'ordre utilisé)
      reg=vm;
      Serial.print(capaciteA); Serial.print(";"); Serial.print(tension,3); Serial.print(";");
      Serial.print(E,3); Serial.print(";"); Serial.print(temperature); Serial.print(";");
      Serial.print(a[4],8); Serial.print(";"); Serial.print(a[3],8); Serial.print(";"); 
      Serial.print(a[2],8); Serial.print(";"); Serial.print(a[1],8); Serial.print(";"); 
      Serial.print(a[0],8); Serial.print(";");
      Serial.print(m); Serial.print(";"); Serial.print(reg); Serial.println(";");

      
      
    }  
    //cap[cycles]=-capacite;
    //val[cycles]=tension;
    cycles++;
  }


//  //temperature=((analogRead(A2)*5.0)/1023.0)*(1/0.01); // la tension de sortie est de : 10mV/°C
//    if(tension<=2.7) {
  //    tone(buzz,100,1); } //si tension de décharge trop faible => ne pas décharger entièrement
}

void temps()
{ if(seconde>=60){seconde=0;
                  minute++;
                  if(minute>=60){minute=0;
                                 heure++;
                                }
                  if(heure>=24){heure=0;
                                lcd.clear();
                               }
                 }          
} // fin void temps

void loop() {
  // put your main code here, to run repeatedly:
  if(digitalRead(bpreset)==1)
  {
    seconde=0; minute=0; heure=0;
    cycles=0; i=0;
    courant=0; capacite=0;
    }
//  if(digitalRead(envoi)==1)
//  {
//    for(j==0;j<=20;j++)
//    {
//      Serial.print(cap[j]); Serial.print(";");
//      Serial.print(val[j]); Serial.println(";");
//      }
//    }
 temps();
  /*************Affichage sur l'écran LCD****************/
 lcd.setCursor(0,0);
 lcd.print(tension,3);
 lcd.print("V      ");
 lcd.print(courant,2);
 lcd.print("A   ");
 lcd.setCursor(0,1);
 lcd.print(E,3);
 lcd.print("V   ");
 lcd.setCursor(10,1);
 lcd.print("C=");
 lcd.print(capacite,0);
 lcd.print("mAh   ");
 
 lcd.setCursor(0,2);
 lcd.print("T=");
 lcd.print(temperature);
 lcd.print(" oC");
 
 lcd.setCursor(0,3);
 lcd.print(heure);
 lcd.print("h ");
 lcd.print(minute);
 lcd.print("m ");
 lcd.print(seconde);
 lcd.print("s ");
 lcd.setCursor(15,3);
 lcd.print("N=");
 lcd.print(cycles-1);
 lcd.print("  ");
}

Décharge à 20A, deuxième résultat :

On peut déduire quelques points similaires à la dernière simulation. Sur le fait que la tension chute beaucoup dès la mise en marche du banc résistif. De ce fait, l’estimation du SOH est de 9A.h au lieu des 10A.h comme indiqué sur les caractéristiques de la batterie. On ne peut pas simuler une décharge plus intense car cela dépasserait la tension de seuil instaurée à 3.7V.

De ce fait, on pourrait conclure que plus le courant de décharge est important, plus la batterie se décharge vite, et plus la capacité que l’on peut utiliser est faible.

Également, la notion de précision est importante durant l’étude de l’état de santé de la batterie. C’est pour cela qu’il faut déterminer la tension offset du capteur de courant et sa pente. Il faut donc mesurer la tension en sortie du capteur de courant et la tension en sortie de l’amplificateur et les indiquer dans Excel.

Exploitation des mesures sur Excel :

Ainsi sur ce fichier Excel sont indiqués le nombre de paliers pour chaque tension mesurée (toujours par tâtonnement), le nombre d’Ampères entre chaque palier, et les résultats de tests pour la détermination de coefficient.

Entre simulations on a changé les calculs entre 5V et 4.66V. Donc pour une décharge de 8A et 14A, on a utilisé la tension de 5V, sachant que la tension de la carte était de 4.66V. Donc il serait préférable et plus judicieux de refaire ces décharges pour pouvoir mieux estimer le SOH avec cette carte.

1 Like

Merci pour cette belle introduction !

Temps d’exécution et calculs avec grands ou très petits nombres :

En effet, lors de la décharge de la batterie, le but est de connaitre à chaque 0.5A.h consommé, l’état de santé de la batterie.

Si c’est possible pour un degré 3 qui demande un calcul avec une précision à 10-4, est ce que c’est possible de le faire pour un degré 5 qui demande un calcul avec une précision à 10-8

Pour pouvoir confirmer que le logiciel établit bien les calculs pour 10-8, il faut établir un petit programme réalisant des calculs avec un nombre à virgule très petit (de l’ordre de 10-9 voire 10-10 environ).

Après avoir simulé ce petit programme, on peut remarquer que lorsque l’on a 0.00… , et des nombres très petits, le calcul sur Arduino est bien réalisé et il n’y a aucun souci. En revanche, si on prend par exemple 1.00… ou un chiffre dans les unités, alors les calculs réalisés avec beaucoup de chiffres après la virgule ne sont pas correctes.

Calcul du SOH et régression par l’Arduino :

On peut remarquer que ces valeurs ne sont pas vraiment correctes, sauf pour les dernières capacités. Cela est justifié de nouveau par le calcul sur Arduino avec 7 chiffres significatifs. Comme il a déjà été expliqué, quand avant la virgule il n’y a pas d’unités, le calcul est plutôt bien réussi. Mais s’il y a des unités, le résultat devient incohérent.

De plus, pour calculer le SOH de la batterie, on doit établir le calcul entre tous les coefficients, donc s’il y a plus de 7 chiffres significatifs, alors le calcul sera incohérent.

Il est donc plus judicieux de réaliser le calcul du SOH par dichotomie via VBA Excel pour des résultats bons et précis. De plus, les résultats relevés sur le terminal Arduino ont été repris pour estimer le SOH manuellement via VBA. Et effectivement, nous obtenons des résultats à peu près cohérents. Donc, on peut émettre l’hypothèse que Arduino aura du mal à faire le calcul avec des nombres très petits, et avec une virgule.

Recherche du temps d’exécution de lignes de code :

Pour trouver le temps nécessaire à l’exécution d’une ligne de code il faut réaliser dans un premier temps un programme simple (allumage et extinction d’une LED) et observer à l’oscilloscope en simulation ou en système réel.

Dans cet oscilloscope, on prend pour unité 1V par division en ordonnée, et 0.5µs par division en abscisse.

Ainsi, pour une instruction sans avoir à ajouter de temporisation, la carte Arduino aura besoin environ de 4.5 µs.

Pour calculer le temps d’exécution d’une régression de degré 3 et de degré 5, il faut prendre en compte le temps d’exécution d’une ligne de code et savoir le nombre d’instructions pour faire la régression.

Ici le temps d’exécution changera en fonction de N (N=degré+1). Il faut par exemple compter le nombre de fois que le programme passe dans les boucles « faire pour » et regarder le temps d’exécution total pour le calcul de la régression.

Ainsi, pour une régression polynomiale de degré 3, le programme exécutera 35 instructions. Ce qui donne un total de 35*4.25 µs = 148.75 µs = 0.14875 ms.

De plus pour une régression de degré 5, le programme exécutera 63 instructions. Soit 63*4.25 µs = 267.75µs = 0.26775ms.

Pour l’ATMEGA 328 de 8 bits :

Le temps de calcul pour un degré 3 par le processeur est de 148.75 µs.

Le temps de calcul pour un degré 5 par le processeur est de 267.75 µs.

Quels seront ces temps avec un processeur 32 bits ?

Pour comparer la réalisation des calculs de grands nombres, il serait intéressant de réaliser ces calculs avec un autre logiciel de programmation. Comme Matlab, ou même encore Python.

La dernière version du programme Arduino :

#include <SoftwareSerial.h>
#include <TimerOne.h>
#include <LiquidCrystal.h>
#include <LiquidCrystal_I2C.h>
#include <MatrixMath.h>
#include <math.h>

#define led13   13
#define bpreset  10
#define envoi   2
#define BP     3
//#define buzz   A4  //buzzer si tension en décharge inférieure à 2.7V
LiquidCrystal lcd(8,9,4,5,6,7); // (RS=8, E=9, D4=4, D5=5, D6=6, D7=7)
/******************************************************************/
float tension,tensionCapt=0;       // Initialisation
float courant=0;
float sensible=0.01;           // Sensibilité du capteur
//float ACSoffset=2.5;           // Valeur de tension de sortie du capteur lorsque le courant=0
float capacite=0,capaciteA=0; 
float ESR=0.021; 
float R;
float E; // tension électrochimique
float temperature;
int heure=0,minute=0,seconde=0; // le temps
float U=5; //Tension d'alimentation de la carte (peut varier selon l'USB)
float val[8];

int  cycles=1,i=0,j=0;                 //nombre d'echantillons
float m,vm,reg;
float A=1.0, B=15.0;
float v0,v1,v2,v3;
float cap[21]={0.5,1,1.5,2,2.5,3,3.5,4,4.5,5,5.5,6,6.5,7,7.5,8,8.5,9,9.5,10,10.5};
float tens[21]={3.358,3.353,3.348,3.343,3.333,3.328,3.324,3.324,3.319,3.314,3.309,3.309,3.299,3.289,3.28,3.265,3.24,3.216,3.196,3.118,2.737};
//              0.5    1    1.5    2    2.5    3    3.5    4    4.5    5    5.5    6    6.5    7     7.5   8    8.5    9   9.5   10    10.5                

#define N (3+1)

float SommeX[2*N],SommeXY[N];
mtx_type S[N][N];
mtx_type v[N];
mtx_type a[N];
float X=0,Y=0;
int n;
 
void setup() {
  // put your setup code here, to run once:
  Timer1.initialize(1000000);          // initialize timer1, and set a 0,1 second period =>  100 000  pour 0.01s  10 000
  Timer1.attachInterrupt(callback);   // attaches callback() as a timer overflow interrupt

  pinMode(bpreset, INPUT);
  pinMode(envoi, INPUT);
  pinMode(BP, INPUT);
  pinMode(A0, INPUT);
  pinMode(A1, INPUT);
  pinMode(A2, INPUT);
  //pinMode(buzz, OUTPUT);
  Serial.begin(9600);
  lcd.begin(20,4);         //et pas lcd begin comme certaines biblio
  lcd.clear();      // Effacer le l'écran

  Serial.println("Capacite;Tension;OCV;Temperature;A3;A2;A1;A0;SOH;REG;"); 
  
}
void callback() 
{
  seconde++;
  tension=analogRead(A0); // Lecture de la valeur récupèrée de A0
  tension=tension*(4.66/1023.0);    // Conversion de cette valeur en une tension comprise entre 0 et 5V (Batterie)
  tensionCapt=analogRead(A1); // Lecture de la valeur récupèrée de A1
  courant=(tensionCapt*4.66)/92.11; // Conversion de cette valeur en une tension comprise entre 0 et 5V (Capteur ASC)
 // courant=tensionCapt/sensible; // Calcul du courant
  capacite=(courant/3.6)+capacite; // (I(A)*1000)/(1s/3600)+capacite
  capaciteA=capacite/1000;
  temperature=((analogRead(A2)*4.66)/1023.0)*(1/0.01); // la tension de sortie est de : 10mV/°C

  E=tension+ESR*courant;
  //R=E/courant;

//  if (seconde==60) { Serial.print(temperature); Serial.println(";"); }
  if(capacite>=cycles*500 and capacite<=cycles*500+30 and tension<=3.7) //si capacité est un multiple de 0.5A.h, alors lecture sur port série des mesures
                                              //on estimera la capacité entre 500 et 505 mA.h car la mesure de la capacité n'est pas souvent pile sur 500 mA.h
  { 
    X=capaciteA;
    Y=E;
    for(i=0;i<=(N-1)*2;i++) { SommeX[i]=SommeX[i]+pow(X,i);}
    for(i=0;i<(N);i++) { SommeXY[i]=SommeXY[i]+Y*pow(X,i);}
    n++;
    if(n<4) {
      Serial.print(heure); Serial.print(":"); Serial.print(minute); Serial.print(":");
      Serial.print(seconde); Serial.print(";"); //affichage temps
      Serial.print(capaciteA); Serial.print(";"); Serial.print(tension,3); Serial.print(";");
      Serial.print(E,3); Serial.print(";");Serial.print(temperature); Serial.print(";");
      Serial.println(" ; ; ; ; ; ;"); //respectivement (a3;a2;a1;a0;SOH;reg)
      //A changer selon l'ordre utilisé
    } 
    if(n>=N) {
      for(i=0;i<N;i++) {
        for(j=0;j<N;j++) {S[i][j]=SommeX[j+i]; }
        v[i]=SommeXY[i];
      }
      Matrix.Invert((mtx_type*)S,N);
      Matrix.Multiply((mtx_type*)S, (mtx_type*)v,N,N,1,(mtx_type*)a);
      //Serial.print(capaciteA); Serial.print(";"); Serial.print(tension,3); Serial.print(";");
      //Serial.print(E,3); Serial.print(";"); Serial.print(temperature); Serial.print(";");
      //Serial.print(a[4],8); Serial.print(";"); Serial.print(a[3],8); Serial.print(";"); 
      //Serial.print(a[2],8); Serial.print(";"); Serial.print(a[1],8); Serial.print(";"); 
      //Serial.print(a[0],8); Serial.print(";");
      //Serial.print(n); Serial.println(";"); 

      //SOH avec coefficients
      while ((B-A) > 0.01) {
        m=(A+B)/2;

        v3=a[3]*m*m*m;
        v2=a[2]*m*m;
        v1=a[1]*m;
        v0=a[0];
        vm = v3+v2+v1+v0;
        if(vm>2.7) { B=m; }
        else { A=m;}
      }
      //Régression (changé selon l'ordre utilisé)
      reg=vm;
      Serial.print(heure); Serial.print(":"); Serial.print(minute); Serial.print(":");
      Serial.print(seconde); Serial.print(";"); //affichage temps
      Serial.print(capaciteA); Serial.print(";"); Serial.print(tension,3); Serial.print(";");
      Serial.print(E,3); Serial.print(";"); Serial.print(temperature); Serial.print(";");
      Serial.print(a[3],8); Serial.print(";"); Serial.print(a[2],8); Serial.print(";"); 
      Serial.print(a[1],8); Serial.print(";"); Serial.print(a[0],8); Serial.print(";"); 
      Serial.print(m); Serial.print(";"); Serial.print(reg); Serial.println(";");
    }  
    //cap[cycles]=-capacite;
    //val[cycles]=tension;
    cycles++;
  }
//  while(n!=18) {
//    X=cap[n];
//    Y=tens[n];
//    for(i=0;i<=(N-1)*2;i++) { SommeX[i]=SommeX[i]+pow(X,i);}
//    for(i=0;i<(N);i++) { SommeXY[i]=SommeXY[i]+Y*pow(X,i);}
//    n++;
//    if(n<4) {
//      Serial.print(cap[n-1]); Serial.print(";"); Serial.print(tens[n-1],3); Serial.print(";");
//      Serial.print(E,3); Serial.print(";");Serial.print(temperature); Serial.print(";");
//      Serial.println(" ; ; ; ; ; ;"); //respectivement (a3;a2;a1;a0;SOH;reg)
      //A changer selon l'ordre utilisé
//    } 
//    if(n>=4) {
//      for(i=0;i<N;i++) {
//        for(j=0;j<N;j++) {S[i][j]=SommeX[j+i]; }
//        v[i]=SommeXY[i];
//      }
//      Matrix.Invert((mtx_type*)S,N);
//      Matrix.Multiply((mtx_type*)S, (mtx_type*)v,N,N,1,(mtx_type*)a);
      //Serial.print(capaciteA); Serial.print(";"); Serial.print(tension,3); Serial.print(";");
      //Serial.print(E,3); Serial.print(";"); Serial.print(temperature); Serial.print(";");
      //Serial.print(a[4],8); Serial.print(";"); Serial.print(a[3],8); Serial.print(";"); 
      //Serial.print(a[2],8); Serial.print(";"); Serial.print(a[1],8); Serial.print(";"); 
      //Serial.print(a[0],8); Serial.print(";");
      //Serial.print(n); Serial.println(";"); 

      //SOH avec coefficients
//      while ((B-A) > 0.01) {
//        m=(A+B)/2;
        
//        v3=a[3]*m*m*m;
//        v2=a[2]*m*m;
//        v1=a[1]*m;
//        v0=a[0];
//        vm = v3+v2+v1+v0;
        
//        if(vm>2.7) { B=m; }
//        else { A=m;}
//      }
      //Régression (à changer selon l'ordre utilisé)
//      reg=vm;
//      Serial.print(cap[n-1]); Serial.print(";"); Serial.print(tens[n-1],3); Serial.print(";");
//      Serial.print(E,3); Serial.print(";"); Serial.print(temperature); Serial.print(";");
//      Serial.print(a[3],8); Serial.print(";"); Serial.print(a[2],8); Serial.print(";"); 
//      Serial.print(a[1],8); Serial.print(";"); Serial.print(a[0],8); Serial.print(";"); 
//      Serial.print(m); Serial.print(";"); Serial.print(reg); Serial.println(";");
//    }  
    //cap[cycles]=-capacite;
    //val[cycles]=tension;
    //cycles++;

//  //temperature=((analogRead(A2)*5.0)/1023.0)*(1/0.01); // la tension de sortie est de : 10mV/°C
//    if(tension<=2.7) {
  //    tone(buzz,100,1); } //si tension de décharge trop faible => ne pas décharger entièrement
}

void temps()
{ if(seconde>=60){seconde=0;
                  minute++;
                  if(minute>=60){minute=0;
                                 heure++;
                                }
                  if(heure>=24){heure=0;
                                lcd.clear();
                               }
                 }          
} // fin void temps

void loop() {
  // put your main code here, to run repeatedly:
  if(digitalRead(bpreset)==1)
  {
    seconde=0; minute=0; heure=0;
    cycles=0; i=0;
    courant=0; capacite=0;
    }
//  if(digitalRead(envoi)==1)
//  {
//    for(j==0;j<=20;j++)
//    {
//      Serial.print(cap[j]); Serial.print(";");
//      Serial.print(val[j]); Serial.println(";");
//      }
//    }
 temps();
  /*************Affichage sur l'écran LCD****************/
 lcd.setCursor(0,0);
 lcd.print(tension,3);
 lcd.print("V      ");
 lcd.print(courant,2);
 lcd.print("A   ");
 lcd.setCursor(0,1);
 lcd.print(E,3);
 lcd.print("V   ");
 lcd.setCursor(10,1);
 lcd.print("C=");
 lcd.print(capacite,0);
 lcd.print("mAh   ");
 
 lcd.setCursor(0,2);
 lcd.print("T=");
 lcd.print(temperature);
 lcd.print(" oC");
 
 lcd.setCursor(0,3);
 lcd.print(heure);
 lcd.print("h ");
 lcd.print(minute);
 lcd.print("m ");
 lcd.print(seconde);
 lcd.print("s ");
 lcd.setCursor(15,3);
 lcd.print("N=");
 lcd.print(cycles-1);
 lcd.print("  ");
}

Rectification des simulations :

Après avoir réalisé ces simulations, on a remarqué qu’il y a eu une erreur majeure durant cette étude. En effet, les simulations ont été prises en compte avec la tension mesurée de la batterie, mais il a fallu prendre en compte l’OCV de la batterie. Ainsi, les décharges seront re simulées avec de nouveau une tension OCV de seuil 3V et la prise en compte de la tension OCV.

Nouveaux résultats de la tension OCV de la batterie :

Ainsi, par ces courbes représentant l’OCV de la batterie, et leur estimation par régression linéaire, on peut remarquer que quel que soit le courant de décharge, le SOH de la batterie est à peu près estimé à 10A.h, comme énoncé dans les caractéristiques de la batterie.

Cependant elle est légèrement plus faible que 10A.h, c’est-à-dire, en majorité, on aura une estimation du SOH d’environ 9.75A.h. C’est un détail à prendre en compte, car si la capacité sera plus grande que 10A.h, alors la tension de la batterie va chuter fortement. Et si on ne prend pas ce détail, la batterie pourra se dégrader voire ne plus fonctionner.

On remarque également que le temps de décharge varie selon le courant déchargé. Ainsi, si on décharge à 8A, alors il faudra beaucoup plus de temps que lorsque l’on veut décharger à 20A par exemple.

Pour les prochaines simulations, on va essayer de prendre en compte le temps écoulé depuis le début de la décharge.

Pour décharger des batteries, j'utilisais des lampes de voitures 12V H1 mais sous 3.3V, le courant n'est que de 2A.

L'ancien banc résistif avec le système de décharge :

Création d’un banc résistif pour les essais sur le pack de batteries :

Les nouvelles simulations que l’on va réaliser seront sur un ensemble d’éléments de batteries de 60A.h, donc si on veut réaliser une décharge à 3C, il faudra un courant maximal de 180A. Malheureusement le banc résistif que nous avions utilisé ne peut supporter que 30A au maximum.
Il faut donc créer un nouveau banc résistif pour établir une bonne décharge de ces éléments de batterie.

Il faut ainsi faire l’étude de différents fils résistifs, de différentes épaisseurs et ainsi déterminer quel type d’enroulement nous allons utiliser, et combien de fils il sera nécessaire.

Pour établir cette étude, nous avons en notre possession des câbles enroulés de 0.2mm², 0.3mm², 0.5mm², 0.6mm², 0.7mm² et un câble composé de deux enroulements de 0.6mm². Le diamètre de ces enroulements est de 7mm.

On va ainsi étudier les différentes caractéristiques des câbles selon un courant donné. De ce fait, ce qui nous préoccupera le plus dans cette étude est la résistance du câble chauffé, ainsi que sa température.

Simulation de différents câbles :

Simulation d'un fil de 0.3mm simple :

Ainsi, par ces résultats on peut montrer que plus la température est importante, plus le courant également est important. De plus, la résistance du câble décroit, et à partir d’une température mesurée, elle devient constante. Il en est de même pour la résistance thermique. Quant à la tension, température calculée du câble et puissance, ils augmentent quand le courant augmente.

La solution qui sera retenue sera un câble enroulé de 0.2mm² avec un enroulement de 10 spires, pouvant supporter 2A à chaque câble. Cependant, si on veut faire une très grande décharge, il est possible de prendre des fils de 0.3mm² sans enroulement et sans spires.

Après un test d’intensité avec deux fils simples, on peut aller facilement jusque 10A en décharge.

Ainsi, plus le fil sera épais, plus la résistance sera faible. De même si le fil comporte moins de spires, et si ce fil est court (voir les tests réalisés sur les différents câbles).

Le nouveau banc résistif :

Ainsi, plus il y aura de courant, plus la température du banc résistif sera haute. Par exemple, pour un courant de 20A, sur le banc résistif, la température maximale sera de 240°C.

1 Like

Dans article sur les batteries NiMh suivant, ou il y a l’explication
du chargeur de batterie avec l’Arduino

Pourquoi les fabricants donnent l’impédance à 1KHz de la batterie et
pas la résistance série ?
Car l’impédance série donne la chute de tension de la batterie en
fonction de la charge
Quel est le temps de pause pour mesurer correctement l’ESR ?

2 Likes

Les méthodes de mesure de l’ESR et de l’impédance sont sur ce lien

https://lygte-info.dk/info/Internal%20impedance%20UK.html


Le fabricant devrait donner ESR, R1 et la valeur de la capacité.
Attention, les valeurs des modèles changent en fonction du vieillissement et du taux de décharge mais cela permettrait d’avoir

1 Like

Décharge à 27A (2.7C) :
Après avoir réglé correctement le montage et les tensions mesurées, on va décharger à peu près à 27A.

Résultats :

soh_27_resul
temp_soh_27

  • Avec ces résultats, on peut remarquer que le SOH de la batterie pour une décharge de 27A est de nouveau aux alentours de 10A.h. Pour 27A, la température maximale de la batterie sera de 35°C à peu près. Mais, pour préciser la mesure de température, on a utilisé une caméra thermique qui va nous donner 45°C. Il y a donc une différence de 10°C entre les deux mesures.

Valeurs de SOH du tableau Excel :

On souhaiterait également étudier à partir de quel pourcentage de la capacité totale de batterie on a une bonne estimation de l’état de santé.

On établit donc les différentes courbes avec les différentes valeurs de coefficients de régression calculés.

Les différentes courbes :

reg_2_10_27A

On peut ainsi constater que c’est uniquement à partir de 65% de la capacité (6.5A.h – courbe bleu très foncé) qu’on a une bonne valeur de SOH.

Rectification du programme pour le calcul du SOH et valeur régression en temps réel :

On cherche à rendre le calcul du SOH instantané, en même temps que le programme calcule les paramètres de la batterie. Comme montré un peu plus haut, sur le terminal, les valeurs du SOH ne sont pas correctes. Il faut donc reprendre l’idée du programme VBA d’Excel, pour mieux l’adapter dans le calcul sur Arduino.

Le programme ne fonctionnait pas, il fallait donc savoir où se situait le problème. Effectivement, il y avait eu une erreur de signe lors d’un calcul, et des variables à réinitialiser après chaque passage dans la boucle de dichotomie.

Nouveaux résultats :

Pour ces valeurs, le SOH est calculé entièrement avec Arduino (sauf le remplissage des cellules qui a été manuel).

Ainsi, l’exemple est pour une fausse décharge de 27A. On peut remarquer que le SOH est à peu près le même que lorsque l’on a utilisé Excel VBA.

Voici le programme Arduino pour le calcul de SOH :

#include <SoftwareSerial.h>
#include <TimerOne.h>
#include <LiquidCrystal.h>
#include <LiquidCrystal_I2C.h>
#include <MatrixMath.h>
#include <math.h>

#define reset   13
//#define bpreset  2

//#define buzz   A4  //buzzer si tension en décharge inférieure à 2.7V
LiquidCrystal lcd(11,12,7,8,9,10); // (RS=8, E=9, D4=4, D5=5, D6=6, D7=7)
/******************************************************************/
float Ubat,Ucapt,Uref,U0=2.509;       // Initialisation
float Ualim=4.61,REF=3.59;
float courant=0,I;
float sensi=0.0125;           // Sensibilité du capteur à effet hall HASS-50-S
float capacite=0,capaciteA=0; 
float ESR=0.021; 
float R;
float E; // tension électrochimique
float temperature;
int heure=0,minute=0,seconde=0; // le temps
//boolean flag_bp=false;
float val[8];

int  cycles=1,i=0,j=0,l=0;                 //nombre d'echantillons
int nb_spire=3;
float m,vm,reg;
float A=1.0, B=15.0;
float v0,v1,v2,v3;
float cap[20]={0.5,1,1.5,2,2.51,3,3.51,4,4.5,5,5.51,6.01,6.5,7.01,7.51,8,8.5,9.01,9.51,10};
float OCV[20]={3.258,3.234,3.227,3.215,3.2,3.199,3.182,3.175,3.172,3.174,3.168,3.155,3.143,3.139,3.129,3.120,3.101,3.086,3.026,2.835};
//              0.5    1    1.5    2    2.5  3   3.5     4    4.5    5    5.5    6    6.5    7     7.5   8    8.5    9    9.5   10                    

#define N (3+1)

float SommeX[2*N],SommeXY[N];
mtx_type S[N][N];
mtx_type v[N];
mtx_type a[N];
float X=0,Y=0;
int n=0;
 
void setup() {
  // put your setup code here, to run once:
  analogReference(EXTERNAL);
  Timer1.initialize(1000000);          // initialize timer1, and set a 0,1 second period =>  100 000  pour 0.01s  10 000
  Timer1.attachInterrupt(callback);   // attaches callback() as a timer overflow interrupt

  //pinMode(bpreset, INPUT);
  pinMode(reset,INPUT_PULLUP);
  pinMode(A0, INPUT); //broche analogique Ubat
  pinMode(A1, INPUT); //broche analogique Capteur
  pinMode(A2, INPUT); //broche température
  pinMode(A3, INPUT); //broche tension ref capteur
  //pinMode(buzz, OUTPUT);
  Serial.begin(9600);
  lcd.begin(20,4);         //et pas lcd begin comme certaines biblio
  lcd.clear();      // Effacer le l'écran

  Serial.println("Temps;Capacite;Tension;OCV;Courant;Ucapt;Uref;Temperature;A3;A2;A1;A0;SOH;REG;Iteration;"); 
}
void callback() 
{
  seconde++;
//  if(digitalRead(led)==0) { digitalWrite(led,HIGH); }
//  else { digitalWrite(led,LOW); }
  Ubat=(analogRead(A0)*REF)/1023.0;   // tension batt
  Ucapt=(analogRead(A1)*REF)/1023.0;  // courant batt HASS-50-S
  
 // courant=tensionCapt/sensible; // Calcul du courant
  temperature=((analogRead(A2)*REF)/1023.0)*(1/0.01); // la tension de sortie est de : 10mV/°C
  Uref=(analogRead(A3)*REF)/1023.0;

  I = (Ucapt-U0)*26.66; //calcul du courant selon les données du capteur    U0=2.509   26.66=1/(sensibilité*nb_spires)= 1/((0.625/50)*3)   
  capacite=(I/3.6)+capacite; // (I(A)*1000)/(1s/3600)+capacite
  capaciteA=capacite/1000;

  E=Ubat+ESR*I;
  //R=E/courant;

//  if (seconde==60) { Serial.print(temperature); Serial.println(";"); }

//  if((capacite>=cycles*500 and capacite<=cycles*500+30) and Ubat<=3.6) //si capacité est un multiple de 0.5A.h, alors lecture sur port série des mesures
                                              //on estimera la capacité entre 500 et 505 mA.h car la mesure de la capacité n'est pas souvent pile sur 500 mA.h
//  { 
//    X=capaciteA;
//    Y=E;
//    for(i=0;i<=(N-1)*2;i++) { SommeX[i]=SommeX[i]+pow(X,i);}
//    for(i=0;i<(N);i++) { SommeXY[i]=SommeXY[i]+Y*pow(X,i);}
//    n++;
//    if(n<4) {
//      Serial.print(heure); Serial.print(":"); Serial.print(minute); Serial.print(":");
//      Serial.print(seconde); Serial.print(";"); //affichage temps
      
//      Serial.print(capaciteA); Serial.print(";"); Serial.print(Ubat,3); Serial.print(";");
//      Serial.print(E,3); Serial.print(";"); Serial.print(I,2); Serial.print(";"); 
//      Serial.print(Ucapt,3); Serial.print(";"); Serial.print(Uref,3); Serial.print(";");
//      Serial.print(temperature); Serial.print(";");
//      Serial.println(" ; ; ; ; ; ;"); //respectivement (a3;a2;a1;a0;SOH;reg)
//      //A changer selon l'ordre utilisé
//    } 
//    if(n>=N) {
//      for(i=0;i<N;i++) {
//        for(j=0;j<N;j++) {S[i][j]=SommeX[j+i]; }
//        v[i]=SommeXY[i];
//      }
//      Matrix.Invert((mtx_type*)S,N);
//      Matrix.Multiply((mtx_type*)S, (mtx_type*)v,N,N,1,(mtx_type*)a);

      //SOH avec coefficients
//      while ((B-A) > 0.01) {
//        m=(A+B)/2;

//        v3=a[3]*m*m*m;
//        v2=a[2]*m*m;
//        v1=a[1]*m;
//        v0=a[0];
//        vm = v3+v2+v1+v0;
//        if(vm<3) { B=m; }
//        else { A=m;}
//      }
      //Régression (changé selon l'ordre utilisé)
//      reg=vm;
//      Serial.print(heure); Serial.print(":"); Serial.print(minute); Serial.print(":");
//      Serial.print(seconde); Serial.print(";"); //affichage temps
      
//      Serial.print(capaciteA); Serial.print(";"); Serial.print(Ubat,3); Serial.print(";");
//      Serial.print(E,3); Serial.print(";"); Serial.print(I,2); Serial.print(";"); 
//      Serial.print(Ucapt,3); Serial.print(";"); Serial.print(Uref,3); Serial.print(";");
//      Serial.print(temperature); Serial.print(";");
//      Serial.print(a[3],8); Serial.print(";"); Serial.print(a[2],8); Serial.print(";"); 
//      Serial.print(a[1],8); Serial.print(";"); Serial.print(a[0],8); Serial.print(";"); 
//      Serial.print(m); Serial.print(";"); Serial.print(reg); Serial.println(";");
//    }  
    //cap[cycles]=-capacite;
    //val[cycles]=tension;
//    cycles++;
//  }

  while(n!=20) {
    A=1; B=15; l=0;
    X=cap[n];
    Y=OCV[n];
    for(i=0;i<=(N-1)*2;i++) { SommeX[i]=SommeX[i]+pow(X,i);}
    for(i=0;i<(N);i++) { SommeXY[i]=SommeXY[i]+Y*pow(X,i);}
    n++;
    if(n<4) {
      Serial.print(heure); Serial.print(":"); Serial.print(minute); Serial.print(":");
      Serial.print(seconde); Serial.print(";"); //affichage temps
      
      Serial.print(cap[n-1]); Serial.print(";"); Serial.print(Ubat,3); Serial.print(";");
      Serial.print(OCV[n-1],3); Serial.print(";"); Serial.print(I,2); Serial.print(";"); 
      Serial.print(Ucapt,3); Serial.print(";"); Serial.print(Uref,3); Serial.print(";");
      Serial.print(temperature,2); Serial.print(";");
      Serial.println(" ; ; ; ; ; ; ;"); //respectivement (a3;a2;a1;a0;SOH;reg)
      //A changer selon l'ordre utilisé
    } 
    if(n>=4) {
      for(i=0;i<N;i++) {
        for(j=0;j<N;j++) {S[i][j]=SommeX[j+i]; }
        v[i]=SommeXY[i];
      }
      Matrix.Invert((mtx_type*)S,N);
      Matrix.Multiply((mtx_type*)S, (mtx_type*)v,N,N,1,(mtx_type*)a);
      
      //SOH avec coefficients
      while ((B-A) > 0.01) {
        m=(A+B)/2;
        
        v3=a[3]*pow(m,3);
        v2=a[2]*pow(m,2);
        v1=a[1]*m;
        v0=a[0];
        reg = v3+v2+v1+v0;
        
        if(reg<3) { B=m; }
        else { A=m;}
        l++;
      }
      //Régression (à changer selon l'ordre utilisé)
      Serial.print(heure); Serial.print(":"); Serial.print(minute); Serial.print(":");
      Serial.print(seconde); Serial.print(";"); //affichage temps
      
      Serial.print(cap[n-1]); Serial.print(";"); Serial.print(Ubat,3); Serial.print(";");
      Serial.print(OCV[n-1],3); Serial.print(";"); Serial.print(I,2); Serial.print(";"); 
      Serial.print(Ucapt,3); Serial.print(";"); Serial.print(Uref,3); Serial.print(";");
      Serial.print(temperature,2); Serial.print(";");
      Serial.print(a[3],8); Serial.print(";"); Serial.print(a[2],8); Serial.print(";"); 
      Serial.print(a[1],8); Serial.print(";"); Serial.print(a[0],8); Serial.print(";"); 
      Serial.print(m,5); Serial.print(";"); Serial.print(reg,5); Serial.print(";");
      Serial.print(l); Serial.println(";");
    }  
    //cap[cycles]=-capacite;
    //val[cycles]=tension;
    //cycles++;
  }
//  //temperature=((analogRead(A2)*5.0)/1023.0)*(1/0.01); // la tension de sortie est de : 10mV/°C
//    if(tension<=2.7) {
  //    tone(buzz,100,1); } //si tension de décharge trop faible => ne pas décharger entièrement
}

void temps()
{ if(seconde>=60){seconde=0;
                  minute++;
                  if(minute>=60){minute=0;
                                 heure++;
                                }
                  if(heure>=24){heure=0;
                                lcd.clear();
                               }
                 }          
} // fin void temps

void loop() {
  // put your main code here, to run repeatedly:
  if(digitalRead(reset)==1)
  {
    seconde=0; minute=0; heure=0;
    cycles=0; capacite=0;
  }
//  if(digitalRead(envoi)==1)
//  {
//    for(j==0;j<=20;j++)
//    {
//      Serial.print(cap[j]); Serial.print(";");
//      Serial.print(val[j]); Serial.println(";");
//      }
//    }
 temps();
  /*************Affichage sur l'écran LCD****************/
 lcd.setCursor(0,0);
 lcd.print("Ub=");
 lcd.print(Ubat,3);
 lcd.print("V  ");
 lcd.setCursor(11,0);
 lcd.print("I=");
 lcd.print(I,2);
 lcd.print("A   ");
 lcd.setCursor(0,1);
 lcd.print("E=");
 lcd.print(E,3);
 lcd.print("V  ");
 lcd.setCursor(11,1);
 lcd.print("Uc=");
 lcd.print(Ucapt,3);
 lcd.print("V   ");

 lcd.setCursor(0,2);
 lcd.print("Ur=");
 lcd.print(Uref,3);
 lcd.print("V  ");
 lcd.setCursor(11,2);
 lcd.print(temperature,2);
 lcd.print("oC");
 
 lcd.setCursor(0,3);
 lcd.print(heure);
 lcd.print("h ");
 lcd.print(minute);
 lcd.print("m ");
 lcd.print(seconde);
 lcd.print("s ");
 lcd.setCursor(13,3);
 lcd.print(capacite,0);
 lcd.print("mA  ");
}

On peut donc utiliser cette méthode de calcul en temps réel pour nos autres futures décharges.

1 Like