Усреднение значений с потенциометра

kiril22
Offline
Зарегистрирован: 06.11.2013

Мне нужно считать показания потенциометра и вывести в serial monitor.

Подключаем потенциометр к ардуино:

Не значительно меняем скетч из примеров


const int analogInPin = A0;  // потенциометр
int sensorValue = 0;        // считанное значение


void setup() {

  Serial.begin(9600); 
}

void loop() {

  sensorValue = analogRead(analogInPin);            

  Serial.print("sensor = " );                       
  Serial.print(sensorValue);      

  delay(50);                     
}

И получаем значения от 0 до 1023. С этим все понятно и все так работает.

Для практического применения этой конструкции мешает "дребезг" потенциметра (из-за низкого качества последнего в состоянии покоя значения могут немного изменяться - "скакать").

Теперь вопрос. Как усреднить считывемые значения? Например, взять значения sensorValue за 10 циклов и посчитать среднее из них.

Головой понимаю, что нужно применить счетчик циклов, но вот не доходит, что вставить вместо вопросов :(

Может есть стандартное решение?

for (int i = 0; i < 10; i++)

????????

}

 

Tomasina
Tomasina аватар
Offline
Зарегистрирован: 09.03.2013
#define analogInPin A0 	        // потенциометр
const byte averageFactor = 5;   // коэффициент сглаживания показаний (0 = не сглаживать)
                                // чем выше, тем больше "инерционность"
int sensorValue = 0;            // считанное значение

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

void loop() 
{
  sensorValue = analogRead(analogInPin);
  
  if (averageFactor > 0)        // усреднение показаний для устранения "скачков"
  {      
    int oldsensorValue = sensorValue;
    sensorValue = (oldsensorValue * (averageFactor - 1) + sensorValue) / averageFactor;  
    // <новое среднее> = (<старое среднее>*19 + <текущее значение>) / 20
  }

  Serial.print("sensor = " );
  Serial.println(sensorValue);

  delay(50);                     
}
leshak
Offline
Зарегистрирован: 29.09.2011

Думаю, для даннго случая и усреднения будет достаточно, но, если нужно, то могу поделится классом реализующий скользящий медианный фильтр. Он лучше работает когда есть редкие "большие выбросы в сторону". Фильтрация через среднее, хоть и сгладит "выброс", но все-таки почувствует его, а медиана, если "выброс" был кратковременным - вообще не заметит. Для потенциометра это не столь актуально, а вот для какого-нибудь ультразвукового дальномера.... (для чего собственно и делал).

leshak
Offline
Зарегистрирован: 29.09.2011

>Может есть стандартное решение?

Конечно есть. Вы его и описали. Только почему-то не захотели в коде реализовать

 

const int analogInPin = A0;  // потенциометр
int sensorValue = 0;        // считанное значение


void setup() {

  Serial.begin(9600); 
}

void loop() {

  sensorValue = readSensor(10);            

  Serial.print("sensor = " );                       
  Serial.print(sensorValue);      

  delay(50);                     
}

int readSensor(int samples){ // samples - сколько раз нужно прочитать сенсор
   unsigned int avg_sum=0;
   for(byte i=0;i<samples;i++){
       avg_sum+=analogRead(analogInPin);
       delay(5); // небольшая пауза между замерами
   }
   
   return avg_sum/samples;
}

Вот это и будет "лобовое" усреднее. А то что дал Tomasina дал это "скользящие среднее". С практической точки зрение его решение - лучше:

- время затрачиваемое на получение "одного усредненнго замера" не зависит от количества замеров "для усредния"
- не нужно следить за тем, что avg_sum  переполнится (в "лобовом варианте", если вы захотите усреднять по очень большому количеству, скажем не 10, а 1000 замеров делать - у вас вылезет переполнение и бред в результатах).

Минус его решения только один: чуть чуть сложнее для понимания "как оно работает".

Andrey_Y_Ostanovsky
Offline
Зарегистрирован: 03.12.2012

leshak пишет:

Минус его решения только один: чуть чуть сложнее для понимания "как оно работает".

Для честной реализации - надо еще и брать не 5 * old.value, а действительно "пять предыдущих результатов".

