Усреднение значений с аналогового входа

Sanyaba
Sanyaba аватар
Offline
Зарегистрирован: 27.07.2015

Есть идея усреднения значений, не могу понять как сделать: берем массив например из 5 ячеек, в каждую ячейку массива пишем значения с аналоговго входа при каждом проходе цикла loop и далее делим сумму всех ячеек на количество ячеек, таким образом получаем как бы среднее арифметическое. 

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

А так же может кто подскажет хорошие способы для усреднения, заранее благодарен ;)

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

Самое лучшее усреднение это конденсатор на аналоговом входе.

Sanyaba
Sanyaba аватар
Offline
Зарегистрирован: 27.07.2015

Руки чешутся, приехали АЦП I2C ADS1115 а значения скакают...

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

А зачем их сдвигать? Ведь сумма не зависит от перестановки слагаемых.
Делайте обычное среднее арифметическое, на первое время хватит и такой точности.

Logik
Онлайн
Зарегистрирован: 05.08.2014

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

ПС. ADS1115 честная, сам проверял, если на входе постоянка, то ничё не скачет, стоит как влитое. В отличии от ардуиновского АЦП кстати. Если скачит - шум на входе.

Sanyaba
Sanyaba аватар
Offline
Зарегистрирован: 27.07.2015

Tomasina пишет:
А зачем их сдвигать?

Как зачем? мы записываем новые показания в первую ячейку сдвигая все старые показания на ячейку ниже, а соответственно из последней удаляются самые старые показания.

 

Logik пишет:
Если скачит - шум на входе.

Все верно, шум есть так как щупаю наряжение с недоделанного лабараторного БП.

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

Не нужно ничего сдвигать. Просто использовать последние n измерений. А их сумма от перестановки не изменится, только лишнее действие и время процессора.
http://arduino.ru/forum/programmirovanie/usrednenie-znachenii-s-potentsi...

Sanyaba
Sanyaba аватар
Offline
Зарегистрирован: 27.07.2015

код работает странно:

коэффициент сглаживания = 2 показывает 3.1735V

коэффициент сглаживания = 3 показывает 3.1151V

коэффициент сглаживания = 4 показывает 3.0852V

коэффициент сглаживания = 5 показывает 3.0689V

и это при неизменном напряжении на входе!

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

Там надо внести исправления (переделать все на float и поменять местами две строчки), ниже расписано, а ту запись нет возможности исправить.
И учтите, что первые n измерений будут по нарастающей.

Волшебник
Offline
Зарегистрирован: 22.12.2016

В самой ардуино ИДЕ есть пример

/*

  Smoothing

  Reads repeatedly from an analog input, calculating a running average
  and printing it to the computer.  Keeps ten readings in an array and
  continually averages them.

  The circuit:
    * Analog sensor (potentiometer will do) attached to analog input 0

  Created 22 April 2007
  By David A. Mellis  <dam@mellis.org>
  modified 9 Apr 2012
  by Tom Igoe
  http://www.arduino.cc/en/Tutorial/Smoothing

  This example code is in the public domain.


*/


// Define the number of samples to keep track of.  The higher the number,
// the more the readings will be smoothed, but the slower the output will
// respond to the input.  Using a constant rather than a normal variable lets
// use this value to determine the size of the readings array.
const int numReadings = 10;

int readings[numReadings];      // the readings from the analog input
int readIndex = 0;              // the index of the current reading
int total = 0;                  // the running total
int average = 0;                // the average

int inputPin = A0;

void setup() {
  // initialize serial communication with computer:
  Serial.begin(9600);
  // initialize all the readings to 0:
  for (int thisReading = 0; thisReading < numReadings; thisReading++) {
    readings[thisReading] = 0;
  }
}

void loop() {
  // subtract the last reading:
  total = total - readings[readIndex];
  // read from the sensor:
  readings[readIndex] = analogRead(inputPin);
  // add the reading to the total:
  total = total + readings[readIndex];
  // advance to the next position in the array:
  readIndex = readIndex + 1;

  // if we're at the end of the array...
  if (readIndex >= numReadings) {
    // ...wrap around to the beginning:
    readIndex = 0;
  }

  // calculate the average:
  average = total / numReadings;
  // send it to the computer as ASCII digits
  Serial.println(average);
  delay(1);        // delay in between reads for stability
}

 

andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

Собственно, работа с массивом делается так.

