Go Down

### Topic: Smoothing improved (Read 352 times)previous topic - next topic

#### antoniodv

##### Dec 10, 2019, 09:41 am
Hello,
I improved library "Smoothing" scketch. It remove abnormal and not signficative peaks.
I tested it with a (very bad) LM35 sensor.

Observe green line.

IMPROVED

NORMAL

Code: [Select]
`/*  Smoothing_improved  Reads repeatedly from an analog input, calculating a running average and  printing it to the computer. Keeps ten readings in an array and continually  averages them.  The circuit:  - analog sensor (potentiometer will do) attached to analog input 0  created 22 Apr 2007  by David A. Mellis  <dam@mellis.org>  modified 9 Apr 2012  by Tom Igoe  improved (remove abnormal peak) 10 Dec 2019   by Antonio De Vincentiis <https://devincentiis.it)  This example code is improved of the the public domain.  http://www.arduino.cc/en/Tutorial/Smoothing*/// Define the number of samples to keep track of. The higher the number, the// more the readings will be smoothed, but the slower the output will respond to// the input. Using a constant rather than a normal variable lets us use this// value to determine the size of the readings array.const int numReadings = 10;float readings[numReadings];      // the readings from the analog inputint readIndex = 0;                // the index of the current readingfloat total = 0;                  // the running totalfloat average = 0;                // the averageint continuativePeak = 0;         // countinuos peak sequence counterint previousPeakIndex = 0;        // readIndex of last peak       bool noSmoothing=false;           // active - deactive delete peak valueint inputPin = A0;void setup() {  // initialize serial communication with computer:  Serial.begin(9600);  // initialize all the readings to 0:  for (int thisReading = 0; thisReading < numReadings; thisReading++) {    readings[thisReading] = 0;  }}void loop() {  // read from the sensor:  float readValue=analogRead(inputPin)/5;   // float readValue=analogRead(inputPin)/5; // example LM35 temperature sensor  if (!(abs(readValue)/abs(readValue-average)<20&&noSmoothing)){      // value accepted only if the difference with the average     // is between 0 and 5% (<20)      // subtract the last reading:    total -= readings[readIndex];    readings[readIndex] = readValue;    // add the reading to the total:    total += readings[readIndex];    // advance to the next position in the array:    readIndex++;    // if we're at the end of the array...    if (readIndex >= numReadings) {      // ...wrap around to the beginning:      readIndex = 0;      noSmoothing=true; // ... and add peak control    }    // calculate the average:    average = total / numReadings;  } else { // read an abnormal peak    //Serial.println("Peak!!");    // if it is a peak immediately following the counter increment     if (previousPeakIndex==readIndex){      continuativePeak++;    }else{ // else reset peak counter      continuativePeak=0;    }        // remove smoothing if more than half of the values exceed the average     if (continuativePeak==(numReadings/2)){       noSmoothing=false;      continuativePeak=0;    }    previousPeakIndex=readIndex;   }  // send it to the plotter  //Serial.print("Read value:");  //Serial.print(readValue);  //Serial.print(", average:");  //Serial.println(average);  delay(10);        // delay in between reads for stability}`

#### Wawa

#1
##### Dec 10, 2019, 10:48 amLast Edit: Dec 10, 2019, 10:53 am by Wawa
The returned A/D value depends on two things. Temp and Aref.
If you measure with the potentially dirty/unstable default Aref (5volt supply), then you never will get a stable readout.

Try measuring the LM35 with 1.1volt Aref enabled in setup.
That will also get you a five times higher resolution.
Also make sure you don't share LM35 ground with other (breadboard) users.
Example sketch attached.
Leo..
Code: [Select]
`// connect LM35 to 5volt A0 and ground// calibrate temp by changing the last digit(s) of "0.1039"const byte tempPin = A0;float calibration = 0.1039;float tempC; // Celciusfloat tempF; // Fahrenheitvoid setup() {  Serial.begin(9600);  analogReference(INTERNAL); // use internal 1.1volt Aref  // change INTERNAL to INTERNAL1V1 for a Mega}void loop() {  tempC = analogRead(tempPin) * calibration; // get temp  tempF = tempC * 1.8 + 32.0; // C to F  Serial.print("Temperature is  ");  Serial.print(tempC, 1); // one decimal place resolution is all you get  Serial.print(" Celcius  ");  Serial.print(tempF, 1);  Serial.println(" Fahrenheit");  delay(1000); // use a non-blocking delay when combined with other code}`

#### antoniodv

#2
##### Dec 10, 2019, 04:21 pm
Thanks Wawa,
I used Vref = 1.1v and 5v directly on the LM35 with the share ground.

I updated the sketch because in my opinion there is another conceptual error when subtracting and adding the reading to total (lines 45 and 49), it could happen that when the microcontroller works for a long time, every possible calculation error is added up.
I preferred to recalculate the total, and therefore the average for each cycle.

