Skip to content

Index

Robotics / filter and estimator

IIR low past filter

first-order IIR low-pass filter, also called an exponential moving average It keeps mostly the slow trend (low frequencies) and suppresses fast changes (high frequencies).

\[y[n] = (1 - \alpha) \cdot x[n] + \alpha \cdot y[n-1]\]
  • x[n] → new input sample
  • y[n] → filtered output
  • y[n−1] is the previous output
  • α → smoothing factor (0–1)

alfa

smoothing factor between (0–1)

  • If α is small (e.g. 0.1), the filter reacts quickly to new changes (less smoothing).
  • If α is large (e.g. 0.9), the filter reacts slowly, producing a smoother result.

Demo

import numpy as np
import matplotlib.pyplot as plt

# Simulated noisy signal
t = np.linspace(0, 2*np.pi, 200)
x = np.sin(t) + 0.5*np.random.randn(len(t))

# Exponential Moving Average (IIR LPF)
alpha = 0.9
y = np.zeros_like(x)
for i in range(1, len(x)):
    y[i] = (1 - alpha) * x[i] + alpha * y[i-1]

plt.figure(figsize=(10,5))
plt.plot(t, x, label="Noisy Signal")
plt.plot(t, y, label=f"IIR Low-pass (alpha={alpha})", linewidth=2)
plt.legend()
plt.show()

alt text


Simple moving average

It smooths a signal by taking the average of the last N samples:

\[y[n] = \frac{1}{N} \sum_{k=0}^{N-1} x[n-k]\]

SMA is great for smoothing sensor readings like distance sensors or temperature, but not ideal for real-time fast response (since it introduces delay).

Demo

import numpy as np
import matplotlib.pyplot as plt
from collections import deque

class MovingAverage:
    def __init__(self, window_size):
        self.window_size = window_size
        self.buffer = deque(maxlen=window_size)
        self.sum = 0.0

    def update(self, new_value):
        if len(self.buffer) == self.window_size:
            self.sum -= self.buffer[0]
        self.buffer.append(new_value)
        self.sum += new_value
        return self.sum / len(self.buffer)


# Generate noisy signal (simulate stream)
t = np.linspace(0, 4*np.pi, 200)
signal = np.sin(t) + 0.5*np.random.randn(len(t))

# Apply streaming moving average
ma = MovingAverage(window_size=5)
ma_15 = MovingAverage(window_size=15)
filtered = []
filtered2 = []

for x in signal:
    y = ma.update(x)
    filtered.append(y)
    y2 = ma_15.update(x)
    filtered2.append(y2)

# Plot results
plt.figure(figsize=(10,5))
plt.plot(t, signal, label="Noisy Signal", alpha=0.6)
plt.plot(t, filtered, label="Streaming SMA (window=5)", linewidth=2)
plt.plot(t, filtered2, label="Streaming SMA (window=15)", linewidth=2)
plt.legend()
plt.show()

alt text

The code implementation return the average event if the window buffer not full


Resource