leshak
Offline
Зарегистрирован: 29.09.2011

Andrey_Y_Ostanovsky пишет:

leshak пишет:

Минус его решения только один: чуть чуть сложнее для понимания "как оно работает".

Для честной реализации - надо еще и брать не 5 * old.value, а действительно "пять предыдущих результатов".

Неа.. тут все "честно". С точки зрения математики это идентично. Вопрос только. Что мы храним, скажем [1,1,2,3,3]  (и вычисляем его сумму) что мы храним [1,2,2,2,3], что мы храним old.value=2 - результат один и тот же

sum([1,1,2,3,3])=10
sum([1,2,2,2,3])=10
2*5=10

Разница может "вылезти", только за счет округления в int. Но, при небольших количествах "старых значений" - она будет не существенной, а при больших достаточно хранить старое сначение в чем-то типа double.

P.S. Я же говорил что "минус в том что чуть сложнее понимать" ;)
 

AS
Offline
Зарегистрирован: 12.03.2014

Tomasina пишет:

#define analogInPin A0 	        // потенциометр
const byte averageFactor = 5;   // коэффициент сглаживания показаний (0 = не сглаживать)
                                // чем выше, тем больше "инерционность"
int sensorValue = 0;            // считанное значение

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

void loop() 
{
  sensorValue = analogRead(analogInPin);
  
  if (averageFactor > 0)        // усреднение показаний для устранения "скачков"
  {      
    int oldsensorValue = sensorValue;
    sensorValue = (oldsensorValue * (averageFactor - 1) + sensorValue) / averageFactor;  
    // <новое среднее> = (<старое среднее>*4 + <текущее значение>) / 5 я тут немного поправил
  }

  Serial.print("sensor = " );
  Serial.println(sensorValue);

  delay(50);                     
}

Подскажите, а какой смысл в формулу подставлять два одинаковых значения? 

int oldsensorValue = sensorValue;

этой строкой мы же их уравниваем?

leshak
Offline
Зарегистрирован: 29.09.2011

>Подскажите, а какой смысл в формулу подставлять два одинаковых значения? 

Ну, смыслов найти можно много... но скорее всего просто впопыхах человек чуток запутался в именах переменных.

Вот мой вариант "впопыхах", чуток подправленный, не проверял правда...

#define analogInPin A0 	        // потенциометр
const byte averageFactor = 5;   // коэффициент сглаживания показаний (0 = не сглаживать)
                                // чем выше, тем больше "инерционность"
int sensorValue = 0;            // считанное значение

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

void loop() 
{
  int newSensorValue = analogRead(analogInPin);
  
  if (averageFactor > 0)        // усреднение показаний для устранения "скачков"
  {      
    sensorValue = (sensorValue * (averageFactor - 1) + newSensorValue) / averageFactor;  
    // <новое среднее> = (<старое среднее>*4 + <текущее значение>) / 5 я тут немного поправил
  } else {
    sensorValue=newSensorValue; // не делаем усреднений, что прочитали то и считаем выводом
  }

  Serial.print("sensor = " );
  Serial.println(sensorValue);

  delay(50);                     
}

 

AS
Offline
Зарегистрирован: 12.03.2014

Опять мимо. первое значение будет в averageFactor (в нашем случае в 5 раз) меньше первого измеренного значения, а потом приближаться к нему по экспоненте. Если измеренное значение не будет меняться. 

Имхо, надо первое измерение сделать перед loop{}, как-то.

com
Offline
Зарегистрирован: 06.09.2013

AS пишет:

Имхо, надо первое измерение сделать перед loop{}, как-то.

обычно в сетапе

leshak
Offline
Зарегистрирован: 29.09.2011

>Имхо, надо первое измерение сделать перед loop{}, как-то.

Это уже зависит от конкретной задачи. Тут нет "правильно" или "не правильно". Начальный этап - это сугубо решение "автора". Волевое. Что считать "начальным состоянием". Если считать что "при выключенном контроллере показание сенсора ноль", то "разогрев датчика" - вполне правомерен и логичен. Если считать "при включении датчик СРАЗУ показывает правильное значение", то да... в setup() нужно впендюрить дополнительный sensorValue=analogRead(analogInPin);

