Wantcan 0 10 января, 2009 Опубликовано 10 января, 2009 · Жалоба Подскажите хороший алгоритм для усреднения вычислений АЦП,я сделал так- // 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 вычислений,но все равно не так сглаженно получается. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
rvk 0 10 января, 2009 Опубликовано 10 января, 2009 (изменено) · Жалоба Конечно не сглаженно, потому что Вы сняв двадцать значений стираете и начинаете набирать по новой. И кстати складываете Вы двадцать значений, а делите только на два. Если это такая фишка, чтобы результат в итоге умножить на два, тогда понятно. И деление на 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 будет содержать усреднение последних снятых двадцати значений, и обновляться будет при каждом чтении АЦП. Изменено 10 января, 2009 пользователем rvk Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Wantcan 0 10 января, 2009 Опубликовано 10 января, 2009 · Жалоба И кстати складываете Вы двадцать значений, а делите только на два-по другому почему-то не получилось-показания выходят из диапазона.Ваш пример попробую,но for не желательно(может даже не допустимо),т.к. этот алгоритм находится внутри программного генератора,for даст задержку и повлияет на частоту генератора,но попробую и отпишусь,что получилось. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
rvk 0 10 января, 2009 Опубликовано 10 января, 2009 · Жалоба Ну используйте вместо for вложенный цикл while: temp = 0; i=20; while(i--){ temp += tempData; } И кстати, поскольку temp имеет тип unsogned short, никаких переполнений быть не должно. Потому что даже максимальное значение АЦП 255*100= 25500 все равно меньше 65536, поэтому можно смело все складывать, а затем найти среднее. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Alex11 5 10 января, 2009 Опубликовано 10 января, 2009 · Жалоба Есть еще один способ получить скользящее среднее. Инициализация: unsigned int sum, Result; #define shift 3 // от 1 до 7 при Ваших условиях sum = 0; На каждое измерение: Result = sum >> shift; sum = sum - Result + read_adc(0); В зависимости от shift получается различная степень усреднения и разная скорость выхода на устоявшееся значение. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Wantcan 0 10 января, 2009 Опубликовано 10 января, 2009 · Жалоба С последним способом у меня не получилось-цифры скачут, а вот так-тоже интересно- 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; -как нагрузку подключаю-цифры на индикаторе пробегают от нуля до нормального значения,после отключения-обратно до нуля. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Herz 5 10 января, 2009 Опубликовано 10 января, 2009 · Жалоба Есть еще один способ получить скользящее среднее. Инициализация: unsigned int sum, Result; #define shift 3 // от 1 до 7 при Ваших условиях sum = 0; На каждое измерение: Result = sum >> shift; sum = sum - Result + read_adc(0); В зависимости от shift получается различная степень усреднения и разная скорость выхода на устоявшееся значение. Здесь, похоже, кое-что упущено. Деление, например, или сдвиг в последней операции. Иначе алгоритм предполагает нарастание результата с различной скоростью, зависящей от shift. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
rvk 0 10 января, 2009 Опубликовано 10 января, 2009 · Жалоба Не забывайте только про переполнение temp. Если значение АЦП будет больше 65535/500 = 131, то произойдет переполнение переменной temp и значения будут неверные. Проще говоря такая формула работает если значения АЦП были меньше 0x80. А то, что от нуля до нормального значения, так и должно быть, ведь для первое значение делится на 100, остальные значения ведь пустые. Второе значение складывается с первым и оба делятся на 100 и т.д. пока не будет заполнен весь массив. Чтобы с первого значения было все верно, нужно отследить весь массив до заполнения и делить пропорционально заполнению. Но это уже мелочи. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
rezident 0 10 января, 2009 Опубликовано 10 января, 2009 · Жалоба Чтобы с первого значения было все верно, нужно отследить весь массив до заполнения и делить пропорционально заполнению.Совсем не обязательно. Достаточно при первом обращении к фильтру проинициализировать весь массив не нулями, а этим самым первым значением. Это для случая фильтрации типа "скользящее среднее" конечно же. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Demeny 0 10 января, 2009 Опубликовано 10 января, 2009 · Жалоба Продвинутый способ, как писал в другой ветке resident, это хранить все двадцать последних значений в массиве, и делать среднее по нему, примерно вот так: ........... Для усреднения по последним 20 значениям совсем необязательно хранить последние 20 значений в массиве, зачем расходовать память и ресурсы процессора, чтобы их каждый раз суммировать. А если нужно будет усреднять по 10000 значений ? Вот простой способ получать усреднённое значение каждый раз при получении текущего измеренного значения: T_average = T_average + (T_current - T_average) / 20.0 где T_average - среднее значение на данный момент времени, T_current - мгновенное измеренное значение, снятое, например, с АЦП в текущий момент времени. Этот пример НЧ-фильтра измерений практически эквивалентен усреднению последних 20 значений. Однако здесь число усреднений (20) необязательно может быть целым значением. Вообще говоря, в этой формуле число 20.0 является постоянной времени фильтра, которая вкупе с периодом измерений определяет скорость реакции (инертность) этого фильтра на резкие изменения входных измеряемых величин. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
rezident 0 10 января, 2009 Опубликовано 10 января, 2009 · Жалоба Этот пример НЧ-фильтра измерений практически эквивалентен усреднению последних 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 - с КИХ. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
rvk 0 10 января, 2009 Опубликовано 10 января, 2009 (изменено) · Жалоба Вот простой способ получать усреднённое значение каждый раз при получении текущего измеренного значения: 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 Поэтому эти формулы какие угодно, только не взаимозаменяемые. И Ваша формула не проще, она просто другая. Изменено 10 января, 2009 пользователем rvk Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Demeny 0 10 января, 2009 Опубликовано 10 января, 2009 · Жалоба Вообще-то это пример фильтра 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). Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
rezident 0 10 января, 2009 Опубликовано 10 января, 2009 · Жалоба Конечно, численно она отличается от простого усреднения, но практически ведёт себя точно такжеДа как же они могут вести себя одинаково, если у них и ФЧХ и ПХ разные!? Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
777777 0 11 января, 2009 Опубликовано 11 января, 2009 · Жалоба Подскажите хороший алгоритм для усреднения вычислений АЦП Если показания АЦП дрожат, то нужно думать не о том, как их отфильтровать, а о том, как устранить источник помех. А для этого нужно совсем немного, как-то: - разводить земли и питание широкими проводниками, желательно чтобы эти проводники не прыгали с одного слоя на другой, а если нужно, то переход делать через большие переходные отверстия. - подводить ко всем 8 ногам контроллера, а для опорного желательно дополнительный RC-фильтр - в непосредственной близости от него должно быть хотя бы две пары конденсаторов по питанию (электролит+керамика) - если в схеме есть сильнопотребляющие элементы (светодиоды, например), то землю от них до источника вести отдельным проводником, чтобы падение напряжения от скачков тока не вызывало скачков напряжения на земле - и т.д. Я бы еще понял, если бы АЦП было 14-16 разрядным, но я не представляю, как надо изуродовать схему, чтобы дрожало 10-разрядное АЦП - чтобы там получить стабильные значения не требуется особо напрягаться. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться