|
Простейший цифровой ФНЧ, для конечного массива точек |
|
|
|
Jan 20 2011, 10:37
|

Местный
  
Группа: Свой
Сообщений: 307
Регистрация: 6-02-08
Из: Россия, Екатеринбург
Пользователь №: 34 798

|
Есть массив, в котором записаны значения с АЦП. Напряжения формируют некую "огибающую" Платформа: STM32, сигнал - массив значений с АЦП в вольтах. 1)Подскажите фильтр (алгоритм на Си) чтобы сгладить(усреднить) значения в массиве? 2) Как определить характерные места у огибающей (резкое увеличение значения, резкий спад)
Сообщение отредактировал Ivan Kuznetzov - Jan 20 2011, 11:26
Эскизы прикрепленных изображений
--------------------
Разработчик
|
|
|
|
3 страниц
1 2 3 >
|
 |
Ответов
(1 - 31)
|
Jan 20 2011, 11:21
|

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

|
Цитата(Ivan Kuznetzov @ Jan 20 2011, 13:37)  Есть массив, в котором записаны значения с АЦП. Напряжения формируют некую "огибающую"
1)Подскажите фильтр чтобы сгладить(усреднить) значения в массиве? Для каждого элемента массива посчитайте среднее значение от окружающих его элементов. Это и есть простейший цифровой ФНЧ. Чем шире окрестность, в которой считается среднее - тем сильнее "сглаживание", но и искажение формы исходного сигнала также сильнее. Если не вдаваться в теорию - вот как-то так.
--------------------
Сделано в Китае. Упаковано в России.
|
|
|
|
|
Jan 20 2011, 12:02
|

Местный
  
Группа: Свой
Сообщений: 307
Регистрация: 6-02-08
Из: Россия, Екатеринбург
Пользователь №: 34 798

|
Demeny, спасибо! функция уже заметно красивей стала!!! Код for (x=1;x<ARRAYSIZE;x++) Pulsearray[x] = (Pulsearray[x-1] + Pulsearray[x+1])/2;
Сообщение отредактировал Ivan Kuznetzov - Jan 20 2011, 12:02
Эскизы прикрепленных изображений
--------------------
Разработчик
|
|
|
|
|
Jan 20 2011, 13:27
|

фанат дивана
     
Группа: Свой
Сообщений: 3 387
Регистрация: 9-08-07
Из: Уфа
Пользователь №: 29 684

|
Цитата(Ivan Kuznetzov @ Jan 20 2011, 17:02)  Код for (x=1;x<ARRAYSIZE;x++) Pulsearray[x] = (Pulsearray[x-1] + Pulsearray[x+1])/2; Наверное лучше Код Pulsearray[x] = (Pulsearray[x-1] + Pulsearray[x] + Pulsearray[x+1])/3;
--------------------
Если бы я знал, что такое электричество...
|
|
|
|
|
Jan 20 2011, 14:18
|

Знающий
   
Группа: Свой
Сообщений: 580
Регистрация: 3-06-08
Пользователь №: 38 041

|
Есть метод скользящего среднего, считается так: (текущий обработанный результат)=( (предыдущий обработанный результат)*(N-1)/N )+(текущий код АЦП)/N Под кодом АЦП может выступать текущий отсчет необработанной вашей реализации. Собственно точки тогда имеют железную привязку  N=1....и до бесконечности на практике достаточно N до 32, ну и естественно с ростом N теряются подробности сигнала. Так характерные места - величины первой производной. Смотрите постоянно разницу предыдущего отсчета (возможно даже с некоторой глубиной, т е не последний, а предпоследний) и текущего. Ну стравнивайте полученную дельту с порогом или за знаком следите. Возможно понадобится и саму производную усреднять, если у вас по производной какие то критичные вычисления делаются.
|
|
|
|
|
Jan 20 2011, 19:16
|

фанат дивана
     
Группа: Свой
Сообщений: 3 387
Регистрация: 9-08-07
Из: Уфа
Пользователь №: 29 684

