0

filtering analog data
Moderators: adafruit_support_bill, adafruit

Please be positive and constructive with your questions and comments.

filtering analog data

by keithg on Sat Nov 07, 2009 6:02 pm

The LED's will change state when the analog output is within six or seven numbers of the threshold, making accurate transitions impossible. The colors oscillate back and forth. There must be a better way! Would appreciate your thoughts.
Code: Select all | TOGGLE FULL SIZE
unsigned long starTime;
int flashRed = 20000;
 
 const int numReadings = 3;
 int readings[numReadings];      // the readings from the analog input
 int index = 0;                  // the index of the current reading
 int total = 0;                  // the running total
 int average = 0;

#define blueLED 11
#define redLED 12
#define greenLED 13
#define AnIn 1
#define redThresh 670
#define blueThresh 660
#define greenThresh 0

int val;


void setup()
{
  for (int thisReading = 0; thisReading < numReadings; thisReading++)
  readings[thisReading] = 0;

  starTime = millis();  //timer
   
  pinMode(redLED,OUTPUT);
  pinMode(blueLED,OUTPUT);
  pinMode(greenLED,OUTPUT);
 

Serial.begin(9600);
}
void loop()
{
  // subtract the last reading:
   total= total - readings[index];         
   // read from the sensor: 
   readings[index] = analogRead(AnIn);
   // add the reading to the total:
   total= total + readings[index];       
   // advance to the next position in the array: 
   index = index + 1;                   
   // if we're at the end of the array...
   if (index >= numReadings)             
     // ...wrap around to the beginning:
     index = 0;                           
   // calculate the average:
   average = total / numReadings;
 
    starTime = millis();  //timer

  //val = analogRead(AnIn);
 // val = val/2;
if(average >= redThresh){
 digitalWrite(redLED,HIGH);
 digitalWrite(blueLED,LOW);
 digitalWrite(greenLED,LOW);
 delay(1000);
}
else {
  digitalWrite(redLED,LOW);

if((average>= blueThresh) && (average <= redThresh))
{
 digitalWrite(blueLED,HIGH);
 digitalWrite(redLED,LOW);
 digitalWrite(greenLED,LOW);
 delay(1000);
}
else {
  digitalWrite(blueLED,LOW);
 
  if((average>= greenThresh) && (average <= blueThresh))
  {
 digitalWrite(greenLED,HIGH);
 digitalWrite(blueLED,LOW);
 digitalWrite(redLED,LOW);
 delay(1000);
  }
else {
  digitalWrite(greenLED,LOW);
}
}
Serial.println(average);
}

}



 
keithg
 
Posts: 82
Joined: Thu Oct 30, 2008 8:30 pm

Re: filtering analog data

by adafruit_support_bill on Sun Nov 08, 2009 7:15 am

Analog inputs on the Arduino are pretty noisy. I usually shift off the 2 low order bits as it is generally all noise. In this case, you may need to do three bits.

adafruit_support_bill
 
Posts: 74337
Joined: Sat Feb 07, 2009 10:11 am

Re: filtering analog data

by keithg on Sun Nov 08, 2009 7:52 am

Sorry, I have no idea how to shift off lower order bits but I certainly need to learn. Would you please write a snippet that illustrates the procedure? Thanks.
keithg
 
Posts: 82
Joined: Thu Oct 30, 2008 8:30 pm

Re: filtering analog data

by adafruit_support_bill on Sun Nov 08, 2009 9:48 am

To shift off the lowest three bits you can simply divide by 8.

Or you can use the C shift operator: '>>'

as in:

Code: Select all | TOGGLE FULL SIZE
value = value >> 3;

adafruit_support_bill
 
Posts: 74337
Joined: Sat Feb 07, 2009 10:11 am

Re: filtering analog data

by keithg on Sun Nov 08, 2009 12:12 pm

That is more simple than expected. Thanks. I have tried to plug this filter into the sketch but the results are quacky; the red flashes, the blue stay on, and the green is never high. I have rewritten this sketch a bunch of different ways but the LED's will not cooperate. Analog readings from 640~680
Code: Select all | TOGGLE FULL SIZE
#define FILTER_SHIFT 3

#define redLED 13
#define blueLED 12
#define greenLED 11
#define redThresh 680
#define blueThresh 678
#define greenThresh 0

