Language: EN

arduino-paso-bajo-exponencial

Exponential Moving Average (EMA) Low Pass and High Pass Filter in Arduino

We have been discussing how to reduce noise in measurements through multiple sampling and the application of filters such as the moving average filter.

In this post, we will see the Exponential Moving Average (EMA) filter. The EMA filter is one of the most widely used in digital electronics due to its good results combined with an incredibly simple and efficient implementation.

The EMA filter consists in obtaining a filtered value from a measurement by applying the following expression:

Where An is the filtered value, An-1 is the previous filtered value, M is the sampled value of the signal to be filtered, and alpha is a factor between 0 and 1.

Therefore, the EMA filter provides a contribution of “new” information through the measurement M, and a smoothing effect based on the memory provided by the previous filtered value An-1. The result of an EMA filter is a smoothed signal where the amount of smoothing depends on the alpha factor, as we will analyze later.

The advantages in terms of simplicity and computational efficiency are evident. The calculation requires a single simple instruction. In terms of memory requirements, we only need to store the previous filtered value. This represents a great computational advantage over other filters that require storing N values and performing calculations on all of them.

Low Pass Filter

Like all filters that smooth a signal (not exclusive to the EMA filter), we can use the exponential filter as a low pass filter, that is, an algorithm that (ideally) allows frequency components lower than a cutoff frequency to pass.

We can use the low pass filter to eliminate high-frequency noise superimposed on the signal, which can be used to improve sensor measurements and communications, among other uses.

Unfortunately, no real filter is perfect and the filters we can generate in reality have limitations that deviate from ideal behavior.

Additionally, even if we could create an ideal filter, it does not mean that we could completely eliminate the noise, since certain frequencies can overlap with the frequencies of interest of the signal itself (the actual variations of the signal).

High Pass Filter

Although less common, it is also possible to obtain a high pass filter, that is, to eliminate frequencies lower than a cutoff frequency.

A high pass filter can be used, for example, to digitally eliminate the direct current component of a signal (bias), slow variations compared to the frequency of interest, detection of abrupt changes in the signal, among others.

If we could build an ideal low pass filter, to obtain the high pass filter we only have to subtract the signal after the low pass filter from the original signal.

Of course, in the real world, it is also impossible to build an ideal high pass filter. In the (very common) case of building the high pass filter from a low pass filter, the limitations we will obtain are the result of the low pass filter used.

Influence of the Alpha Factor

The alpha factor conditions the behavior of the exponential filter and is related to the filter cutoff frequency. However, a simple relationship is not always possible, since it depends on the sampling time of our system, which is initially unknown and possibly variable between cycles.

Quantitatively:

  • A value of Alpha = 1 provides the unfiltered signal, since it dispenses with the filtering effect provided by the previous measurement.
  • A value of Alpha = 0 causes the filtered value to always be 0, since it dispenses with the new information provided by the measurement to the system.

Decreasing the alpha factor increases the signal smoothing, but at the cost of introducing negative consequences as well. On the one hand, we can eliminate frequency components that are actually of interest to us, classifying as noise something that was actually a real variation of the signal.

On the other hand, decreasing the alpha factor increases the system’s response time, that is, the time it takes the system to stabilize in the face of a constant input. This results in the introduction of a delay between the original signal and the filtered signal.

Logically, the appropriate value of Alpha will depend on the characteristics of our system, the sampled signal, and the noise we want to eliminate. In principle, we should adjust the value to be suitable for our setup, with typical values being 0.2-0.6.

Results of Low Pass and High Pass Filters

To illustrate the variation in the behavior of the low pass and high pass filters, let’s take a look at the effect for different values of Alpha on a simulated signal. The signal has a main sinusoidal component and superimposed noise at different frequencies.

Here are the results of the low pass filter for a value of Alpha of 0.6 (a fairly common value in the real world). We can see that a part of the high-frequency noise has been eliminated, and most of the signal has been preserved.

arduino-filtro-paso-bajo-0.6