Вообщем, что считать "началом соотворения мира, после подачи питания" - вопрос религии, а не логики :)

kiril22
Offline
Зарегистрирован: 06.11.2013

Всем ОГРОМНОЕ Спасибо! Даже не думал, что такой простой вопрос поднимет дискуссию. А до решения мне самому не хватило одного шага. Еще раз благодарю всех.

Tomasina
Tomasina аватар
Offline
Зарегистрирован: 09.03.2013

AS пишет:
Подскажите, а какой смысл в формулу подставлять два одинаковых значения? ... этой строкой мы же их уравниваем?

Да, мой косяк, возникший потому что этот пример собран из кусков большого проекта. Немного не в том порядке скопипастено. Правильно так: строку 17 из моего примера вставить после строки 13, т.е.

int oldsensorValue = sensorValue; 

и только затем

sensorValue = analogRead(analogInPin);

leshak пишет:
Это уже зависит от конкретной задачи. Тут нет "правильно" или "не правильно". Начальный этап - это сугубо решение "автора". Волевое. Что считать "начальным состоянием". Если считать что "при выключенном контроллере показание сенсора ноль", то "разогрев датчика" - вполне правомерен и логичен. ...

Это было выкинуто из примера, чтобы не запутывать понимание сути. Данная ситуация у меня реализована следующим образом: первые N показаний, пока они меньше коэффициента сглаживания, выводятся "как есть" и одновременно буферизуются, а как только число измерений превысит коэффициент, выводятся уже сглаженные показания. Таким образом, с момента подачи питания показания сразу отображаются реальными, просто первые N измерений более скачкообразны ("дрейфуют"). В некоторых случаях  важнее сразу видеть показания, максимально близкие к реальным, нежели плавный отчет от нуля.

P.S. можно даже еще немного смягчить этот дрейф, реализовав сразу и "скользящее среднее" и "скользящий коэффициент сглаживания" - первое измерение выводится как есть, второе усредняется с первым, третье усредняется с предыдущими и так далее до тех пор, пока счетчик не достигнет заданного числа сглаживаний, далее сглаживать по поседним N измерениям, но сейчас, думаю, это избыточно. С позиции отображения показаний это равноценно тому, что предложил leshak, но с точки зрения построения архитектуры программы более правильно - не приходится раскидывать куски кода, отвечающие за одно и то же, в разные идеологические блоки, следовательно проще в отладке и поддержании кода.

 

 

dimax
dimax аватар
Онлайн
Зарегистрирован: 25.12.2013

leshak, а можно все же полюбопытствовать у вас,  что за алгоритм "скользящий медианный фильтр" ? , примерчик есть?:)

leshak
Offline
Зарегистрирован: 29.09.2011

dimax пишет:

leshak, а можно все же полюбопытствовать у вас,  что за алгоритм "скользящий медианный фильтр" ? , примерчик есть?:)

Не, примерчиков нет пока.... оно писалось "для себя побыстрому" (поэтому слегка сыровато). Смогу дать где-то ближе к вечеру (на другом компе исходники того проекта). Поэтому, пока, только саму "идею" алгоритма описать могу.

Первая часть: нахождение медианы числового ряда. Предположим у нас есть набор замеров: [100,50, 90,95, 999]

Что-бы найти медиану этого ряда, нужно:

1. вначале отсортировать его: [50,90,95,100,999]
2. Медианой этого ряда является - серидина ряда (по счету). Третий элемент. То есть 95-ть.

Как видим, абсолютные значения крайних элементов не влияют на "сглаженное значение". Важно просто что "они больше других", и они просто попадают "на края". Рояль играет только "серединка".
Если элементов четное количество, скажем [50,90,95,97,100,999], то медианой будет среднеарефметическое двух средних элементов (95+97)/2=96.

Втора часть - "скольжение". Предположим мы сделали новый замер: 85. Нам нужно добавить его кряду. Но, что-ра длина ряда (размер массива) остался неизменным один элемент из него нужно выкинуть. Выкидываем "самый старый" (то есть 100, в исходных данных он у нас был "самым первым замером").

Получаем: [50,90,95,999].  Добавляем к нему новый замер 85, имеем: [50,90,95,999,85] . Опять сортируем [50,85,90,95,999] и берем средние значение....

