Filters¶
Overview¶
Filters in EVA Motors are decorators that clean and smooth control signals before they reach the motor. They remove noise, suppress spikes, and provide predictable signal conditioning. All filters share the same Go(value) interface and can be stacked with behavioral decorators.
| Filter | Type | Best For |
|---|---|---|
| SlidingWindowDecor | Moving average | General noise reduction |
| MinmaxDecor | Morphological | Impulse noise removal |
| AdaptiveSmoothDecor | Adaptive exponential | Mixed noise with varying dynamics |
SlidingWindowDecor – Moving Average¶
Simple moving average filter that computes the arithmetic mean of the last N values.
template <class Motor, unsigned short N>
class SlidingWindowDecor : public Motor
Parameters:
N: Window size (number of values to average)
Behavior:
- Maintains running sum of last N values
- Output = sum / N (arithmetic mean)
- Pass-through until buffer fills
- Constant delay of N/2 samples
Methods:
Go(value)– Apply moving average
Example:
// Smooth throttle with 5-sample window
using SmoothESC = evam::SlidingWindowDecor<evam::ForwardMotor<evam::PwmDriver<9>>, 5>;
SmoothESC motor;
motor.Go(500); // Output = average of last 5 inputs
Use cases:
- General purpose noise reduction
- Pre-filtering for PID control
- Data acquisition smoothing
- Removing quantization noise
MinmaxDecor – Morphological Filter¶
Advanced filter that removes both positive and negative impulse noise while preserving edges.
template <class Motor, unsigned char N>
class MinmaxDecor : public Motor
Parameters:
N: Chunk size (total buffer = N × N elements)
Behavior:
- Divides buffer into N chunks of N elements each
- Finds maximum and minimum in each chunk
- Computes minimax = min of chunk maximums (removes negative spikes)
- Computes maximin = max of chunk minimums (removes positive spikes)
- Output = (minimax + maximin) / 2
Methods:
Go(value)– Apply filter (pass-through until buffer full)getMinimax()– Get current minimax valuegetMaximin()– Get current maximin value
Example:
// Clean noisy potentiometer signal
using CleanMotor = evam::MinmaxDecor<evam::DirectionalMotor<evam::TA6586Driver<9, 10>>, 3>;
CleanMotor motor;
motor.Go(500); // Filtered after 9 samples
Use cases:
- Removing impulse noise from potentiometers
- Cleaning encoder signals with glitches
- Radar/lidar signal conditioning
- Audio pop removal
AdaptiveSmoothDecor – Adaptive Exponential¶
Exponential filter that automatically adjusts its time constant based on input rate of change.
template <class Motor, unsigned short kMinTimeConstantMs = 10, unsigned short kMaxTimeConstantMs = 150>
class AdaptiveSmoothDecor : public Heartbeat, public Motor
Requirements:
Must call eva::tac() in loop() to drive the filter updates (inherits from Heartbeat with 10ms period).
Parameters:
kMinTimeConstantMs: Fast response (5..200ms). Default: 10mskMaxTimeConstantMs: Heavy smoothing (≥ kMinTimeConstantMs, ≤500ms). Default: 150ms
Behavior:
- Large input change (≥200) → fast response (min time constant)
- Small input change (≤5) → heavy smoothing (max time constant)
- Intermediate changes → linear interpolation between min and max
- Deadzone (±3) forces zero output near origin
Methods:
Go(value)– Set target value (range -1000..1000)GetCurrentTimeConstant()– Get current smoothing amount
Example:
using AdaptiveMotor = evam::AdaptiveSmoothDecor<evam::DirectionalMotor<evam::TA6586Driver<9, 10>>>;
AdaptiveMotor motor;
motor.Go(800); // Fast response to large change
motor.Go(810); // Heavy smoothing for small change
Use cases:
- Joystick-controlled vehicles (fast response + smooth idle)
- Camera gimbals (smooth tracking + quick moves)
- Robotic arms (precision positioning + rapid moves)
- Any system requiring both responsiveness and noise rejection
Combining Filters¶
Filters can be stacked for enhanced noise rejection:
#include <evaTac.h>
#include <evamTA6586Driver.h>
#include <evamDirectionalMotor.h>
#include <evamSlidingWindowDecor.h>
#include <evamMinmaxDecor.h>
#include <evamAdaptiveSmoothDecor.h>
using namespace eva;
using namespace evam;
// Remove impulse noise, then smooth, then adapt to dynamics
using BaseMotor = DirectionalMotor<TA6586Driver<9, 10>>;
using SpikeFilter = MinmaxDecor<BaseMotor, 3>; // Remove spikes
using SmoothFilter = SlidingWindowDecor<SpikeFilter, 5>; // General smoothing
using SmartMotor = AdaptiveSmoothDecor<SmoothFilter>; // Adaptive response
SmartMotor motor;
void loop() {
eva::tac(); // Drives AdaptiveSmoothDecor
int raw = analogRead(A0);
int mapped = map(raw, 0, 1023, -1000, 1000);
motor.Go(mapped); // Triple-filtered signal
}
Performance Considerations¶
| Filter | Memory (bytes per N) | Computation | Requires tac() |
|---|---|---|---|
| SlidingWindowDecor | 2 × N + 4 | O(1) | ❌ |
| MinmaxDecor | 2 × N + 2 × N × N | O(N²) | ❌ |
| AdaptiveSmoothDecor | 6 + 4 | O(1) | ✅ |
Recommendations:
- Use
SlidingWindowDecorfor general noise (lowest overhead) - Use
MinmaxDecorwhen spikes are present (N=2 or 3 usually sufficient) - Use
AdaptiveSmoothDecorwhen both fast response and smooth idle are critical - Stack filters in order: spike removal → smoothing → adaptive behavior