One euro filter for signal filtering

When we decided to design an embedded system, eventually we will need to deal with noisy signals.  As you can imagine the characteristics of the noise depends on the time and the nature of the noise source as well, thus digital filters such as FIR, IIR, median, or moving average need to be considered to solve/mitigate the noise problem.

In this article, I’ll focus on a special type of filter -which is called 1 euro filter, for handling the noisy signal especially targeted input devices (e.g: resistive, or capacitive touch screen controllers.)

Each operations on the signals have side effects and the digital filters are not an exception.  As side effects, we can observe phase non-linearities, distortion, and delay(lag) of the signal. Because any lag results poor user experience, I will focus on the ‘lag’ issues in this article, which is a major problem with touch screen controllers.

As you already know from the text books, digital low-pass filters have cut-off frequencies.  The cut-off frequency of a digital filter (e.g.: IIR, FIR, etc.) depends on the sampling frequency (remember, the design of a digital filter will be achieved by normalized frequencies), and does not take into account the signal characteristics.

Let’s focus on the details by an example:

Fig.1 : Low SNR signal.
Fig.2 : High SNR signal.

The response of an generic FIR or IIR filter does not care about the signal characteristics, so the filter behaves identically to the signals depicted in Fig.1, and Fig.2 where signals have different “speeds.” So what if we require low latency response for a high speed signal (such as sweeping very fast on the screen of the mobile phone), or a stationary one (such as holding the finger on the screen).  If we use a regular filter for these type of use cases, either we have a responsive output or low noise output, but not both of them. In turn, we need to trade one to the another.

As you can imagine these are conflicting actions, and we need to find a clever way to reconcile this issue.  The basic paradigm of this solution is to adjust the cut-off frequency, namely bandwidth of the filter, based on the signal characteristics (signal speed).  The signal speed can be defined as the derivative of the signal, If average speed of the signal is close to the zero (which means signal is stationary), cut-off frequency gets close to 0 Hz, and vice versa.  Simply, this filter adjust its band-width based on the signal characteristics.

You  can see the filter responses in terms of different cut-off frequencies depending on the signal speed in Fig.3. Higher signal speed pushes the cut-off frequency Fc of the 1 euro filter to the forward, which results lower latency for the output.  On the other hand slower signal pulls the Fc frequency to the backwards, which increases noise suppression capabilities of the filter.

Fig.3 : Filter characteristics of 1 Euro filter based on different signal speed.

You can see an example code segment for 1 Euro filter implementation as C programming language below.

struct one_euro {
    float fc;
    float fc_min;
    float rate;
    float alpha;
    float beta;
    float previous;
    float output;

float one_euro_init(struct one_euro *filter, float fc_min, float rate, float beta, float prev)
    filter->fc = 0;
    filter->fc_min = fc_min;
    filter->rate = rate;
    filter->alpha = 0;
    filter->beta = beta;
    filter->previous = prev;
    filter->output = 0;

float one_euro_filter(struct one_euro *filter, float input)
    //#define PI (3.14159)
    float pi2_fc_t = 0;
    filter->fc = filter->fc_min + filter->beta * (float)abs((int)input - (int)filter->previous);
    pi2_fc_t = 2.0 * PI * filter->fc / filter->rate;
    filter->alpha = pi2_fc_t / (pi2_fc_t + 1.0);
    filter->output = filter->alpha * input + (1.0 - filter->alpha) * filter->previous;
    filter->previous = filter->output;
    return filter->output;

int main()
    /* Example usage of 1 Euro filter */
    struct one_euro filter;

    int result = 0;
    int output = 0;
    one_euro_init(&filter, 0.005, 10000.0,  0.015, 0);
    while (1) {;
        output = oneeuro_filter(&filter, result);
        * filtered 'output' can be used here...

You can access the detailed reference at this link.  and the presentation of an example project.