И так дальше, следующим мы будем выкидивать 50, потом 90, потом 95, потом 999... Если все новые замеры у нас будут "маленькими", то 999 так никода и не попадет в центр ряда. И этот "большой выброс в сторону" так и останется незамеченым.
 

Ну а можно, конечно и без скольжения... тоже "по тупому": сделал пять замеров - отсортировали, нашли середину, выкинули все сделали следующие пять замеров, отсортировали нашли их середину и т.д.

Но в этом случае приходится дольше ждать пока "медиана созрела" (при скольжении при каждом новом замере мы имеем сразу новую, актуальную медиану). И нужно сортировку реализовывать полноценную. А при скольжении... мы фактически добавляем только один элемент в уже отсортированный массив. Только один не на своем месте, следовательно сортировку можно оптимизировать под этот случай. Только один не "на на своем месте".

inspiritus
Offline
Зарегистрирован: 17.12.2012

Хмм... В случае потенциометрического датчика (скажем положения) , я, не  морочась программной реализацией, просто подбираю конденсатор, который, сглаживая дребезг, не увеличивает реакцию напряжения критично для наблюдаемого процесса. В остальных случаях, например выбросы , можно срезать дополнительным стабилитроном.

dimax
dimax аватар
Онлайн
Зарегистрирован: 25.12.2013

leshak, очень интересно! Хочется посмотреть  как оно в деле будет :) Тогда жду рабочего примера. У меня есть нечто подобное, но базируется малость на других принципах. Вот тут я свой код и описание выкладывал, http://arduino.ru/forum/programmirovanie/rabota-s-atsp-v-spyashchem-rezhime-adc-noise-reduction сообщение №4.

AS
Offline
Зарегистрирован: 12.03.2014

inspiritus пишет:

Хмм... В случае потенциометрического датчика (скажем положения) , я, не  морочась программной реализацией, просто подбираю конденсатор, который, сглаживая дребезг, не увеличивает реакцию напряжения критично для наблюдаемого процесса. В остальных случаях, например выбросы , можно срезать дополнительным стабилитроном.

Как писалось выше, это уже дело религии. Мне проще логическое "И" реализовать в программе, вам, видимо, будет проще аппаратно.

leshak
Offline
Зарегистрирован: 29.09.2011

inspiritus пишет:

Хмм... В случае потенциометрического датчика (скажем положения) , я, не  морочась программной реализацией, 

Значит, скорее всего вы пришли в "мир ардуино" из мира "радиотехники". Вообщем это тоже "религиозный ворос". Мне, как программеру, все что "за пределами контроллера" - это гиморой. Каждая "лишняя деталька" воспринимается как "что-то мутное и если можно обойтись...".. Так что и дребезг кнопки, и потенциометр проще и быстрее "програмно" (лично мне). Конденсатора может банально не оказатся под руками... а програмно и "менять настраивать" легче....

Хотя ваш подход - не менее правильный чем мой. Тут скорее каждый выбирает область "где уверенней себя чувствует".

dimax
dimax аватар
Онлайн
Зарегистрирован: 25.12.2013

inspiritus, AS, leshak, товарищи, надеюсь мух от котлет все отделяют? т.е. разделяют сейчас в суждениях внешний шум, приходящий на вход и "внутримозговой" в меге :) Естессно первый лучше лечить аппаратно, а второй никак кроме как программно не вылечить.

AS
Offline
Зарегистрирован: 12.03.2014

dimax пишет:

 "внутримозговой" в меге :) 

А что это такое?

dimax
dimax аватар
Онлайн
Зарегистрирован: 25.12.2013

AS, это глубокая тема, откуда этот шум. Помехи от соседних элементов в чипе,  девиация порога срабатования компаратора, шум всех активных компонентов. Всё это даёт суммарную девиацию +/- 2 отсчёта ацп при любом идеальном входящем сигнале.

inspiritus
Offline
Зарегистрирован: 17.12.2012

dimax пишет:

AS, это глубокая тема, откуда этот шум. Помехи от соседних элементов в чипе,  девиация порога срабатования компаратора, шум всех активных компонентов. Всё это даёт суммарную девиацию +/- 2 отсчёта ацп при любом идеальном входящем сигнале.