int32_t filter_reg;
int16_t filter_input;
int16_t filter_output;
void setup(){
  pinMode(redLED,OUTPUT);
  pinMode(blueLED,OUTPUT);
  pinMode(greenLED,OUTPUT);
  Serial.begin(9600);
 
}
void loop(){

filter_input = analogRead(1);           // read ADC

// low pass filter to get rid of noise
filter_reg = filter_reg - (filter_reg >> FILTER_SHIFT) + filter_input;
filter_output = filter_reg >> FILTER_SHIFT;
delay(100);

if(filter_output >= redThresh)
{
digitalWrite(redLED, HIGH);
digitalWrite(blueLED,LOW);
digitalWrite(greenLED, LOW);
delay(100);

}
else{
digitalWrite(redLED, LOW);

}

if((filter_output < redThresh) && (filter_output > greenThresh))
{
digitalWrite(blueLED, HIGH);
digitalWrite(redLED, LOW);
digitalWrite(greenLED, HIGH);
delay(100);

}
else{
  digitalWrite(blueLED, LOW);
 

}
if((filter_output >= greenThresh) && (filter_output <= blueThresh))
{
digitalWrite(greenLED, HIGH);
digitalWrite(redLED, LOW);
digitalWrite(blueLED, LOW);
delay(100);

}
else{
 digitalWrite(greenLED, LOW);
}

Serial.println(filter_output);

}
 





keithg
 
Posts: 82
Joined: Thu Oct 30, 2008 8:30 pm

Re: filtering analog data

by adafruit_support_bill on Sun Nov 08, 2009 12:41 pm

You want to be doing the shift on the analog reading. This is not really a low-pass filter. This is just throwing away random bits.

Of course, after the shift the resulting value is divided by 8, so you will need to scale your thresholds accordingly.

Adding a time-domain filter is not a bad idea, but I think your first implementation was closer to what you want. Something like:

Code: Select all | TOGGLE FULL SIZE
 // subtract the last reading:
   total= total - readings[index];         
   // read from the sensor: 
   readings[index] = analogRead(AnIn) >> FILTER_SHIFT;
   // add the reading to the total:
   total= total + readings[index];       
   // advance to the next position in the array: 
   index = index + 1;                   
   // if we're at the end of the array...
   if (index >= numReadings)             

...


You might consider printing the inputs & outputs of the filter to aid in debugging.

adafruit_support_bill
 
Posts: 74337
Joined: Sat Feb 07, 2009 10:11 am

Re: filtering analog data

by zener on Sun Nov 08, 2009 3:25 pm

You might post an algorithym or flow chart.

zener
 
Posts: 4416
Joined: Sat Feb 21, 2009 2:38 am

Re: filtering analog data

by keithg on Sun Nov 08, 2009 3:56 pm

Seems that no matter which way the sketch is tweaked, the odd ball reading sneaks in and makes the lights change state even when their is nothing but silence. I've seen somewhere a procedure that compares new data with old and if there is not close to the current figure, nothing happens. After an exhausting search, I can't find it. Would this eliminate the problem? A sample of the analog input is listed below the code.
Code: Select all | TOGGLE FULL SIZE
#define FILTER_SHIFT 1
int val;

 
 int ledRed =12;
 int ledYellow = 11;
 int ledGreen = 13;
 
 unsigned long starTime;
int flashRed = 20000;
 
 
 const int numReadings = 5;
 int readings[numReadings];      // the readings from the analog input
 int index = 0;                  // the index of the current reading
 int total = 0;                  // the running total
 int average = 0;                // the average
 int inputPin = 1;
 void setup()
 {
  val = 0;
  pinMode(ledRed,OUTPUT);
  pinMode(ledYellow,OUTPUT);
  pinMode(ledGreen,OUTPUT);
  pinMode(inputPin, INPUT);
   
   // initialize serial communication with computer:
   Serial.begin(9600);                   
   // initialize all the readings to 0:
   for (int thisReading = 0; thisReading < numReadings; thisReading++)
     readings[thisReading] = 0;

   starTime = millis();  //timer     
 }
 void loop() {
   // subtract the last reading:
   total= total - readings[index];         
   // read from the sensor: 
   readings[index] = analogRead(inputPin) >> FILTER_SHIFT ;
   // add the reading to the total:
   total= total + readings[index];       
   // advance to the next position in the array: 
   index = index + 1;                   
   // if we're at the end of the array...
   if (index >= numReadings)             
     // ...wrap around to the beginning:
     index = 0;                           
   // calculate the average:
   average = total / numReadings;
   delay(100);
 
    starTime = millis();  //timer

 // if  ((average >200) && (average < 300)) {  // code here will execute if average > 100
 
    if (average > 342) {
    digitalWrite(ledRed,HIGH);
    digitalWrite(ledYellow,LOW);
    digitalWrite(ledGreen,LOW);
    delay(1000);
    val= val +1;
   
    if((val >8) && (starTime> flashRed )) {
     
     
      digitalWrite(ledRed,HIGH);
      delay(1000);
      digitalWrite(ledRed,LOW);
      delay(1000);
      digitalWrite(ledRed,HIGH);
      delay(1000);
      digitalWrite(ledRed,LOW);
      delay(1000);
      digitalWrite(ledRed,HIGH);
      delay(1000);
      digitalWrite(ledRed,LOW);
      delay(1000);
      digitalWrite(ledRed,HIGH);
      delay(1000);
      digitalWrite(ledRed,LOW);
      delay(1000);
      if (val>8){
        val = 0;
    }
    }   
   
  }
  else if ((average > 332) && (average < 341)) {  // code here will execute if average <= 100 and average > 80
   
   
    digitalWrite(ledRed,LOW);
    digitalWrite(ledYellow,HIGH);
    digitalWrite(ledGreen,LOW);
    delay(1000);
  }
  else {  // code here will execute if average <= 80
 
    digitalWrite(ledRed,LOW);
    digitalWrite(ledYellow,LOW);
    digitalWrite(ledGreen,HIGH);
    delay (200);
  }
 
// Serial.println(average, DEC);
 Serial.println(average, DEC);
 //val = 0;
 
 }
 

   
   
   
   
   
   


 


