14

Given a linear time-invariant system: $$x'(t)=Ax(t)+Bu(t)$$ with initial state $x(0)=x0$ and final state $x(T)=xT$.

The performance measure to be minimized is: $$∫_0^Tu(t)^2dt$$

The most important thing is to constrain the input $u$.

What would be the optimal control trajectories in this specific case? I know NDSolve can solve TVBVP, but I don't know how to deal with the constraint of input $u$.

I would appreciate any help on this!

The settings are inside the code. The codes:

A = {{0, 0, 0, 1, 0, 0},{0, 0, 0, 0, 1, 0},{0, 0, 0, 0, 0, 1},{-10.169, 1.406, 10.848, 0, 0, 0},{-15.135, -17.618, 16.146, 0, 0, 0},{26.186, -3.62, -10.883, 0, 0, 0}};

B = {{0, 0, 0},{0, 0, 0},{0, 0, 0},{-0.03, -0.045,0.0789},{-0.0456,0.571, 0.117},{0.0789, 0.1174, -0.0791}};

boundary condition are

x0 = {{-0.2}, {0.2}, {0.2}, {0}, {0}, {0}};
xT = {{0.2}, {-0.2}, {0.2}, {0}, {0}, {0}};

terminal time is

T = 1;

cost funtion

L[t_] = 1/2 (u1[t]^2 + u2[t]^2 + u3[t]^2);

state and costate and input

lambda[t_] := {{l1[t]}, {l2[t]}, {l3[t]}, {l4[t]}, {l5[t]}, {l6[t]}};
x[t_] := {{x1[t]}, {x2[t]}, {x3[t]}, {x4[t]}, {x5[t]}, {x6[t]}};
u[t_] := {{u1[t]}, {u2[t]}, {u3[t]}};

state space form

f[t_] = A.x[t] + B .u[t];

Hamilton

H[t_] = Flatten[L[t] + lambda[t]\[Transpose].f[t]][[1]];

Calculate the u*

u1Sol = First@Solve[0 == -D[H[t], u1[t]], u1[t]];
u2Sol = First@Solve[0 == -D[H[t], u2[t]], u2[t]];
u3Sol = First@Solve[0 == -D[H[t], u3[t]], u3[t]];

Define the state and costate

TableForm[ eqn1 = Table[D[lambda[t][[i, 1]], t] == -D[H[t] /. u1Sol /. u2Sol /. u3Sol, x[t][[i, 1]]], {i, 1, 6, 1}]]; TableForm[ eqn2 = Table[D[x[t][[i, 1]], t] == D[H[t] /. u1Sol /. u2Sol /. u3Sol, lambda[t][[i, 1]]], {i, 1, 6, 1}]]

Define boundary condition

bc1 = Table[x[0][[i, 1]] == x0[[i, 1]], {i, 1, 6, 1}]
bc2 = Table[x[T][[i, 1]] == xT[[i, 1]], {i, 1, 6, 1}]

Calculate the TVBVP

sol = NDSolve[Flatten[{eqn1, eqn2, bc1, bc2}], {x1[t], x2[t], x3[t], x4[t], x5[t], x6[t], l1[t], l2[t], l3[t], l4[t], l5[t], l6[t]}, {t, 0, T}]

and I want to constrain $u$ as $-100<u1<100$,$-50<u2<50$,$-50<u3<50$.

Where should I insert the constraints?

sun
  • 141
  • 5
  • Do you have a meaningful example as test case? I hate coming up with my own one. So far, I can only say that you have to formulate your problem as an optimization problem with quadratic objective, affine-linear equality constraints, and affine-linear inequality constraints, a so-called quadratic program. In principle NMinimize should be able to solve that if you don't use a too fine discretization of the ODE. In general, I would advice the semi-smooth Newton algorithm. I have an implementation on it on my hard drive. Interested? – Henrik Schumacher Jun 24 '18 at 20:41
  • Yes, I'm interested in it. Thank you – sun Jun 24 '18 at 22:46
  • Would you please post a test case? – Henrik Schumacher Jun 24 '18 at 23:54
  • I already posted it. Thank you! – sun Jun 25 '18 at 00:33

1 Answers1

16

Semi-smooth Newton solver

