Table des matières

Calorimétrie

1. Introduction

Ce document décrit un dispositif de mesure calorimétrique piloté par un Arduino, consistant à fournir une puissance contrôlée à une résistance chauffante tout en mesurant une température.

L'objectif est de mettre au point une méthode de calorimétrie diatherme à compensation de puissance : l'échantillon étudié est maintenu à une température constante grace à un apport thermique positif provenant d'un élément chauffant et un apport négatif provenant de l'échange thermique avec un thermostat, plus froid que l'échantillon. Ce type de calorimétrie permet de mesurer des enthalpies de changement d'état ou de réactions chimiques. Dans les expériences réalisées ici, le thermostat est simplement l'air de la pièce.

2. Montage expérimental

Le calorimètre est un simple bécher rempli d'un volume connu d'eau car il faut permettre les échanges thermiques avec le thermostat (ici l'air de la pièce).

Une résistance chauffante (2 ou 4 ohms) est plongée dans le liquide. Un thermomètre numérique DS18B20 relié à l'Arduino (protocole One Wire) permet de relever la température pendant l'expérience, laquelle est maintenue homogène grâce à une agitation magnétique. Un second thermomètre est utilisé pour vérifier l'homogénéité de la température dans le liquide. Le fond du récipient repose sur deux cales qui permettent d'éviter le contact direct avec l'agitateur.

montage-calorimetre-v2-fig.svgFigure pleine page

L'alimentation de la résistance se fait via un pont en H à transistors MOSFET (MD13S) pouvant fournir jusqu'à 13 A et acceptant une tension d'alimentation de 6 à 30 V (valeurs très largement suffisantes). Celui-ci est branché sur l'alimentation stabilisée en tension, délivrant par exemple 12 V. On utilise seulement un demi-pont (celui associé à la sortie B du pont) car le courant dans la résistance chauffante circule toujours dans le même sens (la tension U doit rester positive). Ce type de pont est vendu pour piloter un moteur, c'est pourquoi les deux ports de commande sont intitulés PWR et DIR. Notre utilisation consiste à envoyer un signal binaire à rapport cyclique déterminé sur DIR (signal PWM). La commande PWR est mise à 1 pour activer le passage du courant, à 0 pour le désactiver.

La tension de la sortie B du pont en H par rapport à la masse est une tension carrée dont le rapport cyclique est déterminé dans le programme Arduino en fonction du courant qu'on veut faire passer dans la résistance. La tension U aux bornes de cette résistance doit être constante car nous voulons la mesurer. Il faut donc ajouter une bobine de filtrage en sortie du pont, ce qui est le moyen habituel de filtrage en sortie des régulateurs de tension à découpage. Nous avons utilisé une bobine de 11 mH dont la résistance interne (en continu) est de 2,8 ohms. Sa résistance à la fréquence de découpage (20 kHz) est cependant beaucoup plus grande (environ 20 ohms), ce qui a pour effet d'abaisser notablement la tension U maximale (obtenue avec un rapport cyclique de 1) par rapport à la tension fournie par l'alimentation. Une bobine comportant des fils plus gros, de moindre inductance et moindre résistance serait préférable à condition de pouvoir augmenter la fréquence de découpage. Celle du pont utilisé ne doit pas en principe dépasser 20 kHz. Avec cette bobine, nous avons d'ailleurs constaté que l'utilisation d'une résistance de 2 ohms à la place de 4 ohms ne permet pas d'augmenter la puissance de chauffage car la résistance limitante est celle de la bobine.

Les mesures de l'intensité du courant I dans la résistance et de la tension U à ses bornes sont faites avec la carte DFRobot Wattmeter. Celle-ci met en œuvre le circuit intégré IN219 (Texas Instrument). Une résistance de 10 est placée entre les entrées IN+ et IN -. Le ciruit IN219 mesure la tension aux bornes de cette résistance et en déduit l'intensité du courant I. Il mesure aussi la tension de la borne IN- par rapport à la masse, c'est-à-dire la tension U. Les numérisations sont faites en 12 bits avec un temps de conversion de 532 microsecondes. Le circuit peut réaliser des moyennes : nous avons opté pour une moyenne de 128 échantillons successifs, ce qui fait une durée de 68 ms. Le circuit calcule aussi la puissance. Les données (tension, courant et puissance) sont transmises à l'Arduino par I2C.

3. Fonctionnement électrique

Le rôle de la partie électrique est d'appliquer une tension U constante aux bornes de la résistance rc et de permettre la mesure de la tension U et du courant I. Il s'agit bien sûr d'un dispositif de puissance capable de fournir un courant de plusieurs ampères.

Le dispositif électrique a d'autres applications. Il pourrait par exemple servir pour piloter un système l'électrolyse.

On utilise la moitié du pont en H, car la tension U doit rester positive. Chaque transistor est modélisé par un interrupteur en parallèle avec une diode dans le sens drain-source.

etats-pont-fig.svgFigure pleine page

Les transistors T1,T2 du demi-pont B sont pilotés en opposition par l'entrée DIR. Lorsque DIR=1 et PWR=1, la tension d'alimentation Vs est appliquée au circuit constitué de la bobine et de la résistance chauffante rc. Lorsque DIR=0 et PWR=1, une tension nulle est appliquée. Le courant dans ce circuit est lissé par la bobine, ce qui permet d'obtenir une tension U quasi constante, dont la valeur est proportionnelle en principe au rapport cyclique du signal envoyé sur DIR. Pour obtenir ce filtrage, la fréquence du signal de commande doit vérifier :

frb+rcL(1)

Avec la bobine utilisée et une résistance rb estimée à 20 Ω, la condition est f2kHz . Une fréquence de 20 kHz devrait donner de bons résultats, sachant qu'il s'agit de la fréquence maximale conseillée pour le pont MD13S. Il faut noter que l'augmentation de fréquence conduit aussi à une augmentation de la résistance de la bobine.

Lorsque PWR = 0, les transistors T2,T4 sont à l'état d'interrupteur fermé alors que T1,T3 sont ouverts, quel que soit l'état de DIR. L'état PWR = 0 nous permet donc de désactiver l'alimentation par le pont, quel que soit l'état de DIR. Il faut remarquer que l'alimentation d'un moteur branché entre A et B avec le pont en H complet se fait autrement : on choisit le sens de rotation avec DIR et on applique le signal à rapport cyclique variable sur PWR.

La figure suivante montre la tension U(t) sans et avec la cellule RC de filtrage. Le rapport cyclique du signal de commande (DIR) est 0,5 et Vs=12V.

figAfigA.pdf

Sans la cellule RC, on observe des oscillations juste après les fronts montant et descendant de la tension en sortie du pont. L'origine de ces oscillations et leur réduction par la cellule RC sont expliquées dans Transistor MOSFET de puissance. La cellule RC permet de réduire beaucoup l'amplitude de ces oscillations. On remarque par ailleurs que le filtrage passe-bas effectué par la bobine n'est pas parfait puisque l'ondulation résiduelle est visible. Pour réduire cette ondulation, il faudrait augmenter la fréquence mais cele se traduirait aussi par une augmentation de la résistance de la bobine et donc une baisse de la tension U. On peut cependant considérer que l'effet thermique (dans la résistance chauffante) de cette ondulation est négligeable.

Voici les tensions U(t) pour différentes valeurs du rapport cyclique, avec la cellule RC :

figBfigB.pdf

Lorsque le rapport cyclique est proche de 1, on a U7V (soit une puissance de 12 W) alors que Vs=12V et rc=4Ω. Cette chute de tension est due à la résistance interne de la bobine. On voit donc que réduire la résistance rc n'apporterait pas de hausse de U, à moins d'utiliser une bobine de plus faible résistance interne et de plus faible inductance (un fil beaucoup plus gros et moins de spires), ce qui nécessiterait aussi d'augmenter la fréquence. Pour augmenter la tension U, il est évidemment possible d'augmenter la tension d'alimentation Vs (jusqu'à 30 V pour ce pont). Avec une tension de 24 V, on devrait obtenir une tension U maximale d'environ 14 V, soit un courant dans la résistance chauffante d'environ 3,5A et une puissance dissipée d'environ 49 W (avec bien sûr une alimentation suffisante pour cela).