угу, полностью согласен, плюсанул...

...только вот в у топикстартера вопрос был про шумы хренового потенциометра ;)

... Точно, из радиотехники, с кучей деталек в коробочках и паяльной станцией, живущей постоянно на столе:)

Ардуинство все же не чистое программирование, а в равной мере сплав программера и электронщика, чего всем и желаю :)

kholonkin
Offline
Зарегистрирован: 14.10.2013

Так сложно применить вменяемый потенциометр? Или смазать маслом то что имеем?

dimax
dimax аватар
Онлайн
Зарегистрирован: 25.12.2013

Вообще нападки Т.С. на потенциометр по крайней мере странны, всё таки потенциометр не человек, ему перетаптываться сноги на ногу не нужно, с чего сопротивлению меняться, когда его не трогают. Если у Т.С. шум +/- 2 единицы, то это шум внутри меги. Если чуть больше -то возможно длинные провода или отсутствие конденсатора на ноге aref (которого на ардуино по дефолту нет). У реально иссохшегося, протёртого до дыр потенциометра скорее всего контакт будет пропадать полностью от любой лёгкой вибрации, но это  значит что показание будет прыгать уже максимально.

leshak
Offline
Зарегистрирован: 29.09.2011

dimax пишет:

leshak, очень интересно! Хочется посмотреть  как оно в деле будет :) 

Вот, выдрал из полу-заброшенного проекта и залил... https://bitbucket.org/alxarduino/leshakfilters

Правда сразу предупреждаю.. это писалось "для себя, сырое...", вообщем "внутреняя кухня в процессе".

Пример "длясебятности":  размер "окна" ,только не четный должные быть (так код проще ;) И проверок этого - нет.

leshak
Offline
Зарегистрирован: 29.09.2011

leshak пишет:

Andrey_Y_Ostanovsky пишет:

leshak пишет:

Минус его решения только один: чуть чуть сложнее для понимания "как оно работает".

Для честной реализации - надо еще и брать не 5 * old.value, а действительно "пять предыдущих результатов".

Неа.. тут все "честно".....

Хм... похоже все-таки вы были правы. Не совсем честная это формула (хотя и вполне достаточная для задачи ТС).

Попробовал тест написать.... не дает она ожидаемых значений. Если верить:Скользящая средняя — Википедия, то в формуле присуствует "самое старое значение":

А что-бы всегда иметь "самое старое значение", нам фактически нужно хранить в памяти "все окно целиком". То есть все-таки нужно иметь в памяти массив размером averageFactor.

Эх.... а так многообещающе выглядело. Значит "скольжение", позволяет избавиться нам от проблемы переполнения, но не от проблемы расхода памяти на больших avergeFactor.

К тому же, на больших averageFactor мы будем иметь проблему на малых значениях замеров.  Когда sensorValue<averageFactor, а нас деление sensorValue/averageFactor будет давать значение меньше 1-ницы. Что после округления в int приведет к нулю. Вообщем при sensorValue<averageFactor мы будем иметь проблемму постоянного нуля на выходе. Так что, видимо, нужно, все-таки, переходить на хранение среднего в double.

Вообщем... прийдется еще похмурить мозг с этим. Что-бы добится "честности".

kiril22
Offline
Зарегистрирован: 06.11.2013

dimax пишет:

Вообще нападки Т.С. на потенциометр по крайней мере странны, всё таки потенциометр не человек, ему перетаптываться сноги на ногу не нужно, с чего сопротивлению меняться, когда его не трогают. Если у Т.С. шум +/- 2 единицы, то это шум внутри меги. Если чуть больше -то возможно длинные провода или отсутствие конденсатора на ноге aref (которого на ардуино по дефолту нет). У реально иссохшегося, протёртого до дыр потенциометра скорее всего контакт будет пропадать полностью от любой лёгкой вибрации, но это  значит что показание будет прыгать уже максимально.

Дело в том, что в реальности этот "потенциометр" представляет из себя самодельный, ну или явно не серийный, проволочный переменный резистор. Примерно такой:

 

