ACS712 in corrente alternata...

tema già trattato, tuttavia voglio sottoporre un problema che ho avuto.

utilizzo sensori da 5A per misurare l’assorbimento di pompe sommerse (ca. 3-3,5A 220v).

per la misura ho utilizzato il metodo classico quadratico medio col codice proposto qui

https://forum.arduino.cc/index.php?PHPSESSID=hcck3ttk3h8fsirvp454fdgqd1&topic=179541.msg1334313#msg1334313

La routine sembra funzionare, tuttavia ho dovuto correggere il fattore di conversione, fattore legato al sensore a seconda che sia da 5, 20, o 30A, laddove il codice riporta
“float rms = sqrt((float)currentAcc/(float)numSamples) * (75.7576 / 1024.0);”

esso si calcola rispettivamente :
5v/0,185V = 27,03
5v/0,100V = 50
5v/0,066V = 75,76

confrontando tuttavia i valori di corrente I calcolati dalla routine con quelli misurati con multimetro digitale (uso un UNI-T 61D), risulta una notevole differenza.
Col multimetro risulta una corrente di 3,2A mentre la routine mi fornisce un valore molto diverso, più basso tipo 1,9A. Ho rozzamente aumentato il fattore di conversione (nel caso specifico usavo un sensore da 5A e il fattore di conversione l’ho dovuto portare da 27,03 a 37 per ottenere la stessa misura).

Ovviamente non mi piace questa rozza pezza, anche perché non so se cmq sia rispettata la linearità della lettura, per cui vorrei cercare di capire da cosa può dipendere questa differenza?

secondo voi da cosa può dipendere?

Per calcolare il valore efficace della corrente con precisione, bisogna considerare un periodo di campionamento multiplo della periodo della corrente a 50Hz, cioè multiplo di 20ms (ad esempio 100ms).

Il campionamento di Arduino arriva da un massimo di 9000 sample/s, ma può essere meno se si fanno operazioni floating point e comunque potrebbero esserci degli interrupt che rallentano il campionamento.

Quindi è meglio contare le letture ed usare questo conteggio per dividere la somma dei quadrati delle letture meno l'offset (512).

nel codice suddetto si campiona un periodo totale di 100mS, quindi circa 5 cicli, per un totale di 250 campioni (cioè 25 campioni per semionda) Il che significa che si fa un campione ogni 400uS (2500Hz).
Mentre arduino riesce a campionare ogni ca. 111uS (9000Hz).
Secondo il teorema di Cannon per mantenere l'informazione occorre campionare con frequenza minimo doppia (2x50Hz) qui siamo a 2500Hz, quindi ad occhio entro i 9000Hz ci dovremmo stare larghi (anche calcolando interrupt e altro).

Quindi è meglio contare le letture ed usare questo conteggio per dividere la somma dei quadrati delle letture meno l'offset (512)

Non capisco, il codice che ho preso non lo fa?
fa una lettura ogni 400uS, ne calcola il quadrato, e ripete per 250 volte.

alla fine delle 250 letture prende la somma dei quadrati lo divide per 250, ed estrae la radice.

Se arduino arriva a 9000 cicli al secondo, dici che floating e interrupt possono abbassare questa frequenza sotto i 5000, creando cosi un errore di misura?

Aggiungo una mia riscrittura della routine. Utilizzando il delayMicroseconds() e un ciclo FOR, elimino diverse variabili e righe di codice attimizzandolo, o sbaglio?

const int currentPin = 3;
const unsigned long sampleTime = 400UL;     // micro seconds, 
const unsigned long numSamples = 200UL;     // number of total samples 
const int adc_zero = 511;                   // relative digital zero of the arudino input from ACS712

void setup()
{
 Serial.begin(9600);
}

void loop()
{
 unsigned long currentAcc = 0;
 unsigned int count = 0;

 for (count=0; count <= numSamples; i++){
     int adc_raw = analogRead(currentPin) - adc_zero;
     currentAcc += (unsigned long)(adc_raw * adc_raw);
     delayMicroseconds(400);
   } 
 float rms = sqrt((float)currentAcc/(float)numSamples) * (75.7576 / 1024.0);
 rms = 0.05 * (int) (rms / 0.05); // arrotondamento ai 50mA
 Serial.println(rms);
}