On remarque que la tension aux bornes de la résistance (donc le courant qui la traverse) n'est pas parfaitement constante. L'ondulation résiduelle peut être éliminée en plaçant un condensateur de grande capacité (au moins 100 micro-farads) aux bornes de la résistance. Ce condensateur permet d'obtenir un filtrage passe-bas du second ordre, beaucoup plus sélectif que le filtre RL. Pour l'expérience de calorimétrie, ces ondulations résiduelles sont négligeables.

4. Programme Arduino MEGA

Le rôle de l'Arduino est de :

Le signal de commande pour l'entrée DIR du pont en H est un signal PWM. La tension U est en principe proportionnelle au rapport cyclique de ce signal, donc la puissance délivrée à la résistance chauffante est proportionnelle au carré du rapport cyclique et au carré de la tension d'alimentation. On utilise le Timer 3 pour générer ce signal.

La lecture des températures et des grandeurs électriques (U,I,P) est faite à une fréquence de 1 Hz et c'est aussi à cette fréquence que les données (tension, courant, énergie et températures) sont lues par le PC. On utilise donc le Timer 4 pour générer des interruptions à cette fréquence et le gestionnaire d'interruption fait la lecture des informations en provenance des thermomètres et de l'INA219 puis stocke les données à transmettre au PC dans un tableau prévu pour cela. La récupération de ces données, à une fréquence de 1 Hz (environ), est à l'initiative du PC.

Le DS18B20 (Dallas Semiconductor) est un thermomètre à sortie numérique. Sa connection n'a que trois bornes : VCC (+5 V), GND et DQ (Data In/Out). On trouve dans le commerce des versions encapsulées dans un embout en aluminium, qui peuvent être plongées dans un liquide. La communication se fait avec l'interface 1-Wire, qui n'utilise qu'un fil pour le transfert série dans les deux sens (sans signal d'horloge séparé). Chaque thermomètre possède une adresse unique. Il est donc possible de relier plusieurs thermomètres sur un même port série. Nous utilisons la bibliothèque de fonctions One Wire pour la communication avec le thermomètre. La résolution du convertisseur A/N du DS18B20 est configurable, de 9 bits à 12 bits. Le temps de conversion est d'autant plus long que la conversion est précise. Nous utilisons la conversion 12 bits, qui se fait en 750 ms et apporte une résolution de 0,06 .

La précision du thermomètre est de ±0,5 . Dans notre expérience, c'est la variation de température qui importe et non pas la valeur absolue de la température. Nous avons fait un étalonnage consistant à appliquer un décalage de température pour la valeur d'un des deux thermomètres afin qu'ils donnent tous les deux la même valeur au bit près (soit 0,06 ). Après élévation de la température de l'eau de plusieurs degrés, nous constatons que l'écart de valeur de température entre les deux thermomètres reste égal à 0 ou 0,06, ce qui correspond simplement au bruit de quantification.

Voici l'importation des entêtes et les définitions des constantes :

ArduinoMEGA-calorimetre.cpp
 
#include <Arduino.h>
#include <OneWire.h>
#include "DFRobot_INA219.h"

#define UNFIL 8 // port série (DQ) des thermomètres
#define MESURE 0x44 // commande pour démarrer une conversion
#define LECTURE 0xBE // commande pour lire la température
#define PWM 5 // sortie vers DIR du pont H
#define ENABLE 6 // sortie vers PWR du pont H
#define PWM_PERIOD 50 // période du PWM de chauffage en microsecondes
#define DATA_SAMPLING_PERIOD 1000000 // période du transfert de données

				 

Les adresses des deux thermomètres (8 octets) sont obtenues avec une fonction décrite plus loin. On doit bien sûr les coder en dur :

// adresses des deux thermomètres DS18B20
byte addr1[8]= {0x28,0xFF,0x9E,0x16,0x06E,0x14,0x04,0x8B};
byte addr2[8] = {0x28,0xFF,0xC2,0xCD,0x6D,0x14,0x04,0x4E};
				 

Le tableau suivant définit les diviseurs d'horloge pour la programmation des Timers :

uint16_t diviseur[6] = {0,1,8,64,256,1024};
				  

Les définitions et variables suivantes sont utilisées pour l'échange de données avec le PC. Le protocole d'échange utilisé est décrit dans Échanges de données avec un Arduino.

#define GET_DATA 10
#define SET_DATA 11
#define DATA_0_SIZE 24
#define DATA_1_SIZE 4 // pwm
#define DATA_2_SIZE 4 // temps
uint8_t data_0[DATA_0_SIZE];
uint8_t data_1[DATA_1_SIZE];
uint8_t data_2[DATA_2_SIZE];
float x0[6]; // temps, T1, T2, P , U, I, data_0
float pwm; // rapport cyclique de la commande du pont, data_1
bool data_0_ready = false;
uint16_t data_count = 0;	   
				   

Autres variables globales :

float U,I;
float temps;
float puissance;
float T1,T2;
// étalonnage de la mesure de I
float ina219Reading_mA = 998;
float extMeterReading_mA = 1000;
				   

La communication avec le thermomètre se fait avec la classe OneWire et celle avec l'INA 219 au moyen de la classe ina219 :

OneWire thermo(UNFIL);
DFRobot_INA219_IIC  ina219(&Wire, INA219_I2C_ADDRESS4); //INA219_I2C_ADDRESS4  0x45   A0 = 1  A1 = 1
				 

La fonction suivante permet d'identifier les thermomètres connectés sur le port UNFIL et d'afficher leur adresse :

void afficher_adresses() {
    byte addr[8];
    thermo.reset_search();
    while (thermo.search(addr)) {
        if ( OneWire::crc8( addr, 7) != addr[7]) {
          Serial.print("CRC is not valid!\n");
          return;
        }
        if ( addr[0] == 0x10) {
          Serial.print("Device is a DS18S20 family device.\n");
        }
        else if ( addr[0] == 0x28) {
          Serial.print("Device is a DS18B20 family device.\n");
          for (int i=0; i<8; i++) {
            Serial.print(addr[i],HEX);
            Serial.print(':');
          }
         Serial.print('\n');
        }
        else {
          Serial.print("Device family is not recognized: 0x");
          Serial.println(addr[0],HEX);
          return;
        }
    }
}			 
				 

La fonction suivante permet de demander une lecture de la température pour un thermomètre d'adresse donnée. Celle-ci est disponible après une durée de 750 ms.

void lancer_mesure(byte *addr) {
    thermo.reset();
    thermo.select(addr);
    thermo.write(MESURE,1);
}
				 

La fonction suivante permet de récupérer la dernière valeur de température :

float temperature(byte *addr) {
    byte data[12]; 
    int temp_brute = 0;
    float temp_reelle = 0.0;
    thermo.reset();
    thermo.select(addr);
    thermo.write(LECTURE,1);
    for (int i=0; i<9; i++) {
      data[i] = thermo.read();    
    }
    temp_brute = data[1] & B10000111;
    temp_brute <<= 8;
    temp_brute += data[0];
    temp_reelle = float(temp_brute)*6.25/100.0;
    return temp_reelle;
}

				 

La fonction suivante permet de programmer le Timer 4, qui est chargé de générer des interruptions pour la lecture de la température et des données en provenance de l'ina219 (toutes les secondes).

// Echantillonnage pour l'enregistrement des températures et de l'énergie
void timer4_init(uint32_t period) {
    TCCR4A = 0;
    TCCR4B = 0;
    TCCR4B |= (1 << WGM42); // mode CTC avec OCR1A pour le maximum
    uint32_t top = (F_CPU/1000000*period);
    int clock = 1;
    while ((top>0xFFFF)&&(clock<5)) {
          clock++;
          top = (F_CPU/1000000*period/diviseur[clock]);
      }
    OCR4A = top; // période
    TIMSK4 = (1 << OCIE4A); // interruption lorsque TCNT4=OCR4A
    TCCR4B |= clock;
}			  
				  

La fonction suivante permet de programmer le Timer 3, qui génère le signal PWM pour piloter le pont en H (signal sur l'entrée DIR). Le signal est généré sur la sortie OC3A du microcontrôleur, qui est câblé sur la sortie D5 de l'Arduino.

// PWM pour la résistance chauffante
// OC3A : sortie D5
void time3_init(uint32_t period, float rapport) {
  TCCR3A = (1 << COM3A1) | (1 << COM3A0);
  TCCR3B = 1 << WGM33;
  uint32_t top = (F_CPU/1000000*period/2);
  int clock = 1;
  while ((top>0xFFFF)&&(clock<5)) {
          clock++;
          top = (F_CPU/1000000*period/diviseur[clock]);
  }
  uint16_t ocra = top*(1.0-rapport);
  ICR3 = top;
  OCR3A = ocra;
  TCCR3B |= clock;

}
				   

La fonction suivante permet d'initialiser le convertisseur A/N (ADC) :

void adc_init(uint8_t prescaler) {
  ADCSRA = 0;
  ADCSRA |= (1 << ADEN); // enable ADC
  ADCSRA |= prescaler ;
  ADCSRB = 0;
}			  
				  

La fonction suivante est le gestionnaire des interruptions déclenchées par le Timer 4. Elle déclenche la numérisation des températures et récupère les dernières valeurs des deux températures. Elle lit les dernières valeurs de U,I,P fournies par l'INA219 (moyennes sur 68 ms). Elle calcule aussi le temps écoulé depuis le démarrage de l'acquisition. x0 est un tableau de float où sont stockés provisoirement les données à transmettre au PC. Ces données sont copiées dans data_0, un tampon de 24 octets (soit 6 flottants de 32 bits) qui sera transmis au PC à sa demande. Le booléen data_0_ready est mis à true, pour indiquer que les 6 données sont prêtes et que la transmission au PC peut donc avoir lieu. Il sera mis à false juste après le transfert de ces données.

// interruption pour enregistrement des données
ISR(TIMER4_COMPA_vect) {
  lancer_mesure(addr1);
  lancer_mesure(addr2);
  T1 = temperature(addr1);
  T2 = temperature(addr2)+0.25;
  temps += 1e-6*DATA_SAMPLING_PERIOD;
  U = ina219.getBusVoltage_V();
  I = ina219.getCurrent_mA();
  puissance = ina219.getPower_mW();
  x0[0] = temps;
  x0[1] = T1;
  x0[2] = T2;
  x0[3] = puissance;
  x0[4] = U;
  x0[5] = I;
  memcpy(data_0,x0,DATA_0_SIZE);
  data_0_ready = true;
  data_count++;
}

					 

La fonction setup effectue toutes les initialisations nécessaires. Le Timer 4 est déclenché mais par le Timer 3, qui sera déclenché sur demande du PC avec le rapport cyclique voulu.

void setup() {
  for (int i=0;i<DATA_0_SIZE; i++) data_0[i] = 0;
  Serial.begin(115200);
  while(ina219.begin() != true) {
        Serial.println("INA219 begin faild");
        delay(2000);
    }
    ina219.linearCalibrate(ina219Reading_mA, extMeterReading_mA);
    ina219.setBRNG(ina219.eIna219BusVolRange_32V);
    ina219.setBADC(ina219.eIna219AdcBits_12,ina219.eIna219AdcSample_128);
    ina219.setSADC(ina219.eIna219AdcBits_12, ina219.eIna219AdcSample_128);
 
  char c;
  Serial.setTimeout(0);
  c = 0;
  Serial.write(c);
  c = 255;
  Serial.write(c);
  c = 0;
  Serial.write(c);
  pinMode(ENABLE,OUTPUT);
  digitalWrite(ENABLE,HIGH);
  pinMode(PWM,OUTPUT);
  //time3_init(PWM_PERIOD,0.5);
  //afficher_adresses();
  cli();
  timer4_init(DATA_SAMPLING_PERIOD);
  sei();
  temps = 0;
  
}	  
					  

La fonction get_data est appelée lorsque le PC fait une demande de récupération de données (GET_DATA). L'octet lu sur le port série indique le numéro de la donnée à récupérer. La donnée indiquée est d'abord copiée dans un tampon puis écrite sur le port série avec Serial.write. La donnée numéro 0, correspondant au tampon data_0 rempli dans la fonction ISR(TIMER4_COMPA_vect) nécessite un traitement particulier : sa transmission doit se faire seulement lorsque le booléen data_0_ready devient true, ce qui indique que les données ont été effectivement enregistrées. Après cela, data_0_ready est mis à false puis le tampon est transmis au PC. Ce mécanisme permet au PC de récupérer les données au rythme exact où elles sont générées (période de 1 seconde). Bien sûr, le PC devra faire sa requête à un rythme d'environ 1 par seconde.

void get_data() {
  char n;
  while (Serial.available()<1) {};
  n = Serial.read();
  if (n==0) {
    while (!data_0_ready) {delay(10);};
    data_0_ready = false;
    Serial.write(data_0,DATA_0_SIZE);
  }
  else if (n==1) {
    memcpy(data_1,&pwm,DATA_1_SIZE);
    Serial.write(data_1,DATA_1_SIZE);
  }

}				 
							 

La fonction set_data est appelée lorsque le PC transmet des données de configuration (SET_DATA). L'octet lu sur le port série indique le numéro de la donnée à écrire. La donnée numéro 1 contient le rapport cyclique pour le signal PWM à fournir au pont en H (entrée DIR). Le Timer 3 est programmé avec ce rapport cyclique. La donnée numéro 2 contient la tension Vref, déterminée par étalonnage au moyen du voltmètre (voir script Python plus loin).

void set_data() {
  char n;
  while (Serial.available()<1) {};
  n = Serial.read();
  if (n==1) {
    while (Serial.available()<DATA_1_SIZE) {};
    Serial.readBytes(data_1,DATA_1_SIZE);
    memcpy(&pwm,data_1,DATA_1_SIZE);
    cli();
    time3_init(PWM_PERIOD,pwm);
    sei();
  }
  else if (n==2) {
    while (Serial.available()<DATA_2_SIZE) {};
    Serial.readBytes(data_2,DATA_2_SIZE);
    memcpy(&temps,data_2,DATA_2_SIZE);
  }
  
}		  
					  

La fonction suivante effectue une lecture du port série pour savoir si une requête venant du PC y figure :

void lecture_serie() {
   char com;
   if (Serial.available()>0) {
        com = Serial.read();
        if (com==GET_DATA) get_data();
        else if (com==SET_DATA) set_data();
   }
}
					  

La fonction loop appelle la fonction précédente.

void loop() {
  lecture_serie();
  
}
					  

5. Programme Python

La classe Arduino.py permet d'échanger des données selon le protocole défini dans Échanges de données avec un Arduino.

La création de l'objet de la class Arduino se fait en précisant les tailles des 3 données transmises : data_size = [24,4,4].

Pour le choix des rapports cycliques du signal de commande du pont en H (entrée DIR), il faut se rappeler que la puissance est proportionnelle au carré du rapport cyclique. Un rapport cyclique de 0,95 donne donc une puissance environ 7,4 fois plus grande qu'un rapport 0,35 et 22,6 fois plus grande qu'un rapport de 0,2. Pour une période de 50 microsecondes, la fréquence d'horloge utilisée par le Timer 1 est de 16 MHz, ce qui fait que le compteur va de 0 à 800 à chaque cycle. La résolution du rapport cyclique est donc de 1/800, soit 1,12510-3 . Deux rapports cycliques différents de 2/1000 donnent donc deux puissances différentes.

Il peut être utile de connaître la puissance en fonction du rapport cyclique du signal envoyé sur DIR. Le script suivant applique un rapport cyclique variable (20 secondes pour chaque rapport) afin d'obtenir la puissance pour chaque rapport cyclique.

etalonnagePuissance.py
import numpy as np
import time
from arduino import Arduino
import matplotlib.pyplot as plt
N = 10
pwm = np.arange(N)*1/N
duree=20 # durée pour chaque puissance
ard = Arduino('COM3',[24,4,4])
ard.write_float(2,0.0) # temps
liste_data = []
k = 0
j = 0
ard.write_float(1,pwm[j])
for i in range(N*duree):
    k += 1
    if k==duree and j<N-1:
        k = 0
        j += 1
        ard.write_float(1,pwm[j])
    time.sleep(1)
    data = ard.read_float_array(0)
    data = np.append(data,pwm[j])
    print(data)
    liste_data.append(data)
ard.write_float(1,0)
ard.close()
data_array = np.array(liste_data)
np.savetxt("etalonnagePuissance-12V-4ohms.txt",data_array,header="t(s)\t T1(°C)\t T2(°C)\t P(mW)\t U(V)\t I(mA)\t pwm")
t = data_array[:,0]
T1 = data_array[:,1]
T2 = data_array[:,2]
P = data_array[:,3]
pwm = data_array[:,4]
plt.figure()
plt.plot(pwm**2,P)
plt.grid()
plt.show()
				

Voici la puissance électrique délivrée à la résistance en fonction du carré du rapport cyclique, pour une tension d'alimentation Vs=12V :

figure(figsize=(8,8))
[t,Ta,Tb,P,U,I,pwm] = np.loadtxt('etalonnagePuissance-12V-4ohms.txt',unpack=True,skiprows=1)
coeff = np.polyfit(pwm**2,P/1e3,deg=1)
plot(pwm**2,P/1e3,'o')
plot([0,1],[coeff[1],coeff[0]+coeff[1]],'r-')
grid()
xlabel(r"$r^2$",fontsize=16)
ylabel("P (W)",fontsize=16)	
beta = 1/np.sqrt(coeff[0])		
				
figB1figB1.pdf

La puissance est bien proportionnelle au rapport cyclique au carré. On pourra donc choisir une puissance (en watts) au moyen de la relation suivante :

r=βP(2)
print(beta)
--> 0.28769047280021787

Le script suivant permet de faire une expérience au cours de laquelle on applique une puissance de chauffage constante pendant 8 minutes puis une puissante plus faible pendant 8 minutes. Plusieurs expériences seront faites avec toujours la même puissance pendant la première phase mais une puissance différente pendant la seconde phase.

calorimetrie.py
import numpy as np
import time
from arduino import Arduino
import matplotlib.pyplot as plt


liste_data = []
#rapports cycliques 
pwm = [0,0.95,0.0]
#durées
duree = [30,60*8,60*8]
ard = Arduino('COM3',[24,4,4])
ard.write_float(2,0.0) # temps
ard.write_float(1,pwm[0]) # PWM
for k in range(duree[0]): # pour mesure de la température initiale
    time.sleep(1)
    data = ard.read_float_array(0)
    print(data)
    liste_data.append(data)

print("Chauffage primaire ...")
ard.write_float(1,pwm[1]) # PWM
for k in range(duree[1]):
    time.sleep(1)
    data = ard.read_float_array(0)
    print(data)
    liste_data.append(data)
print("Chauffage secondaire ...")
ard.write_float(1,pwm[2]) # PWM
for k in range(duree[2]):
    time.sleep(1)
    data = ard.read_float_array(0)
    print(data)
    liste_data.append(data)
ard.write_float(1,0)
ard.close()
data_array = np.array(liste_data)
np.savetxt("chauffage-eau150mL-12V-4ohms-pwm0,95-0-4.txt",data_array,header="t(s)\t T1(°C)\t T2(°C)\t P(mW)\t U(V)\t I(mA)")
t = data_array[:,0]
T1 = data_array[:,1]
T2 = data_array[:,2]
P = data_array[:,3]

plt.figure()
plt.plot(t,T1)
plt.ylim(10,40)
plt.grid()

plt.show()

			 

6. Expérience de calorimétrie

L'expérience réalisée par le script ci-dessus permet de déterminer la résistance thermique du récipient. On place dans le récipient un volume de 150 mL d'eau initialement à température ambiante. Une puissance de chauffage P1 est appliquée pendant une durée Δt1 puis une puissance plus faible P2 est appliquée pendant Δt2. L'expérience est répétée plusieurs fois avec une puissance P2 différente jusqu'à obtenir une température constante pendant la seconde phase.

import numpy as np
from matplotlib.pyplot import *
fig,ax = subplots(2,1,figsize=(10,10))
ax[0].grid()
ax[0].set_ylabel('P (W)',fontsize=16)
ax[0].set_ylim(0,20)
ax[1].grid()
ax[1].set_ylabel('T (°C)',fontsize=16)
ax[1].set_xlabel('t (s)',fontsize=16)
ax[1].set_ylim(16,26)
T2 = []
P2 = []
P1 = []
Ti = []
def courbe(fichier):
	[t,Ta,Tb,P,U,I] = np.loadtxt(fichier,unpack=True,skiprows=1)
	P /= 1e3
	T = (Ta+Tb)/2
	Ti.append(np.mean(T[0:29])) # température initiale = température extérieure
	P1m = np.mean(P[100:450])
	P2m = np.mean(P[600:900])
	T2.append(np.mean(T[600:900]))
	P2.append(P2m)
	P1.append(P1m)
	ax[0].plot(t,P,label="P1 = %0.2f W, P2 = %0.2f W"%(P1m,P2m))
	ax[1].plot(t,T,label="P1 = %0.2f W, P2 = %0.2f W"%(P1m,P2m))
	

courbe('chauffage-eau150mL-12V-4ohms-pwm0,95-0-4.txt')
courbe('chauffage-eau150mL-12V-4ohms-pwm0,95-0,3-4.txt')
courbe('chauffage-eau150mL-12V-4ohms-pwm0,95-0,35-4.txt')
ie = 2 # expérience avec T constante dans la seconde phase

ax[0].legend(loc='upper right')
ax[1].legend(loc='lower right')
			  
figCfigC.pdf

Le rapport cyclique du signal DIR pendant la phase de chauffage est 0,95 et la tension d'alimentation est 12 V. La puissance moyenne mesurée P1 est de 10,8W. La puissance de la seconde phase qui donne une température constante est P2=1,4W. Cette puissance correspond aux pertes thermiques à la température :

print(T2[ie])
--> 24.79083333333333

La résistance thermique associée aux échanges thermique entre l'eau et l'extérieur (par les parois du récipient et par la surface libre) s'en déduit :

Rth = (T2[ie]-Ti[ie])/P2[ie]
print(Rth)
--> 5.333999696993912

Cette résistance thermique est la somme de la résistance thermique des parois du récipient, de la résistance thermique des échanges conducto-convectifs au voisinage de ces parois et de la surface libre de l'eau et de la résistance thermique due aux échanges par rayonnement. La contribution conducto-convective est très sensible à l'intensité de la convection. Celle-ci est très intense dans le récipient en raison de l'agitation mais en dehors du récipient, seule la convection libre est présente. L'ajout d'un ventilateur devrait permettre de garantir un niveau constant de convection et une résistance thermique plus basse. Des expériences complémentaires avec des échauffements de durées variables sont nécessaires pour vérifier que la résistance thermique est bien indépendante de la température (elle l'est en théorie, par définition). Pour des températures plus élevées, on peut cependant s'attendre à une contribution non négligeable des effets non linéaires dus au rayonnement.

L'équation différentielle vérifiée par la température pendant la première phase est :

CdTdt=P1-T-TeRth

C est la capacité thermique de l'eau et du récipient et Te la température extérieure. Posons :

τ= CRthTlim=Te+RthP1

La solution est, pour une température initiale Te :

T(t)=(Te-Tlim)exp(-tτ)+Tlim

La capacité thermique devrait être proche de la capacité thermique de l'eau mais légèrement supérieure. Pour déterminer sa valeur, faisons un ajustement de la fonction T(t) sur la courbe expérimentale, en faisant varier C :

m = 150 # masse d'eau
c= 4.18 # capacité massique de l'eau
C = m*c*1.062
figure(figsize=(8,8))
[t,Ta,Tb,P,U,I] = np.loadtxt('chauffage-eau150mL-12V-4ohms-pwm0,95-0,35-4.txt',unpack=True,skiprows=1)
T = (Ta+Tb)/2
t = t[31:480]-34
T = T[31:480]
plot(t,T,'r-')
Te = Ti[ie]
Tlim = Te+Rth*P1[ie]
tau = Rth*C
t_mod = np.linspace(0,450,len(t))
T_mod = (Te-Tlim)*np.exp(-t_mod/tau)+Tlim
plot(t_mod,T_mod,'k-')
grid()
xlabel('t (s)',fontsize=16)
ylabel('T (°C)',fontsize=16)
		
				 
figDfigD.pdf
N = 100
x = np.linspace(1.04,1.08,N)
erreur = np.zeros(N)
for k in range(N):
	T_mod = (Te-Tlim)*np.exp(-t_mod/(m*c*x[k]*Rth))+Tlim
	erreur[k] = 1/N*np.sum((T-T_mod)**2)
figure()
plot(x,erreur)
grid()
xlabel('x')
ylabel('erreur')
				 
figEfigE.pdf

L'ajustement conduit à une capacité thermique 6% plus élevée que celle de l'eau. Cette capacité supplémentaire est celle du récipient en verre, plus précisément de la partie du verre qui s'échauffe pendant l'expérience.

L'ajustement entre le modèle et la courbe expérimentale est très sensible à la valeur de C : une variation de 1% est visible. La sensibilité par rapport à la valeur de Rth est beaucoup plus faible : une variation de 1% de cette résistance n'a pas d'effet visible et il faut une variation de 20% pour obtenir le même effet qu'une variation de 1% de C.

7. Régulation de température

Voyons comment réaliser un asservissement permettant de maintenir une température constante de l'eau dans le récipient, supérieure à la température de l'extérieur Te. Notons Tc la température que l'on souhaite maintenir (température cible).

Supposons que pendant l'intervalle de temps Δt (1 seconde), on relève une température T. Soit ΔT=T-Tc l'écart entre cette température et la température cible. Si cet écart est négatif, on régle la puissance de chauffage à P=αC|ΔT|Δt , où C est la capacité thermique déterminée précédemment (666J⋅K-1) et α un coefficient légèrement supérieur à 1. Cette puissance est appliquée pendant une seconde. Si ΔT est positif, on applique une puissance nulle pendant la seconde qui suit. Une puissance nulle permet à la température de de l'eau de s'abaisser car sa température est supérieure à celle de l'extérieur.

Le rapport cyclique r du signal PWM est déterminé en fonction de la puissance souhaitée par la relation r=βP .

Si le rapport cyclique obtenu est supérieur à 0,99, on applique la valeur 0,99.

L'écart de température qui conduit à un rapport cyclique de 0,99 est :

C = 666
alpha = 1.05
beta = 0.28769
DT_max = 0.99/(beta**2*alpha*C)
			
print(DT_max)
--> 0.017104962795032458

Cet écart est 3,5 fois plus faible que la résolution du thermomètre (Δθ= 0,06C ). En conséquence, pour cette capacité thermique, l'asservissement fonctionnera en tout ou rien : le rapport cyclique sera soit nul soit égal à la valeur maximale (0,99). Pour un calorimètre de plus petit volume, la capacité thermique serait plus faible donc l'asservissement se ferait avec des puissances appliquées plus faibles.

Cet asservissement de la puissance à la température permet de maintenir la température de l'eau constante avec une précision maximale de ±0,06C (la résolution du thermomètre). La puissance moyenne mesurée sur une durée de régulation assez longue (quelques minutes) devrait donner accès à la puissance thermique de perte à la température cible. Notons P1 cette puissance moyenne.

Quelle est l'influence de la résolution du thermomètre sur la précision de la détermination de P1. Considérons un écart de température Δθ , qui correspond à la résolution du thermomètre. Le flux thermique de l'eau vers l'extérieur s'écrit :

Pth=1RthΔθ+1Rth(Tc-Te)(3)

Le second terme correspond à la puissance effectivement mesurée, le premier à celle qui ne peut l'être à cause de la résolution finie du thermomètre (à sa limite supérieure).

Il s'en suit que l'incertitude relative de la mesure de P1 est égale à :

ΔP1P1=ΔθTc-Te(4)

Pour un écart de température de 6 degrés, on a une incertitude relative de 1% . Cette incertitude est du même ordre de grandeur que l'incertitude des mesures électrique (courant et tension).

La résistance thermique est sans effet sur la précision de la mesure de puissance (et d'énergie). Elle a en revanche une influence sur le temps de réponse de la température lorsque la puissance fournie est nulle. En effet, la puissance thermique prélevée est dans ce cas proportionnelle à T-Te et inversement proportionnelle à Rth. En conséquence, une réponse rapide nécessite une résistance thermique relativement faible. Dans l'expérience décrite ici, la résistance thermique est relativement grande car elle fait intervenir des échanges par convection libre dans l'air au voisinage du récipient. Si le récipient était plongé dans un bain d'eau thermostatée de température Te, on aurait une résistance thermique plus faible donc un temps de réponse plus court lorsque la température doit baisser. En ce qui concerne le temps de réponse lorsqu'une puissance de chauffage est appliquée (suite à une diminution de la température), elle est très peu dépendante de la résistance thermique (pour une puissance donnée).

8. Calorimétrie diatherme à compensation de puissance

8.a. Principe

La calorimétrie diatherme consiste à maintenir l'échantillon étudié (ici l'eau dans le récipient) à une température constante Tc (température cible). L'extérieur est un thermostat de température Te, avec lequel le transfert thermique se fait via une résistance thermique Rth. En l'absence de phénomènes exothermiques ou endothermiques, une puissance thermique moyenne P1 (positive) doit être fournie par la résistance chauffante. Le fait que Tc>Te permet à la température de l'échantillon de baisser lorsque la puissance appliquée à la résistance est nulle.

Le rôle du thermostat est donc de permettre un apport thermique négatif pour l'échantillon, alors que l'apport thermique positif est assuré par la résistance chauffante. Il faut bien noter que ce n'est pas le rôle qu'on attribue habituellement à un thermostat (le terme source froide serait peut-être plus approprié pour l'usage qui en est fait ici).

Lorsque un phénomène exothermique et endothermique se produit dans l'échantillon (ici dans l'eau), la puissance électrique appliquée pour maintenir la température cible est P(t). Si le phénomène est endothermique, cette puissance est en moyenne plus grande que P1. Si le phénomène est exothermique, elle est en moyenne plus petite. Plus précisément, l'énergie excédentaire fournie s'écrit :

E=0tf(P(t)-P1)dt(5)

C'est l'opposée de l'énergie libérée par le phénomène étudié.

La puissance P1 est la puissance de compensation du flux thermique sortant; on peut la nommer puissance de base.

Supposons que, à la suite d'un phénomène endothermique ou exothermique dans l'échantillon, une variation de température ΔT survienne. La température est alors T=Tc+ΔT. En réaction à cette variation de température, une puissance P=P1+P2 est appliquée. Si l'écart de température est négatif, la puissance supplémentaire P2 est positive et elle est fournie par la résistance chauffante. Si l'écart de température est positif, la puissance P2 est négative et elle est fournie par l'échange thermique avec le thermostat :

P2=-T-TeRth-Tc-TeRth(6)

Posons :

τ= CRthTlim=Te+Rth(P1+P2)

On a par ailleurs Tc-Te=RthP1 par définition de la puissance de base, donc :

Tlim=Tc+RthP2(7)

L'évolution de la température s'écrit :

T(t)=(Tc+ΔT-Tlim)exp(-tτ)+Tlim

Cette équation est valable tant que la puissance P2 est maintenue, c'est-à-dire tant que la puissance électrique fournie est différente de P1. Définissons le temps de réponse de la température comme le temps qu'il faut pour que la température atteigne Tc+Δ T/2 (réduction d'un facteur 2 de l'écart). Le temps de réponse s'écrit :

tr=CRthln(ΔT-RthP212ΔT-RthP2)(8)

Remarquons que ΔT et P2 sont de signes opposés. ΔT doit être de l'ordre de grandeur de la résolution du thermomètre (un dixième de degré). Si ΔT>0, on a P2Rth-(Tc-Te) , qui est très grand devant ΔT si l'écart de température entre l'échantillon et l'extérieur et de l'ordre de 5 degrés ou plus. Si ΔT<0, il est aisé d'appliquer une puissance électrique telle que RthP2ΔT . Dans le cas où RthP2ΔT , le temps de réponse s'écrit :

tr-CΔTP2(9)

Le phénomène exothermique ou endothermique étudié doit se faire à une vitesse assez faible pour que l'écart entre T et Tc reste très faible, de l'ordre de grandeur de la résolution du thermomètre. Si l'expérience consiste à étudier un phénomène chimique (dissolution ou réaction chimique), le réactif devra être ajouté par petites quantités.

8.b. Puissance de base nulle

Si le phénomène est endothermique, il est possible de choisir Tc=Te. Dans ce cas, la puissance de base est nulle. L'avantage de cette méthode est de ne pas nécessiter de mesure de P1, mais elle ne fonctionne pas pour un phénomène exothermique.

Pour mettre au point la technique, on commence par l'étude d'une dissolution endothermique.

L'expérience consiste à dissoudre une masse m d'un sel dont la dissolution est endothermique dans 150mL d'eau. Dans un premier temps, aucune puissance électrique n'est fournie. Voici la courbe de température pour une masse m=14,45g de chlorure de potassium en poudre. Celle-ci est versée dans le récipient en quelques secondes.

figure(figsize=(8,8))
[t,Ta,Tb,P,U,I] = np.loadtxt('dissolutionKCl-eau150mL-12V-4ohms-pwm0-1.txt',unpack=True,skiprows=1)
T=(Ta+Tb)/2
figure()
plot(t,T)
grid()
xlabel('t (s)',fontsize=16)
ylabel('T (°C)',fontsize=16)
ylim(10,20)
			 
figFfigF.pdf

On se propose de réaliser une expérience de calorimétrie diatherme à compensation de puissance. Le sel sera versé plus progressivement et une puissance de chauffage variable sera appliquée afin de maintenir la température constante, égale à la température extérieure Te (température initiale de l'eau).

Voici le script qui réalise cette expérience :

dissolutionEndoAdapt.py
import numpy as np
import time
from arduino import Arduino
import matplotlib.pyplot as plt

C = 666
beta = 0.28769047280021787 
Dt = 1.0
alpha = 1.05

ard = Arduino('COM3',[24,4,4])

ard.write_float(2,0.0) # temps
ard.write_float(1,0) # PWM
liste_data = []

liste_T = []
last_pwm = 0
N=60
for k in range(N):
    time.sleep(1)
    data = ard.read_float_array(0)
    data = np.append(data,last_pwm)
    print(data)
    liste_data.append(data)
    T = (data[1]+data[2])/2
    liste_T.append(T)
Ti = np.mean(np.array(liste_T)) # température initiale = température extérieure
print("Ti = %f"%Ti)
print("Verser la poudre petit à petit ...")
N = 15*60
for k in range(N):
    time.sleep(1)
    data = ard.read_float_array(0)
    data = np.append(data,last_pwm)
    DT = (data[1]+data[2])/2 - Ti
    if DT>=0:
        pwm = 0
    else:
        pwm = min(beta*np.sqrt(alpha*C*abs(DT)/Dt),0.99)
    ard.write_float(1,pwm)
    last_pwm = pwm
    print(data)
    liste_data.append(data)

ard.write_float(1,0)
ard.close()
data_array = np.array(liste_data)
np.savetxt("dissolutionKCl-eau150mL-12V-4ohms-adapt-1.txt",data_array,header="t(s)\t T1(°C)\t T2(°C)\t P(mW)\t U(V)\t I(mA)\t pwm")
t = data_array[:,0]
T1 = data_array[:,1]
T2 = data_array[:,2]
P = data_array[:,3]

plt.figure()
plt.plot(t,T1)
plt.ylim(10,40)
plt.grid()

plt.show()
        
		 
			 

Voici le résultat pour une dissolution de m=4,74g de KCl :

[t,Ta,Tb,P,U,I,r] = np.loadtxt('dissolutionKCl-eau150mL-12V-4ohms-adapt-1.txt',unpack=True,skiprows=1)
T=(Ta+Tb)/2
figure(figsize=(16,8))
subplot(211)
plot(t,T)
grid()
xlabel('t (s)',fontsize=16)
ylabel('T (°C)',fontsize=16)
ylim(18,21)
xlim(0,400)
subplot(212)
plot(t,P/1e3)
xlim(0,400)
grid()
xlabel('t (s)',fontsize=16)
ylabel('P (W)',fontsize=16)

			 
figGfigG.pdf

Le sel est versé par petites quantités. À chaque fois que le chauffage se met en route, on arrête de le verser jusqu'à arrêt du chauffage.

L'énergie totale fournie est calculée par intégration de la puissance (formule des trapèzes) :

E = 0
for i in range(1,len(P)):
	E += (P[i]+P[i-1])/2
E /= 1e3
m = 4.74
M = 74.55 # masse molaire
			 

La température est constante durant toute la dissolution, égale à la température extérieure. Il n'y a donc pas d'échanges thermiques avec l'extérieur et la puissance fournie compense exactement la puissance négative fournie par la dissolution endothermique. On obtient ainsi l'enthalpie molaire de dissolution de KCl :

print(E/m*M)
--> 17263.20063291139

Cette valeur est à comparer à la valeur de l'enthalpie standard de dissolution ΔrH=18kJmol-1 .

8.c. Puissance de base positive

La méthode générale, permettant d'étudier aussi bien les phénomènes exothermiques qu'endothermiques, consiste à choisir une température cible Tc supérieure à Te. Comme démontré plus haut, un écart d'au moins 6 degrés est nécessaire pour assurer une incertitude relative de la mesure des puissances de l'ordre de 1% ou moins.

Le script suivant permet de piloter l'expérience. Une forte puissance de chauffage (pwm=0.97) est appliquée jusqu'à ce que la température atteigne la température cible (Tc). Pendant 5 min, on laisse fonctionner la régulation pour maintenir la température cible. Comme nous l'avons vu précédemment, nous pouvons faire fonctionner le chauffage en tout ou rien (en raison de la résolution du thermomètre et de la capacité thermique relativement grande du volume d'eau). On a donc des phases de chauffage où la puissance et maximale et des phases où la puissance est nulle. La valeur de P1 est déterminée par moyennage de la puissance mesurée pendant cette phase. Une durée plus grande que 5 min sera peut-être nécessaire pour améliorer la précision. La phase suivante consiste à ajouter la poudre de KCl par petites quantités, en arrêtant de verser à chaque fois que l'alimentation affiche un courant important (environ 2 A dans cette expérience). La régulation de température continue pendant cette phase afin de maintenir la température à la valeur cible.

calorimetrieCompensation.py
import numpy as np
import time
from arduino import Arduino
import matplotlib.pyplot as plt


ard = Arduino('COM3',[24,4,4])

ard.write_float(2,0.0) # temps
ard.write_float(1,0) # PWM
liste_data = []

Tc = 24 # température cible
pwm=0.97
ard.write_float(1,pwm)
T = 19
last_pwm = pwm
while T<Tc:
    time.sleep(1)
    data = ard.read_float_array(0)
    data = np.append(data,last_pwm)
    print(data)
    liste_data.append(data)
    T = (data[1]+data[2])/2

print("Température %0.2f atteinte"%Tc)
# recherche de la puissance pour maintenir Tc
pwm=0.0
ard.write_float(1,pwm)
duree=5*60
liste_pwm = []
for k in range(duree):
    time.sleep(1)
    data = ard.read_float_array(0)
    data = np.append(data,last_pwm)
    DT = (data[1]+data[2])/2 - Tc
    if DT>=0:
        pwm = 0
    else:
        pwm = 0.99
    ard.write_float(1,pwm)
    last_pwm = pwm
    liste_pwm.append(pwm)
    print(data)
    liste_data.append(data)

pwm = np.mean(np.array(liste_pwm))
print("PWM moyen = %f"%pwm)
ard.write_float(1,pwm)
data_array = np.array(liste_data)
np.savetxt("dissolutionKCl-eau150mL-12V-4ohms-adapt-5-start.txt",data_array,header="t(s)\t T1(°C)\t T2(°C)\t P(mW)\t U(V)\t I(mA)\t pwm")

liste_data = []
input("Verser la poudre petit à petit ...")
ard.write_float(2,0.0) # initialisation du temps
duree = 10*60
for k in range(duree):
    time.sleep(1)
    data = ard.read_float_array(0)
    data = np.append(data,last_pwm)
    DT = (data[1]+data[2])/2 - Tc
    if DT>=0:
        pwm = 0
    else:
        pwm = 0.99
    ard.write_float(1,pwm)
    last_pwm = pwm
    print(data)
    liste_data.append(data)

ard.write_float(1,0)
ard.close()
data_array = np.array(liste_data)
np.savetxt("dissolutionKCl-eau150mL-12V-4ohms-adapt-5.txt",data_array,header="t(s)\t T1(°C)\t T2(°C)\t P(mW)\t U(V)\t I(mA)\t pwm")
t = data_array[:,0]
Ta = data_array[:,1]
Tb = data_array[:,2]
P = data_array[:,3]

plt.figure()
plt.plot(t,(Ta+Ta)/2)
plt.ylim(10,40)
plt.grid()

plt.show()


				

Voici une expérience avec m=4,98g. Tout d'abord le chauffage jusqu'à 24 degrés et le maintient de cette température pendant 5 min :

[t,Ta,Tb,P,U,I,r] = np.loadtxt('dissolutionKCl-eau150mL-12V-4ohms-adapt-5-start.txt',unpack=True,skiprows=1)
T=(Ta+Tb)/2
figure(figsize=(16,8))
subplot(211)
plot(t,T)
grid()
xlabel('t (s)',fontsize=16)
ylabel('T (°C)',fontsize=16)
ylim(10,26)
subplot(212)
plot(t,P/1e3)
grid()
xlabel('t (s)',fontsize=16)
ylabel('P (W)',fontsize=16)
P1 = np.mean(P[550:len(P)-1])/1e3
			 
figKfigK.pdf

On peut remarquer que la température initiale, qui est la température extérieure, est particulièrement basse (16°C pour cause de chauffage étteint pendant les vacances). C'est plutôt bien pour cette expérience car pour une température cible de 24 degrés on a 9 degrés d'écart avec l'extérieur. Plus l'écart de température avec l'extérieur est grand, plus le système réagit rapidement en cas de hausse de la température et plus la précision de mesure de puissance est grande.

Voici la puissance moyenne permettant de maintenir la température de 24 degrés :

print(P1)
--> 2.1064727272727275

Dans la seconde partie, on verse progressivement la poudre de KCl :

[t,Ta,Tb,P,U,I,r] = np.loadtxt('dissolutionKCl-eau150mL-12V-4ohms-adapt-5.txt',unpack=True,skiprows=1)
T=(Ta+Tb)/2
figure(figsize=(16,8))
subplot(211)
plot(t,T)
grid()
xlabel('t (s)',fontsize=16)
ylabel('T (°C)',fontsize=16)
ylim(10,26)
subplot(212)
plot(t,P/1e3)
grid()
xlabel('t (s)',fontsize=16)
ylabel('P (W)',fontsize=16)
			 
figLfigL.pdf

Le versement est terminé à t=310s. Voici la puissance moyenne pendant le versement :

tf = 310
Pm = np.mean(P[0:tf])/1e3
			 
print(Pm)
--> 6.122774193548388

L'énergie excédentaire est donc :

E = (Pm-P1)*tf
			 

et l'enthalpie molaire de la dissolution :

m=4.98
			 
print(E/m*M)
--> 18638.30020810515

La valeur obtenue est proche de ΔrH=18kJmol-1 . Il faut remarquer que l'expérience donne l'enthalpie de la dissolution à la température cible (ici 24 degrés). Si on veut que la température cible soit proche de la température ambiante, il faudra compléter le dispositif avec un thermostat froid, de manière à avoir au moins 6 degrés d'écart entre le thermostat et la température cible. Par ailleurs, l'écart de température est surtout important pour l'étude des phénomènes exothermiques, car il faut que le flux thermique vers l'extérieur soit suffisamment fort pour permettre une compensation rapide.

Dans l'expérience précédente, on remarque que la température fluctue légèrement pendant le versement du sel. Voici la courbe de température en détail :

figure(figsize=(16,6))
subplot(211)
plot(t,T)
plot([0,600],[24.06,24.06],'k--')
plot([0,600],[23.94,23.94],'k--')
grid()
xlabel('t (s)',fontsize=16)
ylabel('T (°C)',fontsize=16)
ylim(23,25)
subplot(212)
plot(t,P/1e3)
grid()
xlabel('t (s)',fontsize=16)
ylabel('P (W)',fontsize=16)
			 
figMfigM.pdf

Les deux traits pointillés correspondent à 24±0,06C .

Au début de l'enregistrement, l'écart négatif de température plus important est dû au fait que nous avons, par erreur, oublié de déclencher l'enregistrement au moment de verser les premières pincées de sel. Pour le reste de l'expérience, les variations de température sont dues à la résolution finie du thermomètre (0,06C ).

Quel est l'effet d'un écart de température négatif sur le résultat ? Lorsque la température est inférieure à la température cible, la puissance P1 est plus grande que la puissance qui serait nécessaire pour maintenir cette température. Il s'en suit que les écarts négatifs conduisent à une petite surestimation de l'énergie E.

Les expériences précédentes montrent que l'air de la pièce constitue un bon thermostat car sa température est bien constante pendant la durée de l'expérience. La convection libre autour du bécher semble de dérouler de manière reproductible, mais il faut veiller à éviter des courants d'air au voisinage du bécher. Par ailleurs, des objets chauds au voisinage du bécher (par ex. l'agitateur magnétique) pourraient aussi avoir un effet non négligeable en raison des échanges par rayonnement. Pour faire des mesures à une température de 20 degrés, il faudra cependant mettre en place un thermostat froid (moins de 14 degrés) de manière à garantir une bonne précision des mesures de puissance.

