Noise Filtering Using 1€ Filter

Table of Contents

Introduction

This article explores the 1€ Filter, a simple, but powerful algorithm for filtering noisy real-time signals. The article focuses on the practical implementation of the algorithm, and it covers the mathematical basis, a pseudocode implementation, and simple, pure Python implementation of the algorithm. In order to understand why and how the filter works, we recommend reading the original article 1.

1€ Filter

The 1€ Filter is a low pass filter for filtering noisy signals in real time. It’s also a simple filter with only two configurable parameters. The signal at time $T_i$ is denoted as a value $X_i$ and the filtered signal as value $\hat{X}_i$. The filter is implemented using exponential smoothing

$$\hat{X}_1 = X_1$$

$$\hat{X}_i = α X_i + (1-α) \hat{X} _{i-1},\quad i≥2 \tag{1} \label{1} $$

where the smoothing factor $α∈[0, 1]$, instead of being a constant, is adaptive, i.e. dynamically computed using information about the rate of change (speed) of the signal. This aims to balance the jitter versus lag trade-off since people are more sensitive to jitter at low speeds and more sensitive to lag at high speeds. The smoothing factor is defined as

$$α = \frac{1}{1 + \dfrac{τ}{T_e}},$$

where $T_e$ is the sampling period computed from the time difference between the samples

$$T_e=T_i-T _{i-1}$$

and $τ$ is time constant computed using the cutoff frequency

$$τ = \frac{1}{2πf_C}.$$

The cutoff frequency $f_C$ which is designed to increase linearly as the rate of change, aka speed, increases

$$f_C=f_{C_{min}} + β|\hat{\dot{X}} _i|$$

where $f _{C _{min}}>0$ is the minimum cutoff frequency, $β>0$ is the speed coefficient and $\hat{\dot{X}}_i$ is the filtered rate of change. The rate of change $\hat{X}_i$ is defined as the discrete derivative of the signal

$$\dot{X}_1 = 0$$

$$\dot{X}_i = \frac{X_i-\hat{X} _{i-1}}{T_e}, i≥2$$

which is then filtered using exponential smoothing $\eqref{1}$ with a constant cutoff frequency $f_{C_d},$ by default $f_{C_d}=1$.

Algorithm

The One Euro Filter algorithm as pseudocode. The precise implementation of the One-Euro-filter will depend on the programming language and paradigm in question. We have written this algorithm using a functional style.

$\operatorname{Smoothing-Factor}(f_C, T_e)$

  1. $r=2π⋅f_c⋅T_e$
  2. return $\dfrac{r}{r+1}$

$\operatorname{Exponential-Smoothing}(α, X_i, \hat{X}_{i-1})$

  1. return $α X_i + (1-α) \hat{X}_{i-1}$

$\operatorname{One-Euro-Filter}(T_i,X_i,T_{i-1},\hat{X}_{i-1},\hat{\dot{X}}_{i-1},f_C,β,f_{C_d})$ for $i≥2$

  1. $T_e=T_i-T_{i-1}$
  2. $α_d=\operatorname{Smoothing-Factor}(T_e, f_{C_d})$
  3. $\dot{X}_i = \dfrac{X_i-\hat{X} _{i-1}}{T_e}$
  4. $\hat{\dot{X}}_i=\operatorname{Exponential-Smoothing}(α_d, \dot{X}_i, \hat{\dot{X}} _{i-1})$
  5. $f_C=f_{C_{min}} + β|\hat{\dot{X}}_i|$
  6. $α=\operatorname{Smoothing-Factor}(f_C, T_e)$
  7. $\hat{X}_i=\operatorname{Exponential-Smoothing}(α, X_i, \hat{X} _{i-1})$
  8. return $T_i,\hat{X}_i,\hat{\dot{X}}_i$

Tuning the Filter

There are two configurable parameters in the model, the minimum cutoff frequency $f _{C _{min}}$ and the speed coefficient $β$. Decreasing the minimum cutoff frequency will decrease slow speed jitter. Increasing the speed coefficient will decrease speed lag.

Python Implementation

The following object-oriented Python implementation is also available in OneEuroFilter GitHub repository. The object-oriented approach stores the previous values inside the object instead of giving them explicitly as a return value as functional implementation would. It should be relatively simple to implement this algorithm in other languages.

import math


def smoothing_factor(t_e, cutoff):
    r = 2 * math.pi * cutoff * t_e
    return r / (r + 1)


def exponential_smoothing(a, x, x_prev):
    return a * x + (1 - a) * x_prev


class OneEuroFilter:
    def __init__(self, t0, x0, dx0=0.0, min_cutoff=1.0, beta=0.0,
                 d_cutoff=1.0):
        """Initialize the one euro filter."""
        # The parameters.
        self.min_cutoff = float(min_cutoff)
        self.beta = float(beta)
        self.d_cutoff = float(d_cutoff)
        # Previous values.
        self.x_prev = float(x0)
        self.dx_prev = float(dx0)
        self.t_prev = float(t0)

    def __call__(self, t, x):
        """Compute the filtered signal."""
        t_e = t - self.t_prev

        # The filtered derivative of the signal.
        a_d = smoothing_factor(t_e, self.d_cutoff)
        dx = (x - self.x_prev) / t_e
        dx_hat = exponential_smoothing(a_d, dx, self.dx_prev)

        # The filtered signal.
        cutoff = self.min_cutoff + self.beta * abs(dx_hat)
        a = smoothing_factor(t_e, cutoff)
        x_hat = exponential_smoothing(a, x, self.x_prev)

        # Memorize the previous values.
        self.x_prev = x_hat
        self.dx_prev = dx_hat
        self.t_prev = t

        return x_hat

Here we provide an example of how to use the One Euro Filter for filtering a signal in real-time.

# Let signal be a generator that yields a tuple (t, x)
# where t is time and x the values of the signal.
signal = ...

# Instantiate the OneEuroFilter with initial values and
# your value for parameters min_cutoff and beta.
t0, x0 = next(signal)
one_euro_filter = OneEuroFilter(
    t0=t0,
    x0=x0,
    min_cutoff=...,
    beta=...
)

# Filter the signal in real time.
for (t, x) in signal:
    x_hat = one_euro_filter(t, x)
    ...

References


  1. Casiez, G., Roussel, N., & Vogel, D. (2012). 1€ filter: a simple speed-based low-pass filter for noisy input in interactive systems. In Proceedings of the SIGCHI Conference on Human Factors in Computing Systems (pp. 2527–2530). ↩︎

Jaan Tollander de Balsch
Jaan Tollander de Balsch
Student & Researcher

Jaan Tollander de Balsch is a computer scientist with a background in applied mathematics.

comments powered by Disqus