Допустим, мы хотим делать усреднение по N ячейкам.

1. Определяем массив на N ячеек.

2. Делаем первое измерение (из setup()), его результат заносим во все ячейки сразу (циклом). 

3. Заводим целочисленную переменную (byte K) - номер ячейки в которую будеим писать. Инициализируем этоу переменную любым числом от 0 до N-1. Это все в setup().

4. Теперь в loop() читаем новое значение и записываем его в ячейку массива с номером K.

5. Инкрементируем переменную K, находим остаток по модулю N.

6. Считаем среднее по массиву (любым алгоритмом: арифметическое, геометрическое, медиана...) и используем его по назначению, например, выводим на экран.

В принципе, в случае среднего арифметического в п.6. можно существенно сократить объем вычислений. Для этого вместо 4-6 делаем:

4. Заводим статическую переменную S достаточно разрядности для хранения суммы.

5. После п.2 считаем сумму и заносим ее в переменную S. (в setup())

6. В loop() вычитаем из S значение K-й ячейки массива.

7. Читаем новое значение, заносим его в К-ю ячейку, а также прибавляем к сумме.

8. Инкрементируем К, находим остаток по модулю N.

Таким бразом мы изюавляемся от необходимости суммирования массива на каждом шаге.

 

nik182
Offline
Зарегистрирован: 04.05.2015

Ох...не получилось

ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

Sanyaba пишет:

Tomasina пишет:
А зачем их сдвигать?

Как зачем? мы записываем новые показания в первую ячейку сдвигая все старые показания на ячейку ниже, а соответственно из последней удаляются самые старые показания.

 

Logik пишет:
Если скачит - шум на входе.

Все верно, шум есть так как щупаю наряжение с недоделанного лабараторного БП.

Что-то логик не предложил цифровой фильтр, а ведь работает:
 

void inmax471() {
   for (int j=1; j <= 16; j++) {
   Data_max471=analogRead(Pin_max471);
   G+=Data_max471-(G>>8);
   Data_max471=G>>8; }
   }                         // END INPUT FILTER

В счётчике гейгера для замерянной последовательности из десяти значений среднее получилось - 0.24461, а вот вычисление среднего на каждой итерации (среднее со средним) на этом же блоке значений дало - 0.263529

nik182
Offline
Зарегистрирован: 04.05.2015

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

Sanyaba
Sanyaba аватар
Offline
Зарегистрирован: 27.07.2015

так все же, как получить среднее арефметическое float из 3-5 итераций самым простоым способом?

*пробовал с массивом по идеи из первого поста, но сильно начинает тупить

andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

Самое простое - сложить и поделить на 3-5.

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

Старый
Offline
Зарегистрирован: 09.10.2016
Value = (Value*2+analogRead(A0))/3;

 

andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

Старый, это уже совсем другой фильтр.

qwone
qwone аватар
Offline
Зарегистрирован: 03.07.2016
int a[5];
byte i=0;
int value;
void setup() {
}
void loop() {
  i++;
  if (i>=5) i=0;
  a[i]=analogRead(A0);
  value =(a[0]+a[1]+a[2]+a[3]+a[4])/5;
}

 

Logik
Онлайн
Зарегистрирован: 05.08.2014

ua6em пишет:

 

Что-то логик не предложил цифровой фильтр, а ведь работает:

 

void inmax471() {
   for (int j=1; j <= 16; j++) {
   Data_max471=analogRead(Pin_max471);
   G+=Data_max471-(G>>8);
   Data_max471=G>>8; }
   }                         // END INPUT FILTER

Чегож "не прндложил"?! Еще в 4-м сообщении написал мол ищите. Сколько ж можна мне его разжевывать?!

nik182 пишет:
Этот метод работает только на float. На целочисленной математике никогда не приблизится к истинному значению на величину глубины сглаживания.

Никак нет. Хотя причина вашего сомнения понятна. Действительно приходится работать с дробной частью. И она там, как суслик, она есть ))) Переменная G  в цитированом коде не случайно берется большей розрядности, например long. В ней значение хранится в форме с фиксированой запятой, старшие 24 разряда целая часть, а младшие 8 - дробная. Это четко видно в последней строке кода, где эта самая дробная часть отбрасывется.  А строкой выше мы в G засовываем входное значение, по математической формуле оно должно быб быть Data_max471/256, но т.к. формат G отакой хитрый то деление (или сдвиг) становится не нужным.  

