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

 
 
3 страниц V  < 1 2 3  
Reply to this topicStart new topic
> Усреднение вычислений АЦП
Tanya
сообщение Feb 13 2009, 07:05
Сообщение #31


Гуру
******

Группа: Модераторы
Сообщений: 8 752
Регистрация: 6-01-06
Пользователь №: 12 883



Цитата(blackfin @ Feb 13 2009, 09:26) *
Так ведь, "усреднить помехи" по сути и означает отфильтровать их, т.е. - устранить.. Или нет?

В данном случае, наверное, - нет. См 19 и 20 посты.
Go to the top of the page
 
+Quote Post
Палыч
сообщение Feb 13 2009, 07:09
Сообщение #32


Гуру
******

Группа: Свой
Сообщений: 2 399
Регистрация: 10-05-06
Из: г. Новочеркасск
Пользователь №: 16 954



Цитата(777777 @ Jan 21 2009, 19:05) *
T_average += (T_current - T_average) / 20.0.

То есть из считанного значения вычитаем текущее, делим на коэффициент и прибавляем к текущему. Плавающая точка здесь ни к чему, в целых числах тоже можно считать, правда там есть свои тонкости.
Вот, именно - тонкости! Представьте, что разность между считанным и текущим значениями - меньше двадцати. Что получим? По этой же причине у автора вопроса и прыгают значения: в первом топике он привёл код, якобы усредняющий из двадцати значений... Имхо, сумма подсчитывается из значений измерений делённых на 5 - так усреднять, мягко говоря, не корректно.
Go to the top of the page
 
+Quote Post
777777
сообщение Feb 13 2009, 11:40
Сообщение #33


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

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



Цитата(blackfin @ Feb 13 2009, 09:26) *
Так ведь, "усреднить помехи" по сути и означает отфильтровать их, т.е. - устранить.. Или нет?

Да, но только устраняют их обычно на входе АЦП, в аналоговом виде, так чтобы АЦП выдавал устойчивый код, который не пришлось бы фильтровать. И не путем подключения RC-цепочки, а путем выяснения их природы и устранения того, что их вызывает. Например, распространенная ошибка - обратный ток от светодиодных индикаторов идет по той же земле, на которой висит АЦП. Падение тока на нем вызывает скачки на земле. Эти помехи вы не устраните усреднением, потому что все они "в одну сторону", так что даже усреднив их вы получите неправильный результат.
Go to the top of the page
 
+Quote Post
blackfin
сообщение Feb 13 2009, 11:46
Сообщение #34


Гуру
******

Группа: Свой
Сообщений: 3 106
Регистрация: 18-04-05
Пользователь №: 4 261



Цитата(777777 @ Feb 13 2009, 14:40) *
Да, но только устраняют их обычно на входе АЦП, в аналоговом виде,...

Меня смутила формулировка.. ИМХО, есть разница между "устранение помех" и "устранение причин из-за которых эти помехи возникают на входе АЦП"..
Go to the top of the page
 
+Quote Post
777777
сообщение Feb 13 2009, 11:55
Сообщение #35


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

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



Цитата(Палыч @ Feb 13 2009, 10:09) *
Вот, именно - тонкости! Представьте, что разность между считанным и текущим значениями - меньше двадцати. Что получим?

Получим, что результат никогда не достигнет входного значения. Но ведь и реальная, математическая экспонента никогда его не достигает! Вопрос лишь в необходимой точности. Если нужна большая - результат храним в long, а используем в качестве результата старшую половину - то есть как бы число с фиксированной точкой, в котором младшие 16 разрядов дробные.
Цитата(Палыч @ Feb 13 2009, 10:09) *
По этой же причине у автора вопроса и прыгают значения: в первом топике он привёл код, якобы усредняющий из двадцати значений... Имхо, сумма подсчитывается из значений измерений делённых на 5 - так усреднять, мягко говоря, не корректно.

Почему? Просто результат будет в 4 раза больше.

Цитата(Палыч @ Feb 13 2009, 10:09) *
По этой же причине у автора вопроса и прыгают значения: в первом топике он привёл код, якобы усредняющий из двадцати значений... Имхо, сумма подсчитывается из значений измерений делённых на 5 - так усреднять, мягко говоря, не корректно.

