Attitude Control

The Attitude Control benchmark models a rigid-body system [PPR].

using ClosedLoopReachability
import OrdinaryDiffEq, Plots, DisplayAs
using ReachabilityBase.CurrentPath: @current_path
using ReachabilityBase.Timing: print_timed
using Plots: plot, plot!

Model

There are 6 state variables: $(ω_1, ω_2, ω_3, ψ_1, ψ_2, ψ_3)$. The system dynamics are given as follows:

\[\begin{aligned} \dot{ω}_1 &= 0.25 (u_0 + ω_2 ω_3) \\ \dot{ω}_2 &= 0.5 (u_1 - 3 ω_1 ω_3) \\ \dot{ω}_3 &= u_2 + 2 ω_1 ω_2) \\ \dot{ψ}_1 &= 0.5 (ω₂ (ξ - ψ₃) + ω₃ (ξ + ψ₂) + ω₁ (ξ + 1)) \\ \dot{ψ}_2 &= 0.5 (ω₁ (ξ + ψ₃) + ω₃ (ξ - ψ₁) + ω₂ (ξ + 1)) \\ \dot{ψ}_3 &= 0.5 (ω₁ (ξ - ψ₂) + ω₂ (ξ + ψ₁) + ω₃ (ξ + 1)) \end{aligned}\]

where $ω = (ω_1, ω_2, ω_3)$ is the angular velocity in a body-fixed frame, $ψ = (ψ_1, ψ_2, ψ_3)$ are the Rodrigues parameters, and $ξ = ψ₁^2 + ψ₂^2 + ψ₃^2$.

vars_idx = Dict(:states => 1:6, :controls => 7:9)

@taylorize function AttitudeControl!(dx, x, p, t)
    ω₁, ω₂, ω₃, ψ₁, ψ₂, ψ₃, u₀, u₁, u₂ = x

    ξ = ψ₁^2 + ψ₂^2 + ψ₃^2

    dx[1] = 0.25 * (u₀ + ω₂ * ω₃)
    dx[2] = 0.5 * (u₁ - 3 * ω₁ * ω₃)
    dx[3] = u₂ + 2 * ω₁ * ω₂
    dx[4] = 0.5 * (  ω₂ * (ξ - ψ₃)
                   + ω₃ * (ξ + ψ₂)
                   + ω₁ * (ξ + 1))
    dx[5] = 0.5 * (  ω₁ * (ξ + ψ₃)
                   + ω₃ * (ξ - ψ₁)
                   + ω₂ * (ξ + 1))
    dx[6] = 0.5 * (  ω₁ * (ξ - ψ₂)
                   + ω₂ * (ξ + ψ₁)
                   + ω₃ * (ξ + 1))
    dx[7] = zero(u₀)
    dx[8] = zero(u₁)
    dx[9] = zero(u₂)
    return dx
end;

We are given a neural-network controller with 3 hidden layers of 64 neurons each and sigmoid activations. The controller has 6 inputs (the state variables) and 3 outputs ($u_0, u_1, u_2$).

path = @current_path("AttitudeControl", "AttitudeControl_controller.polar")
controller = read_POLAR(path);

The control period is 0.1 time units.

period = 0.1;

Specification

The uncertain initial condition is:

X₀ = Hyperrectangle(low=[-0.45, -0.55, 0.65, -0.75, 0.85, -0.65],
                    high=[-0.44, -0.54, 0.66, -0.74, 0.86, -0.64])
U₀ = ZeroSet(3);

The control problem is:

ivp = @ivp(x' = AttitudeControl!(x), dim: 9, x(0) ∈ X₀ × U₀)
prob = ControlledPlant(ivp, controller, vars_idx, period);

The safety specification is that a set of unsafe states should not be reached within 3 time units. A sufficient condition for guaranteed verification is to overapproximate the result with hyperrectangles.

unsafe_states = cartesian_product(
    Hyperrectangle(low=[-0.2, -0.5, 0,   -0.7, 0.7, -0.4],
                   high=[0,   -0.4, 0.2, -0.6, 0.8, -0.2]),
    Universe(3))

predicate(sol) = isdisjoint(overapproximate(sol, Hyperrectangle), unsafe_states);

T = 3.0
T_warmup = 2 * period;  # shorter time horizon for warm-up run

Analysis

To enclose the continuous dynamics, we use a Taylor-model-based algorithm:

algorithm_plant = TMJets(abstol=1e-4, orderT=5, orderQ=1);

To propagate sets through the neural network, we use the DeepZ algorithm:

algorithm_controller = DeepZ();

The verification benchmark is given below:

function benchmark(; T=T, silent::Bool=false)
    # Solve the controlled system:
    silent || println("Flowpipe construction:")
    res = @timed solve(prob; T=T, algorithm_controller=algorithm_controller,
                       algorithm_plant=algorithm_plant)
    sol = res.value
    silent || print_timed(res)

    # Check the property:
    silent || println("Property checking:")
    res = @timed predicate(sol)
    silent || print_timed(res)
    if res.value
        silent || println("  The property is satisfied.")
        result = "verified"
    else
        silent || println("  The property may be violated.")
        result = "not verified"
    end

    return sol, result
end;

Run the verification benchmark and compute some simulations:

benchmark(T=T_warmup, silent=true)  # warm-up
res = @timed benchmark(T=T)  # benchmark
sol, result = res.value
@assert (result == "verified") "verification failed"
println("Total analysis time:")
print_timed(res)

println("Simulation:")
res = @timed simulate(prob; T=T, trajectories=10, include_vertices=false)
sim = res.value
print_timed(res);
Flowpipe construction:
  1.474104 seconds (5.66 M allocations: 483.964 MiB, 60.21% gc time)
Property checking:
  0.011338 seconds (117.67 k allocations: 9.516 MiB)
  The property is satisfied.
Total analysis time:
  1.489019 seconds (5.78 M allocations: 494.345 MiB, 59.61% gc time)
Simulation:
  0.613352 seconds (996.81 k allocations: 74.591 MiB)

Results

Script to plot the results:

function plot_helper(vars)
    fig = plot()
    plot!(fig, project(unsafe_states, vars); color=:red, alpha=:0.2,
          lab="unsafe", leg=:topleft)
    plot!(fig, sol; vars=vars, color=:yellow, lw=0, alpha=1, lab="")
    plot!(fig, project(X₀, vars); c=:cornflowerblue, alpha=1, lab="X₀")
    plot_simulation!(fig, sim; vars=vars, color=:black, lab="")
    return fig
end;

Plot the results:

vars = (1, 2)
fig = plot_helper(vars)
plot!(fig; xlab="ω₁", ylab="ω₂")
# Plots.savefig(fig, "AttitudeControl.png")  # command to save the plot to a file
fig = DisplayAs.Text(DisplayAs.PNG(fig))
Example block output

References

  • PPRStephen Prajna, Pablo A. Parrilo, and Anders Rantzer (2004). Nonlinear control synthesis by convex optimization. In IEEE Trans. Autom. Control.