Как настроить рафт-ранговый FIR-фильтр в STM32: от теории до рабочего кода
Ты хочешь убрать шум с датчика температуры, но простой скользящее среднее уже не справляется. Или тебе нужно фильтровать сигнал с акселерометра в реальном времени — без задержек, без артефактов. Ты пробовал IIR, но он нестабилен при изменении температуры микроконтроллера. И тут ты наткнулся на FIR-фильтр с рафт-ранговой обработкой. Звучит сложно? На деле — просто. Я покажу, как это работает на STM32, без лишней теории, только то, что нужно, чтобы заработать.
Почему именно рафт-ранговый FIR?
FIR (Finite Impulse Response) — это фильтр, который не имеет обратной связи. Всё просто: берёшь несколько последних отсчётов, умножаешь на коэффициенты, складываешь — и получил новый выход. Никаких колебаний, никакой нестабильности. Но есть проблема: если в данных есть выброс (например, импульсный шум от реле или электромагнитный скачок), FIR его просто «сглаживает» — и ты теряешь полезный сигнал, или, наоборот, оставляешь артефакт.
Рафт-ранговый (rank-order) FIR — это модификация, где вместо простого усреднения мы сортируем отсчёты и выбираем медиану. Это как если бы ты взял 7 измерений температуры, выстроил их по возрастанию, и взял четвёртое — среднее по порядку. Выбросы? Они оказываются на краях — и не влияют на результат. Это работает даже если 2 из 7 значений — полный мусор.
На STM32 это особенно ценно: ты работаешь в реальном времени, без ОС, с жёсткими ограничениями по памяти и тактовой частоте. IIR может «сойти с ума» при переполнении, а рафт-ранговый FIR — просто работает. И не требует плавающей точки.
Как это работает: шаг за шагом
Представь, что у тебя есть датчик, который даёт 100 отсчётов в секунду. Ты хочешь фильтровать их с окном из 9 значений. Вот что происходит на каждом такте:
- Записываешь новое значение в буфер из 9 ячеек (циклический буфер).
- Сортируешь все 9 значений по возрастанию.
- Берёшь значение с индексом 4 (медиана — центральное в нечётном окне).
- Это и есть твой выход.
Нет умножений. Нет делений. Только сравнения и перестановки. Это идеально для Cortex-M0/M3 — они медленные, но сортировка 9 чисел — это 36 сравнений максимум. На 72 МГц STM32F1 это занимает 15–20 мкс. Ты спокойно можешь фильтровать 1 кГц сигнал.
Вот как выглядит код на C (всё на целых числах, без float):
// Буфер из 9 отсчётов
int16_t buffer[9];
uint8_t buffer_index = 0;
// Функция фильтрации
int16_t raft_rank_filter(int16_t new_sample) {
// Добавляем новый отсчёт в буфер
buffer[buffer_index] = new_sample;
buffer_index = (buffer_index + 1) % 9;
// Сортируем буфер (пузырьком — просто и надёжно)
for (int i = 0; i < 8; i++) {
for (int j = 0; j < 8 - i; j++) {
if (buffer[j] > buffer[j + 1]) {
int16_t temp = buffer[j];
buffer[j] = buffer[j + 1];
buffer[j + 1] = temp;
}
}
}
// Возвращаем медиану (4-й элемент, индекс 4)
return buffer[4];
} Этот код работает на любом STM32 — даже на STM32F030 с 48 МГц. Никаких библиотек, никаких FFT, никаких floating point.
Как выбрать размер окна
Размер окна — это компромисс между задержкой и фильтрацией. Чем больше окно — тем лучше убираются выбросы, но тем больше задержка. Вот что реально работает на практике:
| Размер окна | Задержка (при 1 кГц) | Подходит для | Риск |
|---|---|---|---|
| 5 | 5 мс | Быстрые сигналы: кнопки, энкодеры, импульсные датчики | Мало убирает шум, если выбросы частые |
| 7 | 7 мс | Температура, давление, датчики с низкой частотой изменения | Оптимальный баланс для большинства задач |
| 9 | 9 мс | Акселерометры, шумные аналоговые сигналы, датчики с резкими скачками | Задержка может мешать в системах управления |
| 11+ | 11+ мс | Очень шумные среды: промышленные датчики, моторные драйверы | Слишком медленный отклик — может вызвать нестабильность в ПИД-контроллере |
Большинство моих проектов — 7. Почему? Потому что 7 — это минимальное число, при котором медиана начинает надёжно подавлять одиночные выбросы. Если у тебя два выброса подряд — они всё ещё могут повлиять. Но если один — он уходит. Это работает как шумоподавление в аудио, но без артефактов.
Частые ошибки — и как их избежать
- Используешь float. Зачем? Ты сортируешь целые числа — датчик даёт 12-битный ADC. Переводи в int16_t и работай с ними. Float на Cortex-M0 тормозит в 10 раз.
- Забываешь про инициализацию буфера. Если буфер не заполнен при старте — первые 8 отсчётов будут мусором. Инициализируй его первым значением:
for (int i=0; i<9; i++) buffer[i] = new_sample; - Используешь нечётное окно, но берёшь среднее двух центральных. Нет. Рафт-ранг — это медиана, а не усреднение. Бери одно значение — центральное. Иначе теряешь преимущества.
- Сортируешь весь буфер на каждом шаге. Да, это нормально для 9 элементов. Но если ты хочешь окно 15 — перейди на быструю сортировку или используй алгоритм с вставкой. Пузырёк для 15 — это уже 105 сравнений. На 168 МГц — ок, на 8 МГц — уже заметно.
- Пытаешься фильтровать сигнал с частотой выше 100 Гц окном 9. Ты получаешь задержку 90 мс. Это как управлять роботом с задержкой в полсекунды — не сработает. Уменьшай окно или уменьшай частоту сэмплинга.
Когда использовать, а когда — нет
Не думай, что это «волшебная таблетка». Вот когда он работает идеально:
- Ты фильтруешь сигнал с периодическими выбросами (например, от реле, тиристоров, моторов).
- Шум не гауссовский — а импульсный, «пиковый».
- Тебе не критична задержка в 5–10 мс.
- Ты не можешь позволить себе IIR из-за нестабильности при температурных колебаниях.
А вот когда он — плохой выбор:
- Ты фильтруешь синусоидальный сигнал с высокой частотой (например, 50 Гц). Тут лучше FIR с окном 128 и коэффициентами Чебышёва.
- Тебе нужна фазовая линейность с минимальной задержкой — тогда используй FIR с линейной фазой, но без ранговой обработки.
- Ты работаешь с очень медленными сигналами (например, 1 отсчёт в секунду). Тогда просто сглаживай с экспоненциальным фильтром — он быстрее и проще.
Как лучше сделать: практические рекомендации
- Начни с окна 7. Это золотая середина. Протестируй на реальном датчике — включи/выключи реле рядом с платой и посмотри, исчезают ли пики.
- Используй
int16_tдля буфера. Даже если ADC 12-битный — он даёт значения от 0 до 4095. В int16_t это влезает с запасом. - Не используй рекурсию, динамическую память, malloc. Всё — статически.
- Если у тебя есть DMA — сбрасывай его в буфер, а фильтр запускай в прерывании по окончанию буфера. Так ты не тормозишь основной цикл.
- Тестируй с «плохими» данными. Сгенерируй сигнал с 10% выбросов — и посмотри, как фильтр себя ведёт. Если медиана всё ещё «внутри» сигнала — ты всё сделал правильно.
- Если нужно уменьшить задержку — не уменьшай окно. Уменьшай частоту сэмплинга. Например, бери отсчёт каждые 2 мс (500 Гц), а фильтруй окном 7 — получишь задержку 14 мс, но снизишь нагрузку на процессор.
Сценарии выбора: что делать в твоей ситуации
Если твоя задача выглядит так — вот что делать:
- «У меня датчик температуры в шкафу с реле. Шум — скачки на 20°C» → Окно 7. Сэмплинг 100 Гц. Фильтр в прерывании таймера. Результат — плавная кривая без ложных срабатываний термостата.
- «Мне нужно фильтровать сигнал с акселерометра в роботе-пылесосе. Он дергается при столкновении» → Окно 9. Сэмплинг 500 Гц. Тестируй на ударе — если пики уходят, а основной сигнал сохраняется — отлично.
- «Я измеряю ток через датчик с шумом от PWM-драйвера» → Окно 5. Сэмплинг 10 кГц. Быстрый отклик важен. Медиана 5 — убирает одиночные импульсы, не добавляя задержки.
- «Я хочу фильтровать сигнал с микрофона» → Не используй рафт-ранг. Это аудио — нужна частотная фильтрация. Используй FIR с окном 64 и окном Хэмминга.
Итог: что делать прямо сейчас
Вот твой план на сегодня:
- Возьми свой STM32 и подключи датчик, который шумит.
- Скопируй код фильтра из примера выше — с окном 7.
- Инициализируй буфер первым значением.
- Запусти сэмплинг через таймер (например, 100 Гц).
- Выведи до и после на UART или осциллограф.
- Проверь, исчезли ли выбросы — и сохранился ли полезный сигнал.
Если всё работает — ты только что внедрил промышленный метод фильтрации, который используют в системах управления двигателями, медицинских приборах и космической аппаратуре. Без лицензий. Без дорогих чипов. Без сложных библиотек.
Это не «надо попробовать». Это — рабочее решение, которое ты можешь запустить за час. И оно будет работать через 5 лет, когда другие фильтры сойдут с ума от температуры.
Информация в этой статье носит ознакомительный характер. При проектировании систем управления, безопасности или медицинских устройств всегда консультируйся с инженером, специализирующимся на обработке сигналов и надёжности микроконтроллерных систем.
