Errore relativo, assoluto e double

Buon giorno a tutti,
ho un problema con le variabili di tipo Double e Float, in condizioni normali hanno entrambe 2 cifre decimali anche se sono a virgola mobile. Nel calcolare l'errore relativo dell'oscillazione di un pendolo, dato dall'errore assoluto nel tempo fratto il tempo stesso (), un calcolo che richiede moltissime cifre decimali, mi sono bloccato perché sull'LCD compaiono sempre 2 cifre soltanto dopo la virgola, in ogni caso 0.00, quando le cifre che mi servirebbero sono molte di più. Questo sia con float che con double. Nella ricerca di una soluzione ho provato a fare prima val = 0.000000, cosa che non ha cambiato le cose.
Spero ci sia una soluzione, a meno che virgola mobile non voglia dire un'altra cosa.

Cominciamo con il dire che su Arduino (AVR) il float ed il double sono esattamente la stessa cosa, rappresentazione in floating point a 32 bit.

Il reference chiarisce molto bene che :

Floats have only 6-7 decimal digits of precision. That means the total number of digits, not the number to the right of the decimal point. Unlike other platforms, where you can get more precision by using a double (e.g. up to 15 digits), on the Arduino, double is the same size as float.

Chiarito questo, ovvero che tra parte intera e parte decimale, comunque NON puoi avere più di 6 o 7 cifre, per il tuo problema, in cui vedi solo e sempre due decimali ... ancora una volta la risposta è ... studia il reference poiché i vari metodi print() hanno dei parametri di "formattazione" ed il default, per i float, è due cifre decimali.

Guglielmo

Sì, la reference la avevo già letta, ora ho provato anche con Serial.println(val, 5) stampando 5 cifre dopo la virgola, ma le cose con cambiano, l'errore relativo esce 0.00000 invece di 0.00

 lcd.clear();
  lcd.print("P. t: ");
  lcd.print(periodTime);
  lcd.print(" ms");
  lcd.setCursor(0, 2);
  lcd.write(byte(0));
  lcd.print(1);
  lcd.print(" ms, ");
  lcd.write(1);
  lcd.print("=");
  relativeError = 1/periodTime;
  lcd.print(relativeError, 5);
  Serial.print("Relative error: ");
  Serial.print(relativeError, 5);

Dove lcd.write(byte(0)) è il simbolo dell'errore relativo. Sul'LCD esce come sulla seriale, 0.00000, quindi non fa proprio il conto. La variabile relativeError è un float. Cos'è allora che non mi restituisce la divisione?

Con un pezzo di programma non ci facciamo nulla, non abbiamo "la palla di vetro" ...

... posta l'intero programma, tutti i valori che stampano le lcd.print(), e cosa vorresti ottenere.

Guglielmo

#include <LiquidCrystal.h>

LiquidCrystal lcd(12, 11, 5, 4, 3, 2);

/* PINs SETTINGs */
const byte ballRelease = 7;   //Release sensor
const byte first = 8;         //1st sensor
const byte second = 9;        //2nd sensor
const byte button = 10;       //Main button
const byte button2 = 6;       //Auxiliary button

/* TIME VARIABLEs */
unsigned long startingTime = 0;   //The ballRelease time
unsigned long firstTime = 0;      //The time of the first part of the period
unsigned long secondTime = 0;     //The time of the second part of the period
unsigned long periodTime = 0;     //The total time of the period

/* PHYSICS */
float relativeError = 0.00000;
float gravity = 0.00000;

/* CREATE TWO NEW CHARACTERs */
byte moreOrLess[8] = {
  0b00000,
  0b00100,
  0b00100,
  0b11111,
  0b00100,
  0b00100,
  0b11111,
  0b00000
};

byte error[8] = {
  0b00110,
  0b01001,
  0b00100,
  0b01000,
  0b10000,
  0b10010,
  0b01100,
  0b00000
};

