Эдж-обработка: как фильтровать и агрегировать данные на микроконтроллере

Эдж-обработка: как фильтровать и агрегировать данные на микроконтроллере авг, 17 2026

Представьте ситуацию: ваш датчик температуры отправляет показания каждые 100 миллисекунд. Если передавать все эти данные напрямую в облако или на сервер, канал связи быстро забьется, а батарея устройства сядет за пару дней. Именно здесь на помощь приходит эдж-обработка. Это техника обработки информации непосредственно на устройстве сбора данных, чтобы отправить только то, что действительно важно.

В мире интернета вещей (IoT) это не просто оптимизация, а необходимость. Микроконтроллеры вроде STM32 или ESP32 имеют ограниченные ресурсы памяти и процессорной мощности. Поэтому мы учимся делать работу «умнее», оставляя тяжелые вычисления на стороне устройства, а не на стороне сервера.

Почему сырые данные - это проблема

Датчики редко дают идеальный сигнал. Шум, помехи от электромагнитных полей, кратковременные сбои питания - все это приводит к тому, что значение может скакнуть с 25 градусов до 90 и сразу вернуться обратно. Если система управления примет этот скачок за реальность, она включит вентилятор на полную мощность или запишет ошибку в журнал событий.

Фильтрация решает эту проблему. Мы убираем случайные выбросы, сглаживаем резкие изменения и получаем стабильное значение, которое отражает реальный физический процесс. Агрегация же позволяет сократить объем данных. Вместо тысячи отдельных точек за час мы можем отправить одно среднее значение или максимум/минимум за этот период.

Основные методы фильтрации на MCU

Для микроконтроллеров подходят простые алгоритмы, которые требуют минимум циклов CPU и мало оперативной памяти (RAM). Вот самые эффективные подходы:

  • Среднее скользящее (Moving Average): Берем последние N значений (например, 5 или 10) и считаем их среднее. Это эффективно гасит высокочастотный шум. Минус: инерция. Если температура реально меняется быстро, фильтр будет «догонять» реальность.
  • Медианный фильтр: Сортируем последние N значений и берем среднее по позиции. Этот метод отлично убивает импульсные помехи (спайки), но хуже работает с плавными изменениями. Требует немного больше ресурсов на сортировку, но для N=5 или 7 это ничтожно мало для современного MCU.
  • Алгоритм Калмана (упрощенный): Более сложный математический аппарат, который предсказывает следующее состояние системы и корректирует его по фактическому измерению. Идеален для задач, где есть динамика (например, отслеживание положения дрона), но требует работы с матрицами или хотя бы дробными числами.
  • Пороговая фильтрация (Hysteresis): Простейший метод. Значение считается измененным только тогда, когда оно превысило порог на определенную величину. Полезно для дискретных сигналов (вкл/выкл).

Как устроена агрегация данных

После того как мы получили «чистый» сигнал, нужно решить, что именно слать дальше. Отправка каждого нового отфильтрованного значения - это часто лишняя трата энергии радиосигнала (LoRa, Zigbee, BLE).

Стратегии агрегации зависят от задачи:

  1. Тайм-база: Считаем среднее, максимум, минимум и стандартное отклонение за фиксированный интервал (например, раз в минуту). Затем отправляем пакет из 4-5 чисел. Объем данных падает в разы.
  2. Изменение-база (Change-based): Отправляем данные только если текущее значение отличается от последнего отправленного более чем на заданный процент (например, 2%). Если температура стоит 24.5°C часами, трафик почти нулевой.
  3. Событийная: Отправляем данные только при наступлении события (превышение порога, ошибка датчика, потеря связи).
Концептуальная визуализация фильтрации данных с шумными и гладкими линиями

Практическая реализация на C/C++

Давайте посмотрим, как это выглядит в коде. Рассмотрим пример с использованием кольцевого буфера (circular buffer) для скользящего среднего. Это классика для встраиваемых систем, так как не требует перемещения массива в памяти.