Таким образом G как бы немножко float.

ПС. Везде выше цифра 8 и 2 в этой степени, т.е. 256 - не догма, можна написать к примеру 5 и 32, если нужно. Но 8 особо хороша еще и тем, что >>8 очень просто делается прцессором.

Logik
Онлайн
Зарегистрирован: 05.08.2014

andriano пишет:

Старый, это уже совсем другой фильтр.

На самом деле почти тоже самое, только коэффициент другой. В Value = (Value*2+analogRead(A0))/3; К=1/3 и дробную часть игнорим, а в том, что выше был к=1/256 и дробную учитываем. Разумеется из-за различия коэффициентов степень сглаживания тоже различается, но подход тот же. 

ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

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

ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

Старый пишет:

Value = (Value*2+analogRead(A0))/3;

А что получим на первой итерации при value = 0???
Не годится видимо (0*2+ 1)/3 - треть от значения

Значит первое чтение value перенести в setup

Sanyaba
Sanyaba аватар
Offline
Зарегистрирован: 27.07.2015

Спасибо все откликнувшимся, получилось пока что так:

#include <Wire.h> 
#include <LiquidCrystal_I2C.h>
#include <Adafruit_ADS1015.h>
Adafruit_ADS1115 ADS(0x48); // адрес АЦП
LiquidCrystal_I2C lcd(0x3F, 16, 2);  // адрес lcd 1602

float Voltage = 0.0;
long ads_tmp = 0;

void setup(){
  lcd.init(); // Инициализация дисплея
  lcd.backlight(); // Включаем подсветку дисплея
  // Включаем внутренний опорник АЦП ADS1115 на 4.096V
  ADS.setGain(GAIN_ONE); // 1x gain  +/- 4.096V  1 bit = 2mV  0.125mV
  ADS.begin(); //включаем АЦП
}

void loop(){

  int16_t ADS_a0;  // на выходе преобразования АЦП мы получаем 16-разрядное знаковое целое
  ADS_a0 = ADS.readADC_SingleEnded(0); // измеряем напряжение 
  
 if (ADS_a0+5 <= ads_tmp || ADS_a0-5 >= ads_tmp) {
    ads_tmp = (ads_tmp+ADS_a0)/2;
    Voltage = ads_tmp * (4.096 / 32767.5); // пересчитываем в привычные вольты
  }

  lcd.setCursor(0, 0);
  lcd.print(ADS_a0, 7);
  lcd.print("  ");
  lcd.print(ads_tmp, 7);
  lcd.print("  ");
  
  lcd.setCursor(0, 1);
  lcd.print("U=");
  lcd.print(Voltage, 4);
  lcd.print("V     ");
  
  delay(50);
  
}

 

ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

Глянуть бы ассемблерный код этой конструкции:

 if (ADS_a0+5 <= ads_tmp || ADS_a0-5 >= ads_tmp) {

Это я к чему? Не зная аsm atmega, а для привычного intel8080 результат от сложения или вычитания переменной с константой будет храниться в ячейке этой переменной, кто выделяет ячейку для хранения временного результата, компилятор? Уже в этой конструкции таких ячеек надо две

Sanyaba
Sanyaba аватар
Offline
Зарегистрирован: 27.07.2015

Весь смысл этой конструкции в данном случае, сделать простое вычесление в IF если значения попадают в +-5 едениц, иначе уже делаем чуть сложнее вычисление + предварительное вычисление в IF, а так же получается небольшая как по мне "стабилизация" выходных данных.

Tomasina
Tomasina аватар
Offline
Зарегистрирован: 09.03.2013
if (ADS_a0+5 <= ads_tmp || ADS_a0-5 >= ads_tmp)

я б заменил на это:

if (abs(ADS_a0 - ads_tmp) > 5) // если выходим за гистерезис

Или это (смотря что нужно в функционале):

if (abs(ADS_a0 - ads_tmp) <= 5)  // если в пределах гистерезиса

Суть та же, но нагляднее.

ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

Sanyaba пишет:

Весь смысл этой конструкции в данном случае, сделать простое вычесление в IF если значения попадают в +-5 едениц, иначе уже делаем чуть сложнее вычисление + предварительное вычисление в IF, а так же получается небольшая как по мне "стабилизация" выходных данных.

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

Sanyaba
Sanyaba аватар
Offline
Зарегистрирован: 27.07.2015

ua6em пишет:
Логика мне понятна, мне непонятна физическая сущность, где хранятся промежуточные результаты вычислений )))

