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

Dubolom

✩✩✩✩✩✩✩
2 Фев 2021
77
0
Приветствую.
Вопрос по ADS1115

Иногда для аналоговых датчиков используют Усреднение значений.
Нужно ли это делать с АДСкой? или она сама там усредняет)?
Если нужно - то каким макром?

И второй вопросец:
Вольтаж с АДСки рассчитываем так

adc = ads.readADC_SingleEnded(0);
float voltage= float(adc) * 0.1875 / 1000.0;


А почему не

float(adc) *0.1875 / 1024.0

Там тоже всё учтено)?

// ads.setGain(GAIN_TWOTHIRDS); | 2/3х | +/-6.144V | 1bit = 0.1875mV | default
// ads.setGain(GAIN_ONE); | 1х | +/-4.096V | 1bit = 0.125mV |
// ads.setGain(GAIN_TWO); | 2х | +/-2.048V | 1bit = 0.0625mV |
// ads.setGain(GAIN_FOUR); | 4х | +/-1.024V | 1bit = 0.03125mV |
// ads.setGain(GAIN_EIGHT); | 8х | +/-0.512V | 1bit = 0.015625mV |
// ads.setGain(GAIN_SIXTEEN); | 16х | +/-0.256V | 1bit = 0.0078125mV |
 

kDn

★★★★★✩✩
18 Ноя 2019
1,103
437
А почему не

float(adc) *0.1875 / 1024.0
Потому что для дефолтной чувствительности в 1bit = 0.1875mV вы сначала получаете значение в mV умножением значения с АЦП на вес бита, а затем делите на 1000 для перевода с mV в V (вольты). Вы же когда граммы в килограммы переводите, то не на 1024 ведь делите, не так ли? :)

По усреднению все просто - делается N замеров, далее считается среднее арифметическое или квадратическое или еще какое нужное :), целесообразность усреднения зависит от наличия случайных ошибок (выбросов), а также шума младших битов. Если ни того, ни другого нет или ничтожно малы, то можно и без усреднений.
 
  • Лойс +1
Реакции: Dubolom

Геннадий П

★★★★★★✩
14 Апр 2021
1,975
634
45
Больше выборок - точнее результат.
Если АЦП поддерживает выбор точности замера - можно использовать его, получается почти то же самое: чем дольше замер - тем точнее результат.

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

Dubolom

✩✩✩✩✩✩✩
2 Фев 2021
77
0
С ADSкой понятно.
Вопрос по усреднению значения на аналоговом входе.
Вот нашел пример в инетах - что скажите - стоит на него опираться?
Я же Dubolom)) Меня бы пнуть в нужную сторону)
C++:
//  Функция определения среднего значения напряжения                                          // Эта функция возвращает среднее арифметическое значение данных массива arr без учёта одного максимального и одного минимального значения массива.
double averagearray(int* arr, int number){                                                    //
    int i,max,min;                                                                            // Объявляем переменные для цикла и экстремумов
    double avg;                                                                               // Объявляем переменную для вывода среднего значения
    long amount=0;                                                                            // Определяем переменную для подсчёта среднего значения
    if(number<=0){ Serial.println("Error number for the array to avraging!/n");  return 0;}   // В массиве arr не может быть 0 и менее элементов
    if(number< 5){ for(i=0; i<number; i++){amount+=arr[i];} avg = amount/number; return avg;  // Если в массиве arr менее 5 элементов, то среднее значение является средним арифметическим значением
    }else{                                                                                    // Если в массиве arr более 5 элементов, то среднее значение считаем иначе ...
        if(arr[0]<arr[1]){ min = arr[0]; max=arr[1];}                                         // Определяем минимальное и максимальное число из первых двух элементов массива
        else             { min = arr[1]; max=arr[0];}                                         // Определяем минимальное и максимальное число из первых двух элементов массива
        for(i=2; i<number; i++){                                                              // Проходим по остальным элементам массива
                 if(arr[i]<min){ amount+=min; min=arr[i]; }                                   // Если значение очередного элемента меньше минимального,  то добавляем к значению amount предыдущее минимальное значение  и обновляем значение min
            else if(arr[i]>max){ amount+=max; max=arr[i]; }                                   // Если значение очередного элемента больше максимального, то добавляем к значению amount предыдущее максимальное значение и обновляем значение max
            else               { amount+=arr[i];          }                                   // Если значение очередного элемента находится в пределах между min и max, то добавляем значение этого элемента к amount
        }                                                                                     //
        avg = (double) amount/(number-2);                                                     // Получаем среднее арифметическое значение (без учета значений первых двух элементов массива arr, т.к. они не добавлялись к amount)
    }                                                                                         //
    return avg;                                                                               // Возвращаем полученное среднее значение
}
 

