Усреднение показаний АЦП имеет странные шаги на графике

Я читаю значения АЦП для измерения температуры и усредняю ​​их. Но график от него показывает "шаги", а не от реальных шагов увеличения значений. кто-нибудь знает, почему это произошло?

#include "arduino.h"

#define AVG_COUNT 1300
#define AVG_MAX_COUNT (2147483647/1023)
#define AVG_DURATION_TIME (AVG_COUNT * 150)

#define LOOP_INTERVAL 250

#define LOOP_INTERVAL_X 100

#define E 2.7182818284
#define AREF 4945.0

#define VREF 4945.0 // 3004 at about 22°C and 3012 at about 0°C
//#define S1_R_REF 15000.0
#define S1_R_REF_DIV4 3750.0
#define S1_RM 6260

#define S1_A_1 0.003354016
#define S1_B_1 0.0002744030
#define S1_C_1 0.00000366694
#define S1_D_1 0.000000137549

byte last_ADMUX = 0;
#define ADC_BANDGAP ((1 << MUX3) | (1 << MUX2) |  (1 << MUX1))
#define ADC_RAW_BANDGAP 1110

#define T_B 5.255
#define T_A_x100 0.65

int temperature;

#define SUM_COUNT 10

int getADCRaw(){
    //Start conversion
    ADCSRA |= (1 << ADSC);

    //wait for Measurment to finish
    while(ADCSRA & (1 << ADSC));

    return (ADCL | (ADCH << 8));
}

int getTempX100(int RntcDiv4){
    return (int) (100 / (S1_A_1 + S1_B_1*log(RntcDiv4/S1_R_REF_DIV4) + S1_C_1*pow(log(RntcDiv4/S1_R_REF_DIV4),2) + S1_D_1*pow(log(RntcDiv4/S1_R_REF_DIV4), 3) ));
}


int getTempAvg(int avgCount){
    unsigned long rmSum = 0;

    for(int i = 0; i < avgCount; i++){
        rmSum += (unsigned long) getADCRaw();;
    }

    float rmAvg = mapFloat((float)rmSum / avgCount, 0, 1023, 0, AREF);

    int rntcDiv4 = (int) ((VREF / rmAvg - 1) * S1_RM / 4);

    int tempCelsiusX100 = getTempX100(rntcDiv4) - 27315;

    delay(1);
    return tempCelsiusX100;
}

unsigned long getXBase10(byte exponent){
    unsigned long rValue = 1;

    for(;exponent > 0; exponent--){
        rValue *= 10;
    }
    return rValue;
}

void setup(){
    Serial.begin(57600);
    //Pin A0input
    DDRC &= ~(1 << PC0);

    ADMUX |= (1 << REFS0);  // 5V AREF (ADC0 is 0 by default)
}


unsigned long printTime = 0;
unsigned long nowMillis = 0;
unsigned long measTime = 0;

unsigned long lastPrintTime = 0;

#define INTERVAL 250

void loop(){
  nowMillis = millis();
  int temp = getTempAvg(20);

  measTime = millis() - nowMillis;

  while(millis() - lastPrintTime < INTERVAL)
    delayMicroseconds(333);

  Serial.println(measTime);

  Serial.flush();
  lastPrintTime = millis();
}

При усреднении я хотел увеличить разрешение, чтобы не было таких больших шагов. И более точные показания. Зачем вообще эти ступеньки?

большие шаги и маленькие шаги


person Tobey66    schedule 28.12.2019    source источник


Ответы (2)


Я не проверял всю целочисленную математику, чтобы убедиться, что у вас нет проблемы с округлением, но предположим, что у вас ее нет, а 0,1 градуса соответствует разрешению вашего АЦП, тогда...

Проблема в том, что у вас недостаточно шума во входной цепи.

Если вам не хватает шума, то АЦП выдаст значение, наиболее близкое к реальной температуре. Это не меняется, поэтому, если вы усредните группу, вы получите практически одинаковое значение.

С другой стороны, если в аналоговом сигнале, который вы дискретизируете, присутствует шум в несколько младших разрядов, то АЦП будет выдавать значения в соответствии с распределением вероятностей, и центр этого распределения — среднее значение — изменится. отражать даже небольшие изменения реальной температуры.

Правильный термин для этого — «дизеринг». Его чаще понимают при квантовании изображений, но он также используется для квантования одномерных сигналов: https://en.wikipedia.org/wiki/Dither

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

Вероятно, самый простой способ добавить шум в вашу схему — создать его самостоятельно. Здесь есть хорошая статья о том, как это сделать с помощью arduino: nofollow noreferrer">https://thecavepearlproject.org/2017/02/27/enhancing-arduinos-adc-resolution-by-dithering-oversampling/

person Matt Timmermans    schedule 01.01.2020
comment
Вау, я новее подумал, что шум может быть полезен. Спасибо за Ваш ответ! - person Tobey66; 02.01.2020

Проблема заключается в том, что ваше среднее значение является средним за очень короткий период времени. Предполагая, что для завершения A2D требуется 1 мкс, вы берете данные в окне 20 мкс. Оттуда процедура выполняет простое среднее (rmSum/avgCount) и поиск. Оттуда вы делаете некоторые, возможно, слишком сложные вычисления, чтобы получить один результат. Это окончательное значение (tempCelsiusX100 и temp) никогда не используется как часть среднего значения в следующем цикле. Температура меняется относительно медленно; нормально снимать 20 показаний подряд, но опять же, это фактически одно и то же показание. Вам нужно создать взвешенное скользящее среднее, которое использует предыдущие значения и дает среднее значение за гораздо более длительный период времени. Например, в части цикла вы получаете новое значение каждые ~ 250 мс, вы должны усреднить 10 из этих значений, учитывая общее среднее значение за 2,5 секунды. Я бы порекомендовал взвешенное скользящее среднее, где самое последнее значение взвешивается выше, чем предыдущее. Использование скользящего буфера из 10 значений ограничивает влияние показаний слишком далекого прошлого.

person Joe Thomas    schedule 01.01.2020