|
Цитата(Ivan Kuznetzov @ Jan 20 2011, 18:55)  AHTOXA, спасибо, действительно красивее!  Да, но так всё равно неправильно  Получается, что для первой точки мы взяли начальные данные, а для остальных - одна из точек (которая x-1) - уже отфильтрованная. Надо либо занычивать её в переменной, либо фильтровать в другой массив. А лучше сделать скользящее среднее, как написал firstvald. Там можно регулировать степень сглаживания.
--------------------
Если бы я знал, что такое электричество...
|
|
|
|
|
Jan 21 2011, 11:01
|
Гуру
     
Группа: Свой
Сообщений: 2 712
Регистрация: 28-11-05
Из: Беларусь, Витебск, Строителей 18-4-220
Пользователь №: 11 521

|
Код /************************************************************************ * * * Библиотека для цифровой фильтрации. * * Версия: 1.01. * * * * Файл: fir.c Дата создания: 08.07.2010г. * * Последние изменения: 08.07.2010г. * * Сапего Александр Леонидович. (sapegoal@mail.ru) * * * ************************************************************************/
#include "stdint.h"
// Фильтр 2-го порядка исходя из формулы y0^ = a0*(x0+x2)+a1*x1 + b1*y0 + b2*y1 + c1*y0 // где a и b - коэффициенты, c - целая часть y0 // После выполнения производится сдвиг y. // y1 -> y2, y0 -> y1, y0^ -> y0; // // В процедуру передаётся 2 параметра // 1 - указатель на значения, в последовательности: x0,x1,x2,y0,y1,y2 // 2 - указатель на значения коэффициентов в последовательности: a0,a1,b1,b2,c1 // По финишу результат делится на 32768
struct data_s { int16_t x[3],y[3]; // Данные фильтра };
struct coef_s { int16_t a0, a1, b1, b2, c1; // Коэффициенты фильтра };
void fir2_16(uint8_t * data, uint8_t * coef) { struct data_s *d_s; struct coef_s *c_s; int32_t acc; d_s = (struct data_s *) data; c_s = (struct coef_s *) coef; acc = (int32_t)(d_s->x[0] + d_s->x[2]) * (int32_t)c_s->a0; acc += (int32_t)d_s->x[1] * (int32_t)c_s->a1; acc += (int32_t)d_s->y[0] * (int32_t)c_s->b1; acc += (int32_t)d_s->y[1] * (int32_t)c_s->b2; acc >>= 15; acc += d_s->y[0] * c_s->c1; d_s->y[2] = d_s->y[1]; d_s->y[1] = d_s->y[0]; d_s->y[0] = acc; } Код // Фильтр Баттерворта 4 порядка 1000 -> 100; 200 = -30 дб // // 1 звено y0 = 0,061885*(x0+x2) + 0,123770*x1 + 1,048600*y1 - 0,296140*y2 // Коэффициенты a0,a1,b1,b2,c1 = 2028, 4056, 1593, -9704, 1 // 2 звено y0 = 0,077956*(x0+x2) + 0,155913*x1 + 1,320910*y1 - 0,632739*y2 // Коэффициенты a0,a1,b1,b2,c1 = 2554, 5109, 10516, -20734, 1 // int16_t fltr_c1[5] = {2028, 4056, 1593, -9704, 1}, fltr_c2[5] = {2554, 5109, 10516, -20734, 1}; Код fir2_16((uint8_t *)&lin[cnt_line].x[0],(uint8_t *)fltr_c1);// Первое звено фильтра fir2_16((uint8_t *)&lin[cnt_line].y[0],(uint8_t *)fltr_c2);// Второе звено фильтра Так например. ))
|
|
|
|
|
Jan 21 2011, 14:47
|

неотягощённый злом
     
Группа: Свой
Сообщений: 2 746
Регистрация: 31-01-08
Из: Санкт-Петербург
Пользователь №: 34 643

|
Пробегало давненько: Цитата Код u16_t Yavg;
for(;;) { Yavg -= Yavg/256; Yavg += ADCH; } Запуск и готовность АЦП за Вами. Утверждается, что в старшем байте Yavg получим фильтр НЧ с частотой среза = частоте запуска АЦП/256 с погрешностью обработки <0.5 LSB, по АФЧХ эквивалентный RC цепочке с такой же частотой среза. Почему? Хемминг уже рассказал, у меня лучше не получится.
--------------------
“Будьте внимательны к своим мыслям - они начало поступков” (Лао-Цзы)
|
|
|
|
|
Jan 23 2011, 13:06
|
Гуру
     
Группа: Свой
Сообщений: 2 563
Регистрация: 8-04-05
Из: Nsk
Пользователь №: 3 954

|
Цитата(Ivan Kuznetzov @ Jan 20 2011, 16:37)  1)Подскажите фильтр (алгоритм на Си) чтобы сгладить(усреднить) значения в массиве? 2) Как определить характерные места у огибающей (резкое увеличение значения, резкий спад) 1) demiurg_spb и firstvald уже подсказали простейший IIR фильтр: Код y[0] = x[0]; for (int i = 1; i < len; i++) y[i] = y[i-1] + (x[i] - y[i-1]) * K K = 0.0 .. 1.0, временная постоянная фильтра. Ну и конечно можно (и нужно) заменить на целочисленное умножение и битовый сдвиг вправо. Хотя если надо потом точно определять положение "характерных мест", то fir фильтр (скользящее среднее - частный случай) лучше из-за линейной фазовой характеристики. Кстати, то что предложил SasaVitebsk - fir фильтром не является. 2) искать локальные максимумы на производной, или, если заранее известно то как именно выглядит то что хочется найти, то максимумы корреляционной функции сигнала с тем, что ищем. Цитата(777777) разводить плату надо было так, чтобы программисту не пришлось фильтровать результаты АЦП таким сигнал вполне может быть изначально, и совсем не обязательно вызван шумами на плате.
|
|
|
|
|
Jan 25 2011, 15:04
|
Гуру
     