bort707

★★★★★★✩
21 Сен 2020
3,066
915
странный код, прямо в комментариях написано, что он считает не совсем среднее арифметическое
В заголовке - "функция возвращает среднее арифметическое значение данных массива arr без учёта одного максимального и одного минимального значения"
А в реальности еще хуже - см комментарий в последней строке "получаем среднее арифметическое значение без учета значений первых двух элементов массива"

Очевидно, что две этих величины совсем не одно и то же.


Не надо вам никаких сложных функций - просто сложите все значения и разделите на число элементов. Код для такого, думаю, напишете и сами.
 
Изменено:

Dubolom

✩✩✩✩✩✩✩
2 Фев 2021
77
0
Вот вроде пример простой.
Но не совсем понятно
В примере всё в Лупе считается.
А у меня опрос 1 раз в минуту.
Или это он внутри опроса 500 раз опросит датчик?


C++:
/*

  Элементарная реализация среднего арифметического. Сложили NUM_READINGS измерений,

  затем разделили сумму на NUM_READINGS и всё!

  Является "частным случаем" предыдущего фильтра

  Время выполнения примерно равно: 10 значений 50 мкс, 50 значений 92 мкс, 100 значений 146 мкс

*/

#define NUM_READINGS 500

int average;

void setup() {

  Serial.begin(9600);

}

void loop() {

  long sum = 0;                                  // локальная переменная sum

  for (int i = 0; i < NUM_READINGS; i++) {      // согласно количеству усреднений

    sum += analogRead(0);                        // суммируем значения с любого датчика в переменную sum

  }

  average = sum / NUM_READINGS;                  // находим среднее арифметическое, разделив сумму на число измерений

  Serial.println(average);                       // для примера выводим в порт

}
 

bort707

★★★★★★✩
21 Сен 2020
3,066
915
Или это он внутри опроса 500 раз опросит датчик?
как зададите - так и опросит.
Слушайте. неужели вам нужны чьи-то чужие примеры. чтобы посчитать среднее по N измерениям? в школе это проходят в пятом классе...
 
  • Лойс +1
Реакции: Геннадий П

Dubolom

✩✩✩✩✩✩✩
2 Фев 2021
77
0
Среднее то я считать умею))
А примеры смотрю потому что в них частенько встречается навороты, о которых сразу и не подумаешь, но они имеют место быть. В хороших примерах логика учитывает всякие мелочи..
а я бы написал что типа такого (это тоже пример, а не моё творение))
C++:
int a[5];
byte i=0;   // Тут я бы int поставил))
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;
}
А в предыдущем примере - взял и число опросов сменил быстренько)
А тут.. тоже можно конечно строить и думать, но...
Уже же придумали))

Вы же библиотеки проверенные тоже не переписываете постоянно?
  • А как же логика ... нужно же знать как всё работает - Скажете Вы...
  • Если во всём разбираться - жизни не хватит. Тем более изобретать изобретённое. Я стараюсь собирать для себя коллекцию готовых блоков для выполнения тех или иных задач. Потом из блоков городить продукты. И в процессе создания мыслю тоже блоками. Делаю так не только с ардуино проектами. - Отвечу я))
Вроде понятно))
 

bort707

★★★★★★✩
21 Сен 2020
3,066
915
Пример, прямо скажем, так себе. Зачем складывать пять элементов явно? А если элементов будет 500? Есть же циклы..
Коммент в строке 2 - ваш? Зачем там int?
 

Старик Похабыч

★★★★★★★
14 Авг 2019
4,272
1,303
Москва
Скользящее среднее арифметическое - очень удобная штука
аср=0,8*аср+0,2*а_нов.
Не совсем то же , что запоминать последние 5 значений, но близко к тому. И если запомнить 5 еще как то себя моежет оправдать, то запоминать последние 100 элементов уже расточительно по памяти.
Есть минусы. Изначально надо считать какое то значение с датчика и считать ,что оно среднее.
 

