Skip to content

Commit 84deef7

Browse files
committed
Improve singular control example documentation
- Add comments to control and state bounds in OCP definition - Fix mathematical calculations for singular control derivation - Correct H01, H001, and H101 Poisson bracket calculations - Add non-degeneracy condition proof - Simplify calculation flow and remove redundant steps - Enable draft mode for documentation build
1 parent acbd1a5 commit 84deef7

2 files changed

Lines changed: 47 additions & 47 deletions

File tree

docs/make.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,7 @@ cp(
153153
Draft = false
154154
```
155155
=#
156-
draft = true # Draft mode: if true, @example blocks in markdown are not executed
156+
draft = false # Draft mode: if true, @example blocks in markdown are not executed
157157

158158
# ═══════════════════════════════════════════════════════════════════════════════
159159
# Load extensions

docs/src/example-singular-control.md

Lines changed: 46 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,8 @@ ocp = @def begin
4040
q = (x, y, θ) ∈ R³, state
4141
u ∈ R, control
4242
43-
-1 ≤ u(t) ≤ 1
44-
-π/2 ≤ θ(t) ≤ π/2
43+
-1 ≤ u(t) ≤ 1 # Control bounds
44+
-π/2 ≤ θ(t) ≤ π/2 # State bounds (helps direct method convergence)
4545
4646
x(0) == 0
4747
y(0) == 0
@@ -77,21 +77,21 @@ Let's plot the solution:
7777

7878
```@example main
7979
opt = (state_bounds_style=:none, control_bounds_style=:none)
80-
plt = plot(direct_sol; opt..., size=(800, 800))
80+
plt = plot(direct_sol; label="direct", size=(800, 800), opt...)
8181
```
8282

8383
## Singular control by hand
8484

85-
The pseudo-Hamiltonian for this time-optimal problem is (with $p^0 = -1$):
85+
The pseudo-Hamiltonian for this time-optimal problem is:
8686

8787
```math
88-
H(q, p, u) = p_1 \cos\theta + p_2(\sin\theta + x) + p_3 u - 1.
88+
H(q, p, u) = p_1 \cos\theta + p_2(\sin\theta + x) + p_3 u.
8989
```
9090

9191
This is control-affine: $H = H_0 + u H_1$ with:
9292

9393
```math
94-
H_0(q, p) = p_1 \cos\theta + p_2(\sin\theta + x) - 1, \quad H_1(q, p) = p_3.
94+
H_0(q, p) = p_1 \cos\theta + p_2(\sin\theta + x), \quad H_1(q, p) = p_3.
9595
```
9696

9797
The switching function is $H_1 = p_3$. On a singular arc, we have $H_1 = 0$ and all its time derivatives must vanish.
@@ -110,16 +110,16 @@ H_{01} = \frac{\partial H_0}{\partial p_1} \frac{\partial H_1}{\partial x} - \fr
110110
+ \frac{\partial H_0}{\partial p_3} \frac{\partial H_1}{\partial \theta} - \frac{\partial H_0}{\partial \theta} \frac{\partial H_1}{\partial p_3}.
111111
```
112112

113-
Since $H_1 = p_3$ depends only on $p_3$, most terms vanish:
113+
Since $H_1 = p_3$ depends only on $p_3$, the only non-zero contribution comes from the $(\theta, p_3)$ pair:
114114

115115
```math
116-
H_{01} = -\frac{\partial H_0}{\partial \theta} = -(-p_1 \sin\theta + p_2 \cos\theta) = p_1 \sin\theta - p_2 \cos\theta.
116+
H_{01} = \frac{\partial H_0}{\partial \theta} \frac{\partial H_1}{\partial p_3} - \frac{\partial H_0}{\partial p_3} \frac{\partial H_1}{\partial \theta} = (-p_1 \sin\theta + p_2 \cos\theta) \cdot 1 - 0 = -p_1 \sin\theta + p_2 \cos\theta.
117117
```
118118

119119
On the singular arc, $H_{01} = 0$, which gives the constraint:
120120

121121
```math
122-
p_1 \sin\theta = p_2 \cos\theta.
122+
p_2 \cos\theta = p_1 \sin\theta.
123123
```
124124

125125
**Second derivative:**
@@ -131,27 +131,23 @@ p_1 \sin\theta = p_2 \cos\theta.
131131
For the arc to remain singular, $\dot{H}_{01} = 0$, which gives:
132132

133133
```math
134-
u_s = -\frac{H_{001}}{H_{101}}.
134+
u_s = -\frac{H_{001}}{H_{101}},
135135
```
136136

137-
Computing $H_{001}$:
137+
whenever $H_{101} \neq 0$. Computing $H_{001} = \{H_0, H_{01}\}$ with $H_{01} = -p_1 \sin\theta + p_2 \cos\theta$:
138138

139-
```math
140-
H_{001} = \frac{\partial H_0}{\partial p_2} \frac{\partial H_{01}}{\partial \theta} - \frac{\partial H_0}{\partial \theta} \frac{\partial H_{01}}{\partial p_2}
141-
= (\sin\theta + x)(p_1 \cos\theta + p_2 \sin\theta) - (-p_1 \sin\theta + p_2 \cos\theta)(-\cos\theta)
142-
= (\sin\theta + x)(p_1 \cos\theta + p_2 \sin\theta) - p_1 \sin\theta \cos\theta + p_2 \cos^2\theta.
143-
```
144-
145-
Since we're on the singular arc where $x$ is part of the state trajectory, we simplify by focusing on the terms involving $p$ and $\theta$. After calculation:
139+
The only non-zero contribution comes from the $(x, p_1)$ pair:
146140

147141
```math
148-
H_{001} = -p_2 \sin\theta.
142+
H_{001} = \frac{\partial H_0}{\partial x} \frac{\partial H_{01}}{\partial p_1} - \frac{\partial H_0}{\partial p_1} \frac{\partial H_{01}}{\partial x} = p_2 \cdot (-\sin\theta) - \cos\theta \cdot 0 = -p_2 \sin\theta.
149143
```
150144

151-
Computing $H_{101}$:
145+
Computing $H_{101} = \{H_1, H_{01}\}$ with $H_1 = p_3$ and $H_{01} = -p_1 \sin\theta + p_2 \cos\theta$:
146+
147+
The only non-zero contribution comes from the $(\theta, p_3)$ pair:
152148

153149
```math
154-
H_{101} = \frac{\partial H_1}{\partial p_3} \frac{\partial H_{01}}{\partial \theta} = 1 \cdot (p_1 \cos\theta + p_2 \sin\theta) = p_1 \cos\theta + p_2 \sin\theta.
150+
H_{101} = \frac{\partial H_1}{\partial \theta} \frac{\partial H_{01}}{\partial p_3} - \frac{\partial H_1}{\partial p_3} \frac{\partial H_{01}}{\partial \theta} = 0 - 1 \cdot (-p_1 \cos\theta - p_2 \sin\theta) = p_1 \cos\theta + p_2 \sin\theta.
155151
```
156152

157153
Therefore:
@@ -160,24 +156,29 @@ Therefore:
160156
u_s = -\frac{H_{001}}{H_{101}} = \frac{p_2 \sin\theta}{p_1 \cos\theta + p_2 \sin\theta}.
161157
```
162158

163-
**Simplification using the constraint:**
159+
!!! note "Non-degeneracy condition"
164160

165-
From $p_1 \sin\theta = p_2 \cos\theta$, multiply both sides by $\sin\theta$:
161+
We can show that $H_{101} \neq 0$ on the singular arc. From the constraint $p_1 \sin\theta = p_2 \cos\theta$, if we had $H_{101} = p_1 \cos\theta + p_2 \sin\theta = 0$, then:
166162

167-
```math
168-
p_1 \sin^2\theta = p_2 \sin\theta \cos\theta.
169-
```
163+
```math
164+
\begin{pmatrix} \cos\theta & \sin\theta \\ -\sin\theta & \cos\theta \end{pmatrix}
165+
\begin{pmatrix} p_1 \\ p_2 \end{pmatrix} = \begin{pmatrix} 0 \\ 0 \end{pmatrix}.
166+
```
167+
168+
Since this matrix has determinant 1 (hence is invertible), we would have $p_1 = p_2 = 0$. Combined with $p_3 = 0$ (from $H_1 = 0$), this gives $p = 0$, which is impossible for a time-minimization problem.
170169

171-
Substituting into the numerator of $u_s$:
170+
**Simplification using the constraint:**
171+
172+
Multiply numerator and denominator by $\sin\theta$:
172173

173174
```math
174-
u_s = \frac{p_1 \sin^2\theta}{p_1 \cos\theta \sin\theta + p_1 \sin^2\theta} = \frac{p_1 \sin^2\theta}{p_1(\cos\theta \sin\theta + \sin^2\theta)} = \frac{\sin^2\theta}{\sin\theta(\cos\theta + \sin\theta)} = \sin^2\theta.
175+
u_s = \frac{p_2 \sin^2\theta}{p_1 \cos\theta \sin\theta + p_2 \sin^2\theta}.
175176
```
176177

177-
Wait, let me recalculate more carefully. From the constraint $p_1 \sin\theta = p_2 \cos\theta$, we have $p_2 = p_1 \tan\theta$. Substituting:
178+
From the constraint $p_1 \sin\theta = p_2 \cos\theta$, we have $p_1 \cos\theta \sin\theta = p_2 \cos^2\theta$. Substituting in the denominator:
178179

179180
```math
180-
u_s = \frac{p_1 \tan\theta \sin\theta}{p_1 \cos\theta + p_1 \tan\theta \sin\theta} = \frac{p_1 \frac{\sin^2\theta}{\cos\theta}}{p_1(\cos\theta + \frac{\sin^2\theta}{\cos\theta})} = \frac{\sin^2\theta}{\cos^2\theta + \sin^2\theta} = \sin^2\theta.
181+
u_s = \frac{p_2 \sin^2\theta}{p_2 \cos^2\theta + p_2 \sin^2\theta} = \frac{p_2 \sin^2\theta}{p_2(\cos^2\theta + \sin^2\theta)} = \sin^2\theta.
181182
```
182183

183184
So the singular control is:
@@ -192,7 +193,8 @@ Let's overlay this on the numerical solution:
192193
T = time_grid(direct_sol, :control)
193194
θ(t) = state(direct_sol)(t)[3]
194195
us(t) = sin(θ(t))^2
195-
plot!(plt, T, us; line=:dash, lw=2, subplot=7, label="us (hand)")
196+
plot!(plt, T, us; subplot=7, line=:dash, lw=2, label="us (hand)")
197+
plot(plt[7]; size=(800, 400))
196198
```
197199

198200
## Singular control via Poisson brackets
@@ -237,7 +239,8 @@ Let's verify this gives the same result:
237239
q(t) = state(direct_sol)(t)
238240
p(t) = costate(direct_sol)(t)
239241
us_b(t) = us_bracket(q(t), p(t))
240-
plot!(plt, T, us_b; line=:dashdot, lw=2, subplot=7, label="us (brackets)")
242+
plot!(plt, T, us_b; subplot=7, line=:dashdot, lw=2, label="us (brackets)")
243+
plot(plt[7]; size=(800, 400))
241244
```
242245

243246
Both methods give the same singular control, which matches the numerical solution from the direct method.
@@ -267,13 +270,7 @@ f = Flow(ocp, (x, p, tf) -> u_indirect(x))
267270
nothing # hide
268271
```
269272

270-
Define the shooting function. We have 5 unknowns: the initial costate $p_0 \in \mathbb{R}^3$, the initial orientation $\theta_0$, and the final time $t_f$. The 5 equations are:
271-
272-
1. $x(t_f) = 1$ (final position constraint)
273-
2. $y(t_f) = 0$ (final position constraint)
274-
3. $p_\theta(0) = 0$ (transversality condition: $H_1(0) = 0$)
275-
4. $p_\theta(t_f) = 0$ (transversality condition: $H_1(t_f) = 0$)
276-
5. $H(t_f) = 1$ (Hamiltonian constant for time-optimal problems with free final time)
273+
Define the shooting function. We have 5 unknowns: the initial costate $p_0 \in \mathbb{R}^3$, the initial orientation $\theta_0$, and the final time $t_f$. We must define 5 equations to solve for these unknowns.
277274

278275
```@example main
279276
t0 = 0
@@ -282,10 +279,10 @@ function shoot!(s, p0, θ0, tf)
282279
q_t0, p_t0 = [0, 0, θ0], p0
283280
q_tf, p_tf = f(t0, q_t0, p_t0, tf)
284281
285-
s[1] = q_tf[1] - 1 # x(tf) = 1
286-
s[2] = q_tf[2] # y(tf) = 0
287-
s[3] = p_t0[3] # p_θ(0) = 0
288-
s[4] = p_tf[3] # p_θ(tf) = 0
282+
s[1] = q_tf[1] - 1 # x(tf) = 1 (boundary condition)
283+
s[2] = q_tf[2] # y(tf) = 0 (boundary condition)
284+
s[3] = p_t0[3] # (0) = 0 (transversality condition)
285+
s[4] = p_tf[3] # (tf) = 0 (transversality condition)
289286
290287
# H(tf) = 1 (for time-optimal with p^0 = -1)
291288
pxf = p_tf[1]
@@ -303,7 +300,10 @@ p0 = costate(direct_sol)(t0)
303300
θ0 = state(direct_sol)(t0)[3]
304301
tf = variable(direct_sol)
305302
306-
println("Initial guess: p0 = ", p0, ", θ0 = ", θ0, ", tf = ", tf)
303+
println("Initial guess:")
304+
println("p0 = ", p0)
305+
println("θ0 = ", θ0)
306+
println("tf = ", tf)
307307
nothing # hide
308308
```
309309

@@ -323,7 +323,7 @@ prob = NonlinearProblem(nle!, ξ_guess)
323323
shooting_sol = solve(prob; show_trace=Val(false))
324324
p0_sol, θ0_sol, tf_sol = shooting_sol.u[1:3], shooting_sol.u[4], shooting_sol.u[5]
325325
326-
println("\nShooting solution:")
326+
println("Shooting solution:")
327327
println("p0 = ", p0_sol)
328328
println("θ0 = ", θ0_sol)
329329
println("tf = ", tf_sol)
@@ -340,7 +340,7 @@ nothing # hide
340340
Plot the indirect solution alongside the direct solution:
341341

342342
```@example main
343-
plot!(plt, indirect_sol; label="Indirect", color=2, linestyle=:dash, opt...)
343+
plot!(plt, indirect_sol; label="indirect", color=2, linestyle=:dash, opt...)
344344
```
345345

346346
The indirect and direct solutions match very well, confirming that our singular control computation is correct.

0 commit comments

Comments
 (0)