forked from python-control/python-control
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathplot_gallery.py
More file actions
163 lines (142 loc) · 5.42 KB
/
plot_gallery.py
File metadata and controls
163 lines (142 loc) · 5.42 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
# plot_gallery.py - different types of plots for comparing versions
# RMM, 19 Jun 2024
#
# This file collects together some of the more interesting plots that can
# be generated by python-control and puts them into a PDF file that can be
# used to compare what things look like between different versions of the
# library. It is mainly intended for uses by developers to make sure there
# are no unexpected changes in plot formats, but also has some interest
# examples of things you can plot.
import os
import sys
from math import pi
import matplotlib.pyplot as plt
import numpy as np
import control as ct
# Don't save figures if we are running CI tests
savefigs = 'PYCONTROL_TEST_EXAMPLES' not in os.environ
if savefigs:
# Create a pdf file for storing the results
import subprocess
from matplotlib.backends.backend_pdf import PdfPages
from datetime import date
git_info = subprocess.check_output(['git', 'describe'], text=True).strip()
pdf = PdfPages(
f'plot_gallery-{git_info}-{date.today().isoformat()}.pdf')
# Context manager to handle plotting
class create_figure(object):
def __init__(self, name):
self.name = name
def __enter__(self):
self.fig = plt.figure()
print(f"Generating {self.name} as Figure {self.fig.number}")
return self.fig
def __exit__(self, type, value, traceback):
if type is not None:
print(f"Exception: {type=}, {value=}, {traceback=}")
if savefigs:
pdf.savefig()
if hasattr(sys, 'ps1'):
# Show the figures on the screen
plt.show(block=False)
else:
plt.close()
# Define systems to use throughout
sys1 = ct.tf([1], [1, 2, 1], name='sys1')
sys2 = ct.tf([1, 0.2], [1, 1, 3, 1, 1], name='sys2')
sys_mimo1 = ct.tf2ss(
[[[1], [0.1]], [[0.2], [1]]],
[[[1, 0.6, 1], [1, 1, 1]], [[1, 0.4, 1], [1, 2, 1]]], name="sys_mimo1")
sys_mimo2 = ct.tf2ss(
[[[1], [0.1]], [[0.2], [1]]],
[[[1, 0.2, 1], [1, 24, 22, 5]], [[1, 4, 16, 21], [1, 0.1]]],
name="sys_mimo2")
sys_frd = ct.frd(
[[np.array([10 + 0j, 5 - 5j, 1 - 1j, 0.5 - 1j, -.1j]),
np.array([1j, 0.5 - 0.5j, -0.5, 0.1 - 0.1j, -.05j]) * 0.1],
[np.array([10 + 0j, -20j, -10, 2j, 1]),
np.array([10 + 0j, 5 - 5j, 1 - 1j, 0.5 - 1j, -.1j]) * 0.01]],
np.logspace(-2, 2, 5))
sys_frd.name = 'frd' # For backward compatibility
# Close all existing figures
plt.close('all')
# bode
with create_figure("Bode plot"):
try:
ct.bode_plot([sys_mimo1, sys_mimo2])
except AttributeError:
print(" - falling back to earlier method")
plt.clf()
ct.bode_plot(sys_mimo1)
ct.bode_plot(sys_mimo2)
# describing function
with create_figure("Describing function plot"):
H = ct.tf([1], [1, 2, 2, 1]) * 8
F = ct.descfcn.saturation_nonlinearity(1)
amp = np.linspace(1, 4, 10)
ct.describing_function_response(H, F, amp).plot()
# nichols
with create_figure("Nichols chart"):
response = ct.frequency_response([sys1, sys2])
ct.nichols_plot(response)
# nyquist
with create_figure("Nyquist plot"):
ct.nyquist_plot([sys1, sys2])
# phase plane
with create_figure("Phase plane plot"):
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*pi, 2*pi, -2, 2], 5,
gridtype='meshgrid', gridspec=[5, 8], arrows=3,
plot_separatrices={'gridspec': [12, 9]},
params={'m': 1, 'l': 1, 'b': 0.2, 'g': 1})
# pole zero map
with create_figure("Pole/zero map"):
T = ct.tf(
[-9.0250000e-01, -4.7200750e+01, -8.6812900e+02,
+5.6261850e+03, +2.1258472e+05, +8.4724600e+05,
+1.0192000e+06, +2.3520000e+05],
[9.02500000e-03, 9.92862812e-01, 4.96974094e+01,
1.35705659e+03, 2.09294163e+04, 1.64898435e+05,
6.54572220e+05, 1.25274600e+06, 1.02420000e+06,
2.35200000e+05], name='T')
ct.pole_zero_plot([T, sys2])
# root locus
with create_figure("Root locus plot") as fig:
ax_array = ct.pole_zero_subplots(2, 1, grid=[True, False])
ax1, ax2 = ax_array[:, 0]
sys1 = ct.tf([1, 2], [1, 2, 3], name='sys1')
sys2 = ct.tf([1, 0.2], [1, 1, 3, 1, 1], name='sys2')
ct.root_locus_plot([sys1, sys2], ax=ax1)
ct.root_locus_plot([sys1, sys2], ax=ax2)
plt.suptitle("Root locus plots (w/ specified axes)", fontsize='medium')
# sisotool
with create_figure("sisotool"):
s = ct.tf('s')
H = (s+0.3)/(s**4 + 4*s**3 + 6.25*s**2)
ct.sisotool(H)
# step response
with create_figure("step response") as fig:
try:
ct.step_response([sys_mimo1, sys_mimo2]).plot()
except ValueError:
print(" - falling back to earlier method")
fig.clf()
ct.step_response(sys_mimo1).plot()
ct.step_response(sys_mimo2).plot()
# time response
with create_figure("time response"):
timepts = np.linspace(0, 10)
U = np.vstack([np.sin(timepts), np.cos(2*timepts)])
resp1 = ct.input_output_response(sys_mimo1, timepts, U)
U = np.vstack([np.cos(2*timepts), np.sin(timepts)])
resp2 = ct.input_output_response(sys_mimo1, timepts, U)
resp = ct.combine_time_responses(
[resp1, resp2], trace_labels=["resp1", "resp2"])
resp.plot(transpose=True)
# Show the figures if running in interactive mode
if savefigs:
pdf.close()