The result of the low pass filter for a value of 0.2. The smoothing has increased, and all high-frequency noise has been eliminated. However, the delay in the filtered signal begins to become evident.

arduino-filtro-paso-bajo-0.2

And the result of the low pass filter for a low value of 0.05. All components of the signal have been eliminated except the main one, but in return, we have a very significant delay in the filtered signal.

arduino-filtro-paso-bajo-0.05

Regarding the high pass filter, here are the results for the high pass filter for a value of Alpha of 0.6. We can see that the low-frequency components of the signal are eliminated, leaving the faster variations, which are centered on the Y0 axis.

arduino-filtro-paso-alto-0.6

And here are the results of the high pass filter for a value of Alpha of 0.025. We can see that the shape of the original signal is preserved, but after a certain transitory period, the bias of the signal has been completely eliminated, and it is now centered at the origin.

arduino-filtro-paso-alto-0.025

As you can see, we have a wide variety of behaviors with the same filter and it leads to very interesting results, from signal smoothing and high-frequency noise filtering with the low pass filter, to bias elimination or extraction of main frequencies with the high pass filter.

Why Does the EMA Filter Work?

It is common to find literature and projects where an EMA filter is used. What is not so common is to explain why an EMA filter works.

So why is this simple operation capable of filtering the noise from a signal?

Attention, the next section is a “dense” mathematical demonstration. If you don’t like mathematics, or you don’t feel like getting into it right now, you can skip to the next section.

warning-masths-ahead

In reality, the EMA filter is a particular case of a weighted average, that is, a calculation of the average in which each of the elements Xi has a weight Wi that weighs its effect on the global calculation of the average.

How can the formulation we have seen above for the EMA filter be deduced from a particular case of a weighted average?

Let’s start with a generic expression to obtain a series of filtered values Ai from a series of measurements Mi, subject to two parameters alpha and beta.

Analyzing what happens when using this expression for the series of measurements

Generalizing, we can write,

Therefore, the next filtered value is the sum of all previous measurements weighted by an exponential factor beta (which gives rise to the name of the filter) and scaled with an alpha factor.

Theory indicates that the average calculates the effect of all previous measurements, although the beta^n factor reduces their effect. However, in the real world, due to rounding, the contribution of the terms is null after a few iterations.

Where do the constraints come from that both alpha and beta must be less than 1, and that both added together must be equal to 1? Well, I guess at this point, saying that it is intuitive doesn’t satisfy you, right? Since I’m already into it, let’s go all the way.

First of all, since the filtered value is obtained from a series of geometric type, it follows that, unless all measurements are equal to 0, any beta factor greater than 1 will cause the signal to quickly feedback and tend to infinity.

On the other hand, negative beta values would produce oscillating solutions. Therefore, it follows that to obtain stability, beta must be between 0 and 1.

To verify the relationship between alpha and beta, let’s assume as a simplification that we introduce a constant C as input. It is reasonable to expect that the filtered value obtained is also C. (What a filter it would be if we constantly introduced, for example, 10, and it gave us 8 or 12, right?)

Being B < 1, the previous expression is simplified to

Which finally reduces to,

So the constraints on alpha and beta are explained. You could also have accepted the answer that, from the point of view of a variational formulation of the problem, the constraints on alpha and beta allow the invariant of the energy functional. But hey! You preferred the demonstration with series.

With this, we have demonstrated that the general expression of the EMA filter is actually a weighted average whose weighting factors follow an exponential progression of parameter beta, and that it is necessary for beta to be greater than or equal to 0 and less than 1 for the system to be stable.

Low Pass and High Pass Filters in Arduino

Here is a simple implementation of an exponential low pass and high pass filter (EMA). In the example, we will filter a series of unordered integers that simulate a signal as we would obtain when making a measurement. We access these values through the GetMeasure() function, which simulates the data acquisition process.

The results are shown through the serial port. If you use the Serial Plotter of the Standard IDE, you will be able to see the results graphically in a simple way.

