Jump to content

    

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

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

// 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 вычислений,но все равно не так сглаженно получается.

Share this post


Link to post
Share on other sites

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

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

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

И деление на 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 будет содержать усреднение последних снятых двадцати значений, и обновляться будет

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

Edited by rvk

Share this post


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

Share this post


Link to post
Share on other sites

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

 

temp = 0;

i=20;

while(i--){

temp += tempData;

}

 

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

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

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

Share this post


Link to post
Share on other sites

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

unsigned int sum, Result;

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

sum = 0;

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

Result = sum >> shift;

sum = sum - Result + read_adc(0);

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

Share this post


Link to post
Share on other sites

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

 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;

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

Share this post


Link to post
Share on other sites
Есть еще один способ получить скользящее среднее. Инициализация:

unsigned int sum, Result;

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

sum = 0;

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

Result = sum >> shift;

sum = sum - Result + read_adc(0);

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

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

Share this post


Link to post
Share on other sites

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

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

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

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

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

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

Share this post


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

Share this post


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

...........

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

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

T_average = T_average + (T_current - T_average) / 20.0

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

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

Share this post


Link to post
Share on other sites
Этот пример НЧ-фильтра измерений практически эквивалентен усреднению последних 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 - с КИХ.

Share this post


Link to post
Share on other sites
Вот простой способ получать усреднённое значение каждый раз при получении текущего измеренного значения:

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

 

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

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

Edited by rvk

Share this post


Link to post
Share on other sites
Вообще-то это пример фильтра 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).

Share this post


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

Share this post


Link to post
Share on other sites
Подскажите хороший алгоритм для усреднения вычислений АЦП

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

 

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

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

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

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

- и т.д.

 

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

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
Sign in to follow this