void setup() {
  lcd.begin(16, 2);     //Initialize the LCD

  Serial.begin(115200);

  pinMode(ballRelease, INPUT);   //ballRelease SENSOR
  pinMode(first, INPUT);    //1st SENSOR
  pinMode(second, INPUT);   //2nd SENSOR
  pinMode(button, INPUT);   //START BUTTON
  pinMode(button2, INPUT);  //2nd BUTTON

  lcd.createChar(0, moreOrLess);
  lcd.createChar(1, error);
  lcd.clear();
  lcd.print("Ready!");
  lcd.setCursor(0, 1);
  lcd.print("Press the button");
}

void loop() {
  if (digitalRead(button) == HIGH) {  //If you press the button the board start the measurement
    startMeasure();
  }
  if (Serial.available()) {
    byte received = Serial.parseInt();
    if (received == 1) {    //If the board receive '1' start the measurement
      startMeasure();
    }
  }
}

void startMeasure() {   //Measurement void
  lcd.clear();
  lcd.blink();
  lcd.print("Starting...");
  Serial.println("Starting...");    //Hey! I'm starting!
  startingTime = millis();    //Save the starting time

  while (digitalRead(ballRelease) == LOW) {    //Release the sphere and start
    ;
  }

  firstTime = millis();                       //Save the time after the relasing of the sphere
  startingTime = firstTime - startingTime;    //Relasing time
  Serial.print("Starting time: ");
  Serial.print(startingTime);                 //Print the starting time on the computer
  Serial.println(" ms");
  lcd.clear();
  lcd.print("S. t: ");
  lcd.print(startingTime);
  lcd.print(" ms");
  lcd.setCursor(0, 1);

  while (digitalRead(first) == LOW) {   //Wait until the sphere is down on the 1st sensor
    ;
  }

  secondTime = millis();    //Save the time
  firstTime = secondTime - firstTime;   //Calculate the time of the first part of the period

  while (digitalRead(second) == LOW) {  //wait until the sphere go on the 2nd sensor
    ;
  }

  secondTime = millis() - secondTime;     //Calculate the time of the second part of the period
  periodTime = firstTime + secondTime;    //Calculate the total time of the period
  Serial.print("Period time: ");
  Serial.print(periodTime);
  Serial.println(" ms");
  lcd.print("P. t:");
  lcd.print(periodTime);
  lcd.println(" ms");

  while (digitalRead(button2) == LOW) {
    ;
  }
  lcd.clear();
  lcd.print("P. t: ");
  lcd.print(periodTime);
  lcd.print(" ms");
  lcd.setCursor(0, 2);
  lcd.write(byte(0));
  lcd.print(1);
  lcd.print(" ms, ");
  lcd.write(1);
  lcd.print("=");
  relativeError = 1/periodTime;
  lcd.print(relativeError, 5);
  Serial.print("Relative error: ");
  Serial.println(relativeError, 5);
  
  while (digitalRead(button2) == LOW) {  //DA QUI DEVO FINIRE
    ;
  }
  

  lcd.noBlink();
}

Dovrei ottenere all'inizio il tempo di rilascio che l'utente ha impiegato ed il tempo di oscillazione. Successivamente alla pressione del secondo pulsante vengono visualizzati il tempo, l'errore assoluto (corrispondente alla sensibilità dello strumento, 1 ms per Arduino) e l'errore relativo, e/T.

Mmmm ...
1 è un numero intero, periodTime è un altro intero ... come pretendi che la divisione di due interi ti dia un risultato decimale ? :o

Che ne dici di usare 1.0 per indicare un float e per le variabili utilizzare gli operatori cast ?

Guglielmo

Ok, quindi

relativeError = 1.0 / (float)periodTime;

se ho capito bene, giusto?

