7. Stochastic Systems
The Python Control Systems Library has support for basic operations involving linear and nonlinear I/O systems with Gaussian white noise as an input.
7.1. Stochastic Signals
A stochastic signal is a representation of the output of a random process. NumPy and SciPy have a functions to calculate the covariance and correlation of random signals:
numpy.cov()
- with a single argument, returns the sample variance of a vector random variablewhere the input argument represents samples of
. With two arguments, returns the (cross-)covariance of random variables
and
where the input arguments represent samples of the given random variables.
scipy.signal.correlate()
- the “cross-correlation” between two random (1D) sequences. If these sequences came from a random process, this is a single sample approximation of the (discrete time) correlation function. Use the functionscipy.signal.correlation_lags()
to compute the lagand
scipy.signal.correlate()
to get the (auto) correlation function.
The python-control package has variants of these functions that do appropriate processing for continuous-time models.
The white_noise()
function generates a (multi-variable) white
noise signal of specified intensity as either a sampled continuous-time
signal or a discrete-time signal. A white noise signal along a 1D
array of linearly spaced set of times timepts
can be computing using
V = ct.white_noise(timepts, Q[, dt])
where Q
is a positive definite matrix providing the noise
intensity and dt
is the sampling time (or 0 for continuous time).
In continuous time, the white noise signal is scaled such that the
integral of the covariance over a sample period is Q
, thus
approximating a white noise signal. In discrete time, the white noise
signal has covariance Q
at each point in time (without any
scaling based on the sample time).
The python-control correlation()
function computes the
correlation matrix or
the cross-correlation matrix
, where
represents expectation:
tau, Rtau = ct.correlation(timepts, X[, Y])
The signal X
(and Y
, if present) represents a continuous or
discrete-time signal sampled at regularly spaced times timepts
. The
return value provides the correlation between
and
at a set of time offsets
(determined based on the spacing of entries in the
timepts
vector.
Note that the computation of the correlation function is based on a single time signal (or pair of time signals) and is thus a very crude approximation to the true correlation function between two random processes.
To compute the response of a linear (or nonlinear) system to a white
noise input, use the forced_response()
(or
input_output_response()
) function:
a, c = 1, 1
sys = ct.ss([[-a]], [[1]], [[c]], 0, name='sys')
timepts = np.linspace(0, 5, 1000)
Q = np.array([[0.1]])
V = ct.white_noise(timepts, Q)
resp = ct.forced_response(sys, timepts, V)
resp.plot()

The correlation function for the output can be computed using the
correlation()
function and compared to the analytical expression:
tau, r_Y = ct.correlation(timepts, resp.outputs)
plt.plot(tau, r_Y, label='empirical')
plt.plot(
tau, c**2 * Q.item() / (2 * a) * np.exp(-a * np.abs(tau)),
label='approximation')
plt.xlabel(r"$\tau$")
plt.ylabel(r"$r_\tau$")
plt.title(f"Output correlation for {sys.name}")
plt.legend()

7.2. Linear Quadratic Estimation (Kalman Filter)
A standard application of stochastic linear systems is the computation
of the optimal linear estimator under the assumption of white Gaussian
measurement and process noise. This estimator is called the linear
quadratic estimator (LQE) and its gains can be computed using the
lqe()
function.
We consider a continuous-time, state space system
with unbiased process noise and measurement noise
with covariances satisfying
where represents expectation.
The lqe()
function computes the observer gain matrix
such that the stationary (non-time-varying) Kalman filter
produces a state estimate that minimizes the expected
squared error using the sensor measurements
.
As with the lqr()
function, the lqe()
function can be
called in several forms:
L, P, E = lqe(sys, QN, RN)
L, P, E = lqe(sys, QN, RN, NN)
L, P, E = lqe(A, G, C, QN, RN)
L, P, E = lqe(A, G, C, QN, RN, NN)
where sys
is an LTI
object, and A
, G
, C
, QN
, RN
,
and NN
are 2D arrays of appropriate dimension. If sys
is a
discrete-time system, the first two forms will compute the discrete
time optimal controller. For the second two forms, the dlqr()
function can be used. Additional arguments and details are given on
the lqr()
and dlqr()
documentation pages.
The create_estimator_iosystem()
function can be used to create
an I/O system implementing a Kalman filter, including integration of
the Riccati ODE. The command has the form
estim = ct.create_estimator_iosystem(sys, Qv, Qw)
The input to the estimator is the measured outputs Y
and the system
input U
. To run the estimator on a noisy signal, use the command
resp = ct.input_output_response(estim, timepts, [Y, U], [X0, P0])
If desired, the correct()
parameter can be set to False
to allow prediction with no additional sensor information:
resp = ct.input_output_response(
estim, timepts, 0, [X0, P0], params={'correct': False})
The create_estimator_iosystem()
and
create_statefbk_iosystem()
functions can be used to combine an
estimator with a state feedback controller:
K, _, _ = ct.lqr(sys, Qx, Qu)
estim = ct.create_estimator_iosystem(sys, Qv, Qw, P0)
ctrl, clsys = ct.create_statefbk_iosystem(sys, K, estimator=estim)
The controller will have the same form as a full state feedback
controller, but with the system state input replaced by the
estimated state
(output of
estim
):
The closed loop controller clsys
includes both the state
feedback and the estimator dynamics and takes as its input the desired
state and input
:
resp = ct.input_output_response(
clsys, timepts, [Xd, Ud], [X0, np.zeros_like(X0), P0])
7.3. Maximum Likelihood Estimation
Consider a nonlinear system with discrete-time dynamics of the form
(1)
where ,
, and
, and
and
represent random processes that are not
necessarily Gaussian white noise processes. The estimation problem that we
wish to solve is to find the estimate
that matches
the measured outputs
with “likely” disturbances and
noise.
For a fixed horizon of length , this problem can be formulated as
an optimization problem where we define the likelihood of a given estimate
(and the resulting noise and disturbances predicted by the model) as a cost
function. Suppose we model the likelihood using a conditional probability
density function
.
Then we can pose the state estimation problem as
(2)
subject to the constraints given by equation (1).
The result of this optimization gives us the estimated state for the
previous steps in time, including the “current” time
. The basic idea is thus to compute the state estimate that is
most consistent with our model and penalize the noise and disturbances
according to how likely they are (based on the given stochastic system
model for each).
Given a solution to this fixed-horizon optimal estimation problem, we
can create an estimator for the state over all times by repeatedly
applying the optimization problem (2) over a moving
horizon. At each time , we take the measurements for the
last
time steps along with the previously estimated state at
the start of the horizon,
and reapply the optimization
in equation (2). This approach is known as a moving
horizon estimator (MHE).
The formulation for the moving horizon estimation problem is very general
and various situations can be captured using the conditional probability
function . We start by
noting that if the disturbances are independent of the underlying states of
the system, we can write the conditional probability as
This expression can be further simplified by taking the log of the expression and maximizing the function
(3)
The first term represents the likelihood of the initial state, the second term captures the likelihood of the noise signal, and the final term captures the likelihood of the disturbances.
If we return to the case where and
are modeled as
Gaussian processes, then it can be shown that maximizing equation
(3) is equivalent to solving the optimization
problem given by
(4)
where ,
, and
are the covariances of the
initial state, disturbances, and measurement noise.
Note that while the optimization is carried out only over the estimated
initial state , the entire history of estimated states can
be reconstructed using the system dynamics:
In particular, we can obtain the estimated state at the end of the moving
horizon window, corresponding to the current time, and we can thus
implement an estimator by repeatedly solving the optimization of a window
of length backwards in time.
The optimal
module described in the Optimization-Based Control
section implements functions for solving optimal estimation problems
using maximum likelihood estimation. The
optimal.OptimalEstimationProblem
class is used to define an
optimal estimation problem over a finite horizon:
oep = opt.OptimalEstimationProblem(sys, timepts, cost[, constraints])
Given noisy measurements and control inputs
, an
estimate of the states over the time points can be computed using the
compute_estimate()
method:
estim = oep.compute_optimal(
Y, U[, initial_state=x0, initial_guess=(xhat, v)])
xhat, v, w = estim.states, estim.inputs, estim.outputs
For discrete-time systems, the
create_mhe_iosystem()
method
can be used to generate an input/output system that implements a
moving horizon estimator.
Several functions are available to help set up standard optimal estimation problems:
|
Create cost function for Gaussian likelihoods. |
|
Create constraint for bounded disturbances. |
7.4. Examples
The following examples illustrate the use of tools from the stochastic systems module. Background information for these examples can be found in the FBS2e supplement on Optimization-Based Control).