реклама на сайте
подробности

 
 
3 страниц V   1 2 3 >  
Reply to this topicStart new topic
> Усреднение вычислений АЦП
Wantcan
сообщение Jan 10 2009, 05:35
Сообщение #1


Участник
*

Группа: Участник
Сообщений: 27
Регистрация: 11-12-08
Из: Earth
Пользователь №: 42 366



Подскажите хороший алгоритм для усреднения вычислений АЦП,я сделал так-
Код
// 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 вычислений,но все равно не так сглаженно получается.
Go to the top of the page
 
+Quote Post
rvk
сообщение Jan 10 2009, 05:57
Сообщение #2


Частый гость
**

Группа: Свой
Сообщений: 165
Регистрация: 13-05-06
Из: Камышин
Пользователь №: 17 067



Конечно не сглаженно, потому что Вы сняв двадцать значений стираете и начинаете набирать по новой.
И кстати складываете Вы двадцать значений, а делите только на два. Если это такая фишка, чтобы результат в итоге умножить на два,
тогда понятно.
И деление на 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[i];
}
temp =temp/100; // 20*5

}

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

Сообщение отредактировал rvk - Jan 10 2009, 06:04
Go to the top of the page
 
+Quote Post
Wantcan
сообщение Jan 10 2009, 06:12
Сообщение #3


Участник
*

Группа: Участник
Сообщений: 27
Регистрация: 11-12-08
Из: Earth
Пользователь №: 42 366



Цитата
И кстати складываете Вы двадцать значений, а делите только на два
-по другому почему-то не получилось-показания выходят из диапазона.Ваш пример попробую,но for не желательно(может даже не допустимо),т.к. этот алгоритм находится внутри программного генератора,for даст задержку и повлияет на частоту генератора,но попробую и отпишусь,что получилось.
Go to the top of the page
 
+Quote Post
rvk
сообщение Jan 10 2009, 06:19
Сообщение #4


Частый гость
**

Группа: Свой
Сообщений: 165
Регистрация: 13-05-06
Из: Камышин
Пользователь №: 17 067



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

temp = 0;
i=20;
while(i--){
temp += tempData[i];
}

И кстати, поскольку temp имеет тип unsogned short, никаких переполнений быть не должно.
Потому что даже максимальное значение АЦП 255*100= 25500 все равно меньше
65536, поэтому можно смело все складывать, а затем найти среднее.
Go to the top of the page
 
+Quote Post
Alex11
сообщение Jan 10 2009, 09:27
Сообщение #5


Гуру
******

Группа: Свой
Сообщений: 2 106
Регистрация: 23-10-04
Из: С-Петербург
Пользователь №: 965



Есть еще один способ получить скользящее среднее. Инициализация:
unsigned int sum, Result;
#define shift 3 // от 1 до 7 при Ваших условиях
sum = 0;
На каждое измерение:
Result = sum >> shift;
sum = sum - Result + read_adc(0);
В зависимости от shift получается различная степень усреднения и разная скорость выхода на устоявшееся значение.
Go to the top of the page
 
+Quote Post
Wantcan
сообщение Jan 10 2009, 10:52
Сообщение #6


Участник
*

Группа: Участник
Сообщений: 27
Регистрация: 11-12-08
Из: Earth
Пользователь №: 42 366



С последним способом у меня не получилось-цифры скачут, а вот так-тоже интересно-
Код
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;
-как нагрузку подключаю-цифры на индикаторе пробегают от нуля до нормального значения,после отключения-обратно до нуля.
Go to the top of the page
 
+Quote Post
Herz
сообщение Jan 10 2009, 11:20
Сообщение #7


Гуру
******

Группа: Модераторы
Сообщений: 10 983
Регистрация: 23-11-05
Пользователь №: 11 287



Цитата(Alex11 @ Jan 10 2009, 11:27) *
Есть еще один способ получить скользящее среднее. Инициализация:
unsigned int sum, Result;
#define shift 3 // от 1 до 7 при Ваших условиях
sum = 0;
На каждое измерение:
Result = sum >> shift;
sum = sum - Result + read_adc(0);
В зависимости от shift получается различная степень усреднения и разная скорость выхода на устоявшееся значение.

Здесь, похоже, кое-что упущено. Деление, например, или сдвиг в последней операции. Иначе алгоритм предполагает нарастание результата с различной скоростью, зависящей от shift.
Go to the top of the page
 
+Quote Post
rvk
сообщение Jan 10 2009, 11:23
Сообщение #8


Частый гость
**

Группа: Свой
Сообщений: 165
Регистрация: 13-05-06
Из: Камышин
Пользователь №: 17 067