Ok, funziona bene ora, peccato per la comparsa di un nuovo errore:

  secondTime = millis() - secondTime;     //Calculate the time of the second part of the period
  periodTime = firstTime + secondTime;    //Calculate the total time of the period
  Serial.print("Period time: ");
  Serial.print(periodTime);
  Serial.println(" ms");
  lcd.print("P. t:");
  lcd.print(periodTime);
  lcd.println(" ms");

//ERRORE DELLE TRE LINNE???

  while (digitalRead(button2) == LOW) {
    ;
  }
  lcd.clear();
  lcd.print("P. t: ");
  lcd.print(periodTime);
  lcd.print(" ms");
  lcd.setCursor(0, 2);
  lcd.write(byte(0));
  lcd.print(1);
  lcd.print(" ms,");
  lcd.write(1);
  lcd.print("=");
  relativeError = 1.0 / (float)periodTime;
  lcd.print(relativeError, 5);
  Serial.print("Relative error: ");
  Serial.println(relativeError, 6);
  lcd.noBlink();

Compaiono due caratteri strani che precedentemente non erano presenti... si tratta di un carattere composto da 4 linee parallele, cosa significa?

Come al solito ... continui a credere che noi abbiamo "la sfera di cristallo" :smiley: :smiley: :smiley:

Dove compare quel carattere ?
In quale print ?
Quale riga del codice ?
Cosa stampi ?
Che valore dovrebbe avere la variabile ?

Possibile che bisogna sempre tirare fuori le parole con le pinze ... ::slight_smile:

Guglielmo

#include <LiquidCrystal.h>

LiquidCrystal lcd(12, 11, 5, 4, 3, 2);

/* PINs SETTINGs */
const byte ballRelease = 7;   //Release sensor
const byte middle = 8;         //1st sensor
const byte second = 9;        //2nd sensor

const byte ballReleaseGravity = 14;
const byte secondGravity = 15;

const byte button = 10;       //Main button
const byte button2 = 6;       //Auxiliary button

/* TIME VARIABLEs */
unsigned long startingTime = 0;   //The ballRelease time
unsigned long firstTime = 0;      //The time of the first part of the period
unsigned long secondTime = 0;     //The time of the second part of the period
unsigned long periodTime = 0;     //The total time of the period

/* PHYSICS */
float relativeError = 0.00000;
float gravity = 0.00000;

/* CREATE TWO NEW CHARACTERs */
byte moreOrLess[8] = {
  0b00000,
  0b00100,
  0b00100,
  0b11111,
  0b00100,
  0b00100,
  0b11111,
  0b00000
};

byte error[8] = {
  0b00110,
  0b01001,
  0b00100,
  0b01000,
  0b10000,
  0b10010,
  0b01100,
  0b00000
};

void setup() {
  lcd.begin(16, 2);     //Initialize the LCD

  Serial.begin(115200);

  pinMode(ballRelease, INPUT);   //ballRelease SENSOR
  pinMode(middle, INPUT);    //1st SENSOR
  pinMode(second, INPUT);   //2nd SENSOR
  pinMode(button, INPUT);   //START BUTTON
  pinMode(button2, INPUT);  //2nd BUTTON

  lcd.createChar(0, moreOrLess);
  lcd.createChar(1, error);
  lcd.clear();
  lcd.print("Ready!");
  lcd.setCursor(0, 1);
  lcd.print("Press the button");
}

void loop() {
  if (digitalRead(button) == HIGH) {  //If you press the button the board start the measurement
    periodTimeFinder();
  } else if (digitalRead(button2) == HIGH) {
    gravityFinder();
  }

  if (Serial.available()) {
    byte received = Serial.parseInt();
    if (received == 1) {    //If the board receive '1' start the measurement
      periodTimeFinder();
    } else if (received == 2) {
      gravityFinder();
    } else if (received == 3) {
      Serial.println("Coming soon...");
    } else if (received == 100) {
      Serial.println("***Commands list***");
      Serial.println("1: period time calculator");
      Serial.println("2: gravitational acceleration calculator");
      Serial.println("3: diagnostic service");
      Serial.println("100: commands list");
    } else {
      Serial.println("Unidentified command. Send 100 for a list of commands.");
    }
  }
}

