LowPass2
LowPass2 smooths a noisy signal with a second-order filter: twice the roll-off of a first-order filter, and a damping setting that decides whether the output overshoots.9 minute read
LowPass2 smooths a noisy signal with a second-order filter: twice the
roll-off of a first-order filter, and a damping setting that decides whether
the output overshoots. One cut-off and one damping govern every channel. It
publishes the whole derivative stack — value, velocity, acceleration and jerk —
so a setpoint path can take all four from one block.
flowchart LR
i1(["input — signal to smooth"]) --> B["LowPass2"]
i2(["disable"]) --> B
i3(["vaLimiterDisable"]) --> B
B --> o1(["output — smoothed signal"])
B --> o2(["outputDot — velocity of the output"])
B --> o3(["outputDDot — acceleration of the output"])
B --> o4(["outputDDDot — jerk of the output"])
B --> o5(["isEnabled"])
B --> o6(["vaLimiterIsEnabled"])
$f_c = \omega/2\pi$ —
omega6.283 rad/s is a 1 Hz cut-off.betais the damping ratio: overshoot is $e^{-\pi\beta/\sqrt{1-\beta^2}}$, so 37% atbeta0.3, 16% at 0.5, and none at 1.0 or above. Ringing frequency is $\omega\sqrt{1-\beta^2}$ [rad/s]. At the cut-off the gain is $1/(2\beta)$ — above 1 for anybetabelow 0.5, so a low damping amplifies rather than removes. Phase lag at the cut-off is 90°, twice a first-order filter’s, and that lag is the trade.
Signals
Inputs
| Path | Unit | Range | Description |
|---|---|---|---|
input |
signal unit | unbounded | The signal to smooth. One element per channel; the channel count is fixed by the machine configuration. |
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. |
vaLimiterDisable |
- | - | True switches the velocity and acceleration limiter off, leaving the filter itself running. Use it for a runtime override; use vaLimiterEnable for the configured intent. |
Outputs
| Path | Unit | Description |
|---|---|---|
output |
signal unit | The smoothed signal, one element per channel. Equals input exactly while bypassed. Starts from zero after every controller start, and is not cleared by a stop. |
outputDot |
signal unit per second | Velocity of output. While the filter runs this is integrated, not differenced, so it is as smooth as output. While bypassed it becomes a raw difference over one task period. |
outputDDot |
signal unit per second² | Acceleration of output, and the quantity the limiter acts on. While bypassed it becomes a raw difference of outputDot. |
outputDDDot |
signal unit per second³ | Jerk of output. Always a raw difference, so it is by far the noisiest output and lags the others by one task period. Treat it as diagnostic, not as a control signal. |
isEnabled |
- | True when enable is true and disable is false. A single value for the whole block, not one per channel. |
vaLimiterIsEnabled |
- | True when vaLimiterEnable is true and vaLimiterDisable is false. It does not tell you the limiter is currently cutting, only that it is switched on. |
Parameters
| Path | Unit | Default | Range | Effect |
|---|---|---|---|---|
omega |
rad/s | 6.283 (1 Hz) | 0.1 – 0.628/task period [s] | Cut-off, shared by all channels. Lower removes more noise and adds more delay. Out-of-range values are corrected silently — see Limits and errors. |
beta |
- | 1.0 | 0.1 – 10 | Damping ratio, shared by all channels. Below 1 the output overshoots and rings; below 0.5 it amplifies at the cut-off. Above 1 it is slower and never overshoots. Out-of-range values are corrected silently. |
vaLimiterMaxAcc |
signal unit per second² | 1.0 | must be positive | Largest acceleration the limiter allows. Not validated — see Limits and errors. Only has effect while vaLimiterIsEnabled is true. |
vaLimiterMaxVel |
signal unit per second | 1.0 | must be positive | Largest velocity the limiter allows. Where this and vaLimiterMaxAcc conflict, the velocity bound wins. Not validated. Only has effect while vaLimiterIsEnabled is true. |
enable |
- | true | - | False bypasses the filter: input passes through unchanged. |
vaLimiterEnable |
- | false | - | True switches on the velocity and acceleration limiter. Set both bounds to real machine values before you turn this on. |
All six parameters are persistent and survive a controller restart. The inputs
and outputs do not; output starts from zero. No parameters exist below this
block.
Setup
-
Link the source signal:
…/actuator/actualPosition→…/positionFilter/input.inputfollows the source on a trace. -
Set
enablefalse.outputequalsinputsample for sample andisEnabledreads false. -
Leave
vaLimiterEnablefalse for the whole of setup. The limiter’s default bounds of 1.0 will throttle any real machine signal. -
Set
omegato 300 andbetato 1.0. Readomegaback; a lower value means your task rate capped it, and tells you the highest cut-off this task rate allows. -
Set
enabletrue.isEnabledreads true andoutputstill tracksinputclosely, with the noise still visible. -
Halve
omegain steps, watching the noise and the loop consuming it. Stop one step above where that loop feels soft or hunts.Step 6 changes what the controller sees. A second-order filter costs twice the phase lag of a first-order one at the same cut-off. Lower the cut-off with the axis at rest before trying it in motion.
-
Trace every element of
inputandoutput. An element that reads zero while the machine moves is an unwired channel, not a filtered one. -
Only if you want the block to shape motion as well as smooth it: set
vaLimiterMaxAccandvaLimiterMaxVelto your machine’s real bounds, then setvaLimiterEnabletrue and confirmvaLimiterIsEnabledreads true.
Tuning
- Read the frequency of the ripple you want gone, in Hz, off a trace of
input. Setomegato $2\pi$ times that frequency, then lower it until the ripple is gone. - Leave
betaat 1.0 while you findomega. Damping and cut-off interact, and 1.0 is the setting with no overshoot. - Step the source and measure the overshoot on
output. If you want a faster response and can accept overshoot, lowerbetatoward 0.5. Never go below 0.5 unless you intend a resonant peak — below it the filter amplifies at the cut-off instead of cutting. - Measure the delay you have bought. Step the source and read the time
outputtakes to reach 90% of the step. Keep it below a tenth of the response time of the loop consumingoutput. - Check the loop in motion, not at rest. Delay costs phase margin only while the axis moves fast enough to need it.
- Re-check
omegaafter any task-rate change. The upper bound scales with the task period, so a slower task can clamp a cut-off that used to be accepted. - If you are using
outputDDotoroutputDDDot, look at them on a trace before you rely on them. Each derivative multiplies the remaining noise, and jerk is differenced rather than filtered.
Read the overshoot off any curve as its highest point above 1.0.
| Symptom | Cause | Action |
|---|---|---|
Noise still present on output |
Cut-off too high | Halve omega, then re-check the loop for softness |
output overshoots the step and rings |
beta below 1 |
Raise beta toward 1.0; ringing stops entirely at 1.0 |
| Noise at the cut-off got worse after enabling | beta below 0.5, so the filter has a resonant peak |
Raise beta to 1.0 |
output reaches its target far too slowly |
beta well above 1, or omega too low |
Lower beta toward 1.0 first, then raise omega |
| Loop went soft, hunts or oscillates after enabling | Delay too high — a second-order filter costs twice the lag | Double omega, or use a first-order filter instead |
omega or beta reads back different from what you wrote |
Outside the accepted band | Read the value back and work within it; the task rate sets omega’s upper bound |
omega used to be accepted and now clamps |
The task period grew, so the upper bound fell | Raise the task rate back, or lower omega |
The axis crawls the moment vaLimiterEnable goes true |
The limiter’s default bounds are 1.0, not machine values | Set vaLimiterMaxAcc and vaLimiterMaxVel first, then re-enable |
| Motion is clipped and you cannot tell whether the limiter did it | Expected: no output reports the limiter cutting | Set vaLimiterEnable false and compare; that is the only way to tell |
| The axis runs away in one direction with the limiter on | A negative or zero value in vaLimiterMaxAcc |
Write a positive value; negatives are accepted and are unsafe |
output ramps from zero for a second after every start |
Expected: the filter starts from zero | Gate the consumer off isEnabled plus a short delay |
output stays at zero while the signal moves |
Channel unwired — an unwired element reads zero | Trace input element by element and link every one |
One large spike on outputDot, outputDDot and outputDDDot the moment the filter is bypassed |
Expected: while bypassed the derivatives are raw differences over one task period | Gate every consumer of the derivative stack on isEnabled |
A brief ring on output after re-enabling the filter |
Expected: the filter resumes with the velocity it measured while bypassed | Re-enable while the axis is at rest, or accept a settle of about $1/(\beta\omega)$ seconds |
outputDDDot is unusably noisy |
Expected: jerk is differenced, not filtered | Use it for diagnosis only; filter it downstream if a consumer needs it |
| Some channels smooth more than others | Not possible — all channels share one omega and one beta |
Use a separate filter per channel group |
output went to a non-numeric value and stays there |
A non-numeric value reached input, omega or beta |
Fix the source; the block cannot recover its own state, so restart the controller |
A conservative starting point for a position signal on a 1 ms task, feeding a loop with a 100 ms response time, with the limiter off:
omega = 60.0
beta = 1.0
vaLimiterEnable = false
This is a starting point, not a final tuning. Work step 1 with a trace of your own signal.
Limits and errors
| Limit | Set by | What happens | Reported |
|---|---|---|---|
omega ≥ 0.1 rad/s |
Fixed | A slower value is replaced by 0.1 rad/s | Silently; read omega back |
omega ≤ 0.628 / task period [s] |
Task rate — 628 rad/s on a 1 ms task, 62.8 rad/s on a 10 ms task | A faster value is replaced by the edge. This is what keeps the filter stable at any setting | Silently; read omega back |
beta within 0.1 – 10 |
Fixed | A value outside the band is replaced by the nearest edge | Silently; read beta back |
vaLimiterMaxAcc, vaLimiterMaxVel |
Nothing | Not checked. Zero and negative values are accepted, and a negative acceleration bound drives the output away in one direction until something else stops it | Not reported |
output |
Nothing while the limiter is off; vaLimiterMaxVel and vaLimiterMaxAcc shape it while the limiter is on |
Unbounded in position either way — the limiter bounds rate, not travel. Limit position downstream if the consumer needs a bound | Not reported |
outputDot, outputDDot, outputDDDot |
Nothing | Unbounded, and largest on the cycle the bypass engages | Not reported |
| Channel count | Machine configuration | Fixed once the controller starts; it cannot be changed at runtime | 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).