@@ -107,13 +107,13 @@ each other). The following plot shows the use of `plot_inputs='overlay'`
107
107
as well as the ability to reposition the legends using the `legend_map `
108
108
keyword::
109
109
110
- timepts = np.linspace(0, 10, 100)
111
- U = np.vstack([np.sin(timepts), np.cos(2*timepts)])
112
- ct.input_output_response(sys_mimo, timepts, U).plot(
113
- plot_inputs='overlay',
114
- legend_map=np.array([['lower right'], ['lower right']]),
115
- title="I/O response for 2x2 MIMO system " +
116
- "[plot_inputs='overlay', legend_map]")
110
+ timepts = np.linspace(0, 10, 100)
111
+ U = np.vstack([np.sin(timepts), np.cos(2*timepts)])
112
+ ct.input_output_response(sys_mimo, timepts, U).plot(
113
+ plot_inputs='overlay',
114
+ legend_map=np.array([['lower right'], ['lower right']]),
115
+ title="I/O response for 2x2 MIMO system " +
116
+ "[plot_inputs='overlay', legend_map]")
117
117
118
118
.. image :: timeplot-mimo_ioresp-ov_lm.png
119
119
@@ -122,17 +122,17 @@ instead of plotting the outputs on the top and inputs on the bottom, the
122
122
inputs are plotted on the left and outputs on the right, as shown in the
123
123
following figure::
124
124
125
- U1 = np.vstack([np.sin(timepts), np.cos(2*timepts)])
126
- resp1 = ct.input_output_response(sys_mimo, timepts, U1)
125
+ U1 = np.vstack([np.sin(timepts), np.cos(2*timepts)])
126
+ resp1 = ct.input_output_response(sys_mimo, timepts, U1)
127
127
128
- U2 = np.vstack([np.cos(2*timepts), np.sin(timepts)])
129
- resp2 = ct.input_output_response(sys_mimo, timepts, U2)
128
+ U2 = np.vstack([np.cos(2*timepts), np.sin(timepts)])
129
+ resp2 = ct.input_output_response(sys_mimo, timepts, U2)
130
130
131
- ct.combine_time_responses(
132
- [resp1, resp2], trace_labels=["Scenario #1", "Scenario #2"]).plot(
133
- transpose=True,
134
- title="I/O responses for 2x2 MIMO system, multiple traces "
135
- "[transpose]")
131
+ ct.combine_time_responses(
132
+ [resp1, resp2], trace_labels=["Scenario #1", "Scenario #2"]).plot(
133
+ transpose=True,
134
+ title="I/O responses for 2x2 MIMO system, multiple traces "
135
+ "[transpose]")
136
136
137
137
.. image :: timeplot-mimo_ioresp-mt_tr.png
138
138
@@ -146,11 +146,11 @@ Additional customization is possible using the `input_props`,
146
146
`output_props `, and `trace_props ` keywords to set complementary line colors
147
147
and styles for various signals and traces::
148
148
149
- out = ct.step_response(sys_mimo).plot(
150
- plot_inputs='overlay', overlay_signals=True, overlay_traces=True,
151
- output_props=[{'color': c} for c in ['blue', 'orange']],
152
- input_props=[{'color': c} for c in ['red', 'green']],
153
- trace_props=[{'linestyle': s} for s in ['-', '--']])
149
+ out = ct.step_response(sys_mimo).plot(
150
+ plot_inputs='overlay', overlay_signals=True, overlay_traces=True,
151
+ output_props=[{'color': c} for c in ['blue', 'orange']],
152
+ input_props=[{'color': c} for c in ['red', 'green']],
153
+ trace_props=[{'linestyle': s} for s in ['-', '--']])
154
154
155
155
.. image :: timeplot-mimo_step-linestyle.png
156
156
@@ -196,7 +196,7 @@ overlaying the inputs or outputs::
196
196
197
197
.. image :: freqplot-mimo_bode-magonly.png
198
198
199
- The :func: `~ct .singular_values_response ` function can be used to
199
+ The :func: `~control .singular_values_response ` function can be used to
200
200
generate Bode plots that show the singular values of a transfer
201
201
function::
202
202
@@ -213,16 +213,69 @@ plot, use `plot_type='nichols'`::
213
213
.. image :: freqplot-siso_nichols-default.png
214
214
215
215
Another response function that can be used to generate Bode plots is
216
- the :func: `~ct .gangof4 ` function, which computes the four primary
216
+ the :func: `~control .gangof4 ` function, which computes the four primary
217
217
sensitivity functions for a feedback control system in standard form::
218
218
219
- proc = ct.tf([1], [1, 1, 1], name="process")
220
- ctrl = ct.tf([100], [1, 5], name="control")
221
- response = rect.gangof4_response(proc, ctrl)
222
- ct.bode_plot(response) # or response.plot()
219
+ proc = ct.tf([1], [1, 1, 1], name="process")
220
+ ctrl = ct.tf([100], [1, 5], name="control")
221
+ response = rect.gangof4_response(proc, ctrl)
222
+ ct.bode_plot(response) # or response.plot()
223
223
224
224
.. image :: freqplot-gangof4.png
225
225
226
+ Nyquist analysys can be done using the :func: `~control.nyquist_response `
227
+ function, which evaluates an LTI system along the Nyquist contour, and
228
+ the :func: `~control.nyquist_plot ` function, which generates a Nyquist plot::
229
+
230
+ sys = ct.tf([1, 0.2], [1, 1, 3, 1, 1], name='sys')
231
+ nyquist_plot(sys)
232
+
233
+ .. image :: freqplot-nyquist-default.png
234
+
235
+ The :func: `~control.nyquist_response ` function can be used to compute
236
+ the number of encirclement of the -1 point and can return the Nyquist
237
+ contour that was used to generate the Nyquist curve.
238
+
239
+ By default, the Nyquist response will generate small semicircles around
240
+ poles that are on the imaginary axis. In addition, portions of the Nyquist
241
+ curve that far from the origin are scaled to a maximum value, with the line
242
+ style is changed to reflect the scaling, and it is possible to offset the
243
+ scaled portions to separate out the portions of the Nyquist curve at
244
+ <math>\i nfty</math>. A number of keyword parameters for both are available
245
+ for :func: `~control.nyquist_response`and :func:`~control.nyquist_plot ` to
246
+ tune the computation of the Nyquist curve and the way the data are
247
+ plotted::
248
+
249
+ sys = ct.tf([1, 0.2], [1, 0, 1]) * ct.tf([1], [1, 0])
250
+ nyqresp = ct.nyquist_response(sys)
251
+ nyqresp.plot(
252
+ max_curve_magnitude=6, max_curve_offset=1,
253
+ arrows=[0, 0.15, 0.3, 0.6, 0.7, 0.925], label='sys')
254
+ print("Encirclements =", nyqresp.count)
255
+
256
+ .. image :: freqplot-nyquist-custom.png
257
+
258
+ All frequency domain plotting functions will automatically compute the
259
+ range of frequencies to plot based on the poles and zeros of the frequency
260
+ response. Frequency points can be explicitly specified by including an
261
+ array of frequencies as a second argument (after the list of systems)::
262
+
263
+ sys1 = ct.tf([1], [1, 2, 1], name='sys1')
264
+ sys2 = ct.tf([1, 0.2], [1, 1, 3, 1, 1], name='sys2')
265
+ omega = np.logspace(-2, 2, 500)
266
+ ct.frequency_response([sys1, sys2], omega).plot(initial_phase=0)
267
+
268
+ .. image :: freqplot-siso_bode-omega.png
269
+
270
+ Alternatively. frequency ranges can be specified by passing a list of the
271
+ form ``[wmin, wmax] ``, where ``wmin `` and ``wmax `` are the minimum and
272
+ maximum frequencies in the (log-spaced) frequency range::
273
+
274
+ response = ct.frequency_response([sys1, sys2], [1e-2, 1e2])
275
+
276
+ The number of (log-spaced) points in the frequency can be specified using
277
+ the ``omega_num `` keyword parameter.
278
+
226
279
227
280
Pole/zero data
228
281
==============
@@ -288,7 +341,7 @@ The default method for generating a phase plane plot is to provide a
288
341
2D dynamical system along with a range of coordinates and time limit::
289
342
290
343
sys = ct.nlsys(
291
- lambda t, x, u, params: np.array([[0, 1], [-1, -1]]) @ x,
344
+ lambda t, x, u, params: np.array([[0, 1], [-1, -1]]) @ x,
292
345
states=['position', 'velocity'], inputs=0, name='damped oscillator')
293
346
axis_limits = [-1, 1, -1, 1]
294
347
T = 8
@@ -310,15 +363,15 @@ an inverted pendulum system, which is created using a mesh grid::
310
363
m, l, b, g = params['m'], params['l'], params['b'], params['g']
311
364
return [x[1], -b/m * x[1] + (g * l / m) * np.sin(x[0]) + u[0]/m]
312
365
invpend = ct.nlsys(invpend_update, states=2, inputs=1, name='invpend')
313
-
366
+
314
367
ct.phase_plane_plot(
315
368
invpend, [-2*pi, 2*pi, -2, 2], 5,
316
369
gridtype='meshgrid', gridspec=[5, 8], arrows=3,
317
370
plot_equilpoints={'gridspec': [12, 9]},
318
371
params={'m': 1, 'l': 1, 'b': 0.2, 'g': 1})
319
372
plt.xlabel(r"$\theta$ [rad]")
320
373
plt.ylabel(r"$\dot\theta$ [rad/sec]")
321
-
374
+
322
375
.. image :: phaseplot-invpend-meshgrid.png
323
376
324
377
This figure shows several features of more complex phase plane plots:
@@ -341,7 +394,7 @@ are part of the :mod:`~control.phaseplot` (pp) module::
341
394
-x[0] + x[1] * (1 - x[0]**2 - x[1]**2)]
342
395
oscillator = ct.nlsys(
343
396
oscillator_update, states=2, inputs=0, name='nonlinear oscillator')
344
-
397
+
345
398
ct.phase_plane_plot(oscillator, [-1.5, 1.5, -1.5, 1.5], 0.9)
346
399
pp.streamlines(
347
400
oscillator, np.array([[0, 0]]), 1.5,
0 commit comments