0

I'm trying to smooth out values for my IR distance sensor through running median filtering and a running average. I've tried to do the averaging, but my values don't seem correct and I am confused about how I would do the median filtering. I am also using timer interrupts and I don't want to use libraries when calculating the average and median. I've attached the code where I attempted to calculate the average.

int analogpin=5;
int sum=0;
int index=0;
int averead=0;
const int numreadings=9;
int i;
int timer1_counter;
int adc_val;
int int_flag;
int int_array[9];
float V;


void setup() {

int_flag=0;

Serial.begin(9600);
pinMode(analogpin,OUTPUT);
for(i=0; i<=8; i++){
  int_array[i]=0;
}

noInterrupts();           //

  TCCR1A = 0;
  TCCR1B = 0;


timer1_counter = 59286;  

  TCNT1 = timer1_counter;   
  TCCR1B |= (1 << CS12);    
  TIMSK1 |= (1 << TOIE1);   
  interrupts();             
}

ISR(TIMER1_OVF_vect)        
{
  TCNT1 = timer1_counter;   
  int_flag=1;               
    }

void loop() {


  if (int_flag==1){
    adc_val=analogRead(A2);
    V=5.0*adc_val/1023.0;
    analogWrite(analogpin,adc_val/4);
    }
    sum=sum-int_array[i];
    int_array[i]=analogRead(A2); 
    sum=sum+int_array[i];
    index=index++;
    if(i==9){
      i=0;
    }
    averead=sum/numreadings;
    Serial.print("\n"); Serial.print("Average Filter Reading: "); 
Serial.println(averead);
    delay(1);
    }
  • 2
    index=index++; does nothing. Either use index=index+1; of index++. Also, you are using i for the array-index, but only (try to) increment index. I think that should be i=i+1. Because nowhere else i is changed, you always overwrite the same element in the array. – Gerben Mar 30 at 12:32
0

analogRead() returns an integer and therefore your calculation V=adc_val*(5.0/1023.0) will be casted as int as well. I suggest changing it to V=5.0*adc_val/1023.0.

In general a running mean can be calculated like this:

#define buffer_size 9
#define analog_pin 5

int ring_buffer[buffer_size];
uint8_t index=0;
float running_mean() {
  ring_buffer[index]=analogRead(analog_pin);
  float mean=0;
  for (uint8_t i=0;i<buffer_size;i++) {
    mean+=ring_buffer[i];
  }
  if (++index>=buffer_size) index=0;
  return mean/buffer_size;
}

You should flush the buffer during initialization, otherwise you don't get proper readings before the 9th iteration.

  • Would that give a proper average? – eateat Mar 30 at 3:24
  • You didn't post a complete code snipped, so I can't say. But the line I mentioned will disturb averaging as the resulting resolution will decrease by 200 times. – Sim Son Mar 30 at 3:35
  • But there is no magic about averaging: store data in a ring buffer and to calculate the mean: accumulate this biffer and divide by the buffer length – Sim Son Mar 30 at 3:37
  • I'm doing that, but the values I am getting are very different from the unfiltered A to D readings (ie. like 247 for unfiltered and 63 for filtered). – eateat Mar 30 at 3:41
  • I've added my full code. – eateat Mar 30 at 3:45
0

Here is a simple alternative suggestion: Assuming you already know the average distance d and a new reading v comes in. The difference v-d can be used to update the average. But since you want you want to smooth this out over several readings, you only update d by a fraction of the difference: d += 0.8*(v-d). The factor controls the smoothing.

To get this started, you estimate that the first value you read is the average. This does not require an array to keep past readings around.

0

There are two main problems with your program. The first is noted in Gerben's comment: you (attempt to) increment index (with a bad syntax), while you should be incrementing i instead. The second problem is these lines:

for (i = 0; i <= 8; i++) {
    int_array[i] = 0;
}

Fix the index vs. i confusion, remove the lines above, and your program should work.

Explanation: The first point should be obvious, I hope. I am discussing here the second issue. At the end of the loop above, the index i has the value 9. Later, in loop(), and assuming you fixed the first issue, the program does

i++;
if (i == 9) {
    i = 0;
}

and i has the value 10. From there i will continue incrementing, and you are always writing outside of the array.

The root of the problem is that you used the same variable i for two different purposes. There are several solutions to this:

  • use different variable names for different purposes
  • use local variables whenever applicable (here: in setup())
  • be defensive in your tests: instead of if (i == 9) write if (i >= 9), but note that this alleviates but does not completely solve the problem
  • remove the initialization of the array altogether.

I suggest removing the initialization because the array is a global variable and, per the C++ standard, if not explicitly initialized it is implicitly initialized to zero, so your initialization is redundant.

  • Ok thank you. I will try that. Also, would you be able to explain to me how I would be able to calculate a running median as well? I'm pretty confused about that. – eateat Mar 31 at 15:36
  • The OP does not seem to actually increment i, which might be the real problem. Also, sum isn't initialzed with analog values, so in my understanding averead will just be the last analog value divided by 9. – Sim Son Apr 1 at 8:05
  • @SimSon: Indeed. I edited my answer. – Edgar Bonet Apr 1 at 15:46
  • @eateat: The simplest way to compute a running median is to copy the circular buffer into a temporary array, sort that array, and return the element in the middle. It may not be the most efficient way though. If you need a more detailed answer, look into existing running median libraries, or ask a proper question on this site. – Edgar Bonet Apr 1 at 15:55

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.