diff --git a/control/flatsys/linflat.py b/control/flatsys/linflat.py index 9320eec3a..e03df514d 100644 --- a/control/flatsys/linflat.py +++ b/control/flatsys/linflat.py @@ -119,10 +119,10 @@ def forward(self, x, u, params): x = np.reshape(x, (-1, 1)) u = np.reshape(u, (1, -1)) zflag = [np.zeros(self.nstates + 1)] - zflag[0][0] = self.Cf @ x + zflag[0][0] = (self.Cf @ x).item() H = self.Cf # initial state transformation for i in range(1, self.nstates + 1): - zflag[0][i] = H @ (self.A @ x + self.B @ u) + zflag[0][i] = (H @ (self.A @ x + self.B @ u)).item() H = H @ self.A # derivative for next iteration return zflag diff --git a/control/freqplot.py b/control/freqplot.py index 533515415..6c07b178f 100644 --- a/control/freqplot.py +++ b/control/freqplot.py @@ -473,7 +473,7 @@ def bode_plot( if ax is None: with plt.rc_context(_freqplot_rcParams): ax_array = fig.subplots(nrows, ncols, squeeze=False) - fig.set_tight_layout(True) + fig.set_layout_engine('tight') fig.align_labels() # Set up default sharing of axis limits if not specified diff --git a/control/optimal.py b/control/optimal.py index 81372705c..ce80eccfc 100644 --- a/control/optimal.py +++ b/control/optimal.py @@ -956,7 +956,7 @@ def solve_ocp( transpose=None, return_states=True, print_summary=True, log=False, **kwargs): - """Compute the solution to an optimal control problem. + r"""Compute the solution to an optimal control problem. The optimal trajectory (states and inputs) is computed so as to approximately mimimize a cost function of the following form (for diff --git a/control/robust.py b/control/robust.py index a0e53d199..75930e59e 100644 --- a/control/robust.py +++ b/control/robust.py @@ -41,6 +41,7 @@ # External packages and modules import numpy as np +import warnings from .exception import * from .statesp import StateSpace from .statefbk import * @@ -357,7 +358,12 @@ def augw(g, w1=None, w2=None, w3=None): # output indices oi = np.arange(1, 1 + now1 + now2 + now3 + ny) - p = connect(sysall, q, ii, oi) + # Filter out known warning due to use of connect + with warnings.catch_warnings(): + warnings.filterwarnings( + 'ignore', message="`connect`", category=DeprecationWarning) + + p = connect(sysall, q, ii, oi) return p diff --git a/control/sisotool.py b/control/sisotool.py index 9af5268b9..79069162d 100644 --- a/control/sisotool.py +++ b/control/sisotool.py @@ -186,7 +186,11 @@ def _SisotoolUpdate(sys, fig, K, bode_plot_params, tvect=None): sys_closed = append(sys, -K) connects = [[1, 3], [3, 1]] - sys_closed = connect(sys_closed, connects, 2, 2) + # Filter out known warning due to use of connect + with warnings.catch_warnings(): + warnings.filterwarnings( + 'ignore', message="`connect`", category=DeprecationWarning) + sys_closed = connect(sys_closed, connects, 2, 2) if tvect is None: tvect, yout = step_response(sys_closed, T_num=100) else: diff --git a/control/stochsys.py b/control/stochsys.py index b1bb6ef46..fe11a4fb5 100644 --- a/control/stochsys.py +++ b/control/stochsys.py @@ -183,14 +183,14 @@ def lqe(*args, **kwargs): # contributed by Sawyer B. Fuller def dlqe(*args, **kwargs): - """dlqe(A, G, C, QN, RN, [, N]) + r"""dlqe(A, G, C, QN, RN, [, N]) Linear quadratic estimator design (Kalman filter) for discrete-time systems. Given the system .. math:: - x[n+1] &= Ax[n] + Bu[n] + Gw[n] \\\\ + x[n+1] &= Ax[n] + Bu[n] + Gw[n] \\ y[n] &= Cx[n] + Du[n] + v[n] with unbiased process noise w and measurement noise v with covariances diff --git a/control/tests/flatsys_test.py b/control/tests/flatsys_test.py index 10a5d1bc5..a12bf1480 100644 --- a/control/tests/flatsys_test.py +++ b/control/tests/flatsys_test.py @@ -194,14 +194,17 @@ def test_kinematic_car_ocp( else: initial_guess = None - # Solve the optimal trajectory - traj_ocp = fs.solve_flat_ocp( - vehicle_flat, timepts, x0, u0, - cost=traj_cost, constraints=input_constraints, - terminal_cost=terminal_cost, basis=basis, - initial_guess=initial_guess, - minimize_kwargs={'method': method}, - ) + # Solve the optimal trajectory (allow warnings) + with warnings.catch_warnings(): + warnings.filterwarnings( + 'ignore', message="unable to solve", category=UserWarning) + traj_ocp = fs.solve_flat_ocp( + vehicle_flat, timepts, x0, u0, + cost=traj_cost, constraints=input_constraints, + terminal_cost=terminal_cost, basis=basis, + initial_guess=initial_guess, + minimize_kwargs={'method': method}, + ) xd, ud = traj_ocp.eval(timepts) if not traj_ocp.success: diff --git a/control/tests/freqresp_test.py b/control/tests/freqresp_test.py index f452fe7df..dd236769a 100644 --- a/control/tests/freqresp_test.py +++ b/control/tests/freqresp_test.py @@ -16,7 +16,8 @@ from control.statesp import StateSpace from control.xferfcn import TransferFunction from control.matlab import ss, tf, bode, rss -from control.freqplot import bode_plot, nyquist_plot, singular_values_plot +from control.freqplot import bode_plot, nyquist_plot, nyquist_response, \ + singular_values_plot, singular_values_response from control.tests.conftest import slycotonly pytestmark = pytest.mark.usefixtures("mplcleanup") @@ -97,9 +98,8 @@ def test_nyquist_basic(ss_siso): tf_siso = tf(ss_siso) nyquist_plot(ss_siso) nyquist_plot(tf_siso) - count, contour = nyquist_plot( - tf_siso, plot=False, return_contour=True, omega_num=20) - assert len(contour) == 20 + response = nyquist_response(tf_siso, omega_num=20) + assert len(response.contour) == 20 with pytest.warns(UserWarning, match="encirclements was a non-integer"): count, contour = nyquist_plot( @@ -107,9 +107,8 @@ def test_nyquist_basic(ss_siso): assert_allclose(contour[0], 1j) assert_allclose(contour[-1], 100j) - count, contour = nyquist_plot( - tf_siso, plot=False, omega=np.logspace(-1, 1, 10), return_contour=True) - assert len(contour) == 10 + response = nyquist_response(tf_siso, omega=np.logspace(-1, 1, 10)) + assert len(response.contour) == 10 @pytest.mark.usefixtures("legacy_plot_signature") @@ -200,7 +199,7 @@ def test_bode_margin(dB, maginfty1, maginfty2, gminv, den = [1, 25, 100, 0] sys = ctrl.tf(num, den) plt.figure() - ctrl.bode_plot(sys, margins=True, dB=dB, deg=deg, Hz=Hz) + ctrl.bode_plot(sys, display_margins=True, dB=dB, deg=deg, Hz=Hz) fig = plt.gcf() allaxes = fig.get_axes() @@ -655,7 +654,8 @@ def tsystem(request, ss_mimo_ct, ss_miso_ct, ss_simo_ct, ss_siso_ct, ss_mimo_dt) def test_singular_values_plot(tsystem): sys = tsystem.sys for omega_ref, sigma_ref in zip(tsystem.omegas, tsystem.sigmas): - sigma, _ = singular_values_plot(sys, omega_ref, plot=False) + response = singular_values_response(sys, omega_ref) + sigma = np.real(response.fresp[:, 0, :]) np.testing.assert_almost_equal(sigma, sigma_ref) @@ -663,13 +663,13 @@ def test_singular_values_plot_mpl_base(ss_mimo_ct, ss_mimo_dt): sys_ct = ss_mimo_ct.sys sys_dt = ss_mimo_dt.sys plt.figure() - singular_values_plot(sys_ct, plot=True) + singular_values_plot(sys_ct) fig = plt.gcf() allaxes = fig.get_axes() assert(len(allaxes) == 1) assert(allaxes[0].get_label() == 'control-sigma') plt.figure() - singular_values_plot([sys_ct, sys_dt], plot=True, Hz=True, dB=True, grid=False) + singular_values_plot([sys_ct, sys_dt], Hz=True, dB=True, grid=False) fig = plt.gcf() allaxes = fig.get_axes() assert(len(allaxes) == 1) @@ -679,10 +679,10 @@ def test_singular_values_plot_mpl_base(ss_mimo_ct, ss_mimo_dt): def test_singular_values_plot_mpl_superimpose_nyq(ss_mimo_ct, ss_mimo_dt): sys_ct = ss_mimo_ct.sys sys_dt = ss_mimo_dt.sys - omega_all = np.logspace(-3, 2, 1000) + omega_all = np.logspace(-3, int(math.log10(2 * math.pi/sys_dt.dt)), 1000) plt.figure() - singular_values_plot(sys_ct, omega_all, plot=True) - singular_values_plot(sys_dt, omega_all, plot=True) + singular_values_plot(sys_ct, omega_all) + singular_values_plot(sys_dt, omega_all) fig = plt.gcf() allaxes = fig.get_axes() assert(len(allaxes) == 1) diff --git a/control/tests/optimal_test.py b/control/tests/optimal_test.py index d4a2b1d8c..f746db7d5 100644 --- a/control/tests/optimal_test.py +++ b/control/tests/optimal_test.py @@ -745,12 +745,15 @@ def vehicle_output(t, x, u, params): initial_guess = (state_guess, input_guess) # Solve the optimal control problem - result = opt.solve_ocp( - vehicle, timepts, x0, traj_cost, constraints, - terminal_cost=term_cost, initial_guess=initial_guess, - trajectory_method=method, - # minimize_method='COBYLA', # SLSQP', - ) + with warnings.catch_warnings(): + warnings.filterwarnings( + 'ignore', message="unable to solve", category=UserWarning) + result = opt.solve_ocp( + vehicle, timepts, x0, traj_cost, constraints, + terminal_cost=term_cost, initial_guess=initial_guess, + trajectory_method=method, + # minimize_method='COBYLA', # SLSQP', + ) if fail == 'xfail': assert not result.success diff --git a/control/tests/sisotool_test.py b/control/tests/sisotool_test.py index 5a86c73d0..3dd9168a2 100644 --- a/control/tests/sisotool_test.py +++ b/control/tests/sisotool_test.py @@ -79,8 +79,7 @@ def test_sisotool(self, tsys): 'omega_limits': None, 'omega_num': None, 'ax': np.array([[ax_mag], [ax_phase]]), - 'margins': True, - 'margin_info': True, + 'display_margins': 'overlay', } # Check that the xaxes of the bode plot are shared before the rlocus click diff --git a/control/timeplot.py b/control/timeplot.py index 169e73431..58f7d8382 100644 --- a/control/timeplot.py +++ b/control/timeplot.py @@ -292,7 +292,7 @@ def time_response_plot( if ax is None: with plt.rc_context(timeplot_rcParams): ax_array = fig.subplots(nrows, ncols, sharex=True, squeeze=False) - fig.set_tight_layout(True) + fig.set_layout_engine('tight') fig.align_labels() else: diff --git a/control/timeresp.py b/control/timeresp.py index bc13ad0d1..f431e6b33 100644 --- a/control/timeresp.py +++ b/control/timeresp.py @@ -1084,7 +1084,7 @@ def forced_response(sys, T=None, U=0., X0=0., transpose=False, if U.ndim == 1: U = U.reshape(1, -1) # pylint: disable=E1103 - # Algorithm: to integrate from time 0 to time dt, with linear + # Algorithm: to integrate from time 0 to time dt, with linear # interpolation between inputs u(0) = u0 and u(dt) = u1, we solve # xdot = A x + B u, x(0) = x0 # udot = (u1 - u0) / dt, u(0) = u0. diff --git a/control/xferfcn.py b/control/xferfcn.py index 206f0bc92..099f64258 100644 --- a/control/xferfcn.py +++ b/control/xferfcn.py @@ -468,7 +468,13 @@ def __str__(self, var=None): numstr = _tf_polynomial_to_string(self.num[no][ni], var=var) denstr = _tf_polynomial_to_string(self.den[no][ni], var=var) elif self.display_format == 'zpk': - z, p, k = tf2zpk(self.num[no][ni], self.den[no][ni]) + num = self.num[no][ni] + if num.size == 1 and num.item() == 0: + # Catch a special case that SciPy doesn't handle + z, p, k = tf2zpk([1.], self.den[no][ni]) + k = 0 + else: + z, p, k = tf2zpk(self.num[no][ni], self.den[no][ni]) numstr = _tf_factorized_polynomial_to_string( z, gain=k, var=var) denstr = _tf_factorized_polynomial_to_string(p, var=var)