Пардон, не доглядел - конечно же надо сначала суммировать 20 значений, а результат делить на 5. А еще лучше - использовать степень двойки.

Сообщение отредактировал 777777 - Feb 13 2009, 12:00
Go to the top of the page
 
+Quote Post
Палыч
сообщение Feb 13 2009, 12:59
Сообщение #36


Гуру
******

Группа: Свой
Сообщений: 2 399
Регистрация: 10-05-06
Из: г. Новочеркасск
Пользователь №: 16 954



Цитата(777777 @ Feb 13 2009, 14:55) *
Получим, что результат никогда не достигнет входного значения. Но ведь и реальная, математическая экспонента никогда его не достигает! Вопрос лишь в необходимой точности.
Математическая экспанента хотя бы стремиться к нему. В Вашем же случае мы будем иметь постоянную ошибку. Результат этого - всё тот же - отбрасывание остатка при делении, который тем больше, чем больше делитель (это - ведь, наверное, и есть те тонкости, о которых Вы говорили выше). Тогда, уж лучше что-нибудь такое:

Инициализация переменной
T_average_tmp = T_current * N
...................
T_average_tmp += (T_current - T_average)
T_average= T_average_tmp / N

Последнюю строчку, может быть, записать даже так:
T_average= (T_average_tmp + N/2) / N
Go to the top of the page
 
+Quote Post
xemul
сообщение Feb 13 2009, 14:08
Сообщение #37



*****

Группа: Свой
Сообщений: 1 928
Регистрация: 11-07-06
Пользователь №: 18 731



Цитата(Палыч @ Feb 13 2009, 15:59) *
...
Последнюю строчку, может быть, записать даже так:
T_average= (T_average_tmp + N/2) / N

Последнюю строчку только так и нужно писать, чтобы погрешность от целочисленного деления осталась +-0.5LSB.
Go to the top of the page
 
+Quote Post
777777
сообщение Feb 14 2009, 11:18
Сообщение #38


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

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



Цитата(Палыч @ Feb 13 2009, 15:59) *
Математическая экспанента хотя бы стремиться к нему. В Вашем же случае мы будем иметь постоянную ошибку.

Что значит "постоянную ошибку"? Разумеется, экспонента будет отличаться от реальной из-за округлений, это неизбежно при цифровых вычислениях, вопрос лишь в необходимой точности. Даже если вы примените float, все равно ошибка будет, так как float тоже дискретно. Но задача ведь не в том, чтобы имитировать экспоненту, а в том, чтобы сгладить сигнал. Для этого вычисления в целых вполне достаточно.
Цитата(Палыч @ Feb 13 2009, 15:59) *
Результат этого - всё тот же - отбрасывание остатка при делении, который тем больше, чем больше делитель (это - ведь, наверное, и есть те тонкости, о которых Вы говорили выше). Тогда, уж лучше что-нибудь такое:
Инициализация переменной
T_average_tmp = T_current * N
...................
T_average_tmp += (T_current - T_average)
T_average= T_average_tmp / N

Тогда уж лучше что-нибудь такое:
Код
    static long Adc0, AdcF;

    Adc0 = (long)ADC << 16;
    AdcF += (Adc0 - AdcF)>>3; // или разделить на нужное число, если оно не степень двойки
    int Result = AdcF >> 16;

Правда, gnu-тый компилятор не очень эффективно кодирует сдвиги (не знаю, может IAR поумнее), поэтому этот участок лучше написать на ассемблере.
Смысл в том, что в старших 16 битах AdcF хранится фильтрованное значение, а в младших - его дробная часть, т.е. оно представляет собой число с фиксированной точкой. При кодировании на ассемблере можно даже подойти к делению (сдвигу) более творчески: если в отбрасываемых битах старший 1, то к оставшимся прибавляем 1, а если 0 - то не прибавляем, т.е. выполняем деление с округлением до целого. Тогда не будет ошибки с отбрасыванием, которой ты так боишься.
Go to the top of the page
 
+Quote Post
ReAl
сообщение Feb 14 2009, 15:07
Сообщение #39


Нечётный пользователь.
******

Группа: Свой
Сообщений: 2 033
Регистрация: 26-05-05
Из: Бровари, Україна
Пользователь №: 5 417