ho aggiunto anche un arrotondamento ai 50mA, in quanto la risoluzione A/D è di 39mA/step.

Un segnale a 50 Hz deve essere campionato per lo meno a 100 Hz, ma se particolarmente "sporco" meglio campionare ad una frequenza superiore.

In qualsiasi caso, la frequenza massima di campionamento di Arduino (9000 sample/s) è più che sufficiente.

Il problema è che è difficile determinare a priori quante letture possono essere fatte in 100ms (5 periodi) a causa del tempo di loop che può essere contaminato da interrupt.

Ecco perché ho detto di contare le letture effettive ed usare il valore nella divisione.

ok.
Ho riprovato il tutto con la mia routine. Funziona ma la lettura rimane difforme da quella del multimetro (il multimetro è un uni-t UT61D true RMS).

ho provato con un asciugacapelli.
posizione 1: tester= 2,00A / arduino= 2,60A
posizione 2: tester= 3,60A / arduino= 3,45A

Oltre a differire si nota una notevole non-linearità nella lettura. Non riesco a capire da cosa possa dipendere. Provo a cambiare drasticamente il numero di campioni, anche se 50 per ciclo dovrebbero fornire una misura abbastanza accurata.

No, campioni per 100ms contando i sample ed usando il valore per calcolare la media.

dunque ho fatto come dici, ho riscritto la routine. Fa tutti i campioni che riesce a fare in 80mS (4 cicli della 50Hz). dopodiche divide la somma dei quadrati per il numero di campioni contati, ecc…

il codice è questo

const int currentPin = 3;
const unsigned long sampleTime = 80UL;  // 80mS seconds,
const int adc_zero = 511;               // relative digital zero of the arudino input from ACS712

void setup()
{
  Serial.begin(9600);
  while (!Serial) {}
}

void loop() {
  
  unsigned long numSamples = 0;
  unsigned long currentAcc = 0;
  unsigned long prevMillis = millis();
  while (millis() - prevMillis <= sampleTime)
  {
    int adc_raw = analogRead(currentPin) - adc_zero;
    currentAcc += (unsigned long)(adc_raw * adc_raw);
    ++numSamples;
  }
float rms = sqrt((float)currentAcc / (float)numSamples) * (50 / 1024.0);
rms = 0.05 * (int) (rms / 0.05); // arrotondamento ai 50mA

Serial.println(rms);
delay (500);

}

Il risultato è quasi come prima
ho provato con un asciugacapelli.
posizione 1: tester= 2,00A / arduino= 2,45A
posizione 2: tester= 3,60A / arduino= 3,45A

la differenza rispetto a prima è che la lettura è molto piu stabile, mentre prima dava numeri ballerini (es. 3,45, poi 3,50, poi 3,55…). Comincio quindi a pensare che questa routine è migliore, ma non era il problema che infatti persiste.

siamo punto e a capo.

il mistero si infittisce.
Ho fatto qualche altra prova. Prima ho provato altri sensori di altra marca, sempre da 20A. la sostanza non cambia.

Allora ho cambiato il carico di prova. Ho preso un asciugacapelli con due interruttori velocità (1,2) e calore (0,1,2), in tal modo posso disporre di piu valori di corrente progressivi con cui effettuare un confronto. Infatti il multimetro legge:

v1c0 = 1,04 Amp
v2c0 = 1,36
v1c1 = 2,82
v2c1 = 3,76
v1c2 = 4,93
v2c2 = 6,35

la corrispondente lettura del sensore acs712 mi ha lasciato di stucco

v1c0 = 1,35
v2c0 = 1,35
v1c1 = 2,95
v2c1 = 4,30
v1c2 = 5,00
v2c2 = 3,70

come si vede mentre i valori centrali pur se con molta approssimazione e non linearità, tornano, i due valori estremi v1c0 e v2c2 soprattutto sono completamente sballati (ma anche il v2c1 non scherza).
Ricambiato sensore, ma stessa lettura.
Sono perplesso. Che sia un problema di compatibilità del acs712 con i carichi induttivi?

