Skip to content

Clean up extended_controller: canonical 2-DOF, direct kwarg, validation#140

Merged
baggepinnen merged 2 commits into
masterfrom
extended_controller_cleanup
May 15, 2026
Merged

Clean up extended_controller: canonical 2-DOF, direct kwarg, validation#140
baggepinnen merged 2 commits into
masterfrom
extended_controller_cleanup

Conversation

@baggepinnen
Copy link
Copy Markdown
Member

Summary

Follow-up to the extended_controller review. Five things land in one PR; tests stay green (1378 pass / 6 broken pre-existing / 0 fail).

  • Canonical 2-DOF observer controller. The old implementation set B1 = 0, meaning the observer estimate was biased when xᵣ ≠ 0 and a manually-tuned reference pre-filter was required for any kind of DC tracking. With the change, B1 = (B − KD)·L — the reference enters the observer dynamics so correctly tracks x. For integrator-containing plants (e.g. the cartpole test) this also makes the closed-loop reference-to-output DC gain exactly 1, so the previously-needed dc_gain_compensation prefilter is no longer needed and the related tests now assert dcgain ≈ 1 directly.
  • Delete the broken extended_controller(K::AbstractStateSpace) overload. No callers anywhere; it only logged @error("This has not been verified") on invocation.
  • New direct::Bool=false kwarg mirroring observer_controller's current-time-correction option for discrete plants. The y-channel matches observer_controller(...; direct=true) matrix-for-matrix (so system_mapping(Ce) == -observer_controller(...; direct=true)). The reference path in direct mode is feedforward-only — the canonical 2-DOF B1 formulation gets awkward in the direct/current-time observer form; the docstring records this caveat.
  • Argument validation: size(L) and size(K) are now checked at entry with a clean ArgumentError.
  • Cleanup: drop the stale # should be D21? self-questioning comment, the misplaced # l.D21 does not appear here, see comment in kalman line, and add a comment near the z-branch feedback call recording the default pos_feedback=false convention and the meaning of the U2 slice.

DyadControlSystems was audited and has no extended_controller callers, so this is safe to ship without a deprecation shim.

Docs

docs/src/lqg_disturbance.md gets a new "2-DOF tracking with extended_controller" section demonstrating the controller and how to use the z second return value for DC-gain pre-compensation when the plant has no free integrator.

Test plan

  • Full test suite (Pkg.test()): 1378 pass / 6 broken (pre-existing) / 0 fail / 0 error.
  • New tests in test/test_lqg.jl:
    • Method 7 — discrete-time round-trip: system_mapping(Ce) == -observer_controller(l_d).
    • Method 8 — direct = true round-trip: same equality with direct = true.
    • Dimension validation: wrong-sized L/K raises ArgumentError.
  • cd docs && julia --project make.jl — clean build; the new LQG_DIST @example block evaluates and produces a step response that settles on 1.

🤖 Generated with Claude Code

baggepinnen and others added 2 commits May 15, 2026 10:02
…ation

- Delete the unverified single-argument `extended_controller(K)` overload —
  it had no callers anywhere and only logged `@error` when invoked.
- Make `extended_controller(P, L, K)` produce the canonical 2-DOF observer
  controller. With the old `B1 = 0`, the observer estimate `x̂` was biased
  when `xᵣ ≠ 0`; the new `B1 = (B − KD)·L` lets the reference enter the
  observer dynamics so `x̂` tracks `x`. For integrator-containing plants
  (e.g. the cartpole test), this also makes the closed-loop reference-to-
  output DC gain identity, so the manually-tuned `dc_gain_compensation`
  prefilter in `test_lqg.jl` is no longer needed; the related tests now
  assert `dcgain ≈ 1` directly.
- Add a `direct::Bool=false` kwarg that mirrors `observer_controller`'s
  current-time-correction option for discrete plants. In direct mode the
  reference path remains feedforward-only (no canonical 2-DOF B1); the
  docstring records this.
- Validate `size(L)` and `size(K)` at entry with a clean `ArgumentError`.
- Drop the stale `# should be D21?` self-questioning comment and the
  misplaced `# l.D21 does not appear here, see comment in kalman` line
  (the latter only applied to the `LQGProblem` path).
- Add a comment near the `feedback` call in the `z`-branch recording the
  default `pos_feedback=false` convention and the meaning of the `U2` slice.
- New tests: discrete-time round-trip (Method 7), `direct=true` round-trip
  (Method 8), and dimension validation.
- Docs: append a "2-DOF tracking with extended_controller" section to
  `docs/src/lqg_disturbance.md` demonstrating the controller and the
  DC-gain pre-compensation via `extended_controller(..., z=[...])`.

DyadControlSystems was audited and has no `extended_controller` callers,
so this is safe to ship without a deprecation path.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@codecov-commenter
Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 91.55%. Comparing base (b226e0d) to head (536a8b1).

Additional details and impacted files
@@            Coverage Diff             @@
##           master     #140      +/-   ##
==========================================
+ Coverage   90.96%   91.55%   +0.59%     
==========================================
  Files          20       20              
  Lines        3055     3056       +1     
==========================================
+ Hits         2779     2798      +19     
+ Misses        276      258      -18     
Flag Coverage Δ
unittests 91.55% <100.00%> (+0.59%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@baggepinnen baggepinnen merged commit e199e36 into master May 15, 2026
2 checks passed
@baggepinnen baggepinnen deleted the extended_controller_cleanup branch May 15, 2026 10:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants