# \[ANN\] ModelPredictiveControl.jl

**URL:** <https://discourse.julialang.org/t/ann-modelpredictivecontrol-jl/100147>\
**Category:** Package Announcements\
**Tags:** control, jump, controlsystems, nmpc\
**Created:** [June 10, 2023, 6:10pm UTC](https://discourse.julialang.org/t/ann-modelpredictivecontrol-jl/100147 "2023-06-10T18:10:43Z")\
**Posts on this page:** 20\
**Page:** 2

<div class="post-metadata">

**Author:** ![franckgaga](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/franckgaga/32/218241_2.png) [@franckgaga](https://discourse.julialang.org/u/franckgaga)\
**Post date:** [December 13, 2023, 7:58pm UTC](https://discourse.julialang.org/t/ann-modelpredictivecontrol-jl/100147/21 "2023-12-13T19:58:34Z")

</div>

Alright, I see how it could be useful.

As I said, since \mathbf{\hat{y}}(k) = \mathbf{h} \big( \mathbf{\hat{x}}\_k(k) \big), it is equivalent as bounding the estimated sensor noise \mathbf{\hat{v}}(k) = \mathbf{y}(k) - \mathbf{h} \big( \mathbf{\hat{x}}\_k(k) \big) in the MHE. I will move the definition of \mathbf{\hat{V}} and \mathbf{\hat{W}} vectors in the extended help of `MovingHorizonEstimator` constructor to clarify that point.

I found this [great lecture](https://murray.cds.caltech.edu/images/murray.cds/0/0d/L4-2_MHE.pdf) that mentions bounds on \mathbf{\hat{x}}, \mathbf{\hat{v}} and \mathbf{\hat{w}}. I will implement the three types of bounds. It will probably leads to infeasibilities in some case so I will also add soft constraints later.

---

<div class="post-metadata">

**Author:** ![franckgaga](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/franckgaga/32/218241_2.png) [@franckgaga](https://discourse.julialang.org/u/franckgaga)\
**Post date:** [December 21, 2023, 10:26pm UTC](https://discourse.julialang.org/t/ann-modelpredictivecontrol-jl/100147/22 "2023-12-21T22:26:41Z")

</div>

**ModelPredictiveControl v0.15.0**

The `MovingHorizonEstimator` types now support hard constraints on the estimated state \mathbf{\hat{x}}, process noise \mathbf{\hat{w}} and sensor noise \mathbf{\hat{v}}. Additionally, MHEs based on `LinModel`s are now treated as quadratic programs (QP) solved with `OSQP.jl` by default instead of nonlinear problems. From my quick tests, it’s about 500 times faster than v0.14! I never did the exercise for the MHE, and it was surprisingly a long and hard job to construct [all the prediction matrices](https://franckgaga.github.io/ModelPredictiveControl.jl/dev/internals/state_estim/#ModelPredictiveControl.init_predmat_mhe) that appear in the quadratic objective and the constraints 😆. I was also surprised when I understood that, contrarily to `LinMPC`, the Hessian matrix of the QP is not constant, because of the time-varying value of the arrival covariance \mathbf{\bar{P}}. This means that solvers need to re-factorize the QP matrices at each discrete time k. This is still drastically cheaper than treating the optimization problem as nonlinear.

The changelog is:

- added: MHE hard constraints on estimated process and sensor noises
- added: MHEs based on `LinModel` are now treated as a quadratic optimization instead of nonlinear
- added: Plots recipe avoid repeat xlabels for MIMOs
- added: `getinfo` method for `MovingHorzionEstimator` for troubleshooting
- debug: update MHE arrival covariance with I/Os at arrival
- improved coverage

The next step is supporting soft constraints for the MHE.

---

<div class="post-metadata">

**Author:** ![franckgaga](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/franckgaga/32/218241_2.png) [@franckgaga](https://discourse.julialang.org/u/franckgaga)\
**Post date:** [January 10, 2024, 3:19pm UTC](https://discourse.julialang.org/t/ann-modelpredictivecontrol-jl/100147/23 "2024-01-10T15:19:03Z")

</div>

**ModelPredicitveControl v0.16.0**

I added the support for soft-constraints in the `MovingHorizonEstimator`. I chose to disable this feature by default for two reasons:

- most people will only constraints the state estimate. It should not lead to major infeasibility if the bounds are correctly chosen.
- from my tests, soft-constraining `NonLinModel` estimations leads to problems that `Ipopt` has difficulties to solve, because of the weird scaling of the decision variables (no issue with `LinModel` and `OSQP` tho). Indeed, with the C weight around 10^5, the solution for \epsilon is typically around 10^{-5}, while the other decision variables follow the scale of the states, generally larger in magnitude. Reducing C and `nlp_scaling_max_gradient` option of `Ipopt` can mitigate this.

The changelog is:

- added: MHE soft constraints with a single slack variable ϵ (a.k.a. equal concern for relaxation, disabled by default thus only hard constraints).
- changed: more concise MPC logging in REPL.
- removed: deprecated arguments for `setconstraint!` and `NonLinMPC`.
- doc: hide extended help with `details` admonition.
- doc: more concise `setconstraint!` and `ExplicitMPC` documentation.

I will try to add an example with the MHE in the manual.

---

<div class="post-metadata">

**Author:** ![franckgaga](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/franckgaga/32/218241_2.png) [@franckgaga](https://discourse.julialang.org/u/franckgaga)\
**Post date:** [March 14, 2024, 11:54pm UTC](https://discourse.julialang.org/t/ann-modelpredictivecontrol-jl/100147/24 "2024-03-14T23:54:28Z")

</div>

[**`ModelPredictiveControl v.0.19.0`**](https://juliacontrol.github.io/ModelPredictiveControl.jl/stable/)

It’s been a while since my last post! Many new features were added since then, with a breaking change: `NonLinModel` constructor now supposes continuous dynamics by default. Use `solver=nothing` for discrete-time models.

Here is the changelog since my last update:

- added: support for `NonLinModel` with continuous dynamics
- added: 4th order Runge-Kutta solver with 0 allocation
- added: support for mutating `NonLinModel` (in-place) to reduce the allocations
- added: support for non-diagonal `M_Hp`, `N_Hc` and `L_Hp` weight, allowing terminal costs and LQR tuning
- doc: simple MHE example on the CSTR (manual)
- doc: info on terminal weights, continuous `NonLinModel` and mutating syntax
- doc: the pendulum example now use the built-in Runge-Kutta solver
- added some tests with continuous `NonLinModel`, both mutating and non-mutating

Also, the package is now part of JuliaControl organization! 🥳 Thanks @baggepinnen for the help!

BTW @odow is there any timeline for when `NLopt.jl` will support the new NLP syntax ? I finished the migration but I would like at least two FOSS NLP solvers that works (i.e. other than `Ipopt.jl`) before merging into main.

---

<div class="post-metadata">

**Author:** ![odow](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/odow/32/28685_2.png) [@odow](https://discourse.julialang.org/u/odow)\
**Post date:** [March 18, 2024, 3:43am UTC](https://discourse.julialang.org/t/ann-modelpredictivecontrol-jl/100147/25 "2024-03-18T03:43:45Z")

</div>

Let me take a look

---

<div class="post-metadata">

**Author:** ![franckgaga](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/franckgaga/32/218241_2.png) [@franckgaga](https://discourse.julialang.org/u/franckgaga)\
**Post date:** [April 13, 2024, 1:35pm UTC](https://discourse.julialang.org/t/ann-modelpredictivecontrol-jl/100147/26 "2024-04-13T13:35:43Z")

</div>

I will briefly present the content of `ModelPredictiveControl.jl` in the beautiful Montréal City this summer. See you there!

[**JuMP-dev 2024 | JuMP**](https://jump.dev/meetings/jumpdev2024/) for details.

---

<div class="post-metadata">

**Author:** ![franckgaga](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/franckgaga/32/218241_2.png) [@franckgaga](https://discourse.julialang.org/u/franckgaga)\
**Post date:** [April 26, 2024, 2:42am UTC](https://discourse.julialang.org/t/ann-modelpredictivecontrol-jl/100147/27 "2024-04-26T02:42:59Z")

</div>

**`ModelPredictiveControl v.0.21.0`**

I’m super excited to announce two new major features:

1. linear model adaptation of controller/estimator at runtime
2. model linearization at non-equilibrium points

These features allow adaptive MPC based on online parameter estimation (e.g. recursive system identification). Furthermore, successive linearization MPC can now be implemented with [minimal effort](https://juliacontrol.github.io/ModelPredictiveControl.jl/dev/manual/nonlinmpc/#Adapting-the-Model-via-Successive-Linearization). It’s a great feature since e.g. MATLAB also [supports it](https://www.mathworks.com/help/mpc/ug/adaptive-mpc-control-of-nonlinear-chemical-reactor-using-successive-linearization.html), but the analytical expressions of the Jacobians are required (hidden in the “Successive Linearizer” block). This is not the case here with the AD-based `linearize` function. Also, similar functionalities are available for the `MovingHorizonEstimator`.

A massive and long refactoring of the operating points and the mathematical notation was required to support linearization at non-equilibrium points. It was totally worth it since the [successive linearization MPC on the pendulum](https://juliacontrol.github.io/ModelPredictiveControl.jl/dev/manual/nonlinmpc/#Adapting-the-Model-via-Successive-Linearization) is about 125 times faster than the NMPC counterpart, with similar closed-loop performances! It’s a great application of Julia’s AD tools and its first class linear algebra library!

**BREAKING CHANGE**  
All the keyword arguments related to initial values e.g. `σP0`, `x0` and `x̂0` now require an underscore e.g. `σP_0`, `x_0`, `x̂_0` (to clearly differentiate from deviation vectors around operating points)

- Added: `setmodel!` for runtime model adaptation of controller/estimator based on `LinModel`
- Added: `linearize` and `setop!` now support non-equilibrium points
- Added: successive linearization MPC with the new `setmodel!` and `linearize` functions
- Added: successive linearization MHE with the new `setmodel!` and `linearize` functions
- Added: `linearize!` method for in-place model linearization (to reduce allocations)
- Added: 6 args. `LinModel` constructor now support scalars (similarly to `ss` function)
- Added: `ExtendedKalmanFilter` now compute the Jacobians in-place (to reduce allocations)
- Changed: `struct` state data `x` and state estimate `x̂` renamed to `x0` and `x̂0`
- Debug: `ExplicitMPC` with non-`Float64` now works
- Debug: accept integers in `linearize` arguments
- Debug: call `empty!` on `JuMP.Model` to support re-construction of MPC instances
- Doc: new `setmodel!`, `setop!` and `linearize` function documentation
- Doc: example of model adaptation with successive linearization on the pendulum (very efficient!)

other changes, since my last post on discourse:

- Added: print info on controller and estimator constraint softening (slack var. `ϵ`)
- Added: custom estimator for the approximation of the arrival covariance in the MHE
- Added: improve performance of `LinMPC` and MHE with new JuMP batch update methods
- Various allocation reductions
- Changed: MHE now default to an `UnscentedKalmanFilter` for the arrival covariance estimation (instead of an EKF)
- Debug: MHE with `NonLinModel` update covariance with the correct state estimate

---

<div class="post-metadata">

**Author:** ![BLI](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/bli/32/37206_2.png) [@BLI](https://discourse.julialang.org/u/BLI)\
**Post date:** [April 27, 2024, 1:26pm UTC](https://discourse.julialang.org/t/ann-modelpredictivecontrol-jl/100147/28 "2024-04-27T13:26:16Z")

</div>

One MPC problem that I’ve never seen anyone really address…

- MPC algorithms tend to assume that each optimization takes _zero time_, in other words: that the computed optimal solution u\*[t\_k] for u[t\_k] can be injected into the system at time t = t\_k.
- In practice, computation takes some time, so the computed value u\*[t\_k] is actually injected at a delayed time t = t\_k+tau, i.e., u[t\_k+tau] = u\*[t\_k], while for t\_k-1 + tau \< t \< t\_k+tau, u[t] = u\*[t\_k-1]

In other words: in real life, MPC adds a time delay to the system – an this time delay may actually vary – depending on constraints in the system, current state, etc. This may or may not be a problem.

One way around this is to assume a maximum time delay and then include this time delay in the model so that the computation of u\*[t\_k] already is aware of this time delay.

Anyways, I don’t know whether your MPC package can address such problems?

---

<div class="post-metadata">

**Author:** ![baggepinnen](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/baggepinnen/32/693_2.png) [@baggepinnen](https://discourse.julialang.org/u/baggepinnen)\
**Post date:** [April 27, 2024, 5:21pm UTC](https://discourse.julialang.org/t/ann-modelpredictivecontrol-jl/100147/29 "2024-04-27T17:21:35Z")

</div>

The approach of modeling the computational delay as an input delay in the plant is of course available, even if the delay is a fraction of a sample (using [Thiran approximation](https://juliacontrol.github.io/ControlSystems.jl/stable/lib/constructors/#ControlSystemsBase.thiran)). When using linear MPC, neglecting the computational delay is not always such a bad idea, it takes a couple of microseconds to solve even reasonably large problems (in state dimension and prediction horizon). For computationally more demanding problems, real-time iteration (RTI) is often worthwhile, this is something that isn’t yet supported, but would be good to get in there eventually. With RTI, you don’t bother iterating each problem until convergence, and instead favor to get new measurement data at a higher rate such that later iterations are made using the latest available data. The reasoning being that performing optimizer iterations with “old data” is wasteful if new data is available.

---

<div class="post-metadata">

**Author:** ![BLI](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/bli/32/37206_2.png) [@BLI](https://discourse.julialang.org/u/BLI)\
**Post date:** [April 27, 2024, 6:52pm UTC](https://discourse.julialang.org/t/ann-modelpredictivecontrol-jl/100147/30 "2024-04-27T18:52:42Z")

</div>

I agree on linear MPC, although it may depend on whether you allow for hard constraints on inputs, and more importantly on outputs.

Already 20+ years ago, Biegler and co-workers solved linear MPC with 200 inputs and 800 outputs (?) in … 10 or 100 ms or so; they considered only input constraints.

So if one has n states and a horizon of N, this gives in the order of n\cdot N unknowns (+ inputs etc., etc.). It then probably is important to take advantage of the _structure_ of the resulting problem. Without constraint, this can be solved by handling the n\cdot N \times n\cdot N matrix involved (ok: really larger, by including inputs, outputs, etc.). Alternatively, one can instead solve this using the Riccati-equation formulation, which reduces the complexity to handling N problems of size n\times n. At the outset, this only works for unconstrained problems. But I seem to recall that in a PhD thesis (or licenciate thesis?) from Uppsala (?) some 15-20 years ago, someone (don’t recall the name) reformulated linear quadratic problems with input constraints into a Riccati-like form. [This could also be interesting for receding horizon state estimation.]

With output constraints, QP type algorithms are needed, which may slow the solution down considerably if the problem in certain operating points is infeasible or near infeasible. Output constraints are important in some applications, e.g., electricity generation where there are (semi-) hard constraints on grid frequency, etc.

But if the system is _nonlinear_, or worse: distributed (spatial distribution, population balance, etc.), the problem I raise is more interesting. Of course, for systems that are slow to solve, in practical implementations, one may resort to surrogate models, etc.

---

<div class="post-metadata">

**Author:** ![franckgaga](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/franckgaga/32/218241_2.png) [@franckgaga](https://discourse.julialang.org/u/franckgaga)\
**Post date:** [June 9, 2024, 10:24pm UTC](https://discourse.julialang.org/t/ann-modelpredictivecontrol-jl/100147/31 "2024-06-09T22:24:39Z")

</div>

**ModelPredictiveControl v0.22.0**

A quick update on the last improvements. I first updated the internal code to the new NLP syntax of JuMP.jl. It is a slightly breaking change for some custom optimizers. There is only NLopt.jl that does not support it yet. I tested Ipopt.jl, MadNLP.jl and KNITRO.jl and they work well.

Also, following the model adaptation feature, it is now possible to modify the weight matrices in the objection function at runtime using [`setmodel!`](https://juliacontrol.github.io/ModelPredictiveControl.jl/dev/public/generic_func/#Set-Model-and-Weights). Same thing is possible with the covariance matrices of the Kalman Filter and moving horizon estimator.

Release notes:

**BREAKING CHANGE**

Migration to the [new nonlinear programming](https://jump.dev/JuMP.jl/stable/release_notes/#%5BVersion-1.15.0%5D(https://github.com/jump-dev/JuMP.jl/releases/tag/v1.15.0)-(September-15,-2023)) syntax of `JuMP`. Some optimizers may not support it yet.

- added: online modification of objective function weights and covariance matrices with `setmodel!`
- added: accept indices and ranges in `plot` keyword arguments
- changed: migrate the NLP code to the new syntax
- doc: added `plot` result previews with static images
- new tests for new plot recipes and weights/covariances modification

and, the changelog since my last post:

- added: non-Unicode alternative keyword arguments in public functions
- added: modify plots Y-axis labels with [`setname!`](https://juliacontrol.github.io/ModelPredictiveControl.jl/dev/public/sim_model/#Set-Variable-Names)
- added: show `sim!` % progress and ETA in VS Code status bar
- added: plot estimated state constraints `x̂min`/`x̂max` for moving horizon estimator
- added: [`plot`](https://juliacontrol.github.io/ModelPredictiveControl.jl/dev/public/plot_sim/#Plotting-Results) recipe documentation
- debug: debug: plots Ru instead of Ry for u setpoint (recipe)
- various doc improvements

The package will be registered soon.

---

<div class="post-metadata">

**Author:** ![franckgaga](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/franckgaga/32/218241_2.png) [@franckgaga](https://discourse.julialang.org/u/franckgaga)\
**Post date:** [August 9, 2024, 12:19am UTC](https://discourse.julialang.org/t/ann-modelpredictivecontrol-jl/100147/32 "2024-08-09T00:19:08Z")

</div>

**ModelPredictiveControl.jl v0.23.0**

📢 Updates on the new release of the package. There are some breaking changes in the API related to important new features.

All the state estimator in the package were previously implemented using the predictor form (a.k.a. delayed form), meaning that they estimated the state of the next time step, denoted \mathbf{\hat{x}}\_{k}(k+1) in this package, or \mathbf{\hat{x}}\_{k+1|k} elsewhere. They are a bit simpler to use, and they allow moving the computations of the estimator after the MPC optimization (thus reducing the time delay mentioned by @BLI in the posts above, especially with the MHE).

But, as raised by @baggepinnen (and thanks for the suggestion!), they introduce a one sample time delay in the control loop. The closed-loop robustness is thus slightly affected by them. They are also a bit less accurate than the current form (a.k.a filter form).

Because of that, I added the support for the current form that estimates \mathbf{\hat{x}}\_{k}(k) on all the `StateEstimator` in the package, with the exception of the `MovingHorizonEstimator` for now, since the implementation is slightly more challenging. There is a new `direct` keyword argument (same as `kalman` and `place` in ControlSystems.jl) on all `StateEstimator` (except `InternalModel` since `direct=false` is impossible). The default value is `true`, referring to this new current form. You can notice that disturbance rejection response is indeed improved in the [examples of the manual](https://juliacontrol.github.io/ModelPredictiveControl.jl/dev/manual/nonlinmpc/#Nonlinear-Model-Predictive-Controller). BTW, the steady-state Kalman filter in the current form (the new default) is exactly the same approach as MATLAB.

A new [`preparestate!`](https://juliacontrol.github.io/ModelPredictiveControl.jl/dev/public/generic_func/#Prepare-State-x) function must be called before solving the MPC problem with `moveinput!`. Note that this function does nothing if `direct==false`. This allow writing you own simulation `for` loop with e.g. disturbances, and changing the estimator type or form “on-the-fly” in the constructor. The code inside the `for` loop will still work without any change. Also note that an error will be thrown is the user forget to call `preparestate!` before `updatestate!`. See the `test_mpc` function in [Linear Model Predictive Control](https://juliacontrol.github.io/ModelPredictiveControl.jl/dev/manual/linmpc/#Linear-Model-Predictive-Controller) section of the manual for an example of the new API.

One last thing, there’s a video of my [presentation at JuMP-dev 2024 on youtube](https://www.youtube.com/watch?v=zzEC0BCCknI), for those who are interested.

Cheers!

**BREAKING CHANGES**

- `preparestate!` should be called before solving the MPC optimization with `moveinput!`
- current form by default for all `StateEstimator`
- `ym` keyword argument of `moveinput!` removed (was only for `InternalModel`, I now use the new `preparestate!` method)
- `MovingHorizonEstimator` advanced constructor (with complete covariance matrices): `covestim` and `optim` are now keyword arguments instead of positional arguments.

**Changelog**

- added: current form for all `StateEstimator` except `MovingHorizonEstimator`, to improve accuracy and closed-loop robustness
- reduce allocation for `SimModel`and `StateEstimator` instances by using internal buffers
- multiple doc correction

---

<div class="post-metadata">

**Author:** ![franckgaga](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/franckgaga/32/218241_2.png) [@franckgaga](https://discourse.julialang.org/u/franckgaga)\
**Post date:** [September 5, 2024, 8:14pm UTC](https://discourse.julialang.org/t/ann-modelpredictivecontrol-jl/100147/33 "2024-09-05T20:14:15Z")

</div>

**ModelPredictiveControl.jl v0.24.0**

A quick update on the new release. I worked hard on adding the current/filter formulation of the [`MovingHorizonEstimator`](https://juliacontrol.github.io/ModelPredictiveControl.jl/dev/public/state_estim/#MovingHorizonEstimator) (the `direct` keyword argument, corresponding to p=0 in the equations). It was not easy since this form of the MHE is kind of overlooked in the literature. AFAIK, I only found [http://facta.junis.ni.ac.rs/acar/acar201202/acar20120202.pdf](http://facta.junis.ni.ac.rs/acar/acar201202/acar20120202.pdf) that discuss a little bit more in detailed about this formulation (thank you prof. Morten Hovd!). I documented all the internal computations in the Extended Help of this [function](https://juliacontrol.github.io/ModelPredictiveControl.jl/dev/internals/state_estim/#ModelPredictiveControl.init_predmat_mhe). Like all the other state estimators, the default is now this new formulation, which is a slight breaking change.

The docstring of all the state estimator are also now more detailed in many ways. I explicitly added the keyword arguments, instead of referring to `SteadyKalmanFilter` or `KalmanFilter` documentation, at the risk of being repetitive.

I also added some simple [soft real time utilities](https://juliacontrol.github.io/ModelPredictiveControl.jl/dev/public/generic_func/#Real-Time-Simulate-and-Control). It support both idle sleep and busy-wating, intended for higher frequency sampling.

The changelog is:

- breaking change: `MovingHorizonEstimator` now default to `direct=true`
- added: the current/filter formulation of the `MovingHorizonEstimator`
- doc: explicitly list all the keyword arguments of all the state estimator
- added: soft realtime utilities
- added: `InternalModel` now produces 0 allocation with `preparestate!` and `updatestate!` calls
- tests: new integration tests that compare unconstrained MHE to UKF and KF results
- tests: new integration that compare `LinModel` and `NonLinModel`

and, since my last post:

- `Luenberger`, `SteadyKalmanFilter`, `KalmanFilter` and `UnscentedKalmanFilter` now produce 0 allocation with `preparestate!` and `updatestate!` calls

I will register the release soon.

---

<div class="post-metadata">

**Author:** ![franckgaga](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/franckgaga/32/218241_2.png) [@franckgaga](https://discourse.julialang.org/u/franckgaga)\
**Post date:** [September 27, 2024, 2:21pm UTC](https://discourse.julialang.org/t/ann-modelpredictivecontrol-jl/100147/34 "2024-09-27T14:21:16Z")

</div>

**ModelPredictiveControl v1.0.0**

🎉🎉 This release marks a final breaking change to initiate the first major version! 🎉🎉

I don’t plan for any other breaking change in the next releases. I feel that most essential features are there, and the public interface is flexible enough to accommodate many types of predictive controllers and state estimators.

Two new features are introduced:

- plant model parameters \mathbf{p}
- custom economic function J\_E parameters \mathbf{p} (equal to the model parameters by default).

A closure was needed for that in the past. This can be dangerous on the performance and type stability, especially when working in the global scope. Now closures can completely avoided.

A [new example](https://juliacontrol.github.io/ModelPredictiveControl.jl/dev/manual/mtk/) is also added to the manual: the pendulum model integrated into ModelingToolkit.jl (MTK). A controller is designed and tested from this model to reproduce the results of the previous section in the manual. Note this is not an official interface with MTK and the provided code is not guaranteed to cover all corner cases.

Now, let’s meme to celebrate! (because why not?)

 ![image](https://global.discourse-cdn.com/julialang/original/3X/d/e/de2a55d4c873ef59cec3b9df1c8d00ed11869edb.jpeg)

Happy feedbacking!

**Release Notes**

- breaking change: [`NonLinModel`](https://juliacontrol.github.io/ModelPredictiveControl.jl/dev/public/sim_model/#NonLinModel) new `p` argument in `f`/`f!` and `h`/`h!` function signature
- breaking change: [`NonLinMPC`](https://juliacontrol.github.io/ModelPredictiveControl.jl/dev/public/predictive_control/#NonLinMPC) new `p` argument in `JE` function signature
- added: support for plant model parameters \mathbf{p} in `NonLinModel`
- added: support for economic function parameters \mathbf{p} in `NonLinMPC`
- doc: ModelingToolkit integration example on the pendulum model
- test: improve coverage
- debug: `ExplicitMPC` correct error message with the unsupported `setconstraint!` method
- added: validate economic function J\_E argument signature

---

<div class="post-metadata">

**Author:** ![franckgaga](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/franckgaga/32/218241_2.png) [@franckgaga](https://discourse.julialang.org/u/franckgaga)\
**Post date:** [December 5, 2024, 11:20pm UTC](https://discourse.julialang.org/t/ann-modelpredictivecontrol-jl/100147/35 "2024-12-05T23:20:35Z")

</div>

**ModelPredictiveControl v1.1.2**

A small update to announce a new feature: custom nonlinear inequality constraints \mathbf{g\_c} are now possible for [`NonLinMPC`](https://juliacontrol.github.io/ModelPredictiveControl.jl/dev/public/predictive_control/#ModelPredictiveControl.NonLinMPC) controllers. Now I can finally say that the most common features of a MPC toolbox are there.

I also added a [simple example in the manual](https://juliacontrol.github.io/ModelPredictiveControl.jl/dev/manual/nonlinmpc/#Custom-Nonlinear-Inequality-Constraints) of constraining the motor power using the \mathbf{g\_c} function. The next features will probably be either manipulated input blocking or new ODE solvers.

The change log since my last post is:

- added: support for custom nonlinear inequality constraints `gc` in `NonLinMPC`
- added: call `JE` and `gc` once in `NonLinMPC` constructor and show the stacktrace if it fails (to guide the user with simple bugs)
- added: reduce allocations with `PredictiveController`s using new fields in `mpc.buffer` object
- added: improve performance with 4 `iszero_<X>` fields for each controller weight, to skip computations when possible
- added: save some computations in `NonLinMPC` with `T_lastu` field instead of `T_lastu0`
- fixed: remove a type-instability in `LinMPC` and `NonLinMPC` introduced in v1.1.0
- changed: moved all controller weights in `mpc.weights` struct to improve code re-use
- changed: removed code related to Julia 1.6 compatibility (1.10 is the new LTS)
- changed: more precise error for `LinModel` with non-zero `Du` matrix
- test: improve constraint violation tests (verify the whole horizons, use distinct lower and upper bounds)
- test: new custom constraint violation tests that use `Ue` argument
- doc: added a custom nonlinear constraint example on the motor power in the manual
- doc: various improvements

I will register the release soon!

---

<div class="post-metadata">

**Author:** ![BdeKoning](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/bdekoning/32/214557_2.png) [@BdeKoning](https://discourse.julialang.org/u/BdeKoning)\
**Post date:** [February 10, 2025, 3:35pm UTC](https://discourse.julialang.org/t/ann-modelpredictivecontrol-jl/100147/36 "2025-02-10T15:35:59Z")

</div>

Hi @franckgaga,

Do you think this package could be applicable in water resources management? I skimmed trough the associated paper and you speak of the models as ‘plants’, which to me sounds like a factory, but maybe the term could be interpreted more generally.

I am working on hydrological software, [modeling surface water systems with graphs](https://github.com/Deltares/Ribasim). Our modelling strategy the following components:

- A ‘physical layer’ using `OrdinaryDiffeqEq.jl`, essentially for different types of flow mostly under gravity given various types of ‘connector nodes’ between water bodies. We use `VectorContinuousCallback` for rule based control;
- An ‘allocation layer’ which once in a while takes information from the physical layer in combination with water demands, does a `JuMP.jl` optimization and communicates the result back to the physical layer telling nodes with demands how much they are allowed to abstract.

Lately we have been investigating model predictive control, because our current approach in the allocation layer, which is essentially a modified form of the max flow problem, has some shortcomings. We are also looking for how we can use the allocation layer to give control feedback to the physical layer.

From this high level description, do you think `ModelPredictiveControl.jl` is a good fit for us? Thanks in advance!

---

<div class="post-metadata">

**Author:** ![ufechner7](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/ufechner7/32/51363_2.png) [@ufechner7](https://discourse.julialang.org/u/ufechner7)\
**Post date:** [February 10, 2025, 3:52pm UTC](https://discourse.julialang.org/t/ann-modelpredictivecontrol-jl/100147/37 "2025-02-10T15:52:59Z")

</div>

> [@BdeKoning](#):
>
> you speak of the models as ‘plants’, which to me sounds like a factory,

In a control engineering any dynamic system that shall be controlled is called “plant”.

---

<div class="post-metadata">

**Author:** ![franckgaga](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/franckgaga/32/218241_2.png) [@franckgaga](https://discourse.julialang.org/u/franckgaga)\
**Post date:** [February 10, 2025, 4:40pm UTC](https://discourse.julialang.org/t/ann-modelpredictivecontrol-jl/100147/38 "2025-02-10T16:40:38Z")

</div>

As mentioned by @ufechner7, the term _plant_ should be interpreted as any dynamical system that you want to control in real-time. Some exemples are:

- a single industrial unit process
- a whole factory
- a single motor
- a car
- an electrical grid
- a battery charger
- a DC-DC converter
- etc.

That being said, there are two important criteria to know if MPC is applicable:

1. You need a dynamical model of the plant (that is, ordinary differential equations in which the independent variable is the time). This model can be a rough approximation of the plant since MPC is a closed-loop control strategy, and the feedback can handle modelling error to some extent.
2. The more complex your dynamical model is, the less MPC is feasible. This is because the solving time of the optimization problem in the MPC needs to be always shorter than the control period. That is mainly why linear dynamical models are traditionally used in MPC (`LinModel` + `LinMPC` in the package). It may be possible to derive an approximate linear model of a more complex nonlinear model, by either using the `linearize` function or using [system identification](https://github.com/baggepinnen/ControlSystemIdentification.jl)

---

<div class="post-metadata">

**Author:** ![franckgaga](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/franckgaga/32/218241_2.png) [@franckgaga](https://discourse.julialang.org/u/franckgaga)\
**Post date:** [February 28, 2025, 4:59pm UTC](https://discourse.julialang.org/t/ann-modelpredictivecontrol-jl/100147/39 "2025-02-28T16:59:24Z")

</div>

**ModelPredictiveControl v1.4.0**

🎉 Announcing a new big feature 🎉

A new [`MultipleShooting`](https://juliacontrol.github.io/ModelPredictiveControl.jl/stable/public/predictive_control/#ModelPredictiveControl.MultipleShooting) transcription method for [`LinMPC`](https://juliacontrol.github.io/ModelPredictiveControl.jl/stable/public/predictive_control/#ModelPredictiveControl.LinMPC) and [`NonLinMPC`](https://juliacontrol.github.io/ModelPredictiveControl.jl/stable/public/predictive_control/#ModelPredictiveControl.NonLinMPC) controllers is available to exploit sparse solvers like OSQP.jl and MadNLP.jl. The resulting optimzation problem has more decision variables, and its structure is more complex but sparser (the plant model predictions are implemented as equality constraints, instead of directly running the model in the objective function like for the [`SingleShooting`](https://juliacontrol.github.io/ModelPredictiveControl.jl/stable/public/predictive_control/#ModelPredictiveControl.SingleShooting)), but this approach is supposedly more efficient for large H\_c values and highly nonlinear or unstable plant models.

That being said, I did not find any good paper in the literature that rigorously compare the performances of the two transcription methods for MPC (edit: just found [one on a unmanned aerial vehicle](https://doi.org/10.1109/ICVES.2019.8906364), their results corroborated the consensus, that is, slower as a single shooting transcription). I tested it quickly on the unstable pendulum model of the manual and I was not impressed: the `MultipleShooting` was significantly slower than a direct `SingleShooting` transcription, about 5 times slower. It may be partially caused by the splatting syntax of JuMP, necessary to handle user-defined operators with multiple input arguments (there are more input arguments for this transcription). Another plausible cause is the ForwardDiff.jl dense differentiation, used behind JuMP `@operator` macro with default arguments. I plan to integrate the DifferentiationInterface.jl in an upcoming release, to support any AD tools both at optimization and at linearization, and some of them explicitly handle sparsity!

_ **ADDENDUM:** _ _I ran additional tests on the inverted pendulum. My example in the manual uses a control horizon of H\_c = 2, which is just easy to solve in general. Now, If I chose H\_c = H\_p = 20, the single shooting transcription is not able to converge at all anymore at the inverted position. The multiple shooting transcription is able, but the solving is still too slow for real-time control. My quick conclusion: multiple shooting is interesting for problems in which high H\_c values are needed, on unstable or highly nonlinear plant models._

The next release will add some test to improve the coverage, and maybe include an example in the manual if I have the time.

The change log since my last post is:

- added: multiple shooting transcription for `LinMPC` and `NonLinMPC`
- added: `linearize!` cleaner implementation (and allocation-free)
- added: show `getinfo` dictionary in the `@debug` log if activated (instead of only the solution summary of `JuMP`)
- added: show `@debug` messages in github CI if debug logging is activated
- added: `ForwardEuler` ODE solver
- added: remove 1 useless `.= `op in `RungeKutta(4)`
- added: forcing specialization in the NLP functions by using `Vararg{T, N}` instead of splatting (see this [tip](https://docs.julialang.org/en/v1/manual/performance-tips/#Be-aware-of-when-Julia-avoids-specializing))
- debug: fallback if non-finite arrival covariance after correction/update (we keep the old one with an error message)
- changed: re-worked AD buffers to prepare the ground for `DifferentiationInterface.jl`
- changed: covariance matrix inversion with `cholesky` instead of `inv` in `MovingHorizonEstimator` (`inv` internally uses `lu`)
- changed: bump julia `compat` to 1.10 (new LTS)
- changed: improve code reuse in NLP functions to improve code maintainability
- test: migration to the new VS code test framework 🍾
- test: new tests for `ForwardEuler`
- doc: details on the two `TranscriptionMethod`
- doc: info about types that allocate or not
- doc: internal details on `MultipleShooting` [matrices](https://juliacontrol.github.io/ModelPredictiveControl.jl/stable/internals/predictive_control/#ModelPredictiveControl.init_defectmat) for computing the state defects

---

<div class="post-metadata">

**Author:** ![franckgaga](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/franckgaga/32/218241_2.png) [@franckgaga](https://discourse.julialang.org/u/franckgaga)\
**Post date:** [March 6, 2025, 10:09pm UTC](https://discourse.julialang.org/t/ann-modelpredictivecontrol-jl/100147/40 "2025-03-06T22:09:12Z")

</div>

**ModelPredictiveControl v1.4.2**

🍾🍾🍾 Update on major performance improvements of `NonLinMPC` and `MovingHorizonEstimator` with nonlinear plant models !

The integration of DifferentiationInterface.jl requires a major refactoring of the nonlinear optimization functions. By doing so, I unlocked a major boost in performance in `NonLinMPC` and `MovingHorizonEstimator`. They are now about 5-10x faster than before. Note that the boost is only there when nonlinear constraints are present, that is, in these cases:

- output/state constraints of `NonLinModel`
- multiple shooting of `NonLinModel`
- custom inequality constraints

I tested the new version on the pendulum model. The `MultipleShooting` transcription is now only 1.6x slower than the `SingleShooting` transcription, so it’s plenty fast enough for real time! Additional gains will be presumably possible with sparse AD through DifferentiationInterface.jl.

_Here’s the reason:_

The splatting syntax of JuMP.jl forces us to compute the Jacobians of the constraints as a combination of multiple gradients (concatenated as a Jacobian). This means that the `jacobian` functions of the AD tool (dense `ForwardDiff.jl`, for now) were called redundantly `ng` and `neq` times for a specific decision vector value `Z̃` (where `ng` and `neq` is the respective number of nonlinear inequality and equality constraints). This is wasteful. A caching mechanism was implemented to store the Jacobians of the constraints and reuse them when needed.

I will release the update soon.

[Previous page](https://discourse.julialang.org/t/ann-modelpredictivecontrol-jl/100147.md?page=1)

[Next page](https://discourse.julialang.org/t/ann-modelpredictivecontrol-jl/100147.md?page=3)