Prova ad usare:

float rms = sqrt(currentAcc / numSamples) * 50 / 1023.0;

ed elimina l'arrotondamento

fatto. Non cambia, stesse letture.

Ad alimentazione dl circuito come sei messo?..visto che si tratta pur sempre di una lettura analogica tra il sensore e l'atmega potrebbe essere influenzata da qualche cosa...ti consiglio di dare uno sguardo sul sito OpenEnergy dove si può facilmente recuperare la libreria EmonLib che tiene conto di qualche parametro in più per il calcolo del consumo in alternata tramite un sensore ed effetto Hall o trasformatore amperometrico...più tardi a casa ti posso postare uno sketch che utilizzavo su un Attiny e,tra tutti i metodi provati,risulatava il più preciso con l'utilizzo di tale libreria

Mi lascia perplesso l'istruzione (non conosco bene il C)

++numSamples;

che dovrebbe essere

numSamples ++;

Può essere che il phon abbia un carico induttivo (il motore) che altera la misura: per essere certi che il carico sia resistivo, prova ad usare delle lampadine ad incandescenza da 100W in parallelo oppure una stufetta (non termoconvettore che ha anch'esso un motore) da 1-2kW.

in particolare dalle prove da me effettuate ho visto che non sempre il 511 che si toglie per eliminare l’offset corrisponde veramente a 2,5 volt…ecco perché ti ho suggerito quella libreria che inizialmente controlla la tensione di aliementazione dell’atmega e di conseguenza il riferimento dell’adc…saluti

ibbba:
in particolare dalle prove da me effettuate ho visto che non sempre il 511 che si toglie per eliminare l'offset corrisponde veramente a 2,5 volt...ecco perché ti ho suggerito quella libreria che inizialmente controlla la tensione di aliementazione dell'atmega e di conseguenza il riferimento dell'adc..saluti

ti ringrazio, ma la variabile di offset cmq sposta poco e niente, parliamo di +/-40mA per tutte le letture e non spiega le differenze tra una lettura e l'altra. cmq ho risolto semplicemente facendo una lettura senza carico (a riposo) in fase di setup.

cyberhs:
Mi lascia perplesso l’istruzione (non conosco bene il C)

++numSamples;

che dovrebbe essere

numSamples ++;

Può essere che il phon abbia un carico induttivo (il motore) che altera la misura: per essere certi che il carico sia resistivo, prova ad usare delle lampadine ad incandescenza da 100W in parallelo oppure una stufetta (non termoconvettore che ha anch’esso un motore) da 1-2kW.

ti riporto una correzione al codice, l’istruzione “int adc_raw” introduceva una troncatura di alcuni valori piu alti
adesso infatti l’ultimo valore è cambiato il 6,70, che per lo meno è congruente.
Permangono tuttavia degli scostamenti che introducono errore di non linearità forte.

ecco il codice completo corretto

const int currentPin = 3;
const unsigned long sampleTime = 80UL;  // 80mS seconds sampling,
int adc_zero = 512;

void setup()
{
  int adc_zero = analogRead(currentPin);   // relative digital auto-zero (non necessario)
  Serial.begin(9600);
  while (!Serial) {}
  Serial.println(adc_zero);
}

void loop() {

  unsigned long numSamples = 0;
  unsigned long currentAcc = 0;
  unsigned long prevMillis = millis();
  while (millis() - prevMillis <= sampleTime)
  {
    long adc_raw = analogRead(currentPin) - (long) adc_zero;
    currentAcc += adc_raw * adc_raw;
    numSamples++;
  }
  float rms = sqrt(currentAcc / numSamples) * 50.00 / 1024.00;
 
  Serial.println(rms);
  delay (500);
}

ti allego la libreria che utilizzo io con un ACS712 da 20A…per utilizzarla la includi,poi:

EnergyMonitor emon1;             // Create an instance

void setup() {
           emon1.current(ingressoACSPin, 9.5);       // Current: input pin, calibration(devi cambiare il valore             di calibrazione in base al tuo sensore)
}

void loop{
        double Irms = emon1.calcIrms(1480, 0.10);  
}

dove 1480 è il numero dei campioni e lo 0.10 è una aggiunta che ho fatto per avere la possibilità di un offset del valore restituito per aderire ancora di più al valore restituito da una pinza amperometrica estremamente precisa.
Prova,almeno vedi se può essere un problema di sketch o altro,visto che così deve funzionare senza problemi.
Saluti

EmonLib.zip (12.8 KB)

grazie. faro' sicuramente delle prove. Perchè preferisco risolvere la cosa via sw che aggiungere altro hw (op-amp full rectifier, autocostruito per giunta). Un progetto meno pezzi ha e più è affidabile.

Chissà come mai non hanno mai fabbricato un sensore apposito per AC, fanno 2000 sensori diversi, basta aggiungere un opamp e pochi componenti.... mah!

Il codice che ho proposto sopra, ora sembra funziona bene, anche se persistono degli scostamenti, ma che sembrano dipendere da una certa sensibilità del sensore hall agli sfasamenti da carichi non del tutto resistivi. Sembra.

cmq se la tua lib si rivela migliore tanto meglio. Se invece mi da - per lo stesso apparato di prova - gli stessi valori, allora si rafforza il sospetto che non sia un problema sw.

Appena ho fatto la prova postero' qui il risultato in dettaglio. A presto.

AGGIORNAMENTO
ho fatto un ulteriore tentativo con una funzione del tutto diversa, in questo caso trovo il valore Vpp (picco-picco) e lo trasformo in RMS moltiplicandolo per 0,707. Il risultato finalmente mi soddisfa. Il che significa che che l’altra funzione non va proprio. Deve avere dei problemi di calcolo.

questa la nuova funzione, che ho trovato in rete e rielaborata

const int pinI = A3;
float IA       = 0;

void setup() {
  Serial.begin(9600);
  while (!Serial) {}
}

void loop() {
  IA = misura(pinI);
  Serial.println(IA);
  delay (500);
}

float misura (int pinI) {
  int maxValue = 0;      // store max value
  int minValue = 1023;   // store min value
  unsigned long start_time = millis();

  while (millis() - start_time < 80) {  //sample for 80 mSec
    int readValue = analogRead(pinI);
    if (readValue > maxValue) maxValue = readValue;
    if (readValue < minValue) minValue = readValue;
  }
  float Vrms = (((maxValue - minValue) * 5.0 / 1024.0) / 2.0) * 0.707;
  float AMP = (Vrms * 1000) / 100; // 100 x sens 20A; 185 x sens 5A
  //AMP = 0.05 * (int) ( (AMP) / 0.05); // arrotondamento ai 50mA (facoltativo)
  return AMP;
}

PRIMA. (altro codice vedi prec. post)
…Multim. / Arduino
v1c0 = 1,04 - 1,35 = + 310mA
v2c0 = 1,36 - 1,35 = - 10mA
v1c1 = 2,82 - 2,95 = + 130mA
v2c1 = 3,76 - 4,30 = + 540mA
v1c2 = 4,93 - 5,00 = + 70mA
v2c2 = 6,35 - 6,70 = + 350mA

ADESSO
…Multim. / Arduino
v1c0 = 1,05 - 0,98 = - 70mA
v2c0 = 1,38 - 1,38 = 0 mA
v1c1 = 2,85 - 2,78 = - 70mA
v2c1 = 3,75 - 3,56 = - 190mA
v1c2 = 4,97 - 4,93 = - 40mA
v2c2 = 6,42 - 6,35 = - 70mA

adesso si comincia a ragionare, e per l’impiego che ne faccio, questo scostamento è accettabile. Stiamo pur sempre parlando di uno scostamento massimo del 5% (190/3750).

PS consiglio di modificare il condensatore di filtro del modulo ACS712 portandolo almeno a 470nF.

non ho invece ancora provato la libreria EmonLib.

Per favore puoi indicare lo schema ed il fondo scala del sensore ACS712 (5, 20 o 30A) che stai usando?

La tecnica del valore di picco funziona solo con carichi puramente resistivi, mentre il calcolo del RMS è valido per tutti i tipi di carico.