Feedback Linearization (gnc-autonomy/control/feedback-linearization)
Use when the task is input-output feedback linearization of a nonlinear
plant with a known, exact model: the affine single-input plant xdot =
f(x) + g(x) u, y = h(x) is linearized exactly by canceling its
nonlinear terms through the control channel, so the chosen output obeys
the linear relation y^(r) = v under an outer tracking loop, and the
design is accepted only after the unobservable internal dynamics are
checked stable through their zero dynamics. This leaf implements the
Lie-derivative relative-degree construction (Slotine and Li; Isidori)
in pure Python, stdlib only. It pairs with
gnc-autonomy/control/state-space-analysis for the linear-model
counterpart and with gnc-autonomy/control/sliding-mode-control, the
switching-law sibling of the same nonlinear vein, for a plant with
matched uncertainty instead of an exact model.
Domain quick reference
- Plant: xdot1 = -A11 x1 + x2, xdot2 = CUBIC x1^3 + QUAD x2^2 + x1 u,
the affine model xdot = f(x) + g(x) u with g(x) = (0, x1), a control
effectiveness that vanishes at x1 = 0. The model is exact and given,
never identified or adapted.
- Lie derivatives: L_f^k h(x) is the k-th derivative of the output h
along the drift field f, computed by analytic chain rule (no finite
differences); L_g L_f^k h(x) = <d(L_f^k h)(x), g(x)> is the
relative-degree probe along the control field.
- Relative degree r: the first r with L_g L_f^(r-1) h nonzero at x,
preceded by L_g L_f^k h = 0 for k = 0 .. r - 2. No finite relative
degree exists when every probe up to the system order N vanishes.
- Decoupling scalar: a(x) = L_g L_f^(r-1) h, the quantity that must be
inverted to form the control. It is x1 for both registered outputs
of this plant, invertible on x1 != 0.
- Linearizing control: u = (v - L_f^r h) / a(x) cancels the nonlinear
terms exactly, leaving y^(r) = v on the chosen channel.
- Outer tracking loop: v = -K (y - y_ref) for relative degree 1, and
v = -K^2 (y - y_ref) - 2 K ydot for relative degree 2, placing the
closed-loop pole at s = -K (a double pole at r = 2). K is one fixed
given constant, never tuned, scheduled, or adapted online.
- Zero dynamics: the internal dynamics left over when the output and
its derivatives are held at zero. For this plant's relative-degree-1
design the internal dynamics reduce to x1dot = -A11 x1; the design is
accepted only when that eigenvalue is strictly negative.
- Units are SI-neutral state variables; the module runs a closed-loop
RK4 simulation with the control law recomputed fresh at every
integration stage, so its residual against the exact closed form is
RK4 truncation error only.
- ARP4754A frames the development assurance context for the airborne
or space function that hosts the linearizing loop; the relations
above are standard nonlinear control methodology, summary-only.
Workflow
- Fix the plant and the operating state x, and register the output
channel (x1 or x2) whose relative degree you need.
- Compute the Lie derivatives L_f^k h along the drift field with
Lf_power_h, and probe the control channel with Lg_Lf_power_h.
- Establish the relative degree r with relative_degree: the defining
property L_g L_f^k h = 0 for k < r - 1 with a nonzero probe at
r - 1; a ValueError signals no finite relative degree exists at
that state.
- Read the decoupling scalar a(x) with decoupling_scalar and invert
it, either directly with linearizing_control_from or at the state
level with linearizing_control, to form the linearizing control
u = (v - L_f^r h) / a that cancels the nonlinear terms.
- Set the outer linear command v with outer_command at the assigned
closed-loop pole rate K.
- Run the closed-loop RK4 simulation with closed_loop_sim and compare
the output against the exact closed form (closed_form_r1 or
closed_form_r2) with series_max_abs_error, and confirm the pole
placement with measured_decay_rate.
- Check the internal dynamics of the relative-degree-1 design against
the zero-dynamics stability verdict with zero_dynamics_analysis
before accepting the design.
- Confirm the deterministic checks with the contract test
scripts/test_feedback_linearization.py.
Worked example
Plant: xdot1 = -x1 + x2, xdot2 = x1^3 + x2^2 + x1 u (A11 = CUBIC =
QUAD = 1.0). Worked initial state x0 = (0.5, 0.0), unit-step reference
y_ref = 1.0, assigned closed-loop rate K = 2.0.
- Relative degree at x0: output x2 gives r = 1 with probe table
[(0, 0.5)] (L_g h2 = x1 = 0.5, nonzero at r - 1 = 0); output x1
gives r = 2 with table [(0, 0.0), (1, 0.5)] (L_g h1 = 0 for k = 0 <
r - 1 = 1, L_g L_f h1 = x1 = 0.5 nonzero at r - 1). The decoupling
scalar a(x0) = 0.5 for both outputs, and a(1.0, 0.75) = 1.0.
- Lie terms and controls: L_f h2 = f2 = 0.125 and L_f^2 h1 = x1 - x2 +
x1^3 + x2^2 = 0.625. Outer commands v = 2.0 (r = 1) and v = 4.0
(r = 2) give linearizing controls u = 3.75 (r = 1) and u = 6.75
(r = 2). The cancellation residual f2 + a u - v is 0.000e+00 at x0
and at (1.0, 0.75), so the nonlinear terms leave the channel exactly.
- r = 1 closed loop: the simulated output matches the assigned linear
dynamics 1 - exp(-2 t) with max abs error 4.929e-14 over 10001 RK4
samples (truncation only); y(0.5) = 0.632120558829, y(1) =
0.864664716763, y(2) = 0.981684361111, y(5) = 0.999954600070;
settling to the 1e-3 band at 3.454 s; measured decay rate between
t = 0.5 and t = 1.0 is 2.000000, the pole witness for s = -K.
- Internal state of the r = 1 design: x1 matches the closed form
1 - 1.5 exp(-t) + exp(-2 t) with max abs error 4.552e-14, dipping to
0.437500056838 at t = 0.288 s (theory ln(4/3) = 0.287682 s) before
rising to x1(10) = 0.999931902167, approaching y_ref while staying
bounded.
- r = 2 companion loop: the fully linearized output matches the
double-pole closed form 1 - (0.5 + 1.5 t) exp(-2 t) with max abs
error 1.275e-13; the coefficient B = -1.5 is nonzero, the surviving
t exp(-2 t) signature of the repeated pole (s + 2)^2.
- Zero-dynamics verdict: eigenvalue -1.0, verdict "asymptotically
stable"; the constrained samples x1 = 0.5 exp(-t) at t = 1, 2, 5 s
are 0.183939721, 0.067667642, 0.003368973, so the design is accepted.
Verification
- Confirm relative_degree('x2', (0.5, 0.0)) returns (1, [(0, 0.5)]) and
relative_degree('x1', (0.5, 0.0)) returns (2, [(0, 0.0), (1, 0.5)]).
- Confirm the cancellation identity f2 + a u - v = 0 within 1e-12 at
the worked state and at (1.0, 0.75).
- Confirm the r = 1 closed loop matches 1 - exp(-2 t) within 1e-9 and
the measured decay rate matches K = 2.0 within 1e-3.
- Confirm the r = 2 closed loop matches the double-pole closed form
within 1e-9 and carries a nonzero double-root coefficient B.
- Confirm the zero-dynamics verdict is "asymptotically stable" with
eigenvalue -1.0, and that relative_degree raises ValueError at
x1 = 0 (the decoupling scalar vanishes) and for the constant output.
- Run the contract test offline: python3
scripts/test_feedback_linearization.py (29 tests, deterministic).
Related leaves
- gnc-autonomy/control/state-space-analysis: linear-model eigenvalue
and controllability analysis, the fixed-linear counterpart when the
plant needs no nonlinear cancellation.
- gnc-autonomy/control/sliding-mode-control: the switching-law sibling
of the same nonlinear vein, sizing a discontinuous control against a
matched-uncertainty bound instead of canceling a known model exactly.
- gnc-autonomy/control/adaptive-control: model-reference adaptation of
gains for an unknown plant coefficient, the case where no exact model
is available to cancel.
- gnc-autonomy/control/gain-scheduling: interpolating existing linear
gains across the envelope, the alternative when K should vary with
the operating point instead of staying fixed.
Pitfalls
- Holding the control fixed across an integration step: the linearizing
control must be recomputed at every RK4 stage from that stage's
state, not sampled once per step and held; a held control introduces
first-order-in-dt error instead of the RK4 truncation residual near
1e-13 that the worked example reports.
- Evaluating the relative-degree probe at a singular state: the
decoupling scalar a(x) = x1 vanishes at x1 = 0, so no finite relative
degree exists there and the linearizing control is undefined; check
x1 != 0 before inverting.
- Reading the internal state against the reference instead of its own
closed form: x1 trails y_ref by the 1.5 exp(-t) mode of the internal
dynamics (a 6.81e-5 gap at t = 10 s), so assert against
1 - 1.5 exp(-t) + exp(-2 t), not against y_ref directly.
- Treating K as tunable online: K is one fixed given closed-loop rate
in this leaf; scheduling or adapting it belongs to
gnc-autonomy/control/gain-scheduling or the adaptive-control siblings.
- Skipping the zero-dynamics check: canceling the output dynamics
exactly says nothing about the unobservable internal state; the
relative-degree-1 design is accepted only after its zero-dynamics
eigenvalue is confirmed strictly negative.
Behavior contract (gate 3)
Run the deterministic contract test (stdlib unittest, offline):
python3 scripts/test_feedback_linearization.py
The test covers the Lie-derivative and relative-degree identities at
the worked state and at a probe state, the decoupling-scalar inversion
and the cancellation identity, the linearizing control values for both
the relative-degree-1 and relative-degree-2 designs, the RK4
closed-loop simulation against both exact closed forms with the
measured pole-placement witness, the internal-dynamics closed form and
its minimum, the zero-dynamics stability verdict and its constrained
samples, the relative-degree rejection cases (a singular state, the
constant output, an unknown output), the decoupling-inversion guard,
and ValueError rejection of non-positive closed-loop rate, sample time,
simulation time, reference, and non-physical state inputs.
Compliance
- Standards referenced, not reproduced: ARP4754A frames development
assurance for the airborne or space functions that host a
feedback-linearizing control loop; the Lie-derivative relative-degree
construction above is standard nonlinear control methodology
(Slotine and Li; Isidori), summary-only per standards-map.yaml.
- compliance: STANDARDS-REF, gated: false.
1---2name: feedback-linearization3description: Use when you must apply feedback linearization to a nonlinear plant with a known exact model: compute the Lie derivatives of the output along the drift and control vector fields to establish the relative degree, invert the decoupling scalar at the operating state, form the linearizing control that cancels the nonlinear terms so the output channel obeys the linear relation y^(r) = v, apply the outer linear tracking loop with the assigned closed-loop pole placement, and check the internal dynamics via their zero dynamics before accepting the design. Produces the relative-degree verdict, the linearizing control with the decoupling scalar, the exactly linearized closed-loop response against the assigned closed form, and the zero-dynamics stability verdict that gates the design. Trigger: feedback linearization, input output linearization, lie derivative, relative degree, decoupling matrix, linearizing control, zero dynamics, internal dynamics stability.4license: Apache-2.05---67# Feedback Linearization (gnc-autonomy/control/feedback-linearization)89Use when the task is input-output feedback linearization of a nonlinear10plant with a known, exact model: the affine single-input plant xdot =11f(x) + g(x) u, y = h(x) is linearized exactly by canceling its12nonlinear terms through the control channel, so the chosen output obeys13the linear relation y^(r) = v under an outer tracking loop, and the14design is accepted only after the unobservable internal dynamics are15checked stable through their zero dynamics. This leaf implements the16Lie-derivative relative-degree construction (Slotine and Li; Isidori)17in pure Python, stdlib only. It pairs with18gnc-autonomy/control/state-space-analysis for the linear-model19counterpart and with gnc-autonomy/control/sliding-mode-control, the20switching-law sibling of the same nonlinear vein, for a plant with21matched uncertainty instead of an exact model.2223## Domain quick reference2425- Plant: xdot1 = -A11 x1 + x2, xdot2 = CUBIC x1^3 + QUAD x2^2 + x1 u,26 the affine model xdot = f(x) + g(x) u with g(x) = (0, x1), a control27 effectiveness that vanishes at x1 = 0. The model is exact and given,28 never identified or adapted.29- Lie derivatives: L_f^k h(x) is the k-th derivative of the output h30 along the drift field f, computed by analytic chain rule (no finite31 differences); L_g L_f^k h(x) = <d(L_f^k h)(x), g(x)> is the32 relative-degree probe along the control field.33- Relative degree r: the first r with L_g L_f^(r-1) h nonzero at x,34 preceded by L_g L_f^k h = 0 for k = 0 .. r - 2. No finite relative35 degree exists when every probe up to the system order N vanishes.36- Decoupling scalar: a(x) = L_g L_f^(r-1) h, the quantity that must be37 inverted to form the control. It is x1 for both registered outputs38 of this plant, invertible on x1 != 0.39- Linearizing control: u = (v - L_f^r h) / a(x) cancels the nonlinear40 terms exactly, leaving y^(r) = v on the chosen channel.41- Outer tracking loop: v = -K (y - y_ref) for relative degree 1, and42 v = -K^2 (y - y_ref) - 2 K ydot for relative degree 2, placing the43 closed-loop pole at s = -K (a double pole at r = 2). K is one fixed44 given constant, never tuned, scheduled, or adapted online.45- Zero dynamics: the internal dynamics left over when the output and46 its derivatives are held at zero. For this plant's relative-degree-147 design the internal dynamics reduce to x1dot = -A11 x1; the design is48 accepted only when that eigenvalue is strictly negative.49- Units are SI-neutral state variables; the module runs a closed-loop50 RK4 simulation with the control law recomputed fresh at every51 integration stage, so its residual against the exact closed form is52 RK4 truncation error only.53- ARP4754A frames the development assurance context for the airborne54 or space function that hosts the linearizing loop; the relations55 above are standard nonlinear control methodology, summary-only.5657## Workflow58591. Fix the plant and the operating state x, and register the output60 channel (x1 or x2) whose relative degree you need.612. Compute the Lie derivatives L_f^k h along the drift field with62 Lf_power_h, and probe the control channel with Lg_Lf_power_h.633. Establish the relative degree r with relative_degree: the defining64 property L_g L_f^k h = 0 for k < r - 1 with a nonzero probe at65 r - 1; a ValueError signals no finite relative degree exists at66 that state.674. Read the decoupling scalar a(x) with decoupling_scalar and invert68 it, either directly with linearizing_control_from or at the state69 level with linearizing_control, to form the linearizing control70 u = (v - L_f^r h) / a that cancels the nonlinear terms.715. Set the outer linear command v with outer_command at the assigned72 closed-loop pole rate K.736. Run the closed-loop RK4 simulation with closed_loop_sim and compare74 the output against the exact closed form (closed_form_r1 or75 closed_form_r2) with series_max_abs_error, and confirm the pole76 placement with measured_decay_rate.777. Check the internal dynamics of the relative-degree-1 design against78 the zero-dynamics stability verdict with zero_dynamics_analysis79 before accepting the design.808. Confirm the deterministic checks with the contract test81 scripts/test_feedback_linearization.py.8283## Worked example8485Plant: xdot1 = -x1 + x2, xdot2 = x1^3 + x2^2 + x1 u (A11 = CUBIC =86QUAD = 1.0). Worked initial state x0 = (0.5, 0.0), unit-step reference87y_ref = 1.0, assigned closed-loop rate K = 2.0.8889- Relative degree at x0: output x2 gives r = 1 with probe table90 [(0, 0.5)] (L_g h2 = x1 = 0.5, nonzero at r - 1 = 0); output x191 gives r = 2 with table [(0, 0.0), (1, 0.5)] (L_g h1 = 0 for k = 0 <92 r - 1 = 1, L_g L_f h1 = x1 = 0.5 nonzero at r - 1). The decoupling93 scalar a(x0) = 0.5 for both outputs, and a(1.0, 0.75) = 1.0.94- Lie terms and controls: L_f h2 = f2 = 0.125 and L_f^2 h1 = x1 - x2 +95 x1^3 + x2^2 = 0.625. Outer commands v = 2.0 (r = 1) and v = 4.096 (r = 2) give linearizing controls u = 3.75 (r = 1) and u = 6.7597 (r = 2). The cancellation residual f2 + a u - v is 0.000e+00 at x098 and at (1.0, 0.75), so the nonlinear terms leave the channel exactly.99- r = 1 closed loop: the simulated output matches the assigned linear100 dynamics 1 - exp(-2 t) with max abs error 4.929e-14 over 10001 RK4101 samples (truncation only); y(0.5) = 0.632120558829, y(1) =102 0.864664716763, y(2) = 0.981684361111, y(5) = 0.999954600070;103 settling to the 1e-3 band at 3.454 s; measured decay rate between104 t = 0.5 and t = 1.0 is 2.000000, the pole witness for s = -K.105- Internal state of the r = 1 design: x1 matches the closed form106 1 - 1.5 exp(-t) + exp(-2 t) with max abs error 4.552e-14, dipping to107 0.437500056838 at t = 0.288 s (theory ln(4/3) = 0.287682 s) before108 rising to x1(10) = 0.999931902167, approaching y_ref while staying109 bounded.110- r = 2 companion loop: the fully linearized output matches the111 double-pole closed form 1 - (0.5 + 1.5 t) exp(-2 t) with max abs112 error 1.275e-13; the coefficient B = -1.5 is nonzero, the surviving113 t exp(-2 t) signature of the repeated pole (s + 2)^2.114- Zero-dynamics verdict: eigenvalue -1.0, verdict "asymptotically115 stable"; the constrained samples x1 = 0.5 exp(-t) at t = 1, 2, 5 s116 are 0.183939721, 0.067667642, 0.003368973, so the design is accepted.117118## Verification119120- Confirm relative_degree('x2', (0.5, 0.0)) returns (1, [(0, 0.5)]) and121 relative_degree('x1', (0.5, 0.0)) returns (2, [(0, 0.0), (1, 0.5)]).122- Confirm the cancellation identity f2 + a u - v = 0 within 1e-12 at123 the worked state and at (1.0, 0.75).124- Confirm the r = 1 closed loop matches 1 - exp(-2 t) within 1e-9 and125 the measured decay rate matches K = 2.0 within 1e-3.126- Confirm the r = 2 closed loop matches the double-pole closed form127 within 1e-9 and carries a nonzero double-root coefficient B.128- Confirm the zero-dynamics verdict is "asymptotically stable" with129 eigenvalue -1.0, and that relative_degree raises ValueError at130 x1 = 0 (the decoupling scalar vanishes) and for the constant output.131- Run the contract test offline: python3132 scripts/test_feedback_linearization.py (29 tests, deterministic).133134## Related leaves135136- gnc-autonomy/control/state-space-analysis: linear-model eigenvalue137 and controllability analysis, the fixed-linear counterpart when the138 plant needs no nonlinear cancellation.139- gnc-autonomy/control/sliding-mode-control: the switching-law sibling140 of the same nonlinear vein, sizing a discontinuous control against a141 matched-uncertainty bound instead of canceling a known model exactly.142- gnc-autonomy/control/adaptive-control: model-reference adaptation of143 gains for an unknown plant coefficient, the case where no exact model144 is available to cancel.145- gnc-autonomy/control/gain-scheduling: interpolating existing linear146 gains across the envelope, the alternative when K should vary with147 the operating point instead of staying fixed.148149## Pitfalls150151- Holding the control fixed across an integration step: the linearizing152 control must be recomputed at every RK4 stage from that stage's153 state, not sampled once per step and held; a held control introduces154 first-order-in-dt error instead of the RK4 truncation residual near155 1e-13 that the worked example reports.156- Evaluating the relative-degree probe at a singular state: the157 decoupling scalar a(x) = x1 vanishes at x1 = 0, so no finite relative158 degree exists there and the linearizing control is undefined; check159 x1 != 0 before inverting.160- Reading the internal state against the reference instead of its own161 closed form: x1 trails y_ref by the 1.5 exp(-t) mode of the internal162 dynamics (a 6.81e-5 gap at t = 10 s), so assert against163 1 - 1.5 exp(-t) + exp(-2 t), not against y_ref directly.164- Treating K as tunable online: K is one fixed given closed-loop rate165 in this leaf; scheduling or adapting it belongs to166 gnc-autonomy/control/gain-scheduling or the adaptive-control siblings.167- Skipping the zero-dynamics check: canceling the output dynamics168 exactly says nothing about the unobservable internal state; the169 relative-degree-1 design is accepted only after its zero-dynamics170 eigenvalue is confirmed strictly negative.171172## Behavior contract (gate 3)173174Run the deterministic contract test (stdlib unittest, offline):175176 python3 scripts/test_feedback_linearization.py177178The test covers the Lie-derivative and relative-degree identities at179the worked state and at a probe state, the decoupling-scalar inversion180and the cancellation identity, the linearizing control values for both181the relative-degree-1 and relative-degree-2 designs, the RK4182closed-loop simulation against both exact closed forms with the183measured pole-placement witness, the internal-dynamics closed form and184its minimum, the zero-dynamics stability verdict and its constrained185samples, the relative-degree rejection cases (a singular state, the186constant output, an unknown output), the decoupling-inversion guard,187and ValueError rejection of non-positive closed-loop rate, sample time,188simulation time, reference, and non-physical state inputs.189190## Compliance191192- Standards referenced, not reproduced: ARP4754A frames development193 assurance for the airborne or space functions that host a194 feedback-linearizing control loop; the Lie-derivative relative-degree195 construction above is standard nonlinear control methodology196 (Slotine and Li; Isidori), summary-only per standards-map.yaml.197- compliance: STANDARDS-REF, gated: false.