Датчик расстояния. Вычисление текущей медианы и среднего значения
Я пытаюсь сгладить значения для моего ИК-датчика расстояния с помощью фильтрации медианы и среднего значения. Я пытался выполнить усреднение, но мои значения кажутся неправильными, и я не совсем понимаю, как я буду выполнять медианную фильтрацию. Я также использую прерывания таймера, и я не хочу использовать библиотеки при расчете среднего и медианы. Я прикрепил код, в котором я пытался рассчитать среднее значение.
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);
}
@eateat, 👍1
Обсуждение3 ответа
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
Вот простое альтернативное предложение: предположим, что вы уже знаете среднее расстояние 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
В вашей программе есть две основные проблемы. Первое отмечено в
Комментарий Гербена: вы (пытаетесь) увеличить индекс
(с плохим
синтаксисом), в то время как вместо этого вы должны увеличивать 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
- Как использовать SPI на Arduino?
- Нажать клавишу Windows, используя «keyboard.press();»
- Как подключить вывод INT MPU 6050?
- Улучшенное циклическое переключение цветов RGB.
- Проблема с загрузкой моего скетча на Ардуино
- Отправка мультимедийных клавиш клавиатуры с помощью библиотеки клавиатур
- Распиновка аналога Arduino Pro Micro
- Как преобразовать скетч примера Arduino в полный проект C++?
index=index++;
ничего не делает. Либо используйтеindex=index+1;
вместоindex++
. Кроме того, вы используете " i " для индекса массива, но только (попробуйте) увеличитьindex
. Я думаю, что это должно быть "i=i+1". Поскольку нигде больше " i " не изменяется, вы всегда перезаписываете один и тот же элемент в массиве., @GerbenВы используете int_flag в ISR, разве он не должен быть «летучим»? Кроме того, в цикле() вы никогда не сбрасываете его в 0. Это не может быть правильно..., @dda