0
$\begingroup$

I'm using exponential decay to smooth out changes in object transformations in a UI implementation. A scale going from 0 to 1 is animated by interpolating between current value and target value with some t once per frame, e.g. scale = lerp(scale, targetScale, 0.1). As far as I can tell, this is effectively the same as running an exponential moving average low pass filter with a kernel [0.9, 0.1]. This produces a transition representing an ease-out curve, and works for many cases.

Now I'm interested in achieving a transition resembling an ease-in-out curve, or an s-curve, in the same manner. I'm unable to find the correct search terms to find more information on this. I believe a second order lpf could work, but I'm not finding much material on this.

Additionally, the solution should take into account a varying delta time, but this is a secondary requirement.

$\endgroup$
1
  • $\begingroup$ A simpler way to go to get it to ease in and out is a 2nd-order recursive LPF filter with two real poles. That means two 1st-order LPF filters in cascade. Depending on the difference between the "ease in" rate and "ease out" rate, you might want these two real poles to be coincident. - - - - - - But it you want the ease in and ease out curve to be perfectly symmetrical, you will need an FIR approach as shown below. Remember that this CIC thing (I like to call it a comb filter) is actually FIR even though it is normally implemented with a recursive stage. $\endgroup$ Commented Oct 17 at 15:10

2 Answers 2

1
$\begingroup$

To achieve a symmetric start and end transition necessitates using a filter with a symmetric impulse response, as the resulting step response is the integration of the impulse response. A symmetric impulse response is a linear phase filter, thus the only way to truly do this is with an FIR filter. However linear phase filters can be approximated with IIR solutions, and at the end I show a post-processing "trick" in which an IIR filter could be used if post-processing was feasible for the application.

That said, here are some options to consider:

ease-in ease-out

The first plot is the input step.

The second plot is the result with the exponential moving average filter as given by the OP.

The third plot is the result of using two moving average filters in cascade. (A moving average filter is an FIR filter with unity gain coefficients and then scaled by the sum of the coefficients, so passing through this filter twice or convolving the coefficients to be a single filter). You can also achieve a moving average filter by cascading an accumulator with a difference filter (as a CIC) as follows:

CIC

I used two moving average filters each over 40 samples for the resulting plot I gave above so the difference if done as a CIC would be $1-z^{-40}$.

The fourth plot is the result of using a Gaussian Filter. This is the result of a FIR filter with coefficients given by a Gaussian. You can also get that by repeatably passing the signal through a two tap FIR filter with unity gain coefficients scaled by 1/2 ([0.5, 0.5]), which will approach a Gaussian as given by the Central Limit Theorem.

Another option is to use a standard window as the coefficients for an FIR filter (any of the windows that are available in Matlab, Octave, or scipy.signal would be suitable creating such transitions, resulting in a symmetric weighted moving average which is what is needed).

Below is a demonstration with the Hann window and Kaiser window. The transition duration is set by the length of the window used. I like Kaiser as you can adjust the taper directly to match that of most of the other windows just by changing $\beta$.

window results

And a final option if it is feasible to post process the waveform is to pass the waveform through the Exponential Moving Average Filter, and then time-reverse the result, and pass it through the same filter again. This results in a non-causal linear phase IIR filter solution, which provides the desired start and end transition. The command filtfilt available in Matlab, Octave and Python's scipy.signal provides this functionality with the result given below for the OP's EMA filter coefficients. Notice how the result is non-causal, and for that reason I shifted the start transition further to the right to capture the full start of the resulting output:

filtfilt

$\endgroup$
0
$\begingroup$

Small addition to Dan's excellent answer:

You can have any transition curve you like: just draw it, sample it and differentiate it. This will be your FIR filter kernel.

This is based on the simple fact that the impulse response of an FIR filter is the first derivative of the step response.

Example: let's say we want a transition curve that's fully symmetric and has flat derivatives on both ends.

One way to get the general shape is by shifting and scaling the polynomial $-0.5x^3 + 1.5x$ enter image description here

We can sample this with, for example, 20 points and differentiate it to get an impulse response, which looks like this:

enter image description here

We can then evaluate the response to a step change, whch is indeed our initial curve.

enter image description here

Note that there is a half window "delay" between the curves, which is typical for any linear phase filter.

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.