332

332

332

333

333

328

339

336

339

338

337

337

331

338

333

340

338

338

339

339

339

339

338

336

332

335

328

338

336

335

333

331

332

326

335

334

333

332
keithg
 
Posts: 82
Joined: Thu Oct 30, 2008 8:30 pm

Re: filtering analog data

by zener on Sun Nov 08, 2009 5:27 pm

The advice I have been given before is "Post the absolute smallest piece of code that exhibits the problem."

As for analog filtering, the top two methods used are:

Hardware RC filter
Running average (also known by another name I cannot think of...)

zener
 
Posts: 4416
Joined: Sat Feb 21, 2009 2:38 am

Re: filtering analog data

by ahdavidson on Sun Nov 08, 2009 5:52 pm

Running average (also known by another name I cannot think of...)


Isn't that one of the simplest of low-pass filters?
.andy
ahdavidson
 
Posts: 131
Joined: Wed Jun 03, 2009 9:59 am
Location: Seattle, WA

Re: filtering analog data

by keithg on Sun Nov 08, 2009 9:55 pm

Is this the running average?

input = analogRead(1);
adcResult = adcResult * 0.95 + analogRead(1) * .05;
keithg
 
Posts: 82
Joined: Thu Oct 30, 2008 8:30 pm

Re: filtering analog data

by zener on Sun Nov 08, 2009 11:38 pm

An easier way is you have a counter and an analog-read total. Every time you take an analog read you add it to your total and increment the counter. At some point you divide the total by the counter and there is your average. Then you start over. That is a simple way that works for many applications.
Last edited by zener on Mon Nov 09, 2009 4:03 pm, edited 1 time in total.

zener
 
Posts: 4416
Joined: Sat Feb 21, 2009 2:38 am

Re: filtering analog data

by adafruit_support_bill on Mon Nov 09, 2009 7:11 am

Your analog values range from 326 to 340 (a range of 14). If that represents "silence" you have about 4 bits of noise.

What is the application?

adafruit_support_bill
 
Posts: 74337
Joined: Sat Feb 07, 2009 10:11 am

Re: filtering analog data

by keithg on Mon Nov 09, 2009 8:12 pm

We want a noise monitor for our cafeteria, which is near riot every lunch hour. Similar commercial units are about $70 but we need one for each classroom if I can persuade the circuit/sketch to turn red, blue, and green LED's on when the noise exceeds their relative threshold.
keithg
 
Posts: 82
Joined: Thu Oct 30, 2008 8:30 pm

Re: filtering analog data

by adafruit_support_bill on Tue Nov 10, 2009 7:33 am

Before going much further, your original code shows a difference of 10 between the blue and red thresholds. But your sample output shows a range of 14 for a steady-state input. I think you will have some trouble separating the signal from the noise here. You may want to re-evaluate your sensor.

On the processing side, your application can tolerate a fairly aggressive low-pass filter. A running average of 20 (or more!) samples over several seconds should smooth things out a bit. But you should also consider adding some hysteresis to prevent flickering around the threshold levels. For example, once the device is in the "red" state, the raw input level should have to go several counts below the red threshold before changing state to "blue".

adafruit_support_bill
 
Posts: 74337
Joined: Sat Feb 07, 2009 10:11 am

Please be positive and constructive with your questions and comments.