И конструктивно его поменять на что-то приличное гораздо сложнее, чем написать программное усреднение его показателей.

dimax
dimax аватар
Онлайн
Зарегистрирован: 25.12.2013

kiril22, ну и что , что самодельный, это ещё не повод все проблемы на него сваливать :) У вас есть какой нибудь хороший измерительный прибор? Мультиметр приличный или осциллограф, вот им проверьте меняется ли напряжение с движка резистора само собой, или нет.  Если у вас скачет более 2 единиц, то и на другом приборе вы засечёте колебания более 10 милливольт.

kiril22
Offline
Зарегистрирован: 06.11.2013

Померить есть чем, да вот физически сделать это нереально, т.к. все это хозяйство за пару сотен км от меня. И в следующий раз  когда поеду я смогу это сделать, но проще же будет мне залить  прошивку с усреднением, чем искать причину скачков. Еще один факт против переменника, это то, что в процессе отладки переменное сопротивление из-за сложностей подключения заменялось рядом постоянных резисторов и скачков не было. НО! я не берусь утверждать, что вся проблема исключительно в переменнике; вполне допускаю, что  присутствует и шум контроллера. Однако, если даже предположить, что шумит исключительно контроллер, разве усреднение его шума не является решением проблемы? Мне кажется, для обоих первопричин решение вполне годное.

Спасибо Вам за помощь и дискуссию.

ps: на входе микроконтреллера стоит 0,1 мкф на землю.

dimax
dimax аватар
Онлайн
Зарегистрирован: 25.12.2013

leshak, по поводу медианного фильтра нужна доп.консультация :) Куда там класть прочитанные значения с аналогового входа? И откуда потом результат забирать?

leshak
Offline
Зарегистрирован: 29.09.2011

dimax пишет:

leshak, по поводу медианного фильтра нужна доп.консультация :) Куда там класть прочитанные значения с аналогового входа? И откуда потом результат забирать?

Два сопсоба доступны

Способ 1:

filter.registerValue(int measureValue) - класть
filter.getCurrentValue() - Забирать.

Забирать можно сколько хочешь раз (будет возвращать одно и тоже, до тех пор пока не будет "покладено" новое значение с помощью getCurrentValue()).

Способ 2:

filter.process(int measureValue) - сразу "кладет" новое значение, и возвращает "обновленное фильтрованное".

Кстати "Способ 2" показан в примере идущим с библиотекой. Только в том примере "значения" берутся из массива предопределенного, а вы будете брать из analogRead. То есть что-то типа такого:   

filteredValue=filter.process(analogRead(A0));

 

 

leshak
Offline
Зарегистрирован: 29.09.2011

"Это был воскресный день и я не лазил по карманам..." (С)  в воскресенье отдыхать - вот мой девиз ;)

Вообещем пока светит солнце и вертолеты на головой не баражируют.... решил и фильтрацию средним тоже в виде библиотчески оформить.

Закинул туда же. https://bitbucket.org/alxarduino/leshakfilters

Заимплементил оба варианта. И упрощенный вариант (без выделения массива под хранение данных), и "честное скользящие".

Получились вот такие результаты:

            Input=[5, 10, 15, 20, 25, 30, 35]

FilteredSimple=[5, 6, 7, 10, 13, 16, 20]
  FilteredReal=[5, 6, 8, 11, 15, 20, 25]
      Expected=[5, 6, 8, 11, 15, 20, 25]

 

Input - это входящие "сырые значения".
FilteredSimple - это фильтрованое "упрощенным способом" (примерно как в #7)
FilteredReal -это фильтрованное "по честняку"
Expected - это я "отфильтровал" руками на бумажке. Для "самопроверки".

P.S. И еще туда же добавил примерчик, где читается A0, и выводится в Serial сырое прочитанное значение, и отфильтрованное тремя способами медианой, "упрощенно", и "по честному". Так что можно сравнить что работает лучше в каждой конкретной ситуации.

 

 

sasha_programist
Offline
Зарегистрирован: 11.11.2013

люди! какой у вас програматор выбран на ардуино иде? а то я у себя поменял и забыл какой был до етого по умолчанию

dimax
dimax аватар
Онлайн
Зарегистрирован: 25.12.2013

leshak, Потестировал ваш фильтр :) Небольшая девиация результата к сожалению есть в любом из 3х вариантов, причём наименьшая как мне показалось у simpleAvgFilter. Скорострельность хорошая, наверное единицы миллисекунд занимает весь процесс.