This is supposed to solve constrained optimization problems of the form

$$ \text{Minimize } F(x) \text{ subject to } \varPhi(x) = 0 \text{ and } \varPsi(x) \leq 0.$$

More precisely, it attempts to solve the KKT conditions for $x$ and the Lagrange multipliers $\lambda$ and $\mu$:

$$ \begin{array}{rcl} DF(x) + \lambda^T D\varPhi(x) + \mu^T D\varPsi &=&0,\\ \varPhi(x) &= &0,\\ \varPsi(x) &\leq &0,\\ \mu & \geq &0,\\ \mu^T \varPsi(x) &=&0. \end{array} $$

These conditions are necessary conditions for a local minimum if $\varPhi$ and $\varPsi$ satisfy certain constraint qualifications (e.g., Mangasarian-Fromovitz constraint qualification or Slater condition (for convex problems)) and are sufficient if the optimization problem is convex. The current problem is a quadratic program with strictly convex objective, so it is convex.

For some more mathematical background of the algorithm, see chapter 2 in these great lecture notes by Michael Hintermüller.

ClearAll[SemiSmoothNewton];
Options[SemiSmoothNewton] = {
   "EqualityMultiplier" -> Automatic,
   "InequalityMultiplier" -> Automatic,
   "MaxIterations" -> 1000,
   "Tolerance" -> 10^-8,
   "ArmijoSlope" -> 0.001,
   "BacktrackingFactor" -> 0.25,
   "InitialStepSize" -> 1.,
   "MaxBacktrackingIterations" -> 20,
   "PrintReport" -> True
   };

SemiSmoothNewton[x0_, F_, Φ, Ψ, OptionsPattern[]] := Module[{iter, biter, x, y, z, λ, μ, τ, xτ, λτ, μτ, n, mΦ, mΨ, TOL, τ0, residual, maxiter, maxbiter, σ, γ, Fval, ΦQ, ΨQ, Θ0, DΘ0, Θτ, DΘτ, ϕ0, ϕτ, u, δx, δλ, δμ, timing, maxstepsize},

ΦQ = Φ =!= None; ΨQ = Ψ =!= None;

x = x0; n = Length[x]; If[ΦQ, mΦ = Length[Φ[x]], mΦ = 0]; If[ΨQ, mΨ = Length[Ψ[x]], mΨ = 0];

λ = OptionValue["EqualityMultiplier"]; If[λ === Automatic, λ = ConstantArray[0., mΦ]]; μ = OptionValue["InequalityMultiplier"]; If[μ === Automatic, μ = ConstantArray[0., mΨ]];

iter = 0; maxstepsize = 0.; TOL = OptionValue["Tolerance"]; maxiter = OptionValue["MaxIterations"]; maxbiter = OptionValue["MaxBacktrackingIterations"]; σ = OptionValue["ArmijoSlope"]; γ = OptionValue["BacktrackingFactor"]; τ0 = OptionValue["InitialStepSize"]/γ; residual = 2 TOL; (enforce first iteration) timing = AbsoluteTiming[ While[ residual > TOL && iter < maxiter , iter++; {Θ0, DΘ0} = SSNΘDΘ[x, λ, μ, F, Φ, Ψ]; ϕ0 = Θ0.Θ0; u = -LinearSolve[DΘ0, Θ0]; δx = u[[1 ;; n]]; δλ = u[[n + 1 ;; n + mΦ]]; δμ = u[[n + mΦ + 1 ;; n + mΦ + mΨ]];

    (*backtracking line search*)
    biter = 0;
    τ = τ0;
    ϕτ = 2 ϕ0; (*enforce first iteration*)
    While[
     ϕτ &gt;= (1. - σ τ) ϕ0  &amp;&amp; biter &lt; maxbiter,
     biter++;
     τ = γ τ;
     xτ = x + τ δx; λτ = λ + τ δλ; μτ = μ + τ δμ;
     Θτ = SSNΘ[xτ, λτ, μτ, F, Φ, Ψ];
     ϕτ = Θτ.Θτ;
     ];
    residual = Sqrt[ϕτ/n];
    If[biter === maxbiter, Print[&quot;Oops. Backtracking was interrupted.&quot;]];
    x = xτ; λ = λτ; μ = μτ;
    maxstepsize = Max[maxstepsize, τ];
    Fval = F[x];
    ];
  ][[1]];

If[iter === maxiter, Print["Oops. Maximal number of iterations reached without satisfying the tolerance goal."]]; Association[ "Solution" -> x, "EqualityMultiplier" -> λ, "InequalityMultiplier" -> μ, "ObjectiveValue" -> Fval, "Iterations" -> iter, "Timing" -> timing, "Residual" -> residual, "MaxStepSize" -> maxstepsize ] ];