Группа: Свой
Сообщений: 2 712
Регистрация: 28-11-05
Из: Беларусь, Витебск, Строителей 18-4-220
Пользователь №: 11 521

|
Цитата(Ivan Kuznetzov @ Jan 21 2011, 17:24)  SasaVitebsk, поясните пожалуйста как пользоваться? Есть массив, 200 точек. Входные параметры фильтра - это data? Там же всё видно. )) Давайте я упрощу. 1. Объявляем Код // Каналы АЦП struct data_ADC { int16_t x[3],y[3],z[3]; // Данные фильтра };
struct data_ADC lin[8]; // Объявляем массив данных. 2. Объявляем массив констант коэффициентов фильтра так, как это в предыдущем моём посте показано. 3. Читаем АЦП Код lin[cnt_line].x[2] = lin[cnt_line].x[1]; // Коэффициенты x переписать lin[cnt_line].x[1] = lin[cnt_line].x[0]; // Коэффициенты x переписать lin[cnt_line].x[0] = ADC; // Прочитать АЦП fir2_16((uint8_t *)&lin[cnt_line].x[0],(uint8_t *)fltr_c1);// Первое звено фильтра fir2_16((uint8_t *)&lin[cnt_line].y[0],(uint8_t *)fltr_c2);// Второе звено фильтра 4. Результат получаем в lin[cnt_line].z[0] Если у вас фильтр 6 порядка, то вам надо добавить в структуру data_ADC ещё один массив, и вызвать ещё одно звено фильтра с соответствующими коэффициентами. PS: Есть и IIR фильтр если хотите. ))
|
|
|
|
|
Jan 26 2011, 07:20
|
Гуру
     
Группа: Свой
Сообщений: 2 712
Регистрация: 28-11-05
Из: Беларусь, Витебск, Строителей 18-4-220
Пользователь №: 11 521

