Skip to content

Commit bb68996

Browse files
authored
Merge pull request #174 from JuliaControl/diff_interface_final
added: `NonLinMPC` and `MovingHorizonEstimator` integration with DI.jl
2 parents c12d7bc + 990f9c5 commit bb68996

23 files changed

+486
-331
lines changed

Project.toml

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,51 +1,51 @@
11
name = "ModelPredictiveControl"
22
uuid = "61f9bdb8-6ae4-484a-811f-bbf86720c31c"
33
authors = ["Francis Gagnon"]
4-
version = "1.4.4"
4+
version = "1.5.0"
55

66
[deps]
7-
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
8-
Logging = "56ddb016-857b-54e1-b83d-db4d58db5568"
9-
Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"
107
ControlSystemsBase = "aaaaaaaa-a6ca-5380-bf3e-84a91bcd477e"
8+
DifferentiationInterface = "a0c0ee7d-e4b9-4e03-894e-1c5f64a51d63"
119
ForwardDiff = "f6369f11-7733-5829-9624-2563aa707210"
1210
Ipopt = "b6b21f68-93f8-5de0-b562-5493be1d77c9"
1311
JuMP = "4076af6c-e467-56ae-b986-b466b2749572"
12+
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
13+
Logging = "56ddb016-857b-54e1-b83d-db4d58db5568"
1414
OSQP = "ab2f91bb-94b4-55e3-9ba0-7f65df51de79"
1515
PreallocationTools = "d236fae5-4411-538c-8e31-a6e3d9e00b46"
1616
PrecompileTools = "aea7be01-6a6a-4083-8856-8a6e6704d82a"
1717
ProgressLogging = "33c8b6b6-d38a-422a-b730-caa89a2f386c"
18+
Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"
1819
RecipesBase = "3cdcf5f2-1ef4-517c-9805-6587b60abb01"
20+
SparseConnectivityTracer = "9f842d2f-2579-4b1d-911e-f412cf18a3f5"
21+
SparseMatrixColorings = "0a514795-09f3-496d-8182-132a7b665d35"
1922

2023
[compat]
21-
julia = "1.10"
22-
LinearAlgebra = "1.10"
23-
Logging = "1.10"
24-
Random = "1.10"
2524
ControlSystemsBase = "1.9"
25+
DifferentiationInterface = "0.6.45"
2626
ForwardDiff = "0.10"
2727
Ipopt = "1"
2828
JuMP = "1.21"
29+
LinearAlgebra = "1.10"
30+
Logging = "1.10"
2931
OSQP = "0.8"
3032
PreallocationTools = "0.4.14"
3133
PrecompileTools = "1"
3234
ProgressLogging = "0.1"
35+
Random = "1.10"
3336
RecipesBase = "1"
37+
SparseConnectivityTracer = "0.6.13"
38+
SparseMatrixColorings = "0.4.14"
39+
julia = "1.10"
3440

3541
[extras]
3642
DAQP = "c47d62df-3981-49c8-9651-128b1cd08617"
3743
Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4"
44+
FiniteDiff = "6a86dc24-6348-571c-b903-95158fe2bd41"
3845
Plots = "91a5bcdd-55d7-5caf-9e0b-520d859cae80"
3946
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
40-
TestItems = "1c621080-faea-4a02-84b6-bbd5e436b8fe"
4147
TestItemRunner = "f8b46487-2199-4994-9208-9a1283c18c0a"
48+
TestItems = "1c621080-faea-4a02-84b6-bbd5e436b8fe"
4249

4350
[targets]
44-
test = [
45-
"Test",
46-
"TestItems",
47-
"TestItemRunner",
48-
"Documenter",
49-
"Plots",
50-
"DAQP"
51-
]
51+
test = ["Test", "TestItems", "TestItemRunner", "Documenter", "Plots", "DAQP", "FiniteDiff"]