void periodTimeFinder() {   //Measurement void
  lcd.clear();
  lcd.blink();
  lcd.print("Starting...");
  Serial.println("Starting...");    //Hey! I'm starting!
  startingTime = millis();    //Save the starting time

  while (digitalRead(ballRelease) == LOW) {    //Release the sphere and start
    ;
  }

  firstTime = millis();                       //Save the time after the relasing of the sphere
  startingTime = firstTime - startingTime;    //Relasing time
  Serial.print("Starting time: ");
  Serial.print(startingTime);                 //Print the starting time on the computer
  Serial.println(" ms");
  lcd.clear();
  lcd.print("S. t: ");
  lcd.print(startingTime);
  lcd.print(" ms");
  lcd.setCursor(0, 1);

  while (digitalRead(middle) == LOW) {   //Wait until the sphere is down on the 1st sensor
    ;
  }

  secondTime = millis();    //Save the time
  firstTime = secondTime - firstTime;   //Calculate the time of the first part of the period

  while (digitalRead(second) == LOW) {  //wait until the sphere go on the 2nd sensor
    ;
  }

  secondTime = millis() - secondTime;     //Calculate the time of the second part of the period
  periodTime = firstTime + secondTime;    //Calculate the total time of the period
  Serial.print("Period time: ");
  Serial.print(periodTime);
  Serial.println(" ms");
  lcd.print("P. t:");
  lcd.print(periodTime);
  lcd.println(" ms");

//qui vengono visualizzate tre linee sospette parallele di origine sconosciuta, dopo lcd.println(" ms")
//P.t: [periodo] ms≡≡ è quello che viene scritto sull'LCD

  while (digitalRead(button2) == LOW) {
    ;
  }
  lcd.clear();
  lcd.print("P. t: ");
  lcd.print(periodTime);
  lcd.print(" ms");
  lcd.setCursor(0, 2);
  lcd.write(byte(0));
  lcd.print(1);
  lcd.print(" ms,");
  lcd.write(1);
  lcd.print("=");
  relativeError = 1.0 / (float)periodTime;
  lcd.print(relativeError, 5);
  Serial.print("Relative error: ");
  Serial.println(relativeError, 6);
  lcd.noBlink();
  deleteValues();
}

void gravityFinder() {
  lcd.clear();
  lcd.blink();
  lcd.print("Starting... (gravity)");
  Serial.println("Starting... (gravity)");    //Hey! I'm starting!
  startingTime = millis();    //Save the starting time

  while (digitalRead(ballReleaseGravity) == LOW) {    //Release the sphere and start
    ;
  }

  firstTime = millis();                       //Save the time after the relasing of the sphere
  startingTime = firstTime - startingTime;    //Relasing time
  Serial.print("Starting time: ");
  Serial.print(startingTime);                 //Print the starting time on the computer
  Serial.println(" ms");
  lcd.clear();
  lcd.print("S. t: ");
  lcd.print(startingTime);
  lcd.print(" ms");
  lcd.setCursor(0, 1);

  while (digitalRead(middle) == LOW) {   //Wait until the sphere is down on the 1st sensor
    ;
  }

  secondTime = millis();    //Save the time
  firstTime = secondTime - firstTime;   //Calculate the time of the first part of the period

  while (digitalRead(secondGravity) == LOW) {  //wait until the sphere go on the 2nd sensor
    ;
  }

  secondTime = millis() - secondTime;     //Calculate the time of the second part of the period
  periodTime = firstTime + secondTime;    //Calculate the total time of the period
  Serial.print("Period time: ");
  Serial.print(periodTime);
  Serial.println(" ms");
  lcd.print("P. t:");
  lcd.print(periodTime);
  lcd.println(" ms");

  while (digitalRead(button2) == LOW) {
    ;
  }
  lcd.clear();
  lcd.print("P. t: ");
  lcd.print(periodTime);
  lcd.print(" ms");
  lcd.setCursor(0, 2);
  lcd.write(byte(0));
  lcd.print(1);
  lcd.print(" ms,");
  lcd.print("g=");
  gravity = ((4.0 * pow(3.141592, 2)) * 50.0) / pow((float)periodTime, 2); //l=50.0
  lcd.print(gravity, 5);
  Serial.print("Gravitational acceleration: ");
  Serial.println(gravity, 6);
  lcd.noBlink();
  deleteValues();
}