9. Méthode différentielle

La calorimétrie diatherme à compensation de puissance peut être améliorée par une méthode différentielle, qui consiste à déterminer la puissance de base P1 au moyen d'un échantillon de référence. Le dispositif est constitué de deux récipients identiques, l'un est le récipient de mesure, l'autre le récipient de référence. Dans chacun d'eux est placé la même quantité de solvant, un thermomètre et un élément chauffant. Les deux récipients échangent par transfert thermique avec le même thermostat, de température Te. La température cible Tc est la même pour les deux récipients. La puissance de chauffage délivrée dans le récipient de référence est P1, la puissance de compensation en l'absence du phénomène étudié. La puissance délivrée dans le récipient de mesure est P, la puissance de compensation en présence du phénomène étudié. Si celui-ci est exothermique on a P1>P, s'il est endothermique on a P1<P. Comme précédemment, l'énergie excédentaire fournie au récipient de mesure par rapport au récipient de référence est :

E=0tf(P(t)-P1)dt(10)

C'est l'opposée de l'énergie libérée par le phénomène étudié.

L'avantage de la méthode différentielle est l'obtention simultanée des deux puissances, alors qu'elles sont déterminées en deux phases successives dans la méthode simple. Par ailleurs, cette méthode permet de faire varier la température cible au cours du temps (de manière très lente) : on obtient ainsi un calorimètre différentiel à balayage à compensation de puissance. La variation de température permet d'accéder aux mesures de capacités thermiques, ce que ne permet pas de faire la méthode simple. En effet, si le contenu du récipient de mesure a une capacité thermique plus grande que le contenu du récipient de référence, une élévation de température donnée demandera plus de puissance de chauffage dans le récipient de mesure.

Creative Commons LicenseTextes et figures sont mis à disposition sous contrat Creative Commons.