#define BUFFER_SIZE 10
uint8_t buffer[BUFFER_SIZE];
uint8_t head = 0;
uint8_t count = 0;

float get_filtered_value(float new_sample) {
    // Записываем новое значение в буфер
    buffer[head] = new_sample;
    
    // Сдвигаем указатель
    head = (head + 1) % BUFFER_SIZE;
    
    // Увеличиваем счетчик заполненности
    if (count < BUFFER_SIZE) {
        count++;
    }
    
    // Считаем сумму всех элементов в буфере
    float sum = 0.0f;
    for (int i = 0; i < count; i++) {
        sum += buffer[i];
    }
    
    return sum / count;
}

Обратите внимание на использование типа float. Для многих простых задач достаточно даже int с фиксированной точностью (fixed-point arithmetic), что еще быстрее на архитектурах без FPU (Floating Point Unit), таких как старые AVR или базовые Cortex-M0.

Ограничения ресурсов: RAM и CPU

Главный враг эдж-обработки - ограниченность памяти. Если вам нужен длинный исторический ряд для анализа трендов, RAM закончится. Решение: использовать энергонезависимую память (Flash) или внешнюю EEPROM для хранения архива, а в RAM держать только активный рабочий буфер.

Также важно учитывать задержку обработки. Если цикл основной программы занимает 10 мс, а расчет фильтра занимает 8 мс, у вас останется всего 2 мс на обработку прерываний и другие задачи. Используйте профильеры или простой замер времени через таймер, чтобы убедиться, что алгоритм не тормозит систему.

Автономная метеостанция на закате в открытом поле

Когда эдж-обработка не нужна?

Не стоит усложнять жизнь зря. Если у вас мощный шлюз (gateway) рядом с датчиками, лучше собирать сырые данные там и обрабатывать уже на нем. Шлюзы обычно имеют ARM-процессоры с сотнями мегабайт RAM и могут работать с Linux. Там можно запустить полноценный скрипт Python или легкую базу данных InfluxDB.

Эдж-обработка критична, когда:

  • Канал связи дорогой или медленный (GSM, спутник).
  • Устройство питается от батареи и должно работать годами.
  • Нужна мгновенная реакция (безопасность, промышленная автоматика), где задержка рандома до облака недопустима.

Частые ошибки новичков

Одна из самых частых проблем - неправильная настройка коэффициентов фильтра. Слишком агрессивное сглаживание скрывает важные быстрые изменения. Слишком мягкое оставляет шум. Начинайте с малого окна (N=3-5) и увеличивайте, глядя на осциллограф или логгер данных.

Вторая ошибка - игнорирование дрейфа датчика. Фильтр сглаживает шум, но не исправляет смещение нуля. Периодически калибруйте датчик или используйте алгоритмы адаптивной фильтрации, которые учитывают долгосрочные тренды.

Какой микроконтроллер лучше подходит для сложной эдж-обработки?

Для сложных алгоритмов, таких как нейросети или KNN, лучше выбирать чипы с DSP-ядрами и большим объемом RAM, например, STM32F4/F7 серии или ESP32-S3. Для простых фильтров хватит даже ATmega328P или STM32F1.

Что выбрать: скользящее среднее или медианный фильтр?

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

Как хранить историю данных на устройстве с малым объемом Flash?

Используйте форматированные секторы Flash-памяти или внешние микросхемы EEPROM. Храните данные в бинарном формате, а не текстовом, чтобы сэкономить место. Также можно применять сжатие, если данные коррелируют во времени.

Влияет ли эдж-обработка на точность измерений?

Да, всегда есть компромисс между точностью мгновенного значения и стабильностью. Фильтрация вносит запаздывание (lag). В задачах, где важна скорость реакции, минимизируйте размер окна фильтра.

Можно ли использовать нейросети на микроконтроллере?

Да, технологии TinyML позволяют запускать мелкие модели машинного обучения (например, для распознавания звука или вибрации) прямо на MCU. Но это требует специализированных библиотек (TFLite Micro) и тщательной оптимизации модели.