.. currentmodule:: control
Insight into nonlinear systems can often be obtained by looking at phase plane diagrams. The :func:`phase_plane_plot` function allows the creation of a 2-dimensional phase plane diagram for a system. This functionality is supported by a set of mapping functions that are part of the phaseplot module.
The default method for generating a phase plane plot is to provide a 2D dynamical system along with a range of coordinates in phase space:
.. testsetup:: phaseplot
import matplotlib.pyplot as plt
plt.close('all')
.. testcode:: phaseplot
def sys_update(t, x, u, params):
return np.array([[0, 1], [-1, -1]]) @ x
sys = ct.nlsys(
sys_update, states=['position', 'velocity'],
inputs=0, name='damped oscillator')
axis_limits = [-1, 1, -1, 1]
ct.phase_plane_plot(sys, axis_limits)
.. testcode:: phaseplot
:hide:
import matplotlib.pyplot as plt
plt.savefig('figures/phaseplot-dampedosc-default.png')
By default the plot includes streamlines infered from function values on a grid, equilibrium points and separatrices if they exist. A variety of options are available to modify the information that is plotted, including plotting a grid of vectors instead of streamlines, plotting streamlines from arbitrary starting points and turning on and off various features of the plot.
To illustrate some of these possibilities, consider a phase plane plot for an inverted pendulum system, which is created using a mesh grid:
.. testcode:: phaseplot
:hide:
plt.figure()
.. testcode:: phaseplot
def invpend_update(t, x, u, params):
m, l, b, g = params['m'], params['l'], params['b'], params['g']
return [x[1], -b/m * x[1] + (g * l / m) * np.sin(x[0]) + u[0]/m]
invpend = ct.nlsys(invpend_update, states=2, inputs=1, name='invpend')
ct.phase_plane_plot(
invpend, [-2 * np.pi, 2 * np.pi, -2, 2],
params={'m': 1, 'l': 1, 'b': 0.2, 'g': 1})
plt.xlabel(r"$\theta$ [rad]")
plt.ylabel(r"$\dot\theta$ [rad/sec]")
.. testcode:: phaseplot
:hide:
plt.savefig('figures/phaseplot-invpend-meshgrid.png')
This figure shows several features of more complex phase plane plots: multiple equilibrium points are shown, with saddle points showing separatrices, and streamlines generated generated from a rectangular 25x25 grid (default) of function evaluations. Together, the multiple features in the phase plane plot give a good global picture of the topological structure of solutions of the dynamical system.
Phase plots can be built up by hand using a variety of helper functions that are part of the :mod:`phaseplot` (pp) module. For more precise control, the streamlines can also generated by integrating the system forwards or backwards in time from a set of initial conditions. The initial conditions can be chosen on a rectangular grid, rectangual boundary, circle or from an arbitrary set of points.
.. testcode:: phaseplot
:hide:
plt.figure()
.. testcode:: phaseplot
import control.phaseplot as pp
def oscillator_update(t, x, u, params):
return [x[1] + x[0] * (1 - x[0]**2 - x[1]**2),
-x[0] + x[1] * (1 - x[0]**2 - x[1]**2)]
oscillator = ct.nlsys(
oscillator_update, states=2, inputs=0, name='nonlinear oscillator')
ct.phase_plane_plot(oscillator, [-1.5, 1.5, -1.5, 1.5], 0.9,
plot_streamlines=True)
pp.streamlines(
oscillator, np.array([[0, 0]]), 1.5,
gridtype='circlegrid', gridspec=[0.5, 6], dir='both')
pp.streamlines(
oscillator, np.array([[1, 0]]), 2 * np.pi, arrows=6, color='b')
plt.gca().set_aspect('equal')
.. testcode:: phaseplot
:hide:
plt.savefig('figures/phaseplot-oscillator-helpers.png')
The following helper functions are available:
.. autosummary:: phaseplot.equilpoints phaseplot.separatrices phaseplot.streamlines phaseplot.streamplot phaseplot.vectorfield
The :func:`phase_plane_plot` function calls these helper functions based on the options it is passed.
Note that unlike other plotting functions, phase plane plots do not
involve computing a response and then plotting the result via a
plot() method. Instead, the plot is generated directly be a call
to the :func:`phase_plane_plot` function (or one of the
:mod:`~control.phaseplot` helper functions).