|
Цитата(_pv @ Jan 23 2011, 16:06)  Кстати, то что предложил SasaVitebsk - fir фильтром не является. )) Во-первых Вы мне льстите. Это предложил не я, а Баттерворт. )) Во-вторых, а чем по вашему мнению является уравнение вида "y0 = 0,061885*(x0+x2) + 0,123770*x1 + 1,048600*y1 - 0,296140*y2"? (где x1 = x(n-1) и так далее) Я не математик, если честно, и не теоретик, но некоторые источники типа фильтрсолюшн, QED и другие, просто вводят нас в заблуждение, а Вы нам сейчас откроете всю правду. )) Ждём. === 2 777777 по поводу Цитата разводить плату надо было так, чтобы программисту не пришлось фильтровать результаты АЦП . А причём здесь АЦП? Иногда входной сигнал требует фильтрации. Или обработанный. То есть даже если этот сигнал идеально снят АЦП.
|
|
|
|
|
Jan 26 2011, 15:29
|
Гуру
     
Группа: Свой
Сообщений: 2 563
Регистрация: 8-04-05
Из: Nsk
Пользователь №: 3 954

|
Цитата(SasaVitebsk @ Jan 26 2011, 13:20)  )) Во-первых Вы мне льстите. Это предложил не я, а Баттерворт. )) Во-вторых, а чем по вашему мнению является уравнение вида "y0 = 0,061885*(x0+x2) + 0,123770*x1 + 1,048600*y1 - 0,296140*y2"? (где x1 = x(n-1) и так далее) Я не математик, если честно, и не теоретик, но некоторые источники типа фильтрсолюшн, QED и другие, просто вводят нас в заблуждение, а Вы нам сейчас откроете всю правду. )) Ждём. я всегда считал что, соответсвенно названию, FIR фильтры (с конечной импульсной характеристикой) это Y = A1*X1 + A2 * X2 + ..., одна из особенностей которых - линейная фазовая характеристика, т.е. просто задержка, а это может быть важно для автора исходного сообщения, он хочет определять после фильтрации когда именно спад на сигнале произошол. а IIR - с обратной связью и, соответственно, с бесконечной импульсной характеристикой это Y = A1*X1 + A2*X2 + ... + B1*Y1 + B2*Y2 + ... и, соответственно, с нелинейной фазовой характеристикой не думаю что qed и фильтрсолюшен могут их перепутать, посмотрите внимательнее.
|
|
|
|
|
Jan 26 2011, 21:14
|
Гуру
     
Группа: Свой
Сообщений: 2 712
Регистрация: 28-11-05
Из: Беларусь, Витебск, Строителей 18-4-220
Пользователь №: 11 521

|
Цитата(_pv @ Jan 26 2011, 18:29)  не думаю что qed и фильтрсолюшен могут их перепутать, посмотрите внимательнее. Я вот даже не буду с Вами спорить. Просто посмотрите сами любую прогу из перечисленных. Лучше фильтрсолюшен - там прямо формулу можно посмотреть. И задайте любой фильтр 2 порядка, к примеру. Фильтр ведь можно рассматривать как последовательное соединение звеньев. Цитата(firstvald @ Jan 26 2011, 18:38)  Навсякий случай: FIR все же постабильнее, хотя и длиннее, IIR может так зазвенеть, что не задушишь не убъешь. Если честно, топику надо простейшими методами добиться удовлетворительной работы, иначе утопнет в этих фильтрах. Если не на обум фильтр выбирать, то ничего там звенеть не будет. И не надо пугать человека. Пусть человек возьмёт и выведет результирующий график с моего примера. Хотя, этот фильтр не под него делался. Характеристики его я привёл. А если захочет, то может и свой фильтр построить с любыми характеристиками. Работы на подбор фильтра - 15 минут.
|
|
|
|
|
Jan 27 2011, 11:54
|
Местный
  
Группа: Свой
Сообщений: 437
Регистрация: 27-08-04
Пользователь №: 551