void deleteValues() {
  startingTime = 0;
  firstTime = 0;
  secondTime = 0;
  periodTime = 0;
  relativeError = 0.00000;
  gravity = 0.00000;
}

"P.t: [periodo] ms≡≡" è quello che viene scritto sull'LCD. (Viene visualizzato dove ho messo il commento)

Io non vedo tra i metodi della LiquidCristal il metodo println(), vedo solo il print() ... sicuro che sia utilizzabile ? ::slight_smile:

Guglielmo

Ok... risolto, grazie dell'aiuto. println non c'è più, chissà perché il compilatore non avvisa, le 2 barrette parallele non credo siano contemplate nella libreria.

... ho idea che in realtà la println() c'è (... visto che quella classe probabilmente deriva dalla Stream), ma il problema è che ... il display male interpreta il CR/LF (0x0D, 0x0A) inviato appunto dalla println() dopo aver stampato il valore che gli dici di stampare :wink:

Guglielmo

Però nel reference dicono anche che...

On the Arduino Due, doubles have 8-byte (64 bit) precision.

Parlano della Due.
Forse anche nelle altre nuove schede...

paulus1969:
Però nel reference dicono anche che...

Parlano della Due.
Forse anche nelle altre nuove schede...

Paulus1969 ... leggi BENE quello che io ho scritto ...

gpb01:
Cominciamo con il dire che su Arduino (AVR) il float ed il double sono esattamente la stessa cosa

... AVR, non SAM, ARM, o altre board a ... 32 bit :smiling_imp:

Guglielmo

paulus1969:
Parlano della Due.
Forse anche nelle altre nuove schede...

Su micro/mcu otto bit nessun compilatore usa i double per due semplicissimi motivi, primo l'impegno della ram visto che ogni numero occupa otto byte, secondo la velocità di calcolo che già è bassa con i float, con i double diventa inaccettabile.
Ti rammento che ogni operazione che coinvolge valore più grandi della word gestita dal micro richiede molti cicli macchina per essere gestita visto che le operazioni vengono comunque fatte a 1 byte per volta.
Anche se presenti i double, come dichiarazione, per motivi di compatibilità del codice questi vengono comunque utilizzati come dei normale float a 32 bit.
Esistono delle librerie per gestire numeri molto grossi, però esclusivamente come interi e solo per le quattro operazioni matematiche, non è possibile usare funzioni trigonometriche, logaritmi, radici, etc.

gpb01:
Paulus1969 ... leggi BENE quello che io ho scritto ...

:blush:

Non mi sono espresso bene, non volevo contraddirti, volevo dire che se qualcuno avesse bisogno di usare variabili double, può fare ricorso alle nuove schede.

paulus1969:
Non mi sono espresso bene, non volevo contraddirti, volevo dire che se qualcuno avesse bisogno di usare variabili double, può fare ricorso alle nuove schede.

Le nuove schede è troppo generico, dovevi dire di usare la DUE o la ZERO che usano processori a 32 bit e supportano i double, oppure la Teensy 3.x che è compatibile al 99.99% con Arduino incluso il 5V tolerant.

Hai ragione.

P.S.
Questa Teensy prima o poi la provo, mi sta incuriosendo.