Skip to content

Commit 76e5a30

Browse files
committed
Extract state constraint example to separate file
- Create new autonomous example-state-constraint.md with state constraint variant - Remove state constraint section from example-double-integrator-energy.md - Add cross-references between the two examples - Add state constraint example to Basic Examples menu in make.jl
1 parent 583767d commit 76e5a30

3 files changed

Lines changed: 247 additions & 147 deletions

File tree

docs/make.jl

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,7 @@ cp(
151151
Draft = false
152152
```
153153
=#
154-
draft = false # Draft mode: if true, @example blocks in markdown are not executed
154+
draft = true # Draft mode: if true, @example blocks in markdown are not executed
155155

156156
# ═══════════════════════════════════════════════════════════════════════════════
157157
# Load extensions
@@ -207,6 +207,7 @@ with_api_reference(src_dir, ext_dir) do api_pages
207207
"Time mininimisation" => "example-double-integrator-time.md",
208208
"Control-free problems" => "example-control-free.md",
209209
"Singular control" => "example-singular-control.md",
210+
"State constraint" => "example-state-constraint.md",
210211
],
211212
"Manual" => [
212213
"Define a problem" => "manual-abstract.md",

docs/src/example-double-integrator-energy.md

Lines changed: 1 addition & 146 deletions
Original file line numberDiff line numberDiff line change
@@ -166,149 +166,4 @@ plot(indirect_sol)
166166
- You can use [MINPACK.jl](@extref Tutorials Resolution-of-the-shooting-equation) instead of [NonlinearSolve.jl](https://docs.sciml.ai/NonlinearSolve).
167167
- For more details about the flow construction, visit the [Compute flows from optimal control problems](@ref manual-flow-ocp) page.
168168
- In this simple example, we have set an arbitrary initial guess. It can be helpful to use the solution of the direct method to initialise the shooting method. See the [Goddard tutorial](@extref Tutorials tutorial-goddard) for such a concrete application.
169-
170-
## State constraint
171-
172-
The following example illustrates both direct and indirect solution approaches for the energy minimization problem with a state constraint on the maximal velocity. The workflow demonstrates a practical strategy: a direct method on a coarse grid first identifies the problem structure and provides an initial guess for the indirect method, which then computes a precise solution via shooting based on Pontryagin's Maximum Principle.
173-
174-
!!! note
175-
176-
The direct solution can be refined using a finer discretization grid for higher accuracy.
177-
178-
### Direct method: constrained case
179-
180-
We add the path constraint
181-
182-
```math
183-
v(t) \le 1.2.
184-
```
185-
186-
Let us model, solve and plot the optimal control problem with this constraint.
187-
188-
```@example main
189-
# the upper bound for v
190-
v_max = 1.2
191-
192-
# the optimal control problem
193-
ocp = @def begin
194-
t ∈ [t0, tf], time
195-
x = (q, v) ∈ R², state
196-
u ∈ R, control
197-
198-
v(t) ≤ v_max # state constraint
199-
200-
x(t0) == x0
201-
x(tf) == xf
202-
203-
ẋ(t) == [v(t), u(t)]
204-
205-
0.5∫( u(t)^2 ) → min
206-
end
207-
208-
# solve with a direct method
209-
direct_sol = solve(ocp; grid_size=50)
210-
211-
# plot the solution
212-
plt = plot(direct_sol; label="Direct", size=(800, 600))
213-
```
214-
215-
The solution has three phases (unconstrained-constrained-unconstrained arcs), requiring definition of Hamiltonian flows for each phase and a shooting function to enforce boundary and switching conditions.
216-
217-
### Indirect method: constrained case
218-
219-
Under the normal case, the pseudo-Hamiltonian reads:
220-
221-
```math
222-
H(x, p, u, \mu) = p_1 v + p_2 u - \frac{u^2}{2} + \mu\, g(x),
223-
```
224-
225-
where $g(x) = v_{\max} - v$. Along a boundary arc we have $g(x(t)) = 0$; differentiating gives:
226-
227-
```math
228-
\frac{\mathrm{d}}{\mathrm{d}t}g(x(t)) = -\dot{v}(t) = -u(t) = 0.
229-
```
230-
231-
The zero control maximises the Hamiltonian, so $p_2(t) = 0$ along that arc. From the adjoint equation we then have
232-
233-
```math
234-
\dot{p}_2(t) = -p_1(t) + \mu(t) = 0 \quad \Rightarrow \mu(t) = p_1(t).
235-
```
236-
237-
Because the adjoint vector is continuous at both the entry time $t_1$ and the exit time $t_2$, the unknowns are $p_0 \in \mathbb{R}^2$ together with $t_1$ and $t_2$. The target condition supplies two equations, $g(x(t_1)) = 0$ enforces the state constraint, and $p_2(t_1) = 0$ encodes the switching condition.
238-
239-
```@example main
240-
# flow for unconstrained extremals
241-
f_interior = Flow(ocp, (x, p) -> p[2])
242-
243-
ub = 0 # boundary control
244-
g(x) = v_max - x[2] # constraint: g(x) ≥ 0
245-
μ(p) = p[1] # dual variable
246-
247-
# flow for boundary extremals
248-
f_boundary = Flow(ocp, (x, p) -> ub, (x, u) -> g(x), (x, p) -> μ(p))
249-
250-
# shooting function
251-
function shoot!(s, p0, t1, t2)
252-
x_t0, p_t0 = x0, p0
253-
x_t1, p_t1 = f_interior(t0, x_t0, p_t0, t1)
254-
x_t2, p_t2 = f_boundary(t1, x_t1, p_t1, t2)
255-
x_tf, p_tf = f_interior(t2, x_t2, p_t2, tf)
256-
s[1:2] = x_tf - xf
257-
s[3] = g(x_t1)
258-
s[4] = p_t1[2]
259-
end
260-
nothing # hide
261-
```
262-
263-
We can derive an initial guess for the costate and the entry/exit times from the direct solution:
264-
265-
```@example main
266-
t = time_grid(direct_sol) # the time grid as a vector
267-
x = state(direct_sol) # the state as a function of time
268-
p = costate(direct_sol) # the costate as a function of time
269-
270-
# initial costate
271-
p0 = p(t0)
272-
273-
# times where constraint is active
274-
t12 = t[ 0 .≤ (g ∘ x).(t) .≤ 1e-3 ]
275-
276-
# entry and exit times
277-
t1 = minimum(t12) # entry time
278-
t2 = maximum(t12) # exit time
279-
nothing # hide
280-
```
281-
282-
We can now solve the shooting equations.
283-
284-
```@example main
285-
# auxiliary in-place NLE function
286-
nle!(s, ξ, _) = shoot!(s, ξ[1:2], ξ[3], ξ[4])
287-
288-
# initial guess for the Newton solver
289-
ξ_guess = [p0..., t1, t2]
290-
291-
# NLE problem with initial guess
292-
prob = NonlinearProblem(nle!, ξ_guess)
293-
294-
# resolution of the shooting equations
295-
shooting_sol = solve(prob; show_trace=Val(true))
296-
p0, t1, t2 = shooting_sol.u[1:2], shooting_sol.u[3], shooting_sol.u[4]
297-
298-
# print the costate solution and the entry and exit times
299-
println("\np0 = ", p0, "\nt1 = ", t1, "\nt2 = ", t2)
300-
```
301-
302-
To reconstruct the constrained trajectory, concatenate the flows as follows: an unconstrained arc until $t_1$, a boundary arc from $t_1$ to $t_2$, and a final unconstrained arc from $t_2$ to $t_f$.
303-
This composition yields the full solution (state, costate, and control), which we then plot alongside the direct method for comparison.
304-
305-
```@example main
306-
# concatenation of the flows
307-
φ = f_interior * (t1, f_boundary) * (t2, f_interior)
308-
309-
# compute the solution: state, costate, control...
310-
indirect_sol = φ((t0, tf), x0, p0; saveat=range(t0, tf, 100))
311-
312-
# plot the solution on the previous plot
313-
plot!(plt, indirect_sol; label="Indirect", color=2, linestyle=:dash)
314-
```
169+
- For a version with a state constraint on the velocity, see the [State constraint](@ref example-state-constraint) example.

0 commit comments

Comments
 (0)