Processing math: 100%

Control

Convex optimization can be used to solve many problems that arise in control. In this example we show how to solve such a problem using CVXPY. We have a system with a state xtRn that varies over the time steps t=0,,T, and inputs or actions utRm we can use at each time step to affect the state. For example, xt might be the position and velocity of a rocket and ut the output of the rocket's thrusters. We model the evolution of the state as a linear dynamical system, i.e.,

xt+1=Axt+But

where ARn×n and BRn×m are known matrices.

Our goal is to find the optimal actions u0,,uT1 by solving the optimization problems

minimizeT1t=0(xt,ut)+T(xT)subject toxt+1=Axt+But(xt,ut)C,xTCT,

where :Rn×RmR is the stage cost, T is the terminal cost, C is the state/action constraints, and CT is the terminal constraint. The optimization problem is convex if the costs and constraints are convex.

Example

In the following code we solve a control problem with n=8 states, m=2 inputs, and horizon T=50. The matrices A and B and the initial state x0 are randomly chosen (with AI). We use the (traditional) stage cost (x,u)=x22+u22, the input constraint ut1, and the terminal constraint xT=0.

In [1]:
# Generate data for control problem.
import numpy as np
np.random.seed(1)
n = 8
m = 2
T = 50
alpha = 0.2
beta = 5
A = np.eye(n) + alpha*np.random.randn(n,n)
B = np.random.randn(n,m)
x_0 = beta*np.random.randn(n,1)
In [2]:
# Form and solve control problem.
from cvxpy import *
x = Variable(n, T+1)
u = Variable(m, T)

states = []
for t in range(T):
    cost = sum_squares(x[:,t+1]) + sum_squares(u[:,t])
    constr = [x[:,t+1] == A*x[:,t] + B*u[:,t],
              norm(u[:,t], 'inf') <= 1]
    states.append( Problem(Minimize(cost), constr) )
# sums problem objectives and concatenates constraints.
prob = sum(states)
prob.constraints += [x[:,T] == 0, x[:,0] == x_0]
prob.solve()
Out[2]:
64470.574741668934

We display the results below as a 4-high stack of plots showing u1, u2, x1, and x2 vs t. Notice that ut is saturated (i.e., ut=1) over more than half of the horizon, which shows that the input constraint is meaningful.

In [3]:
# Plot results.
import matplotlib.pyplot as plt
%matplotlib inline
%config InlineBackend.figure_format = 'svg'

f = plt.figure()

# Plot (u_t)_1.
ax = f.add_subplot(411)
plt.plot(u[0,:].value.A.flatten())
plt.ylabel(r"$(u_t)_1$", fontsize=16)
plt.yticks(np.linspace(-1.0, 1.0, 3))
plt.xticks([])

# Plot (u_t)_2.
plt.subplot(4,1,2)
plt.plot(u[1,:].value.A.flatten())
plt.ylabel(r"$(u_t)_2$", fontsize=16)
plt.yticks(np.linspace(-1, 1, 3))
plt.xticks([])

# Plot (x_t)_1.
plt.subplot(4,1,3)
x1 = x[0,:].value.A.flatten()
plt.plot(x1)
plt.ylabel(r"$(x_t)_1$", fontsize=16)
plt.yticks([-10, 0, 10])
plt.ylim([-10, 10])
plt.xticks([])

# Plot (x_t)_2.
plt.subplot(4,1,4)
x2 = x[1,:].value.A.flatten()
plt.plot(range(51), x2)
plt.yticks([-25, 0, 25])
plt.ylim([-25, 25])
plt.ylabel(r"$(x_t)_2$", fontsize=16)
plt.xlabel(r"$t$", fontsize=16)
plt.tight_layout()
plt.show()