float EMA_ALPHA = 0.6;
int EMA_LP = 0;
int EMA_HP = 0;

int values[] = { 7729, 7330, 10075, 10998, 11502, 11781, 12413, 12530, 14070, 13789, 18186, 14401, 16691, 16654, 17424, 21104, 17230, 20656, 21584, 21297, 19986, 20808, 19455, 24029, 21455, 21350, 19854, 23476, 19349, 16996, 20546, 17187, 15548, 9179, 8586, 7095, 9718, 5148, 4047, 3873, 4398, 2989, 3848, 2916, 1142, 2427, 250, 2995, 1918, 4297, 617, 2715, 1662, 1621, 960, 500, 2114, 2354, 2900, 4878, 8972, 9460, 11283, 16147, 16617, 16778, 18711, 22036, 28432, 29756, 24944, 27199, 27760, 30706, 31671, 32185, 32290, 30470, 32616, 32075, 32210, 28822, 30823, 29632, 29157, 31585, 24133, 23245, 22516, 18513, 18330, 15450, 12685, 11451, 11280, 9116, 7975, 8263, 8203, 4641, 5232, 5724, 4347, 4319, 3045, 1099, 2035, 2411, 1727, 852, 1134, 966, 2838, 6033, 2319, 3294, 3587, 9076, 5194, 6725, 6032, 6444, 10293, 9507, 10881, 11036, 12789, 12813, 14893, 16465, 16336, 16854, 19249, 23126, 21461, 18657, 20474, 24871, 20046, 22832, 21681, 21978, 23053, 20569, 24801, 19045, 20092, 19470, 18446, 18851, 18210, 15078, 16309, 15055, 14427, 15074, 10776, 14319, 14183, 7984, 8344, 7071, 9675, 5985, 3679, 2321, 6757, 3291, 5003, 1401, 1724, 1857, 2605, 803, 2742, 2971, 2306, 3722, 3332, 4427, 5762, 5383, 7692, 8436, 13660, 8018, 9303, 10626, 16171, 14163, 17161, 19214, 21171, 17274, 20616, 18281, 21171, 18220, 19315, 22558, 21393, 22431, 20186, 24619, 21997, 23938, 20029, 20694, 20648, 21173, 20377, 19147, 18578, 16839, 15735, 15907, 18059, 12111, 12178, 11201, 10577, 11160, 8485, 7065, 7852, 5865, 4856, 3955, 6803, 3444, 1616, 717, 3105, 704, 1473, 1948, 4534, 5800, 1757, 1038, 2435, 4677, 8155, 6870, 4611, 5372, 6304, 7868, 10336, 9091 };
int valuesLength = sizeof(values) / sizeof(int);

int getMeasure()
{
  static int index = 0;
  index++;
  return values[index - 1];
}

void setup()
{
  Serial.begin(115200);

  for (int iCount = 0; iCount < valuesLength; iCount++)
  {
    int value = getMeasure();
    int filteredLP = EMALowPassFilter(value);
    int filteredHP = EMAHighPassFilter(value);
    Serial.print(value);
    Serial.print(",");
    Serial.print(filteredLP);
    Serial.print(",");
    Serial.println(filteredHP);
  }
}

void loop()
{
  delay(10000);
}

int EMALowPassFilter(int value)
{
  EMA_LP = EMA_ALPHA * value + (1 - EMA_ALPHA) * EMA_LP;
  return EMA_LP;
}

int EMAHighPassFilter(int value)
{
  EMA_LP = EMA_ALPHA * value + (1 - EMA_ALPHA) * EMA_LP;
  EMA_HP = value - EMA_LP;

  return EMA_HP;
}

The results in the Serial Plotter of the Standard IDE will be as follows,

arduino-filtro-paso-bajo-resultados

Low Pass and High Pass Filters in a Library

What if we put it in a library to make it more convenient to use? Of course, here is a Single EMA Filter library for Arduino. Enjoy it!

Descarga el código

Todo el código de esta entrada está disponible para su descarga en Github. github-full