Геннадий П

★★★★★★✩
14 Апр 2021
1,975
634
45
Вы же библиотеки проверенные тоже не переписываете постоянно?
Иногда приходится допиливать под свои нужды.
Например выкинуть что то лишнее, какую нть функцию свою добавить, что то оптимизировать под свою железку т.к. универсальность обычно снижает производительность.
 

Dubolom

✩✩✩✩✩✩✩
2 Фев 2021
77
0
Поигрался я с усреднением. И не знаю то ли радоваться то ли нет)
Разницы в показаниях нет.
Датчик на конце висел, сравнивал 1 и 50 опросов - разницы нет...
Делал так
C++:
#define NUM_READINGS 500

int average;

void setup() {

  Serial.begin(9600);

}

void loop() {

  long sum = 0;                                  // локальная переменная sum

  for (int i = 0; i < NUM_READINGS; i++) {      // согласно количеству усреднений

    sum += analogRead(0);                        // суммируем значения с любого датчика в переменную sum

  }

  average = sum / NUM_READINGS;                  // находим среднее арифметическое, разделив сумму на число измерений

  Serial.println(average);                       // для примера выводим в порт

}
И ещё такое дело:
Перед опросом датчиков на них будет подаваться питание. Дак вот как выдержать паузу между включением и опросом без Delay? Мой вариант - таймер?) У меня там и так уже таймер в таймере на таймере))
 

Геннадий П

★★★★★★✩
14 Апр 2021
1,975
634
45
average сделай float, а не int. А то смысл теряется делать 500 замеров.

Если нужно именно int, то проще делать три замера и отбрасывать самое большое и самое маленькое значение. Обычно этого хватает.

А по поводу задержки считывания. Если питание на датчики подается вместе с контроллером - просто выдержать паузу перед первым считыванием.

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

Dubolom

✩✩✩✩✩✩✩
2 Фев 2021
77
0
average сделай float, а не int. А то смысл теряется делать 500 замеров.
Сделал - показания абсолютно одинаковые)
Делаю так -
C++:
 #define NUM_READINGS 50
float average;

void setup() {
  // открываем Serial-порт
  Serial.begin(9600);
}
void loop() {
   Serial.print("anlog read = ");
   Serial.println(analogRead(0));
   delay(2000);

long sum = 0;                                  // локальная переменная sum
  for (int i = 0; i < NUM_READINGS; i++) {      // согласно количеству усреднений
    sum += analogRead(0);                        // суммируем значения с любого датчика в переменную sum
  }
  average = sum / NUM_READINGS;                  // находим среднее арифметическое, разделив сумму на число измерений
   Serial.print("anlog read srednee = ");
   Serial.println(average);
   delay(2000);
}
А по поводу задержки считывания
А паузу я имею ввиду для того чтобы датчики успели запуститься и опроситься..

void opros() { // Опрос датчиков
digitalWrite(PinPower, HIGH); // Включаем питание датчиков и АЦП и Всей белеберды

А ВОТ ТУТ Я ДУМАЮ ПАУЗА НУЖНА...
??

DS18B20.requestTemperatures(); // опрашиваем
 

bort707

★★★★★★✩
21 Сен 2020
3,066
915
Показания одинаковые - так может у тебя там шумов нет и показания стабильны? Какое хоть значение при одном чтении и среднее?
 

Геннадий П

★★★★★★✩
14 Апр 2021
1,975
634
45
А ВОТ ТУТ Я ДУМАЮ ПАУЗА НУЖНА...
Питание в произвольное время включается или с запуском МК?

Если с запуском МК, то примерно так:
C++:
byte started = 0;
void setup() {
  // всякая инициализация и включение питания датчиков
}
void loop() {
    if (started == 0) {
        if (millis() > 2000) started = 1; // если прошло более 2сек, то поднимаем флаг
    } else {
        // слитывает датчики и т.д. и т.п.
    }
  // прочая логика
}
 
Изменено:

kostyamat

★★★★★★✩
29 Окт 2019
1,098
632
average сделай float, а не int. А то смысл теряется делать 500 замеров.
А смысл во флоат, если он long int на int делит? Имхо, усе правильно у человека было, суммировал 500 раз замер/поделил. Не выскочило бы только за пределы переменной (лень искать разрядность adc и пересчитывать). 500 раз явный перебор, раз 10 хватит, я думаю.