README.md

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,9 @@ An open source [model predictive control](https://en.wikipedia.org/wiki/Model_pr
99
package for Julia.
1010

1111
The package depends on [`ControlSystemsBase.jl`](https://github.com/JuliaControl/ControlSystems.jl)
12-
for the linear systems and [`JuMP.jl`](https://github.com/jump-dev/JuMP.jl) for the solving.
12+
for the linear systems, [`JuMP.jl`](https://github.com/jump-dev/JuMP.jl) for the
13+
optimization and [`DifferentiationInterface.jl`](https://github.com/JuliaDiff/DifferentiationInterface.jl)
14+
for the differentiation.
1315

1416
## Installation
1517

@@ -102,9 +104,13 @@ for more detailed examples.
102104
- measured disturbances
103105
- input setpoints
104106
- easy integration with `Plots.jl`
105-
- optimization based on `JuMP.jl`:
106-
- quickly compare multiple optimizers
107-
- nonlinear solvers relying on automatic differentiation (exact derivative)
107+
- optimization based on `JuMP.jl` to quickly compare multiple optimizers:
108+
- many quadratic solvers for linear control
109+
- many nonlinear solvers for nonlinear control (local or global)
110+
- derivatives based on `DifferentiationInterface.jl` to compare different approaches:
111+
- automatic differentiation (exact solution)
112+
- symbolic differentiation (exact solution)
113+
- finite difference (approximate solution)
108114
- supported transcription methods of the optimization problem:
109115
- direct single shooting
110116
- direct multiple shooting

docs/Project.toml

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,20 @@
11
[deps]
2-
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
3-
Logging = "56ddb016-857b-54e1-b83d-db4d58db5568"
42
ControlSystemsBase = "aaaaaaaa-a6ca-5380-bf3e-84a91bcd477e"
53
DAQP = "c47d62df-3981-49c8-9651-128b1cd08617"
64
Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4"
5+
DocumenterInterLinks = "d12716ef-a0f6-4df4-a9f1-a5a34e75c656"
76
JuMP = "4076af6c-e467-56ae-b986-b466b2749572"
7+
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
8+
Logging = "56ddb016-857b-54e1-b83d-db4d58db5568"
89
ModelingToolkit = "961ee093-0014-501f-94e3-6117800e7a78"
910
Plots = "91a5bcdd-55d7-5caf-9e0b-520d859cae80"
1011

1112
[compat]
12-
LinearAlgebra = "1.10"
13-
Logging = "1.10"
1413
ControlSystemsBase = "1"
14+
DAQP = "0.6"
1515
Documenter = "1"
1616
JuMP = "1"
17-
DAQP = "0.6"
18-
Plots = "1"
17+
LinearAlgebra = "1.10"
18+
Logging = "1.10"
1919
ModelingToolkit = "9.50"
20+
Plots = "1"

docs/make.jl

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,17 @@ ENV["PLOTS_TEST"] = "true"
33
ENV["GKSwstype"] = "nul"
44
push!(LOAD_PATH,"../src/")
55

6-
using Documenter
6+
using Documenter, DocumenterInterLinks
77
using ModelPredictiveControl
88

9+
links = InterLinks(
10+
"Julia" => "https://docs.julialang.org/en/v1/objects.inv",
11+
"ControlSystemsBase" => "https://juliacontrol.github.io/ControlSystems.jl/stable/objects.inv",
12+
"JuMP" => "https://jump.dev/JuMP.jl/stable/objects.inv",
13+
"DifferentiationInterface" => "https://juliadiff.org/DifferentiationInterface.jl/DifferentiationInterface/stable/objects.inv",
14+
"ForwardDiff" => "https://juliadiff.org/ForwardDiff.jl/stable/objects.inv",
15+
)
16+
917
DocMeta.setdocmeta!(
1018
ModelPredictiveControl,
1119
:DocTestSetup,
@@ -16,6 +24,7 @@ makedocs(
1624
sitename = "ModelPredictiveControl.jl",
1725
#format = Documenter.LaTeX(platform = "none"),
1826
doctest = true,
27+
plugins = [links],
1928
format = Documenter.HTML(
2029
prettyurls = get(ENV, "CI", nothing) == "true",
2130
edit_link = "main"

docs/src/index.md

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,17 @@ An open source [model predictive control](https://en.wikipedia.org/wiki/Model_pr
44
package for Julia.
55

66
The package depends on [`ControlSystemsBase.jl`](https://github.com/JuliaControl/ControlSystems.jl)
7-
for the linear systems and [`JuMP.jl`](https://github.com/jump-dev/JuMP.jl) for the solving.
7+
for the linear systems, [`JuMP.jl`](https://github.com/jump-dev/JuMP.jl) for the
8+
optimization and [`DifferentiationInterface.jl`](https://github.com/JuliaDiff/DifferentiationInterface.jl)
9+
for the differentiation.
810

911
The objective is to provide a simple, clear and modular framework to quickly design model
1012
predictive controllers (MPCs) in Julia, while preserving the flexibility for advanced
1113
real-time optimization. Modern MPCs based on closed-loop state estimators are the main focus
1214
of the package, but classical approaches that rely on internal models are also possible. The
13-
`JuMP.jl` interface allows the user to test different solvers easily if the performance of
14-
the default settings is not satisfactory.
15+
`JuMP` and `DifferentiationInterface` dependencies allows the user to test different
16+
optimizers and automatic differentiation (AD) backends easily if the performances of the
17+
default settings are not satisfactory.
1518

1619
The documentation is divided in two parts:
1720

docs/src/manual/mtk.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@ old_logger = global_logger(); global_logger(errlogger);
1111

1212
## Pendulum Model
1313

14-
This example integrates the simple pendulum model of the [last section](@ref man_nonlin) in the
15-
[`ModelingToolkit.jl`](https://docs.sciml.ai/ModelingToolkit/stable/) (MTK) framework and
14+
This example integrates the simple pendulum model of the [last section](@ref man_nonlin) in
15+
[`ModelingToolkit`](https://docs.sciml.ai/ModelingToolkit/stable/) (MTK) framework and
1616
extracts appropriate `f!` and `h!` functions to construct a [`NonLinModel`](@ref). An
1717
[`NonLinMPC`](@ref) is designed from this model and simulated to reproduce the results of
1818
the last section.

docs/src/manual/nonlinmpc.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -329,7 +329,7 @@ Nonlinear MPC is more computationally expensive than [`LinMPC`](@ref). Solving t
329329
should always be faster than the sampling time ``T_s = 0.1`` s for real-time operation. This
330330
requirement is sometimes hard to meet on electronics or mechanical systems because of the
331331
fast dynamics. To ease the design and comparison with [`LinMPC`](@ref), the [`linearize`](@ref)
332-
function allows automatic linearization of [`NonLinModel`](@ref) based on [`ForwardDiff.jl`](https://juliadiff.org/ForwardDiff.jl/stable/).
332+
function allows automatic linearization of [`NonLinModel`](@ref) based on [`ForwardDiff`](@extref ForwardDiff).
333333
We first linearize `model` at the point ``θ = π`` rad and ``ω = τ = 0`` (inverted position):
334334

335335
```@example man_nonlin

src/ModelPredictiveControl.jl

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,19 @@
11
module ModelPredictiveControl
22

3-
using PrecompileTools
3+
using PrecompileTools # TODO: remove this dep if possible (with Cache of DI.jl)
44
using LinearAlgebra
55
using Random: randn
66

77
using RecipesBase
88
using ProgressLogging
9-
using ForwardDiff
9+
10+
using DifferentiationInterface: ADTypes.AbstractADType, AutoForwardDiff, AutoSparse
11+
using DifferentiationInterface: gradient!, jacobian!, prepare_gradient, prepare_jacobian
12+
using DifferentiationInterface: Constant, Cache
13+
using SparseConnectivityTracer: TracerSparsityDetector
14+
using SparseMatrixColorings: GreedyColoringAlgorithm, sparsity_pattern
15+
16+
import ForwardDiff #TODO: delete this after `linearize!` and `ExtendedKalmanFilter` are updated
1017

1118
import ControlSystemsBase
1219
import ControlSystemsBase: ss, tf, delay

src/controller/execute.jl

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -123,19 +123,19 @@ function getinfo(mpc::PredictiveController{NT}) where NT<:Real
123123
x̂0end = similar(mpc.estim.x̂0)
124124
Ue, Ŷe = Vector{NT}(undef, nUe), Vector{NT}(undef, nŶe)
125125
U0, Ŷ0 = similar(mpc.Uop), similar(mpc.Yop)
126-
X̂0, Û0 = Vector{NT}(undef, nX̂0), Vector{NT}(undef, nÛ0)
126+
Û0, X̂0 = Vector{NT}(undef, nÛ0), Vector{NT}(undef, nX̂0)
127127
U, Ŷ = similar(mpc.Uop), similar(mpc.Yop)
128-
Ŷs = similar(mpc.Yop)
129128
U0 = getU0!(U0, mpc, Z̃)
130-
ΔŨ = getΔŨ!(ΔŨ, mpc, mpc.transcription, Z̃)
129+
ΔŨ = getΔŨ!(ΔŨ, mpc, transcription, Z̃)
131130
Ŷ0, x̂0end = predict!(Ŷ0, x̂0end, X̂0, Û0, mpc, model, transcription, U0, Z̃)
132131
Ue, Ŷe = extended_vectors!(Ue, Ŷe, mpc, U0, Ŷ0)
133132
U .= U0 .+ mpc.Uop
134133
Ŷ .= Ŷ0 .+ mpc.Yop
135134
J = obj_nonlinprog!(Ŷ0, U0, mpc, model, Ue, Ŷe, ΔŨ)
135+
Ŷs = similar(mpc.Yop)
136136
predictstoch!(Ŷs, mpc, mpc.estim)
137137
info[:ΔU] = Z̃[1:mpc.Hc*model.nu]
138-
info[] = mpc.== 1 ? mpc.Z̃[end] : zero(NT)
138+
info[] = getϵ(mpc, Z̃)
139139
info[:J] = J
140140
info[:U] = U
141141
info[:u] = info[:U][1:model.nu]
@@ -161,6 +161,15 @@ function getinfo(mpc::PredictiveController{NT}) where NT<:Real
161161
return info
162162
end
163163

164+
"""
165+
getϵ(mpc::PredictiveController, Z̃) -> ϵ
166+
167+
Get the slack `ϵ` from the decision vector `Z̃` if present, otherwise return 0.
168+
"""
169+
function getϵ(mpc::PredictiveController, Z̃::AbstractVector{NT}) where NT<:Real
170+
return mpc. 0 ? Z̃[end] : zero(NT)
171+
end
172+
164173
"""
165174
addinfo!(info, mpc::PredictiveController) -> info
166175
@@ -361,6 +370,9 @@ function obj_nonlinprog!(
361370
return JR̂y + JΔŨ + JR̂u + E_JE
362371
end
363372

373+
"No custom nonlinear constraints `gc` by default, return `gc` unchanged."
374+
con_custom!(gc, ::PredictiveController, _ , _, _ ) = gc
375+
364376
"By default, the economic term is zero."
365377
function obj_econ(::PredictiveController, ::SimModel, _ , ::AbstractVector{NT}) where NT
366378
return zero(NT)

src/controller/linmpc.jl

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -146,8 +146,8 @@ arguments. This controller allocates memory at each time step for the optimizati
146146
- `Cwt=1e5` : slack variable weight ``C`` (scalar), use `Cwt=Inf` for hard constraints only.
147147
- `transcription=SingleShooting()` : a [`TranscriptionMethod`](@ref) for the optimization.
148148
- `optim=JuMP.Model(OSQP.MathOptInterfaceOSQP.Optimizer)` : quadratic optimizer used in
149-
the predictive controller, provided as a [`JuMP.Model`](https://jump.dev/JuMP.jl/stable/api/JuMP/#JuMP.Model)
150-
(default to [`OSQP`](https://osqp.org/docs/parsers/jump.html) optimizer).
149+
the predictive controller, provided as a [`JuMP.Model`](@extref) object (default to
150+
[`OSQP`](https://osqp.org/docs/parsers/jump.html) optimizer).
151151
- additional keyword arguments are passed to [`SteadyKalmanFilter`](@ref) constructor.
152152
153153
# Examples
@@ -259,11 +259,11 @@ function LinMPC(
259259
end
260260

261261
"""
262-
init_optimization!(mpc::LinMPC, model::LinModel, optim)
262+
init_optimization!(mpc::LinMPC, model::LinModel, optim::JuMP.GenericModel) -> nothing
263263
264264
Init the quadratic optimization for [`LinMPC`](@ref) controllers.
265265
"""
266-
function init_optimization!(mpc::LinMPC, model::LinModel, optim)
266+
function init_optimization!(mpc::LinMPC, model::LinModel, optim::JuMP.GenericModel)
267267
# --- variables and linear constraints ---
268268
con = mpc.con
269269
nZ̃ = length(mpc.Z̃)

0 commit comments

Comments
 (0)