SSNΘ[x_, λ, μ,F_, Φ, Ψ] := Join[F'[x] + λ.Φ'[x] + μ.Ψ'[x], Φ[x], Ramp[Ψ[x] + μ] - μ] SSNΘDΘ[x_, λ, μ, F_, Φ, Ψ] := With[{A = Φ'[x], B = Ψ'[x], zμ = Ψ[x] + μ}, With[{a = SparseArray[UnitStep[zμ - $MachineEpsilon] + $MachineEpsilon]}, { Join[F'[x] + λ.A + μ.B, Φ[x], Ramp[zμ] - μ], ArrayFlatten[{ {F''[x] + λ.Φ''[x] + μ.Ψ''[x], A[Transpose], B[Transpose]}, {A, 0., 0.}, {a B, 0., DiagonalMatrix[a - 1.]} }] } ] ];

SSNΘ[x_, λ, {}, F, Φ, Ψ] := Join[F'[x] + λ.Φ'[x], Φ[x]] SSNΘDΘ[x_, λ, {}, F, Φ, Ψ] := With[{A = Φ'[x]}, { Join[F'[x] + λ.A, Φ[x]], ArrayFlatten[{ {F''[x] + λ.Φ''[x], A[Transpose]}, {A, 0.} }] } ];

SSNΘ[x_, {}, μ, F, Φ, Ψ] := Join[F'[x] + μ.Ψ'[x], Ramp[Ψ[x] + μ] - μ] SSNΘDΘ[x_, {}, μ, F, Φ, Ψ] := With[{B = Ψ'[x], zμ = Ψ[x] + μ}, With[{a = SparseArray[UnitStep[zμ - $MachineEpsilon] + $MachineEpsilon]}, { Join[F'[x] + μ.B, Ramp[zμ] - μ], ArrayFlatten[{ {F''[x] + μ.Ψ''[x], B[Transpose]}, {a B, DiagonalMatrix[a - 1.]} }] } ] ];

SSNΘ[x_, {}, {}, F_, Φ, Ψ] := F'[x]; SSNΘDΘ[x_, {}, {}, F_, Φ, Ψ] := {F'[x], F''[x]};

Casting the problem into a constrained optimization problem

Problem specifications.