|
QUOTE (_pv @ Jan 23 2011, 15:06)  Кстати, то что предложил SasaVitebsk - fir фильтром не является. QUOTE (SasaVitebsk @ Jan 26 2011, 09:20)  Во-вторых, а чем по вашему мнению является уравнение вида "y0 = 0,061885*(x0+x2) + 0,123770*x1 + 1,048600*y1 - 0,296140*y2"? (где x1 = x(n-1) и так далее) Я не математик, если честно, и не теоретик, но некоторые источники типа фильтрсолюшн, QED и другие, просто вводят нас в заблуждение, а Вы нам сейчас откроете всю правду. )) Ждём. Так все таки, каким типом является этот фильтр? Если по вашему мнению утверждение "fir фильтром не является" есть ложное, то это все же fir фильтр? Я тоже не математик, и не теоретик, как и многие здесь. Поэтому вы заинтриговали. Ждем. И если не затруднит, на пальцах, без ссылок на проги. Это ведь вопрос принципа, а не реализации
|
|
|
|
|
Jan 28 2011, 08:48
|

Универсальный солдатик
     
Группа: Модераторы
Сообщений: 8 634
Регистрация: 1-11-05
Из: Минск
Пользователь №: 10 362

|
Цитата(ig_z @ Jan 27 2011, 13:54)  Так все таки, каким типом является этот фильтр? И если не затруднит, на пальцах, без ссылок на проги. Это ведь вопрос принципа, а не реализации Из приведенных формул не совсем понятно, какие индексы подразумеваются для отсчетов y. Но если в выражении используются не только входные данные (x), но и выходные (y), рассчитанные чуть раньше, то это будет фильтр с бесконечной импульсной характеристикой (БИХ, по-английски IIR). Если бы использовались только входные данные, это был бы фильтр с конечной импульсной характеристикой (КИХ, по-английски FIR).
|
|
|
|
|
Jan 30 2011, 09:00
|

pontificator
     
Группа: Свой
Сообщений: 3 055
Регистрация: 8-02-05
Из: страны Оз
Пользователь №: 2 483

|
Цитата(Ivan Kuznetzov @ Jan 20 2011, 21:07)  1)Подскажите фильтр (алгоритм на Си) чтобы сгладить(усреднить) значения в массиве? Можно сделать доморощенное грубое подобие фильтра Калмана. Его достоинство - в простоте реализации: не нужно запоминать массив данных, фильтр Калмана фильтрует данные "на лету", по мере их поступления. Пусть Vin - результат на выходе АЦП, а Vout - отфильтрованный текущий результат, n - некое число, при помощи которого оценивается "достоверность" текущего результата. Вычисление ведется по простой формуле: Vout = Vout - (Vout/n) + (Vin/n), где n по ходу фильтрации увеличивается от 1 до некоторого разумного (т.е. не очень большого) значения, определяемого ожидаемыми свойствами сигнала. Пример: 1. Самое первое измерение принимаем как данность, поскольку сравнивать и усреднять не с чем, n=1: Vout = Vin 2. У второго измерения "достоверность" такая же, какая была у первого, так что принимаем n=2: Vout = Vout - (Vout/2) + (Vin/2) 3. Для третьего измерения принимаем n=3: Vout = Vout - (Vout/3) + (Vin/3) ... 16. Ограничимся, например, n=16, иначе все вообще усреднится нафиг. Это и все последующие измерения считаем так: Vout = Vout - (Vout/16) + (Vin/16) Если говорить упрощенно, то у настоящего фильтра Калмана n меняется динамически в зависимости от сигнала. Когда сигнал меняется быстро, вес новых измерений увеличивается (т.е. n уменьшается), когда же изменения сигнала невелики, то вес каждого нового измерения уменьшается (n увеличивается).
|
|
|
|
Guest_orthodox_*
|
Jan 30 2011, 09:50
|
Guests

|
Цитата(ViKo @ Jan 28 2011, 10:48)  Из приведенных формул не совсем понятно, какие индексы подразумеваются для отсчетов y. Но если в выражении используются не только входные данные (x), но и выходные (y), рассчитанные чуть раньше, то это будет фильтр с бесконечной импульсной характеристикой (БИХ, по-английски IIR). Если бы использовались только входные данные, это был бы фильтр с конечной импульсной характеристикой (КИХ, по-английски FIR). Несколько FIR звеньев, включенных последовательно , по этому определению попали бы в IIR?
|
|
|
|
2 чел. читают эту тему (гостей: 2, скрытых пользователей: 0)
Пользователей: 0
|
|
|