в ads_tmp и если попадаем в IF то перезаписываем ads_tmp и Voltage

andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

Logik пишет:

На самом деле почти тоже самое, только коэффициент другой.

Ну т.е. КИХ и БИХ отличаются только коэффициентом?

 

PS. И еще: fixed ни разу не float. Даже немножко.

andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

ua6em пишет:

Не зная аsm atmega, а для привычного intel8080 результат от сложения или вычитания переменной с константой будет храниться в ячейке этой переменной, кто выделяет ячейку для хранения временного результата, компилятор? Уже в этой конструкции таких ячеек надо две

В AVR 32 РОН против 6 у i8080.

ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

andriano пишет:

ua6em пишет:

Не зная аsm atmega, а для привычного intel8080 результат от сложения или вычитания переменной с константой будет храниться в ячейке этой переменной, кто выделяет ячейку для хранения временного результата, компилятор? Уже в этой конструкции таких ячеек надо две

В AVR 32 РОН против 6 у i8080.

я выделял память в основном блоке, ну или стеком пользовался

Logik
Онлайн
Зарегистрирован: 05.08.2014

andriano пишет:

Logik пишет:

На самом деле почти тоже самое, только коэффициент другой.

Ну т.е. КИХ и БИХ отличаются только коэффициентом?

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

andriano пишет:

PS. И еще: fixed ни разу не float. Даже немножко.

И то и другое представляет числа с дробной частю. Это их обединяет и отличает от остального целого.

andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

Logik пишет:

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

Вот именно средее арифметическое по массиву - это и есть КИХ.

Цитата:

andriano пишет:

PS. И еще: fixed ни разу не float. Даже немножко.

И то и другое представляет числа с дробной частю. Это их обединяет и отличает от остального целого.

Отнюдь.

Целые - частный случай fixed point, у них нет ни мантиссы, ни порядка. В отличие от float, у которых все это есть + дополнительные свойства типа единичного старшего бита и пр. Ну и способы обработки на ЭВМ у всех fixed point (включая integer) одни, а у всех float point - другие.

Т.е. fixed point - это одно, а float - совсем другое.

 

Да, кстати, как у fixed, так и у float дробной части может не быть, так что в качестве отличительного признака не годится.

mikelari
Offline
Зарегистрирован: 14.05.2015

... на 1115  с фильтром http://arduino.ru/forum/proekty/ochen-tochnyi-voltmetr-na-arduino посты 13, 28 .

Может опытные скажут "костыли", мне же  нравится как работает.

 

Logik
Онлайн
Зарегистрирован: 05.08.2014

andriano пишет:

Logik пишет:

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

Вот именно средее арифметическое по массиву - это и есть КИХ.

Вы читаете только первое предложение из постов? Спецом цитирую два предложения.

//На самом деле почти тоже самое, только коэффициент другой. В Value = (Value*2+analogRead(A0))/3; К=1/3 и дробную часть игнорим, а в том, что выше был к=1/256 и дробную учитываем. 

Четко видно к чему относится "почти тоже самое". Убогость на массивах не рассматривается вобще.

 

Logik
Онлайн
Зарегистрирован: 05.08.2014

andriano пишет:

PS. И еще: fixed ни разу не float. Даже немножко.

И то и другое представляет числа с дробной частю. Это их обединяет и отличает от остального целого.

[/quote]

Отнюдь.

Целые - частный случай fixed point, у них нет ни мантиссы, ни порядка. В отличие от float, у которых все это есть + дополнительные свойства типа единичного старшего бита и пр. Ну и способы обработки на ЭВМ у всех fixed point (включая integer) одни, а у всех float point - другие.

Т.е. fixed point - это одно, а float - совсем другое.

 

Да, кстати, как у fixed, так и у float дробной части может не быть, так что в качестве отличительного признака не годится.

[/quote]

Может  быть, может не быть,  дополнительніе свойства и способ представления- софизм,  пи..еж и провокация. 

Есть конкретные возражения по обединяющему факту , что fixed и float  представляет числа с дробной частю и имея необходимость вести расчет в таковых числах возможно применение именно этих форматов а не других? 

ПС. про double в курсе, про разные виды fixed и float информирован, потому кончай флудить не по теме, мы и так изумлены инфой про мантисы и порядок, с 6-го класса школы не слышали ;) Сразу пиши этюд про float.