Обнаружение движения по дисперсии

Урок-практикум: ловим движение по тому, что сигнал датчика начинает «гулять» сильнее обычного.

Детектор движения по дисперсии объявляет движение, когда разброс (стандартное отклонение) сигнала в окне превышает порог, заданный с запасом над уровнем шума покоя.

Многие движения проще обнаружить не по среднему уровню, а по изменчивости. В покое датчик (PIR, акселерометр, освещённость) даёт почти постоянный сигнал с малым шумом; при движении сигнал начинает колебаться сильнее. Сравним дисперсию окна с порогом.

Дисперсия как мера активности

Стандартное отклонение окна из $N$ отсчётов:

$$ \sigma = \sqrt{\frac{1}{N}\sum_{i=1}^{N}(x_i - \bar{x})^2} $$

Если $\sigma \gt \sigma_{thr}$ — фиксируем движение. Порог берут заметно выше шума покоя.

import statistics

def is_motion(window, thr):
    return statistics.pstdev(window) > thr

quiet  = [100, 101, 99, 100, 102]   # покой
moving = [100, 140, 99, 100, 30]    # движение

print("покой:", round(statistics.pstdev(quiet), 2),
      "-> движение?", is_motion(quiet, 5.0))
print("движение:", round(statistics.pstdev(moving), 2),
      "-> движение?", is_motion(moving, 5.0))

Вывод:

покой: 1.02 -> движение? False
движение: 28.0 -> движение? True

Скользящее окно и удержание

На потоке дисперсию считают по скользящему окну. Чтобы событие не дёргалось, к нему применяют тот же гистерезис, что и к порогам (раздел 7): включаем по высокому порогу, держим состояние «движение» ещё несколько отсчётов после спада.

import statistics

def motion_stream(signal, w=4, thr=5.0):
    out = []
    for i in range(len(signal)):
        lo = max(0, i - w + 1)
        window = signal[lo:i+1]
        sd = statistics.pstdev(window) if len(window) > 1 else 0.0
        out.append(1 if sd > thr else 0)
    return out

sig = [100, 100, 101, 130, 90, 105, 100, 100, 100]
print(motion_stream(sig))

Вывод:

[0, 0, 0, 1, 1, 1, 1, 1, 0]

Как работает под капотом

Детектор по дисперсии нечувствителен к постоянному смещению: его волнует не уровень, а изменчивость, поэтому медленный дрейф нуля его не обманет. Порог $\sigma_{thr}$ калибруют, замерив дисперсию покоя и взяв запас в несколько раз. Слишком низкий порог даст ложные срабатывания от шума, слишком высокий пропустит слабое движение. Это та же логика «сигнал против шума», что пронизывает весь курс: мы отделяем полезное событие от фоновой дрожи, зная статистику покоя.

Частые ошибки

  • Ставить порог близко к шуму покоя — детектор будет ложно срабатывать.
  • Детектировать движение по среднему уровню — дрейф нуля собьёт его.
  • Брать слишком короткое окно — дисперсия станет неустойчивой и шумной.

Итог

  • Движение надёжно ловится по росту дисперсии (разброса) сигнала.
  • Порог берут с запасом над дисперсией покоя; помогает гистерезис.
  • Метод нечувствителен к смещению — реагирует на изменчивость, а не на уровень.
Проверьте себя
1. По какой характеристике сигнала детектор обнаруживает движение?
AПо среднему уровню
BПо росту дисперсии (разброса) в окне
CПо цвету
DПо частоте дискретизации
2. Почему детектор по дисперсии устойчив к медленному дрейфу нуля?
AОн измеряет изменчивость, а не уровень сигнала
BОн усредняет сигнал
CОн игнорирует шум полностью
DДрейфа не бывает