Цитата(777777 @ Feb 14 2009, 13:18) *
Тогда уж лучше что-нибудь такое:
Код
    static long Adc0, AdcF;

    Adc0 = (long)ADC << 16;
    AdcF += (Adc0 - AdcF)>>3; // или разделить на нужное число, если оно не степень двойки
    int Result = AdcF >> 16;

...
Смысл в том, что в старших 16 битах AdcF хранится фильтрованное значение, а в младших - его дробная часть, т.е. оно представляет собой число с фиксированной точкой.
+1
Давно делаю похожим образом, часто запас разрядов в аккумуляторе делаю равным числу сдвигов для деления на коффициент фильтра (он степень двойки). Т.е. для беззнаковых 10-битных отсчётов и "рециркуляции" 7/8, т.е. весе очередного отсчёта 1/8 будет так:
Код
    uint16_t acc;
void update(uint16_t val) {
   acc = acc - ((acc + 4) >> 3) + val;
}

uint16_t get_result() {
   return  (acc + 4) >> 3;
}

Собственно, с не-степенью двойки будет аналогично, просто
Код
(acc+4)>>3
езде заменится на
Код
(acc+DIVIDER/2)/DIVIDER
, но я не вижу в этом особого смысла. Если задача так чувствительна к тому, что новый отсчёт будет входить не с 1/8 или 1/16, а имено с 1/10 или 1/13, то скорее всего там и фильтр нужен более высокого порядка.


--------------------
Ну, я пошёл… Если что – звоните…
Go to the top of the page
 
+Quote Post
Wantcan
сообщение Mar 2 2009, 16:57
Сообщение #40


Участник
*

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



Пожалуйста,помогите переделать найденный на этом форуме программный ФНЧ под мои условия,сам не могу разобраться.
Код
int nStepFilter( int val)
{
#define  shift  (4)   // n = 2^shift
#define  lsb    ((1 << (shift)) >> 1)

    static int x = 0;
    int av = (x + lsb) >> shift;

    x += val - av;

    return av;
#undef lsb
#undef shift
}
Go to the top of the page
 
+Quote Post
Wantcan
сообщение Mar 16 2009, 11:10
Сообщение #41


Участник
*

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



Остановился на таком варианте
Код
ta++; if(ta==20){ta=0;temp=tempADC/100;}
   if(ta==0)tempADC=0;  
   tempADC+=read_adc(0);
Go to the top of the page
 
+Quote Post
Флюктуация вакку...
сообщение Aug 15 2015, 12:22
Сообщение #42


Местный
***

Группа: Участник
Сообщений: 346
Регистрация: 15-12-13
Из: Планета Земля
Пользователь №: 79 630



Нужно создать в оперативной памяти закольцованный FIFO-буфер с двумя указателями (PTR_first и PTR_last).

И регистр суммы. Разрядность его должна быть достаточной для хранения суммы 20-ти значний без округления.

Как только приходит очередное значение от АЦП Вы его записываете в ячейку, на которую указывает PTR_last, добавляете его к сумме и вычитаете из суммы значение, на которое указывает PTR_first.

После чего двигаете PTR_first и PTR_last на следующие ячейки в кольце.

Таким образом у Вас всегда в регистре cуммы будет акутульное значение среднего, умноженное на 20.

Чтобы получить среднее достаточно будет поделить значение, находящееся в данный момент в регистре суммы на 20.

Сообщение отредактировал Флюктуация ваккума - Aug 15 2015, 12:23
Go to the top of the page
 
+Quote Post
Bronislav
сообщение Sep 4 2015, 06:15
Сообщение #43


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

Группа: Участник
Сообщений: 118
Регистрация: 23-01-06
Пользователь №: 13 477



Такой вариант не подойдет?
for(i=0;i<20;i++) // усредняем 20 выборки
{
ADC_cur = ADС;
if (i == 0)
{
ADC_old = ADC_cur; // первое значение отправляем в сумму
ADC_sum = ADC_old;
}
else
{
ADC_old += ADC_cur; // сложили текущее значение со старым
ADC_old /= 2; // поделили на 2
ADCsum += ADC_old; // просуммировали
}
}
ADC_cur = ADC_sum/20; // cумму поделили на 20 */
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 Текстовая версия Сейчас: 18th June 2025 - 17:25
Рейтинг@Mail.ru


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