Code: [Select]
`/*  Smoothing  Reads repeatedly from an analog input, calculating a running average and  printing it to the computer. Keeps ten readings in an array and continually  averages them.  The circuit:  - analog sensor (potentiometer will do) attached to analog input 0  created 22 Apr 2007  by David A. Mellis  <dam@mellis.org>  modified 9 Apr 2012  by Tom Igoe  improved (remove abnormal peak) 10 Dec 2019   by Antonio De Vincentiis <https://devincentiis.it)  This example code is improved of the the public domain.  http://www.arduino.cc/en/Tutorial/Smoothing*/// Define the number of samples to keep track of. The higher the number, the// more the readings will be smoothed, but the slower the output will respond to// the input. Using a constant rather than a normal variable lets us use this// value to determine the size of the readings array.const int numReadings = 10;int readValue =0;int readings[numReadings];        // the readings from the analog inputint readIndex = 0;                // the index of the current readingint total = 0;                    // the running totalfloat average = 0.01;             // the averageint continuativePeak = 0;         // countinuos peak sequence counterint previousPeakIndex = 0;        // readIndex of last peak       bool noSmoothing=false;           // active - deactive delete peak valueint inputPin = A0;void setup() {  // initialize serial communication with computer:  Serial.begin(9600);  // initialize all the readings to 0:  for (int thisReading = 0; thisReading < numReadings; thisReading++) {    readings[thisReading] = 0;  }}void loop() {  // read from the sensor:  readValue=analogRead(inputPin);   // value accepted only if the difference with the average   // is between 0 and 5% (<20)    if (!((abs(readValue)/abs(readValue-average))<20&&noSmoothing)){      readings[readIndex] = readValue;    // advance to the next position in the array:    readIndex++;    // compute total from array content    total=0;    for (int thisReading = 0; thisReading < numReadings; thisReading++) {      total += readings[thisReading];    }    // calculate the average:    average = total / numReadings/1.01;    // if we're at the end of the array...    if (readIndex >= numReadings) {      // ...wrap around to the beginning:      readIndex = 0;      noSmoothing=true; // ... and add peak control    }  } else { // read an abnormal peak    Serial.println("Peak!!");    // if it is a peak immediately following the counter increment     if (previousPeakIndex==readIndex){      continuativePeak++;    }else{ // else reset peak counter      continuativePeak=0;    }        // remove smoothing if more than half of the values exceed the average     if (continuativePeak==(numReadings/2)){       noSmoothing=false;      continuativePeak=0;    }    previousPeakIndex=readIndex;   }  // send it to the plotter  Serial.print("Total:");  Serial.print(total);  Serial.print("Read value:");  Serial.print(readValue);  Serial.print(", average:");  Serial.println(average);  delay(1);        // delay in between reads for stability}`

#3

#### enjoyneering

#4
##### Dec 10, 2019, 05:57 pm
But there are still some peaks in the IMPROVED chart. Should it be like that?

#### Wawa

#5
##### Dec 10, 2019, 08:20 pmLast Edit: Dec 10, 2019, 08:23 pm by Wawa
Thanks Wawa,
I used Vref = 1.1v and 5v directly on the LM35 with the share ground.
What does that mean.
I see nothing of that in your code, and I did say NOT share ground.

An LM35 should NOT have ANY peaks at all.
Post a picture of the setup.
Leo..

#### antoniodv

#6
##### Dec 11, 2019, 07:49 am
What does that mean.
I see nothing of that in your code, and I did say NOT share ground.

An LM35 should NOT have ANY peaks at all.
Post a picture of the setup.
Leo..
I have voluntarily used and created a hardware device as noisy as possible to analyze and improve the result through the code. The aim is to achieve a very resilient response to noise.
Greetings

P.S.
Not even the original example Smoothing.ino has Vref

#### antoniodv

#7
##### Dec 11, 2019, 08:13 am
But there are still some peaks in the IMPROVED chart. Should it be like that?
Yes.
I said "improved" not "flatted".
If you want peaks more flatted, then increase numReadings value.

#### Wawa

#8
##### Dec 11, 2019, 09:51 am
Not even the original example Smoothing.ino has Vref
The Arduino smoothing example uses a ratiometric source (a potentiometer).
Then you must use default Aref (which is internally connected to the same supply as the pot).
If both sensor and A/D are ratiometric, then supply fluctuations are cancelled out.

You told us you were testing an LM35, which has a 'voltage' output (not ratiometric).
Then it's a bad idea to use default Aref.
A stable LM35 and a potentially unstable/fluctuating supply results in unstable A/D values.

All of this has nothing to do with smoothing, just the correct way of measuring a 'sensor'.
The wrong way can introduce instablility. No amount of smoothing can fix that.
Leo..

Go Up