FIRFilter
FIRFilter filters a signal with a weighted sum of its last few samples.7 minute read
FIRFilter filters a signal with a weighted sum of its last few samples. It
has no feedback, which makes it unconditionally stable: no coefficient set
you can write will make it run away. That is the reason to choose it over a
coefficient-driven filter with feedback, and the trade is cost — matching the
same roll-off takes many more taps. It is single channel: one instance
filters one signal.
flowchart LR
i1(["input — signal to filter"]) --> B["FIRFilter"]
i2(["disable"]) --> B
B --> o1(["output — filtered signal"])
B --> o2(["isEnabled"])
The filter computes $y[k] = h_0,u[k] + h_1,u[k-1] + \dots + h_N,u[k-N]$, with $h$ =
coefficients. DC gain is the sum of the coefficients, so an averaging filter needs $1/M$ in each of its $M$ taps. A symmetric coefficient set delays the signal by exactly half the tap count, with perfectly linear phase. A step settles completely after one tap count and never overshoots for non-negative coefficients. The coefficients are defined in samples, not seconds, so the same set on a different task rate is a different filter.
The tap count is fixed when the machine is built and cannot be changed at runtime. This block also has no reset — see Limits and errors.
Signals
Inputs
| Path | Unit | Range | Description |
|---|---|---|---|
input |
signal unit | unbounded | The signal to filter. A single value, not an array. |
disable |
- | - | True bypasses the filter: input passes straight to output. Use it for a runtime override from a supervisor; use enable for the configured intent. |
Outputs
| Path | Unit | Description |
|---|---|---|
output |
signal unit × the DC gain | The filtered signal, a single value. Equals input exactly while bypassed. Starts from zero after every controller start and reaches its true value after one tap count of samples. |
isEnabled |
- | True when enable is true and disable is false. A single value for the whole block. |
Parameters
| Path | Unit | Default | Range | Effect |
|---|---|---|---|---|
coefficients |
- | 1 in the first tap, 0 in the rest | any | The taps, most recent sample first. All of them are always used — writing zeros into the tail is the only way to shorten the filter, and it does not make it cheaper. Check the DC gain after every change. |
enable |
- | true | - | False bypasses the filter: input passes through unchanged. |
Both parameters are persistent and survive a controller restart, which is how a filter design ships with a machine. No parameters exist below this block.
Setup
-
Design your filter against your task period. A 1 ms task is 1000 samples per second, so a six-tap filter spans 6 ms.
-
Read the length of the
coefficientsarray from the parameter tree. That is your tap count, and it is fixed when the machine is built. -
Link the source signal into
inputand confirm on a trace thatinputfollows the source. -
Set
enablefalse.outputequalsinputsample for sample andisEnabledreads false. -
Write the whole
coefficientsarray, including any trailing zeros. A short write leaves the old values in the tail, and they keep filtering.Step 5 must cover every tap. A leftover coefficient in the tail is the most common misconfiguration of this block, and it shows up as a filter that behaves almost right.
-
Set
enabletrue and confirmisEnabledreads true. -
Feed a constant into
inputand readoutput. Divide the two: the ratio must equal the sum of your coefficients. -
Step the input and count the samples until
outputstops moving. It must be exactly the tap count; anything longer means a coefficient is in a tap you did not intend.
Tuning
There is nothing to tune here in the usual sense. The work is choosing the coefficients and verifying them on the machine.
- Fix the task period before you design anything. Every coefficient depends on it. If the task rate changes later, redesign the whole set — no parameter in this block rescales for you.
- For plain noise reduction, start with an equal-weight average: $1/M$ in each of $M$ taps. It is the simplest set that works and its DC gain is exactly 1.
- For less ripple at the cost of a slightly wider response, taper the weights toward the ends — triangular weights are the usual next step.
- Keep the coefficients symmetric if two filtered signals have to stay in step with each other. Symmetry is what buys exactly linear phase.
- Measure the delay you have bought: half the tap count, in samples, for a
symmetric set. Keep it below a tenth of the response time of the loop
consuming
output. - Verify the DC gain by summing the coefficients. It should be 1 for a filter that is not meant to change the signal’s size.
- Re-verify after any task-rate change, starting from step 1.
Read the settling time off any curve as the sample it first reaches 1.
| Symptom | Cause | Action |
|---|---|---|
output is a constant multiple of what you expected |
The coefficients do not sum to 1 | Divide every coefficient by their sum |
| The filter behaves almost right but not quite | A leftover coefficient in the tail of the array | Write the whole array including trailing zeros, then re-check step 8 of Setup |
output settles more slowly than the tap count |
A non-zero coefficient sits in a tap you did not intend | Write zeros over the whole array, then write your set |
| Writing zeros into the tail did not make the block cheaper | Expected: every tap is computed regardless of its value | Size the instance to the requirement when the machine is built |
| The filter behaves nothing like the design | The design assumed a different sample rate | Redesign at the controller’s actual task period |
| The filter changed behaviour after a task-rate change | Expected: the coefficients are defined in samples, not seconds | Redesign the whole set for the new rate |
| Two filtered signals drifted out of step | The coefficient sets are not both symmetric, or not the same length | Use symmetric sets of equal length on both |
A short glitch on output for a few samples after re-enabling |
Expected: the filter still holds the samples from before the bypass | Wait one tap count, or gate the consumer off isEnabled plus that long |
output ramps up from zero for a few samples after every start |
Expected: the filter memory starts empty | Gate the consumer off isEnabled plus one tap count |
| You cannot flush the filter’s memory | There is no reset in this block | Bypass and re-enable, then wait one tap count |
output went to a non-numeric value |
A non-numeric value reached input or a coefficient |
Fix the source; the value clears itself after one tap count |
| You need more taps than the instance has | The tap count is fixed when the machine is built | It cannot be changed at runtime; it needs a configuration change |
| You need to filter several axes | Not possible — this block is single channel | Use one instance per axis |
A five-tap equal-weight average in a six-tap instance, which smooths without changing the signal’s size:
coefficients = 0.2, 0.2, 0.2, 0.2, 0.2, 0.0
enable = true
A pass-through, which is also what the block ships with:
coefficients = 1.0, 0.0, 0.0, 0.0, 0.0, 0.0
Limits and errors
| Limit | Set by | What happens | Reported |
|---|---|---|---|
| Filter stability | Guaranteed | The block has no feedback, so no coefficient set can make it diverge. This is why you would choose it | Not applicable |
| DC gain | Nothing | Not normalised and not checked. Whatever the coefficients sum to is the gain you get | Not reported |
| Tap count | Machine configuration | Fixed when the machine is built. A longer coefficient write has its tail dropped; a shorter one leaves the old tail in place and keeps using it | Not reported |
| Filter memory | Nothing | There is no reset input. Bypassing the block and re-enabling it is the only way to flush it, and it takes one tap count to clear | Not reported |
output |
Nothing | Unbounded. Limit it downstream if the consumer needs a bound | Not reported |
| Channel count | Fixed | One channel per instance, always | Not reported |
The block raises no errors or warnings and logs nothing. Every failure above shows as a value on a trace, not as a message.
Verified against motorcortex-control3 3.30.0 (bc348fd).