Не забывайте только про переполнение temp. Если значение АЦП будет больше 65535/500 = 131, то произойдет переполнение
переменной temp и значения будут неверные. Проще говоря такая формула работает если значения АЦП были меньше 0x80.
А то, что от нуля до нормального значения, так и должно быть, ведь для первое значение делится на 100, остальные значения
ведь пустые. Второе значение складывается с первым и оба делятся на 100 и т.д. пока не будет заполнен весь массив.
Чтобы с первого значения было все верно, нужно отследить весь массив до заполнения и делить пропорционально
заполнению. Но это уже мелочи.
Go to the top of the page
 
+Quote Post
rezident
сообщение Jan 10 2009, 11:34
Сообщение #9


Гуру
******

Группа: Свой
Сообщений: 10 920
Регистрация: 5-04-05
Пользователь №: 3 882



Цитата(rvk @ Jan 10 2009, 16:23) *
Чтобы с первого значения было все верно, нужно отследить весь массив до заполнения и делить пропорционально заполнению.
Совсем не обязательно. Достаточно при первом обращении к фильтру проинициализировать весь массив не нулями, а этим самым первым значением. Это для случая фильтрации типа "скользящее среднее" конечно же.
Go to the top of the page
 
+Quote Post
Demeny
сообщение Jan 10 2009, 18:33
Сообщение #10


Знающий
****

Группа: Свой
Сообщений: 648
Регистрация: 11-02-06
Из: Санкт-Петербург
Пользователь №: 14 237



Цитата(rvk @ Jan 10 2009, 08:57) *
Продвинутый способ, как писал в другой ветке resident, это хранить все двадцать последних значений в массиве, и делать среднее по нему, примерно вот так:
...........

Для усреднения по последним 20 значениям совсем необязательно хранить последние 20 значений в массиве, зачем расходовать память и ресурсы процессора, чтобы их каждый раз суммировать. А если нужно будет усреднять по 10000 значений ?
Вот простой способ получать усреднённое значение каждый раз при получении текущего измеренного значения:
Код
T_average = T_average + (T_current - T_average) / 20.0

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


--------------------
Сделано в Китае. Упаковано в России.
Go to the top of the page
 
+Quote Post
rezident
сообщение Jan 10 2009, 18:55
Сообщение #11


Гуру
******

Группа: Свой
Сообщений: 10 920
Регистрация: 5-04-05
Пользователь №: 3 882



Цитата(Demeny @ Jan 10 2009, 23:33) *
Этот пример НЧ-фильтра измерений практически эквивалентен усреднению последних 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 - с КИХ.
Go to the top of the page
 
+Quote Post
rvk
сообщение Jan 10 2009, 19:02
Сообщение #12


Частый гость
**

Группа: Свой
Сообщений: 165
Регистрация: 13-05-06
Из: Камышин
Пользователь №: 17 067



Цитата(Demeny @ Jan 10 2009, 21:33) *
Вот простой способ получать усреднённое значение каждый раз при получении текущего измеренного значения:
Код
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 - Jan 10 2009, 19:08
Go to the top of the page
 
+Quote Post
Demeny
сообщение Jan 10 2009, 19:42
Сообщение #13


Знающий
****

Группа: Свой
Сообщений: 648
Регистрация: 11-02-06
Из: Санкт-Петербург
Пользователь №: 14 237



Цитата(rezident @ Jan 10 2009, 21:55) *
Вообще-то это пример фильтра 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).


--------------------
Сделано в Китае. Упаковано в России.
Go to the top of the page
 
+Quote Post
rezident
сообщение Jan 10 2009, 20:26
Сообщение #14


Гуру
******

Группа: Свой
Сообщений: 10 920
Регистрация: 5-04-05
Пользователь №: 3 882



Цитата(Demeny @ Jan 11 2009, 00:42) *
Конечно, численно она отличается от простого усреднения, но практически ведёт себя точно также
Да как же они могут вести себя одинаково, если у них и ФЧХ и ПХ разные!?
Go to the top of the page
 
+Quote Post
777777
сообщение Jan 11 2009, 07:46
Сообщение #15


Профессионал
*****

Группа: Участник
Сообщений: 1 091
Регистрация: 25-07-07
Из: Саратов
Пользователь №: 29 357



Цитата(Wantcan @ Jan 10 2009, 08:35) *
Подскажите хороший алгоритм для усреднения вычислений АЦП

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

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

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

3 страниц V   1 2 3 >
Reply to this topicStart new topic
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0

 


RSS Текстовая версия Сейчас: 22nd June 2025 - 05:44
Рейтинг@Mail.ru


Страница сгенерированна за 0.01523 секунд с 7
ELECTRONIX ©2004-2016