Average of a sensor

Hey im working on a project using a load cell to to turn voltages into weights. Im finished coding and everything is fine but the readings i get are not very stable, shifting slightly up and down for no reason. Is there a simple method where the only thing that appears on my serial monitor will be a more filtered result. I have looked through tons of filters on here but i cant seem to get it right. Any help is appreciated, thanks, Tom.

How much variation are you getting in your readings? Is it a stability or repeatability problem? I like to take multiple samples and compute a simple average at the very least.

Something like this
126
126
126
126
125.5
125.5
126
125.5
126
126
126
Im not really how to code an average for this as its just constantly taking readings so do i need to place them in an array?

Well first any averaging you do should be done with the raw integer values returned by analogRead() statements, not after you convert them (if you indeed do) to floating point numbers like the 125.5 example you posted. Anyway a simple averaging method might be to just add every four consecutive values in a row you read with a analogRead() statements and then divide the accumulated number by four. No need for an array.

Lefty

I do this in one program, but you will always get some toggling of the last bit of the result.

 // Sample the LM34 a bunch of times then compute a nice smooth average temperature

  temptot = 0;
  for(x = 0; x < 64; x++) {
    temptot += analogRead(LM34Pin);
  }
  temp = temptot >> 6;   // divide by 64

What you could do then is to build in some hysteresis into the software by only acknowledging changes above your "noise" threshold. You could simply ignore the last bit.

Well first any averaging you do should be done with the raw integer values returned by analogRead() statements, not after you convert them (if you indeed do) to floating point numbers like the 125.5 example you posted.

@lefty

I do not agree on this per se. Averaging of integer values have a truncating error that might be greater than when averaging the converted values.
you can minimize this with rounding.

temptot = 0;
  for(x = 0; x < 64; x++) 
    temptot += analogRead(LM34Pin);
  temp = (temptot + 32) /64;   // rounding iso truncking

the number to add (32) is half the number of samples you take (64)

BTW Averaging floats is definitely slower than averaging integers.

robtillaart:

Well first any averaging you do should be done with the raw integer values returned by analogRead() statements, not after you convert them (if you indeed do) to floating point numbers like the 125.5 example you posted.

@lefty

I do not agree on this per se. Averaging of integer values have a truncating error that might be greater than when averaging the converted values.
you can minimize this with rounding.

temptot = 0;

for(x = 0; x < 64; x++)
    temptot += analogRead(LM34Pin);
  temp = (temptot + 32) /64;   // rounding iso truncking



the number to add (32) is half the number of samples you take (64)

BTW Averaging floats is definitely slower than averaging integers.

Well I do not agree per se back. I think as a general rule one should avoid using floating point math all together (if at all possible) on micro-controller applications, just too much code bloat and speed penalty to pay. For an applications like reading a load cell and converting to units of measurement it can all be done in integer math.

Lefty

I think as a general rule one should avoid using floating point math all together (if at all possible) on micro-controller applications, just too much code bloat and speed penalty to pay. For an applications like reading a load cell and converting to units of measurement it can all be done in integer math.

Agree on these :wink:

However the point I wanted to make is that averaging the float values might be more accurate than the averaging the raw integers.
And sometimes one wants to trade speed for precision ...

Tom D,
Found this on this forum and I'm sorry to say I forgot to record the original senders ID, but it works well for me.
Changing the ratio of the decimal fractions changes the frequency of update.
TomJ

//Then I'd do a 'rolling average' of the form
//to smooth off the jitter.
//light1 is of type float)

Code:
light1 = light1 * 0.9 + float(analogRead(light1Pin)) * 0.1;

robtillaart:

Well first any averaging you do should be done with the raw integer values returned by analogRead() statements, not after you convert them (if you indeed do) to floating point numbers like the 125.5 example you posted.

@lefty

I do not agree on this per se. Averaging of integer values have a truncating error that might be greater than when averaging the converted values.
you can minimize this with rounding.

temptot = 0;

for(x = 0; x < 64; x++)
    temptot += analogRead(LM34Pin);
  temp = (temptot + 32) /64;   // rounding iso truncking



the number to add (32) is half the number of samples you take (64)

BTW Averaging floats is definitely slower than averaging integers.

Just in case, I didn't use any floats. I also disagree with the rounding. I don't believe it correct to do that, I believe truncation is better. Here is my reference:

I don't believe it correct to do that, I believe truncation is better.

simple example, 10 measurements of an analogRead()

The integer average with truncating
{ 109 109 109 109 109 109 109 109 109 108 } = 1089/10 = 108

The integer average with rounding
{ 109 109 109 109 109 109 109 109 109 108 } = (1089+5)/10 = 109

The float average
{ 109 109 109 109 109 109 109 109 109 108 } = 1089/10 = 108.9

(for above example)
with rounding the max error = 0.5 so the average error is 0.25
with truncating the max error = 0.9 the average error is 0.45
So there is a factor 1.8 difference in the max & avg error

However I do not know the requirements of your project, so I cannot say if rounding is correct or not.

// the numerical implication of error propagation in a rolling average are a bit more complex
// but it is a faster method than taking dozens of samples (when executed in integer domain)

I myself don't have a project, but the document I provided says that decimation is done by truncation. Given that they (Actel) manufacture ADCs, I'm going to have to side with them. No offense intended. :slight_smile:

Maybe it's the number of samples, because to do true decimation requires more like 64 over samples. At that point, I believe the truncation is truly just noise and not meaningful information as the "division" is just a simple shift removing noise bits that never were a part of the answer. The assumption being that there is 1LSB of noise in the data that is randomly distributed white noise.

In some cases (not all), a fairly simple 1nF capacitor can be placed between signal and ground, either on the sensor side or on the Arduino analog-in side. It will work as a low-pass filter, 'shorting' out possible high frequency disturbing crosstalk or noise, and stabilizing the measurements. Details depending on the actual measurements done, and works for analog sensors only.

Also, when using analogRead(), make sure to use the internal 3.3V reference source for scaling, it will produce more accurate digital readings.

Finally - make sure that the actual sensor is 'stable', e.g. it gets a stable power supply, stable as in when compared to the one that's powering the Arduino. Groundloops and all that...

Hey thanks for everyone who posted a reply, alot of helpful information. Im having a little trouble with the how to program the arduino to separate ten samples to average from. Im including some code i found which i used some of in the project, if anyone could tell me the best way to include an average fileter into it i would be extremely greatful, sorry if this sounds stupid but im pretty crap at all this but im starting to cop on to how it all works.
I will also try the capacitor method when im next in the lab.
Apologies the original poster of this information but i couldnt find the name.
// Arduino with load cell

// Put two known loads on the sensor and take readings. Put those values
// here.
float aReading = 120.0;
float aLoad = 60.0; // lbs.
float bReading = 344.0;
float bLoad = 172.0; // lbs.

long time = 0;
int interval = 1000; // Take a reading every 500 ms

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

void loop() {
float newReading = analogRead(0);

// Calculate load based on A and B readings above
float load = ((bLoad - aLoad)/(bReading - aReading)) * (newReading - aReading) + aLoad;

// millis returns the number of milliseconds since the board started the current program
if(millis() > time + interval) {
Serial.print("Reading: ");
Serial.print(newReading,1); // 1 decimal place
Serial.print(" Load: ");
Serial.println(load,1); // 1 decimal place, println adds a carriage return
time = millis();
}
}