leshak
Offline
Зарегистрирован: 29.09.2011

dimax пишет:

leshak, Потестировал ваш фильтр :) Небольшая девиация результата к сожалению есть в любом из 3х вариантов, 

Ну так не существует же "идеального фильтра" на все случае жизни. Зависит и от вида шума. Медиана - лучше работает для кратковременных импульсных помех, лучше справляется с амплитудными выбросами. А вот небольшие постоянные "плавания", она не сильно хорошо давит.

"Уровень фильтрации" можно увеличить если увеличить "размер окна". Не по 5-ти замерам делать усреднее, а скажем по 25-ти... Только расплатой за это будет что когда сигнал поменяется "по настоящему", на выходе фильтра мы увидем это "с запазданием". Чем сильнее "давим", тем медленее наша система будет реагировать на реальные события.

А еще можем комбинировать фильтры. Выход одного подавать на вход другого. скажем A0->median->avg

Тогда медиана будет нам убирать высокачастотные помехи, а avg - убирать малые "плавания".

А можем еще погуглить и найти десятки разных фильтров :) Или просто вообще потупому:

int newSensorValue=analogRead(A0);

if( abs(newSensorValue-sensorValue)>10)){
    sensorValue=newSensorValue;
}

Тогда все "плавания" меньше 10-ти, будут вообще тупо игнорится.... можно сделать нелинейность реакции на отклонения, можно сделать имитацию "инерционности" и т.д. и т.п.

А еще... тут пришла подозрение, а может вам вообще не фильрация нужна?  В чем вообще проблематичность того что аналоговый датчик "плавает" на 3-4 "попугая"? Как правило это "не проблема". Скажем если вы по показания датчика включаете/выключаете реле, и из-за дрожания у вас оно постоянно щелкает, так это решить нужно не фильтрацией, а гистерезисом (что и эффективней, и проще в реализации). Вообщем тут уже смотреть нужно "нафига оно вообще нужно".

dimax
dimax аватар
Онлайн
Зарегистрирован: 25.12.2013

leshak пишет:

А еще... тут пришла подозрение, а может вам вообще не фильрация нужна?  В чем вообще проблематичность того что аналоговый датчик "плавает" на 3-4 "попугая"? Как правило это "не проблема". Скажем если вы по показания датчика включаете/выключаете реле, и из-за дрожания у вас оно постоянно щелкает, так это решить нужно не фильтрацией, а гистерезисом (что и эффективней, и проще в реализации). Вообщем тут уже смотреть нужно "нафига оно вообще нужно".

Иногда критично и +/- 1 один попугай. В вольтметре например. Я могу смирится с тем, что он врёт на свою положенную величину, но с тем, что он всё время меняет показания не могу :) Ну вот тот мой алгоритм, на который я давал ссылку я использую в зарядно-разрядном устройстве, там 1 попугай -это  6 милливольт,  при рассчёте внутреннего сопротивления, когда вычисляется сопротивление из очень незначительной дельты напряжения - это весьма существенная единица, которой не хотелось бы жертвовать.

kiril22
Offline
Зарегистрирован: 06.11.2013

leshak пишет:

А еще... тут пришла подозрение, а может вам вообще не фильрация нужна?  В чем вообще проблематичность того что аналоговый датчик "плавает" на 3-4 "попугая"? Как правило это "не проблема". Скажем если вы по показания датчика включаете/выключаете реле, и из-за дрожания у вас оно постоянно щелкает, так это решить нужно не фильтрацией, а гистерезисом (что и эффективней, и проще в реализации). Вообщем тут уже смотреть нужно "нафига оно вообще нужно".

Этот потенциометр задает параметр "установленная температура", который отображается на LCD. Так вот, там показания и прыгают, что исключительно визуально не красиво.

ourlive
Offline
Зарегистрирован: 26.05.2012

Округлите до 5-10ти единиц в таком случае, точность установки это кардинально не изменит. Установочные данные станут немного более дискретными, но код упростится значительно.

