Prestabilization

The optimization problem that is solved is obtained by simulating the system forward using the dynamics

\[x_{k+1} = F x_k + G u_k.\]

This might lead to an ill-conditioned optimization problem if $F$ is unstable and the prediction horizon is long. To mitigate numerical problems, one can use a prestabilizing feedback, which reparametrizes $u$ with a new decision variable $v$ and a feedback gain $K$ as

\[u_k = -K x_k +v_k.\]

This gives the new closed-loop system

\[x_{k+1} = (F-GK) x_k + G v_k.\]

If $K$ is selected such that $F-GK$ is stable, the condensed Quadratic program will be better conditioned compared to the unstable $F$.

In LinearMPC.jl you can set a stabilizing feedback $K$ for an MPC controller mpc with

set_stabilizing_feedback!(mpc,K)

A popular choice of $K$ is as the gain from solving an infinite horizon LQR problem. This gain can be set as the stabilizing feedback with

set_stabilizing_feedback!(mpc)

Example

Consider a first-order system with a pole in 10, which has the transfer function

\[G(s) = \frac{10}{s-10}.\]

Let's create an MPC controller with prediction horizon $N_p = 50$ and sample time $T_s = 0.1$ seconds for this system:

using LinearMPC
using ControlSystemsBase

sys = tf([1],[1, -10])
mpc = LinearMPC.MPC(sys;Np=50, Ts=0.1)
set_objective!(mpc;Q=1,Rr=1,R=0)

Moreover, we set the weights $Q = 1$ and $R_r = 1$ to be able to track references.

If we try to simulate the system with this controller for $N=100$ time steps with the setpoint $r=1$

sim = LinearMPC.Simulation(mpc;r=[1],N=100)

we get an error telling us "Could not setup optimization problem". The reason for this error is that the resulting optimization problem is very ill-conditioned, and cannot handled by the optimization solver.

To see this more closely, we can check the condition number for the Hessian of the resulting optimization problem:

using LinearAlgebra
optimization_problem = LinearMPC.mpc2mpqp(mpc)
display(cond(optimization_problem.H))

which is around 1e43. This is a very ill-conditioned problem (typically condition numbers above $\approx$ 1e6 might lead to numerical problems.)

To make the optimization problems better conditioned, we can add a prestabilizing feedback:

set_prestabilizing_feedback!(mpc)

Now if we check the condition number again

optimization_problem = LinearMPC.mpc2mpqp(mpc)
display(cond(optimization_problem.H))

we get that it is $\approx$ 8e2, which is a striking improvement! Now, if we simulate the system again, but with our updated controller, we get that the output successfully tracks the reference $r=1$.

sim = LinearMPC.Simulation(mpc;r=[1],N=100)
using Plots
plot(sim)

simple_sim1