You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Feedforward2Dof cannot host a dynamic feedforward because Evaluate() is const #277
Evaluate being const, with no Reset() on the interface, rules out any feedforward that carries state. The canonical 2-DOF design — a reference pre-filter F(z) shaping tracking independently of the feedback that sets disturbance stiffness — needs exactly that.
Feedforward2Dof::Reset() reinforces the assumption:
The feedforward is never reset, because it is presumed to have nothing to reset.
Why it matters
The two structures are not interchangeable:
What the class implements: u = clamp(F(r) + C(r - y)), with F a static map. Good for inverse-plant or gravity/friction feedforward.
What a 2-DOF controller usually means: u = C(F(z) r - y), with F(z) dynamic. This is what decouples command tracking from load stiffness, and it is the form described in the standard references (Astrom & Hagglund, Advanced PID Control).
A caller wanting the second form has three options today, all bad: mutable state behind a const method, a const_cast, or bypassing Feedforward2Dof altogether.
Suggested direction
Drop const from Evaluate.
Add virtual void Reset() to Feedforward (defaulted to a no-op so existing static implementations are unaffected), and call it from Feedforward2Dof::Reset().
Both are source-compatible for memoryless feedforwards, which appear to be the only ones in the tree today.
If the intent is that Feedforward2Dof only ever models the static-feedforward structure, then the naming and documentation should say so, since "2-DOF" reads as the pre-filter form to most control engineers.
Context
Hit while implementing a two-degree-of-freedom speed controller. Ended up composing filters::passive::ExponentialMovingAverage as F(z) with an incremental PI directly, rather than through Feedforward2Dof, because the pre-filter needs to advance its state on every Compute.
Activity
fix(controllers): allow stateful feedforward in Feedforward2Dof (#277) (