ToPack = Developer`ToPackedArray;
A = ToPack[N[{{0, 0, 0, 1, 0, 0}, {0, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 
      1}, {-10.169, 1.406, 10.848, 0, 0, 0}, {-15.135, -17.618, 16.146, 0, 0, 0}, {26.186, -3.62, -10.883, 0, 0, 0}}]];
B = ToPack[N[{{0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {-0.03, -0.045,0.0789}, {-0.0456, 0.571, 0.117}, {0.0789, 0.1174, -0.0791}}]];
x0 = ToPack@N@{-0.2, 0.2, 0.2, 0, 0, 0};
xT = ToPack@N@{0.2, -0.2, 0.2, 0, 0, 0};
umin = {-100., -50., -50.};
umax = {100., 50., 50.};
T = 1.;

Discretization of ODE (n time steps, Crank-Nicolson scheme, control piecewise-constant in time).

n = 1000;
τ = N[T/n];
ndofs = n Dimensions[B][[2]];
dim = Length[A];

uumin = Flatten[ConstantArray[umin, n]]; uumax = Flatten[ConstantArray[umax, n]]; AA = SparseArraySparseBlockMatrix[{ {1, 1} -&gt; IdentityMatrix[dim, SparseArray], Band[{2, 2}] -&gt; IdentityMatrix[dim, SparseArray] + τ/2 SparseArray[A], Band[{2, 1}] -&gt; -IdentityMatrix[dim, SparseArray] + τ/2 SparseArray[A] }, {n + 1, n + 1}, 0. ]; BB = SparseArraySparseBlockMatrix[{Band[{2,2}] -> τ SparseArray[B]}, {n + 1, n + 1}, 0.];

AAinv = LinearSolve[AA]; A1 = Transpose[AAinv[Join[ConstantArray[0., {(n + 1) dim - dim, dim}], N@IdentityMatrix[dim]], "T"]]; A2 = A1[[All, dim + 1 ;;]].BB; b = A1[[All, 1 ;; dim]].x0;

trajectory[u_] := Partition[AAinv[Join[x0, BB.u]], dim];

Defining objective funtion F, equality constraint mapping Φ and inequality constraint mapping Ψ along with their first two derivatives.

F[u_?VectorQ] := 1/(2 n) u.u;
F'[u_?VectorQ] := u/n;
F''[u_?VectorQ] = N[1/n IdentityMatrix[ndofs, SparseArray]];

Φ[u_?VectorQ] := b + A2.u - xT; Φ'[u_?VectorQ] = SparseArray[A2]; Φ''[u_?VectorQ] = SparseArray[{}, {dim, ndofs, ndofs}, 0.];

Ψ[u_?VectorQ] := Join[u - uumax, uumin - u]; Ψ'[u_?VectorQ] = Join[N@IdentityMatrix[ndofs, SparseArray], -N@ IdentityMatrix[ndofs, SparseArray]]; Ψ''[u_?VectorQ] = SparseArray[{}, {2 ndofs, ndofs, ndofs}, 0.];

Creating a starting point for Newton search and performing the actual search.

u0 = LeastSquares[A2, xT];
data = SemiSmoothNewton[u0, F, Φ, Ψ, "Tolerance" -> 10^-8]; // AbsoluteTiming // First
u = data[["Solution"]];

0.306972

Plotting the results.

ListLinePlot[Transpose[{Rest@Subdivide[0., T, n], #}] & /@ Transpose[Partition[u, 3]],
 AxesLabel -> {"t", "u"},
 PlotLegends -> Table["u" <> ToString[i], {i, 1, Dimensions[B][[2]]}],
 PlotLabel -> "Controls"
 ]

ListLinePlot[ Transpose[{Subdivide[0., T, n], #}] & /@ Transpose[trajectory[u]], AxesLabel -> {"t", "u"}, PlotLegends -> Table["x" <> ToString[i], {i, 1, dim}], PlotLabel -> "Trajectories" ]

enter image description here

enter image description here

Henrik Schumacher
  • 106,770
  • 7
  • 179
  • 309
  • 0.306972 is a result, not one part of the algorithm right? It seems like running such an algorithm costs a long time? – sun Jun 26 '18 at 09:58
  • 1
    It's the timing of the whole optimization process. The result is data[["Solution"]]. Whether this is long or not lies in the eye of the beholder. Maybe it can be made a bit faster with another linear solver. Inspecting Dataset@data will tell you that only 6(!) steps of Newton's method were needed. Simply solving the ODE once with NDSolve needs 0.047 seconds, so there is actually not much discrepancy. Notice also that NDSolve computes only 70 time steps (though probably with a higher order method). – Henrik Schumacher Jun 26 '18 at 13:37
  • I run the algorithm you gave me, and it cost more than 10 hours and still running now. I' m confused that there may be something wrong. – sun Jun 26 '18 at 23:59
  • What are you doing there? Once again, for the probem you posted, everything should be done within a single second. Did you apply the code to a higher dimensional problem? – Henrik Schumacher Jun 27 '18 at 04:07
  • I found the reason. Mathematica 9 costs much more time than Mathematica 11. Sorry. – sun Jun 27 '18 at 04:35
  • What's the size of your actual problem (size of A and B)? Where do A and B stem from? Maybe there is more potential for optimization. – Henrik Schumacher Jun 27 '18 at 05:30
  • A is 66, B is 63, it is a linearized 3-link walking dynamic model. You see solving nonlinear model costs too much time. There are 3 inputs and applied on each link separately. In fact, I am trying to use the cost function: $L=u1x4+u2x5+u3*x6$, which means the work done by the inputs. – sun Jun 27 '18 at 05:46
  • Okay, the problem size is tiny. You are not satisfied by the performance of the method? Once again, if set up correctly, the problem is solved within a fraction of a second. What do you expect? – Henrik Schumacher Jun 27 '18 at 05:51
  • If you change the objective F, you have also to adjust F' and F''. They need to be exaclty first and second derivative of F. – Henrik Schumacher Jun 27 '18 at 06:07
  • I' m so satisfied on your algorithm! It's amazing. You are brilliant both on programming and mathmatics. Eventhough it is awkward, but I have to say I still can not understand the textbook on semismooth Newton method. I am lack of the knowledge on mathematics. That is why I always ask you some easy questions. – sun Jun 27 '18 at 06:22
  • Meanwhile, I don't really understand your algorithm. How can I change the $F=u^2$, It seems like there is no definition of state variables $x$. How can I change it to the form of $u1∗x4+u2∗x5+u3∗x6$? – sun Jun 27 '18 at 06:25
  • 2
    I drew most of what I know about the semismooth Newton algorithm from this script by by Michael Hintermüller, basically chapter 2. Maybe that will also help you. – Henrik Schumacher Jun 27 '18 at 06:28
  • In order to obtain the state x, call trajectory[u] in the bodies of F, F', and F''. This is not the most efficient method as the ODE gets solved every time but it's the easiest way to obtain the state. A better implementation would cache the state somehow until a new control is computed. – Henrik Schumacher Jun 27 '18 at 06:32
  • 1
    Would you mind explaining further or show me the code? How to change the cost function to the form of $u1∗x4+u2∗x5+u3∗x6$ ? I don' t really understand how to apply such a step. – sun Jun 27 '18 at 06:49
  • Uh. Yeah. Now that I think of it, it is not that simple if the state occurs explicitly in the objective. While the objective F and its derivative can be implemented by F[u_?VectorQ] := 1/(2 n) u.Flatten[trajectory[u][[2 ;;, 4 ;; 6]]]; F'[u_?VectorQ] := Plus[ Flatten[trajectory[u][[2 ;;, 4 ;; 6]]], AAinv[ Join[ ConstantArray[0., dim], Flatten[Join[ConstantArray[0., {n, 3}], Partition[u, 3], 2]] ], "T" ][[dim + 1 ;;]].BB ]/(2 n);, the second derivative will involve the inverse of the matrix AA which will be a dense matrix. – Henrik Schumacher Jun 27 '18 at 07:15
  • If I had know that objective earlier, I might have proposed another method... At the moment, I am leaving for a conference. Quite likely, I won't find time to mull over this for the next couple of days. – Henrik Schumacher Jun 27 '18 at 07:29
  • I see, thank you! – sun Jun 27 '18 at 07:33
  • 1
    Sorry to disturb you, do you have enough time to talk about how to solve this problem when the cost function is written as $L=u1∗x4+u2∗x5+u3∗x6$? You said you may have another method. I'm so interested in it. – sun Jul 06 '18 at 06:28
  • 1
    Have you already checked the literature on quadratic optimal control problems with control constraints? What one can employ in any case (and which is often proposed) is the interior point method in conjunction with the BFGS algorithm (so no second derivatives are needed). One can also try to implement the matrix-vector product F''[u].v and use the semi-smooth Newton method by employing a iterative linear solver. If the problem size is small enough, it might be sufficient to to use FindMinimum (just submit F[u] and the constraints with a symbolic vector u). – Henrik Schumacher Jul 06 '18 at 06:49
  • 2
    In total, this is a fairly standard problem in optimal control and there are plenty of tools around for solving them. I just posted the semi-smooth Newton alrgorithm, because it is not so well-known and because I already had code for it. I don't mean to be rude, but implementing another method would take me several hours and nobody is going to pay me for that. – Henrik Schumacher Jul 06 '18 at 06:55
  • I see, thank you. – sun Jul 06 '18 at 07:13
  • @HenrikSchumacher Sorry from the side. Is $D$ in $DF(x)$ a differential operator? – Xminer Mar 20 '19 at 11:51
  • 2
    @Xminer Yes, of course. It is the Fréchet derivative (also called total derivative or Jacobian). – Henrik Schumacher Mar 20 '19 at 11:53