Инструменты пользователя

Инструменты сайта


filt

Различия

Показаны различия между двумя версиями страницы.

Ссылка на это сравнение

Предыдущая версия справа и слева Предыдущая версия
Следующая версия
Предыдущая версия
filt [2026/02/27 12:14]
root
filt [2026/02/27 13:46] (текущий)
root [Огибающие фильтры]
Строка 14: Строка 14:
 <wrap ks> <wrap ks>
 Рассмотрим несколько примеров для работы с данными алгоритмами. Для удобства восприятия данных, будем их отображать в виде графика. Для этого разместим в рабочей области элемент [[lib.ds.grafdata|График данных]]. В свойстве [[prop.config|Конфигурация]] зададим ему следующие настройки: Рассмотрим несколько примеров для работы с данными алгоритмами. Для удобства восприятия данных, будем их отображать в виде графика. Для этого разместим в рабочей области элемент [[lib.ds.grafdata|График данных]]. В свойстве [[prop.config|Конфигурация]] зададим ему следующие настройки:
 +</wrap>
 +
 +<code javascript>
 +{
 + type: 'line',
 + data: {
 + labels: [],
 + datasets: [
 + {
 + label: 'Исходный',
 + borderWidth: 1,
 + borderColor: '#FF4444',
 + tension: 0,
 + data: [],
 + yAxisID: 'sig1',
 + },
 + {
 + label: 'Обработанный',
 + borderWidth: 4,
 + borderColor: '#8888FF',
 + tension: 0,
 + data: [],
 + yAxisID: 'sig2',
 + },
 + ],
 + },
 + options: {
 + maintainAspectRatio: false,
 + scales: {
 + x: {},
 + sig1: {
 + title: {
 + display: true,
 + text: 'Исходный',
 + },
 + ticks: {
 + color: '#FF4444',
 + },
 + position: 'left',
 + beginAtZero: true,
 + min: 0,
 + max: 100,
 + },
 + sig2: {
 + title: {
 + display: true,
 + text: 'Обработанный',
 + },
 + ticks: {
 + color: '#8888FF',
 + },
 + position: 'left',
 + beginAtZero: true,
 + min: 0,
 + max: 100,
 + },
 + },
 + elements: {
 + point: {
 + radius: 0,
 + },
 + },
 + plugins: {
 + legend: {
 + display: false,
 + },
 + },
 + },
 +}
 +</code>
 +
 +<wrap ks>
 +В результате получим элемент графика, на котором будем отображать две кривые: исходный сигнал и обработанный фильтром.
 +</wrap>
 +
 +{{ :filt1.png?nolink |}}
 +
 +<wrap ks>
 +Далее в свойстве экрана, в событии **Запуск** зададим скрипт, который будет создавать значения, якобы полученные с нашего датчика, и над которыми будем проводить эксперименты. Для примера выведем обычную синусоиду.
 +</wrap>
 +
 +<code javascript>
 +async function main() {
 +    let pointList = []; // список точек графика
 +    for (let i = 0; i < 300; i++) {
 +    
 +        // создаем точки в рабочей области графика
 +        let val = Math.sin(i / 30) * 40 + 50;
 +        
 +        // пока выведем точки только исходного графика
 + pointList.push([ i, val]);
 +    }
 +    
 +    // выведем созданный список точек на график
 +    await ds.objCall('График данных 1', 'addPointList', pointList);
 +}
 +</code>
 +
 +<wrap ks>
 +После чего запустим проект на исполнение, и увидим результат работы нашего испытательного стенда.
 +</wrap>
 +
 +{{ :filt2.png?nolink |}}
 +
 +<wrap ks>
 +В данной заготовке мы видим как выглядит идеальный сигнал с нашего выдуманного датчика. В дальнейшем будем вносить в него помехи и пробовать пропустить полученный сигнал через фильтр, и наблюдать как он влияет сигнал.
 +</wrap>
 +
 +====== Среднеарифметический фильтр ======
 +
 +<wrap ks>
 +Среднеарифметический фильтр, судя по его названию, пытается усреднять значения, выдавая их среднеарифметической значение на указанном диапазоне. Для примера добавим в код небольшую помеху со случайным отклонением сигнала, и попробуем применить к нему данный фильтр.
 +</wrap>
 +
 +<code javascript>
 +// создаем фильтр с размерностью в 10 значений
 +let filt = ds.crtFilt('sraf', 10);
 +
 +let pointList = [];
 +for (let i = 0; i < 300; i++) {
 +    let val = Math.sin(i / 30) * 40 + 50;
 +    
 +    // добавляем к исходному сигналу случайную помеху
 +    val += Math.random() * 4 - 2;
 +    
 +    let filtVal = filt.add(val);
 +    pointList.push([ i, val, filtVal ]);
 +}
 +
 +await ds.objCall('График данных 1', 'addPointList', pointList);
 +</code>
 +
 +{{ :filt3.png?nolink |}}
 +
 +<wrap ks>
 +В результате видим, что не смотря на то что в изначальном сигнале присутствует большое количество колебаний сигнала, поле пропускания через фильтр, нам удалось практически полностью восстановить его исходную форму. Однако при этом видно, что полученные данные немного отстают от исходных по времени (о чем было сказано ранее), но зачастую при небольших фильтрациях такие задержки допустимы.
 +</wrap>
 +
 +<wrap ks>
 +Теперь рассмотрим ситуацию, где помеха гораздо значительнее влияет на сигнал.
 +</wrap>
 +
 +<code javascript>
 +let filt = ds.crtFilt('sraf', 10);
 +
 +let pointList = [];
 +for (let i = 0; i < 300; i++) {
 +    let val = Math.sin(i / 30) * 40 + 50;
 +    
 +    // увеличиваем размер помехи
 +    val += Math.random() * 20 - 10;
 +    
 +    let filtVal = filt.add(val);
 +    pointList.push([ i, val, filtVal ]);
 +}
 +
 +await ds.objCall('График данных 1', 'addPointList', pointList);
 +</code>
 +
 +{{ :filt4.png?nolink |}}
 +
 +<wrap ks>
 +В данном случае видим что отфильтрованный сигнал все равно сильно искажен, и вряд ли с ним можно будет работать в дальнейшем. Можно попробовать увеличить размер фильтра, попытавшись сгладить большее количество точек.
 +</wrap>
 +
 +<code javascript>
 +// увеличиваем размер фильтра
 +let filt = ds.crtFilt('sraf', 50);
 +
 +let pointList = [];
 +for (let i = 0; i < 300; i++) {
 +    let val = Math.sin(i / 30) * 40 + 50;
 +    
 +    val += Math.random() * 20 - 10;
 +    
 +    let filtVal = filt.add(val);
 +    pointList.push([ i, val, filtVal ]);
 +}
 +
 +await ds.objCall('График данных 1', 'addPointList', pointList);
 +</code>
 +
 +{{ :filt5.png?nolink |}}
 +
 +<wrap ks>
 +В результате видим, что хоть отфильтрованный график и начал принимать форму, похожую на изначальную синусоиду, но при этом видно что ее амплитуда сильно меньше, так же по времени отставание достаточно значительное. В таком случае зачастую никакой фильтр уже не поможет, необходимо разбираться с первопричиной помехи.
 +</wrap>
 +
 +====== Медианный фильтр ======
 +
 +<wrap ks>
 +Иногда бывает так, что сигнал продолжительное время достаточно чисто идет, и лишь кратковременно прилетает какая-то помеха (ошибка измерения, или включился где-то контактом с большим электромагнитным импульсом), в таком случае среднеарифметический фильтр справляется уже не так хорошо.
 +</wrap>
 +
 +<code javascript>
 +let filt = ds.crtFilt('sraf', 10);
 +
 +let pointList = [];
 +for (let i = 0; i < 300; i++) {
 +    let val = Math.sin(i / 30) * 40 + 50;
 +    
 +    // добавляем кратковременную большую помеху
 +    if (i % 33 == 0) val += Math.random() * 100 - 50;
 +    
 +    let filtVal = filt.add(val);
 +    pointList.push([ i, val, filtVal ]);
 +}
 +
 +await ds.objCall('График данных 1', 'addPointList', pointList);
 +</code>
 +
 +{{ :filt6.png?nolink |}}
 +
 +<wrap ks>
 +На графике видно, что если даже одна точка выбивается из чистого графика, алгоритм среднеарифметического фильтра возмущение от нее еще видит достаточно продолжительное время. В данной ситуации хорошо работает медианный фильтр. Он, в отличие от среднеарифметического, который использует весь диапазон точек на интервале (в том числе и ту, которая с помехой), а выбирает из диапазона среднее значение из всех точек. Соответственно в данной ситуации точки с большим отклонением от остальных будут исключаться.
 +</wrap>
 +
 +<code javascript>
 +// используем вместо среднеарифметического, медианный фильтр с таким же размером
 +let filt = ds.crtFilt('median', 10);
 +
 +let pointList = [];
 +for (let i = 0; i < 300; i++) {
 +    let val = Math.sin(i / 30) * 40 + 50;
 +    
 +    if (i % 33 == 0) val += Math.random() * 100 - 50;
 +    
 +    let filtVal = filt.add(val);
 +    pointList.push([ i, val, filtVal ]);
 +}
 +
 +await ds.objCall('График данных 1', 'addPointList', pointList);
 +</code>
 +
 +{{ :filt7.png?nolink |}}
 +
 +<wrap ks>
 +Здесь хорошо видно, что помеха лишь незначительно исказила график (связано это с тем, что пришлось пропустить "бракованную" точку).
 +</wrap>
 +
 +====== Огибающие фильтры ======
 +
 +<wrap ks>
 +Не редко так же бывает ситуация, когда помеха улетает только в одну сторону относительно исходного графика. Такое часто бывает, к примеру, при периодическом пропадании связи с датчиком, и вместо показаний, получаем нулевое значение. В таких случаях хорошо отрабатывают огибающие фильтры. Их алгоритм работы похож на среднемедианный, но в отличие от него берется не среднее значение, а минимальное или максимальное на диапазоне значений (в зависимости от типа огибающего фильтра).
 +</wrap>
 +
 +
 +<code javascript>
 +// создаем огибающий фильтр по максимальному значению
 +let filt = ds.crtFilt('max', 10);
 +
 +let pointList = [];
 +for (let i = 0; i < 300; i++) {
 +    let val = Math.sin(i / 30) * 40 + 50;
 +    
 +    // эмулируем потерю сигнала
 +    if (i % 33 == 0) val = 0;
 +    
 +    let filtVal = filt.add(val);
 +    pointList.push([ i, val, filtVal ]);
 +}
 +
 +await ds.objCall('График данных 1', 'addPointList', pointList);
 +</code>
 +
 +{{ :filt8.png?nolink |}}
 +
 +<wrap ks>
 +Так же плюс данного фильтра, в отличие от рассмотренных ранее, в том, что на участке роста значений, он не имеет отставания, и четко повторяет исходный сигнал. Такой фильтр удобно использовать с данными, которые изменяются в одном направлении (растут или падают, в случае с огибающим по минимальному значению). Так же продолжительность возмущения от помехи на данном типе фильтра длится не всю продолжительность работы фильтра, а лишь на единственной точке.
 </wrap> </wrap>
  
filt.1772183648.txt.gz · Последнее изменение: 2026/02/27 12:14 — root