Generated reference › Complementary Filter — Robotics/Perception Filters
kind: generated#block#robotics-perception-filters

Complementary Filter — Robotics/Perception Filters

Robotics/Perception_Filters/Complementary_Filter · 2 input / 1 output port(s) at insert · exports to Python, MATLAB, Java, Rust, C, C++, VHDL, Verilog, SystemVerilog, PLC Structured Text

Description#

The block's own DESCRIPTION_HTML, rendered verbatim — the same text the config dialog's info panel and the library navigator show. Fix a wrong sentence in the block's .cpp (R-D9), never here.

Complementary Filter

Robotics / Perception Filters

Fuses a gyro rate with an accelerometer-derived angle into one tilt estimate:

θ[k+1] = α·(θ[k] + gyro[k]·Ts) + (1−α)·θacc[k]

The gyro path integrates cleanly over a step but drifts; the accelerometer angle is drift-free but noisy, and wrong whenever the body accelerates. The filter high-passes the first and low-passes the second with weights that sum to one – which is what complementary means, and why there is only one knob.

Ports

  • gyro – the angular rate about the measured axis, in rad/s, a scalar.
  • theta_acc – the angle derived from the accelerometer, in rad, a scalar. It must already be an ANGLE: this block does no atan2 – put Cartesian To Polar or Trigonometric Function in front of it.
  • theta – the fused estimate in rad, a scalar.

Parameters

  • Filter Coefficient (alpha) – the gyro's weight, in [0, 1]. Near 1 the estimate follows the gyro and the accelerometer corrects it slowly; at 0 the block passes theta_acc straight through. The crossover time constant is τ = α·Ts / (1 − α) – at α = 0.98 and Ts = 0.01 s that is about 0.49 s.
  • Initial Angle (rad) – the estimate at t = 0.
  • Sampling Time (s) – zero or less inherits the solver's rate; a positive value runs the block at that period.

Code export

All ten targets: Python, MATLAB, Java, Rust, C, C++, VHDL, Verilog, SystemVerilog and PLC Structured Text. The three folded coefficients – α, α·Ts and 1−α – are baked into the generated body at export time rather than exposed as tunable parameters, so retuning means re-exporting.

The three HDL targets are genuinely synthesizable Q16.16, not simulation-only: the whole filter is three multiplies and two adds, with no trigonometry, division or square root anywhere.

Simulink bridge

None, measured rather than assumed: the sensor-fusion blocks ship in the Sensor Fusion and Tracking Toolbox and the Robotics System Toolbox, neither of which is installed on this machine. The filter is three blocks to rebuild there, and the description above is the whole specification.

Notes

  • Stateful and inherently discrete. The state is the estimate itself.
  • The output is the estimate at the START of the step, published before the update, so the first sample is exactly the configured seed – the same convention as Unicycle Odometry and Gyro Quaternion Integration.
  • α is a weight, not a frequency. Its meaning depends on Ts: the same α at a different rate is a different filter, which is why the sample time is folded into the exported coefficients.
  • One axis per block. For roll and pitch together, use two.
  • No angle wrapping. Both inputs and the output are raw radians; a filter that wrapped would blend across the discontinuity and produce a spin. Keep the inputs on one branch, or wrap downstream with Angle Wrap.

Code facts#

FactValue
registered typeRobotics/Perception_Filters/Complementary_Filter
familyRobotics/Perception_Filters
solver environment classICoreBlock_0_Robotics_1_Perception_Filters_2_Complementary_Filter
sourcesrc/ICoreSDK/ICoreBlockLibrary/Blocks/Robotics/Perception_Filters/Complementary_Filter/ICoreBlock_0_Robotics_1_Perception_Filters_2_Complementary_Filter.cpp
headersrc/ICoreSDK/ICoreBlockLibrary/Blocks/Robotics/Perception_Filters/Complementary_Filter/ICoreBlock_0_Robotics_1_Perception_Filters_2_Complementary_Filter.h
default size on canvas140 × 84 px
ports at insert2 in, 1 out
code generators implementedPython, MATLAB, Java, Rust, C, C++, VHDL, Verilog, SystemVerilog, PLC Structured Text

Ports#

#DirectionSignal typeDescription label
1inICoreDoublegyro
2inICoreDoubletheta_acc
3outICoreDoubletheta

Ports the constructor creates. A block whose port list changes with its configuration adds or removes ports at load time; the count above is the one a freshly inserted block has.

Configuration variables#

Config variableDefaultSimulink parameter
Filter Coefficient (alpha)0.98
Initial Angle (rad)0

Every block also carries Sampling Time (s) from ICoreBlockSolverEnvironment: zero or less inherits the solver's rate, a positive value runs the block at that period.

supportSupport::None
Simulink path
port-count rulePortsParam::None
SampleTime parameteryes

Caveat (shown to the user): no Simulink equivalent available: the sensor-fusion blocks ship in the Sensor Fusion and Tracking Toolbox and the Robotics System Toolbox, neither of which is installed. The filter is a Gain, a Sum and a Unit Delay to rebuild there - keep alpha's weight on the GYRO path, as this block does

Catalog contract: src/ICoreSDK/ICoreCoder/ICoreCommandSystem/SimulinkBridge/ICoreSimulinkBlockCatalog.h

Description vs code#

The lists agree. check_block_descriptions.py finds no disagreement between the description's Ports, Parameters, Code export and Simulink bridge lists and the code's.

The verdict above is tools/docs/check_block_descriptions.py (P7.1), which compares LISTS. It cannot read a sentence: "stateless" on a block with a state, an initial-value semantic the recursion does not implement, a "not synthesizable" caveat the HDL banner contradicts. That is the agent audit (P7.3) on BLOCK_DESCRIPTION_AUDIT.md, and this tool's green is not a substitute for one.

File banner (developer view)#

The top comment of the block's .cpp — the maths, the realization and the export strategy, addressed to whoever changes it. It must not contradict the description above (P7.5).

Complementary Filter — the IMU tilt estimator theta[k+1] = alpha*theta[k] + (alpha*Ts)*gyro[k] + (1-alpha)*theta_acc[k]

which is the folded form of alpha*(theta + gyro*Ts) + (1-alpha)*theta_acc. The FOLDED form is the contract: compute_h_discrete() and all ten generators use the same three coefficients, so the block agrees with its own exported core to the last ulp rather than differing by a rounding of alpha*Ts.

Affine, so the three HDL targets are genuinely synthesizable Q16.16 - see the header for why that is worth stating explicitly in this family.

Sample results#

Complementary Filter — Step: 0 -> 1 at t = 1 sComplementary Filter — Step: 0 -> 1 at t = 1 s0123012345t (s)in ICoreDouble-Out-0in ICoreDouble-Out-0out ICoreDouble-Out-0

The same rig also ran:

StimulusWhat it isOutput range
impulseImpulse: one sample of 1 at k = 5, 0 elsewhere (Repeating Sequence Stair)0 … 0.118
rampRamp: slope 1 from t = 00 … 13.86
sineSine Wave: amplitude 1, 2 rad/s, no phase, no bias-0.2787 … 1.027
tableRepeating Sequence Stair: [-2 -1 -0.5 0 0.5 1 2 3], one entry per sample-0.4013 … 1.777

Plotted: step — Step: 0 -> 1 at t = 1 s

Category dynamic · sample time 0.1 · 60 steps · commit ccf005c8 · produced by docsSample --out <folder> --steps 60 · data docs/generated/samples/Robotics__Perception_Filters__Complementary_Filter.json · the SVG is generated from those numbers by tools/docs/plot_svg.py, so it is a run and not a drawing (R-D10).