@@ -107,13 +107,13 @@ each other). The following plot shows the use of `plot_inputs='overlay'`
107107as well as the ability to reposition the legends using the `legend_map `
108108keyword::
109109
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]")
117117
118118.. image :: timeplot-mimo_ioresp-ov_lm.png
119119
@@ -122,17 +122,17 @@ instead of plotting the outputs on the top and inputs on the bottom, the
122122inputs are plotted on the left and outputs on the right, as shown in the
123123following figure::
124124
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)
127127
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)
130130
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]")
136136
137137.. image :: timeplot-mimo_ioresp-mt_tr.png
138138
@@ -146,11 +146,11 @@ Additional customization is possible using the `input_props`,
146146`output_props `, and `trace_props ` keywords to set complementary line colors
147147and styles for various signals and traces::
148148
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 ['-', '--']])
154154
155155.. image :: timeplot-mimo_step-linestyle.png
156156
@@ -196,7 +196,7 @@ overlaying the inputs or outputs::
196196
197197.. image :: freqplot-mimo_bode-magonly.png
198198
199- The :func: `~ct .singular_values_response ` function can be used to
199+ The :func: `~control .singular_values_response ` function can be used to
200200generate Bode plots that show the singular values of a transfer
201201function::
202202
@@ -213,16 +213,69 @@ plot, use `plot_type='nichols'`::
213213.. image :: freqplot-siso_nichols-default.png
214214
215215Another 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
217217sensitivity functions for a feedback control system in standard form::
218218
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()
223223
224224.. image :: freqplot-gangof4.png
225225
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+
226279
227280Pole/zero data
228281==============
@@ -288,7 +341,7 @@ The default method for generating a phase plane plot is to provide a
2883412D dynamical system along with a range of coordinates and time limit::
289342
290343 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,
292345 states=['position', 'velocity'], inputs=0, name='damped oscillator')
293346 axis_limits = [-1, 1, -1, 1]
294347 T = 8
@@ -310,15 +363,15 @@ an inverted pendulum system, which is created using a mesh grid::
310363 m, l, b, g = params['m'], params['l'], params['b'], params['g']
311364 return [x[1], -b/m * x[1] + (g * l / m) * np.sin(x[0]) + u[0]/m]
312365 invpend = ct.nlsys(invpend_update, states=2, inputs=1, name='invpend')
313-
366+
314367 ct.phase_plane_plot(
315368 invpend, [-2*pi, 2*pi, -2, 2], 5,
316369 gridtype='meshgrid', gridspec=[5, 8], arrows=3,
317370 plot_equilpoints={'gridspec': [12, 9]},
318371 params={'m': 1, 'l': 1, 'b': 0.2, 'g': 1})
319372 plt.xlabel(r"$\theta$ [rad]")
320373 plt.ylabel(r"$\dot\theta$ [rad/sec]")
321-
374+
322375.. image :: phaseplot-invpend-meshgrid.png
323376
324377This figure shows several features of more complex phase plane plots:
@@ -341,7 +394,7 @@ are part of the :mod:`~control.phaseplot` (pp) module::
341394 -x[0] + x[1] * (1 - x[0]**2 - x[1]**2)]
342395 oscillator = ct.nlsys(
343396 oscillator_update, states=2, inputs=0, name='nonlinear oscillator')
344-
397+
345398 ct.phase_plane_plot(oscillator, [-1.5, 1.5, -1.5, 1.5], 0.9)
346399 pp.streamlines(
347400 oscillator, np.array([[0, 0]]), 1.5,
0 commit comments