Перейти к содержанию
    

Усреднение вычислений АЦП

Подскажите хороший алгоритм для усреднения вычислений АЦП,я сделал так-

// Read the 8 most significant bits
// of the AD conversion result
unsigned char read_adc(unsigned char adc_input){
ADMUX=adc_input|ADC_VREF_TYPE;
// Start the AD conversion
ADCSRA|=0x40;
// Wait for the AD conversion to complete
while ((ADCSRA & 0x10)==0);
ADCSRA|=0x10;
return ADCH;}

while (1){....

   ta++;if(ta==21)ta=0;
   tempADC=(read_adc(0)/5);
   tempADC1=tempADC+(read_adc(0)/5);                  
   if(ta==20)temp=tempADC1/2;tempADC=0;tempADC1=0;

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

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

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

тогда понятно.

И деление на 5 я провожу в самом конце, всего один раз над переменной temp. Это сделает более точными вычисления.

 

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

unsigned char tempData[20];

unsigned char ta=0;

unsigned char i;

unsigend short temp;

 

while (1){....

 

ta++;if(ta==20) ta=0;

tempData[ta]=read_adc(0);// деление на 5 перенесено в расчет temp

 

temp = 0;

for(i=0;i<20;i++){

temp += tempData;

}

temp =temp/100; // 20*5

 

}

 

В этом случае, переменная temp будет содержать усреднение последних снятых двадцати значений, и обновляться будет

при каждом чтении АЦП.

Изменено пользователем rvk

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Ну используйте вместо for вложенный цикл while:

 

temp = 0;

i=20;

while(i--){

temp += tempData;

}

 

И кстати, поскольку temp имеет тип unsogned short, никаких переполнений быть не должно.

Потому что даже максимальное значение АЦП 255*100= 25500 все равно меньше

65536, поэтому можно смело все складывать, а затем найти среднее.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Есть еще один способ получить скользящее среднее. Инициализация:

unsigned int sum, Result;

#define shift 3 // от 1 до 7 при Ваших условиях

sum = 0;

На каждое измерение:

Result = sum >> shift;

sum = sum - Result + read_adc(0);

В зависимости от shift получается различная степень усреднения и разная скорость выхода на устоявшееся значение.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

С последним способом у меня не получилось-цифры скачут, а вот так-тоже интересно-

 ta++;if(ta==100) ta=0;
   tempData[ta]=read_adc(0);
   tempADC=0;   
   for(a=0;a<100;a++){
   tempADC+=tempData[a];}
   temp=tempADC/500;

-как нагрузку подключаю-цифры на индикаторе пробегают от нуля до нормального значения,после отключения-обратно до нуля.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Есть еще один способ получить скользящее среднее. Инициализация:

unsigned int sum, Result;

#define shift 3 // от 1 до 7 при Ваших условиях

sum = 0;

На каждое измерение:

Result = sum >> shift;

sum = sum - Result + read_adc(0);

В зависимости от shift получается различная степень усреднения и разная скорость выхода на устоявшееся значение.

Здесь, похоже, кое-что упущено. Деление, например, или сдвиг в последней операции. Иначе алгоритм предполагает нарастание результата с различной скоростью, зависящей от shift.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Не забывайте только про переполнение temp. Если значение АЦП будет больше 65535/500 = 131, то произойдет переполнение

переменной temp и значения будут неверные. Проще говоря такая формула работает если значения АЦП были меньше 0x80.

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

ведь пустые. Второе значение складывается с первым и оба делятся на 100 и т.д. пока не будет заполнен весь массив.

Чтобы с первого значения было все верно, нужно отследить весь массив до заполнения и делить пропорционально

заполнению. Но это уже мелочи.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Чтобы с первого значения было все верно, нужно отследить весь массив до заполнения и делить пропорционально заполнению.
Совсем не обязательно. Достаточно при первом обращении к фильтру проинициализировать весь массив не нулями, а этим самым первым значением. Это для случая фильтрации типа "скользящее среднее" конечно же.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Продвинутый способ, как писал в другой ветке resident, это хранить все двадцать последних значений в массиве, и делать среднее по нему, примерно вот так:

...........

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

Вот простой способ получать усреднённое значение каждый раз при получении текущего измеренного значения:

T_average = T_average + (T_current - T_average) / 20.0

где T_average - среднее значение на данный момент времени, T_current - мгновенное измеренное значение, снятое, например, с АЦП в текущий момент времени.

Этот пример НЧ-фильтра измерений практически эквивалентен усреднению последних 20 значений. Однако здесь число усреднений (20) необязательно может быть целым значением. Вообще говоря, в этой формуле число 20.0 является постоянной времени фильтра, которая вкупе с периодом измерений определяет скорость реакции (инертность) этого фильтра на резкие изменения входных измеряемых величин.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Этот пример НЧ-фильтра измерений практически эквивалентен усреднению последних 20 значений.
Вообще-то это пример фильтра EMA (экспоненциальное скользящее среднее), а не SMA (простое скользящее среднее). В общем виде EMA выглядит как рекурсия Y(i+1)=Y(i)+(X(i)–Y(i))*K, где K=2/(N+1), Y(i+1) выходное значение фильтра, Y(i) - предыдущее выходное значение фильтра, X(i) - значение текущего отсчета. N имеет тот же смысл, что и "тау" RC-цепочки. Отличие EMA от SMA еще в том, что EMA это фильтр с БИХ, а SMA - с КИХ.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Вот простой способ получать усреднённое значение каждый раз при получении текущего измеренного значения:

T_average = T_average + (T_current - T_average) / 20.0

где T_average - среднее значение на данный момент времени, T_current - мгновенное измеренное значение, снятое, например, с АЦП в текущий момент времени.

Вот пример в числах есть 10 чисел. Берем скользящее среднее 5 значений:

11, 9, 11, 9, 10, 12, 11, 7, 8, 5,

среднее____ 10, 10.4, 10.52, 9.816, 9.4528, 8.56224 Это по Вашей формуле

 

А это по массиву из последних набранных пяти значений:

11, 9, 11, 9, 10, 12, 11, 7, 8, 5

среднее____ 10, 10.2, 10.6, 9.8, 9.6, 8.6

 

Поэтому эти формулы какие угодно, только не взаимозаменяемые. И Ваша формула не проще,

она просто другая.

Изменено пользователем rvk

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Вообще-то это пример фильтра EMA (экспоненциальное скользящее среднее), а не SMA (простое скользящее среднее). В общем виде EMA выглядит как рекурсия Y(i+1)=Y(i)+(X(i)–Y(i))*K, где K=2/(N+1), Y(i+1) выходное значение фильтра, Y(i) - предыдущее выходное значение фильтра, X(i) - значение текущего отсчета. N имеет тот же смысл, что и "тау" RC-цепочки. Отличие EMA от SMA еще в том, что EMA это фильтр с БИХ, а SMA - с КИХ.

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

Конечно, численно она отличается от простого усреднения, но практически ведёт себя точно также и позволяет избежать хранения и суммирования последних N измерений, что особенно актуально для систем на маломощных вычислителях с ограниченными ресурсами (AVR, PIC).

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Конечно, численно она отличается от простого усреднения, но практически ведёт себя точно также
Да как же они могут вести себя одинаково, если у них и ФЧХ и ПХ разные!?

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Подскажите хороший алгоритм для усреднения вычислений АЦП

Если показания АЦП дрожат, то нужно думать не о том, как их отфильтровать, а о том, как устранить источник помех. А для этого нужно совсем немного, как-то:

 

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

- подводить ко всем 8 ногам контроллера, а для опорного желательно дополнительный RC-фильтр

- в непосредственной близости от него должно быть хотя бы две пары конденсаторов по питанию (электролит+керамика)

- если в схеме есть сильнопотребляющие элементы (светодиоды, например), то землю от них до источника вести отдельным проводником, чтобы падение напряжения от скачков тока не вызывало скачков напряжения на земле

- и т.д.

 

Я бы еще понял, если бы АЦП было 14-16 разрядным, но я не представляю, как надо изуродовать схему, чтобы дрожало 10-разрядное АЦП - чтобы там получить стабильные значения не требуется особо напрягаться.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Присоединяйтесь к обсуждению

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

Гость
Ответить в этой теме...

×   Вставлено с форматированием.   Вставить как обычный текст

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

×
×
  • Создать...