kiril22
Offline
Зарегистрирован: 06.11.2013

как округлить? есть пример?

ourlive
Offline
Зарегистрирован: 26.05.2012
int a=105/10*10; //результат равен 100 - округление до 10ти единиц.

 

dimax
dimax аватар
Онлайн
Зарегистрирован: 25.12.2013

У методики обычных округлений проблема в том, что всегда найдётся такое входное напряжение, которое попадёт в неустойчивое  состояние, и результат задребезжит, польза лишь в том, что такое состояние будет в n раз реже, при округлении до n. Как показала практика из самых простых средств устранения девиации с потерей разрядности  самое эффективное -это сравнение.

int aread, newread;
void setup() {
Serial.begin(9600);
}
void loop()
{  
newread = analogRead(A5);
if (newread > aread +2 || newread < aread -2 ) aread=newread;
Serial.println(aread);
 }
leshak
Offline
Зарегистрирован: 29.09.2011
if (newread > aread +2 || newread < aread -2 ) aread=newread;

А чем это принципиально отличается от кода из #35? if(abs(newread-aread)>2)....

 

inspiritus
Offline
Зарегистрирован: 17.12.2012

Хм .. А зачем потенциометром устанавливать температуру ?

Пара-тройка кнопок и несложный код. Есть на ибэе четырехкнопочная мембранная самоклеющаяся клава , практически даром. Работает замечательно и напрямую от выходов арды, и через i2c расширитель.

... Разве что требование заказчика.

kiril22
Offline
Зарегистрирован: 06.11.2013

inspiritus пишет:

Хм .. А зачем потенциометром устанавливать температуру ?

Просто в существующее старое устройство, которое раньше работало на рассыпухе, устанавливаются мовременные "мозги". А этот потенциометр конструктивно связан с устройством. Я об этом выше писал.

ourlive
Offline
Зарегистрирован: 26.05.2012

Если уж теоретизировать, то можно и без введения дополнительных переменных обойтись, только это всё бессмысленно усложняет код для второстепенных функций.

dimax
dimax аватар
Онлайн
Зарегистрирован: 25.12.2013

leshak пишет:

if (newread > aread +2 || newread < aread -2 ) aread=newread;

А чем это принципиально отличается от кода из #35? if(abs(newread-aread)>2)....

Идентично конечно, но этот просто нагляднее,  не все хорошо понимают что такое модуль числа, и как его использовать :)

kisoft
kisoft аватар
Offline
Зарегистрирован: 13.11.2012

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

 

leshak
Offline
Зарегистрирован: 29.09.2011

kisoft пишет:

остальное - философия 

Дык это же "наше фсё" ;)

HWman
HWman аватар
Offline
Зарегистрирован: 26.02.2013

Не знаю говорили ли тут что об оверсемплинге, вот результат 16 семплов:

Можно взять 32 или 64 семпла, шумы тогда будут ещё меньше.

http://arduino.ru/forum/proekty/mnogokanalnyi-voltmetr-c-lcd-displeem-na-atmega-8#comment-57555

При 256 семплах шумов почти нету вообще, но ресурсы хавает капитально.

Sloper
Sloper аватар
Offline
Зарегистрирован: 30.03.2015

Подниму темку:

Я, что бы сгладить значени потенциометра, вот такой код накидал, работает на ура. 

int Pot1Max = 0;
int Pot1Min = 1024;

// ...


int Pot1Val = analogRead(Pot1Pin); 
    // ищем мах мин отклонений и получаем среднее значение
    if (Pot1Val > Pot1Max) Pot1Max = Pot1Val;
    if (Pot1Val < Pot1Min) Pot1Min = Pot1Val;
    
    Pot1Val = (Pot1Max+Pot1Min)/2;

    // отрабатываем поворот ручки потенциаометра
    if ((Pot1Val-Pot1Min)>5) Pot1Min=Pot1Val; // 5 - фактор сглаживания. Подбирается под конкретные дерганья. 
    if ((Pot1Max-Pot1Val)>5) Pot1Max=Pot1Val; // 5 - фактор сглаживания. Подбирается под конкретные дерганья. 
    
    display.print(Pot1Val);