Датчик расстояния. Вычисление текущей медианы и среднего значения

Я пытаюсь сгладить значения для моего ИК-датчика расстояния с помощью фильтрации медианы и среднего значения. Я пытался выполнить усреднение, но мои значения кажутся неправильными, и я не совсем понимаю, как я буду выполнять медианную фильтрацию. Я также использую прерывания таймера, и я не хочу использовать библиотеки при расчете среднего и медианы. Я прикрепил код, в котором я пытался рассчитать среднее значение.

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);
    }

, 👍1

Обсуждение

index=index++; ничего не делает. Либо используйте index=index+1; вместо index++. Кроме того, вы используете " i " для индекса массива, но только (попробуйте) увеличить index. Я думаю, что это должно быть "i=i+1". Поскольку нигде больше " i " не изменяется, вы всегда перезаписываете один и тот же элемент в массиве., @Gerben

Вы используете int_flag в ISR, разве он не должен быть «летучим»? Кроме того, в цикле() вы никогда не сбрасываете его в 0. Это не может быть правильно..., @dda


3 ответа


0

analogRead() возвращает целое число, и поэтому ваш расчет V=adc_val*(5.0/1023.0) также будет приведен как int. Я предлагаю изменить его на V=5.0*adc_val/1023.0.

В общем случае текущее среднее значение может быть рассчитано следующим образом:

#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;
}

Вы должны очистить буфер во время инициализации, иначе вы не получите правильных показаний до 9-й итерации.

,

Даст ли это правильное среднее значение?, @eateat

Вы не опубликовали полный обрезанный код, так что я не могу сказать. Но линия, о которой я упомянул, нарушит усреднение, так как результирующее разрешение уменьшится в 200 раз., @Sim Son

Но в усреднении нет никакой магии: храните данные в кольцевом буфере и вычисляйте среднее значение: накапливайте этот биффер и делите на длину буфера, @Sim Son

Я делаю это, но значения, которые я получаю, сильно отличаются от нефильтрованных показаний A - D (т. е. например, 247 для нефильтрованного и 63 для фильтрованного)., @eateat

Я добавил свой полный код., @eateat

Хорошо, я видел, что вы даже не используете "V", и в этом коде есть другие части, которые не имеют большого смысла (по крайней мере, в этом фрагменте). Приведите минимальный пример, который воспроизводит ошибку и является полным, чтобы мы могли его скомпилировать. Кроме того, вы должны включить предупреждения компилятора, я уверен, что есть некоторые, @Sim Son

Поэтому, когда я запускаю код, который я вам дал, он возвращает следующее:: Среднее значение фильтра: 61 Среднее значение фильтра: 61 Среднее значение фильтра: 60 Среднее значение фильтра: 61 Среднее значение фильтра: 61 Среднее значение фильтра: 61 Среднее значение фильтра: 61 Среднее значение фильтра: 61, @eateat

вы должны привести в порядок или переосмыслить/переосмыслить свой код, а затем устранить некоторые ошибки, как есть: вы не сбрасываете свой флаг; вы не накапливаете "сумму" при сложении и вычитании из нее; вы не увеличиваете "i", даже если проверяете его значение. Погуглите "кольцевой буфер", и вы поймете, как писать в " int_array`. Затем вам нужна функция, которая считывает новое аналоговое значение, записывает его в массив, вычисляет и возвращает среднее значение int_array. Эта функция может каким-то образом заменить фактическую функцию analogRead ()., @Sim Son

Хорошо, спасибо. Я займусь этим вопросом. Кроме того, как бы я рассчитал текущую медиану?, @eateat

Я не могу сказать наизусть, но это не будет проблемой, и нужны только некоторые небольшие изменения. Кроме того, я не думаю, что это внесет изменения, так как у вас есть только 9 элементов., @Sim Son

Я был добр и отредактировал свой ответ с возможной функцией. Пожалуйста, обратите внимание, что у меня нет возможности проверить это прямо сейчас, @Sim Son

Re “_your calculation V=adc_val*(5.0/1023.0) будет приведено как int, так и well”: Нет, это неверно. Это будет плавающая точка., @Edgar Bonet

Также обратите внимание, что, хотя исходный код OP имеет некоторые ошибки, он использует алгоритм, который более эффективен, чем тот, который вы предлагаете здесь., @Edgar Bonet


0

Вот простое альтернативное предложение: предположим, что вы уже знаете среднее расстояние d, и появляется новое значение v. Разница v-d может быть использована для обновления среднего значения. Но поскольку вы хотите сгладить это по нескольким показаниям, вы обновляете d только на долю разницы: d += 0,8*(v-d). Фактор управляет сглаживанием.

Чтобы начать это, вы оцениваете, что первое значение, которое вы прочитали, является средним. Для этого не требуется массив для хранения прошлых показаний.

,

Как простейший [ИИР](https://en.wikipedia.org/wiki/Infinite_impulse_response) фильтр нижних частот, это один из наиболее широко используемых. Он известен как [экспоненциально взвешенная скользящая средняя](https://en.wikipedia.org/wiki/Moving_average#Exponential_moving_average)., @Edgar Bonet


1

В вашей программе есть две основные проблемы. Первое отмечено в Комментарий Гербена: вы (пытаетесь) увеличить индекс (с плохим синтаксисом), в то время как вместо этого вы должны увеличивать i. Вторая проблема заключается в следующих строках:

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

Исправьте путаницу между индексом и i, удалите строки выше, и ваша программа должна работать.

Пояснение: Я надеюсь, что первый пункт должен быть очевиден. Я обсуждаю здесь второй вопрос. В конце приведенного выше цикла индекс i имеет значение 9. Позже, в цикле (), и если предположить, что вы исправили первую проблему, программа сделает

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

и у меня есть значение 10. Оттуда я буду продолжать увеличивать, а вы всегда пишете за пределами массива.

Корень проблемы в том, что вы использовали одну и ту же переменную i для двух разных целей. Для этого существует несколько решений:

  • используйте разные имена переменных для разных целей
  • используйте локальные переменные, когда это применимо (здесь: в настройке())
  • будьте осторожны в своих тестах: вместо "если" (i == 9) напишите если (i >= 9)>, но обратите внимание, что это облегчает, но не полностью решает проблему
  • полностью удалите инициализацию массива.

Я предлагаю удалить инициализацию, потому что массив является глобальной переменной и, согласно стандарту C++, если он явно не инициализирован, он неявно инициализируется до нуля, поэтому ваша инициализация избыточна.

,

Хорошо, спасибо. Я попробую это сделать. Кроме того, не могли бы вы объяснить мне, как я смогу рассчитать текущую медиану? Меня это очень смущает., @eateat

ОП, похоже, на самом деле не увеличивает "i", что может быть реальной проблемой. Кроме того, сумма не инициализируется аналоговыми значениями, поэтому, в моем понимании, "averead" будет просто последним аналоговым значением, деленным на 9., @Sim Son

@SimSon: Действительно. Я отредактировал свой ответ., @Edgar Bonet

@eateat: Самый простой способ вычислить текущую медиану-это [copy](https://www.nongnu.org/avr-libc/user-manual/group__avr__string.html#ga5f60008005ea7557430149926cf583d7) круговой буфер во временный массив, [sort](https://www.nongnu.org/avr-libc/user-manual/group__avr__stdlib.html#gafd4bf2faec43342e7ad3d2ab37bac1fe) этот массив и возвращает элемент в середине. Однако это может быть не самый эффективный способ. Если вам нужен более подробный ответ, загляните в существующие работающие медианные библиотеки или задайте правильный вопрос на этом сайте., @Edgar Bonet