diff --git a/README.rst b/README.rst index f3e3a13ff..ebcf77c43 100644 --- a/README.rst +++ b/README.rst @@ -22,14 +22,17 @@ Python Control Systems Library The Python Control Systems Library is a Python module that implements basic operations for analysis and design of feedback control systems. - Have a go now! -============== +-------------- Try out the examples in the examples folder using the binder service. .. image:: https://mybinder.org/badge_logo.svg :target: https://mybinder.org/v2/gh/python-control/python-control/HEAD +The package can also be installed on Google Colab using the commands:: + + !pip install control + import control as ct Features -------- @@ -44,7 +47,7 @@ Features - Nonlinear systems: optimization-based control, describing functions, differential flatness Links -===== +----- - Project home page: http://python-control.org - Source code repository: https://github.com/python-control/python-control @@ -52,9 +55,8 @@ Links - Issue tracker: https://github.com/python-control/python-control/issues - Mailing list: http://sourceforge.net/p/python-control/mailman/ - Dependencies -============ +------------ The package requires numpy, scipy, and matplotlib. In addition, some routines use a module called slycot, that is a Python wrapper around some FORTRAN @@ -64,6 +66,7 @@ functionality is limited or absent, and installation of slycot is recommended https://github.com/python-control/Slycot + Installation ============ @@ -73,7 +76,7 @@ Conda and conda-forge The easiest way to get started with the Control Systems library is using `Conda `_. -The Control Systems library has been packages for the `conda-forge +The Control Systems library has packages available using the `conda-forge `_ Conda channel, and as of Slycot version 0.3.4, binaries for that package are available for 64-bit Windows, OSX, and Linux. @@ -83,6 +86,10 @@ conda environment, run:: conda install -c conda-forge control slycot +Mixing packages from conda-forge and the default conda channel can +sometimes cause problems with dependencies, so it is usually best to +instally NumPy, SciPy, and Matplotlib from conda-forge as well. + Pip --- @@ -92,7 +99,8 @@ To install using pip:: pip install control If you install Slycot using pip you'll need a development environment -(e.g., Python development files, C and Fortran compilers). +(e.g., Python development files, C and Fortran compilers). Pip +installation can be particularly complicated for Windows. Installing from source ---------------------- @@ -106,11 +114,13 @@ toplevel `python-control` directory:: Article and Citation Information ================================ -An `article `_ about the library is available on IEEE Explore. If the Python Control Systems Library helped you in your research, please cite:: +An `article `_ about +the library is available on IEEE Explore. If the Python Control Systems Library helped you in your research, please cite:: @inproceedings{python-control2021, title={The Python Control Systems Library (python-control)}, - author={Fuller, Sawyer and Greiner, Ben and Moore, Jason and Murray, Richard and van Paassen, Ren{\'e} and Yorke, Rory}, + author={Fuller, Sawyer and Greiner, Ben and Moore, Jason and + Murray, Richard and van Paassen, Ren{\'e} and Yorke, Rory}, booktitle={60th IEEE Conference on Decision and Control (CDC)}, pages={4875--4881}, year={2021}, diff --git a/control/config.py b/control/config.py index f7f449bb8..f75bd52db 100644 --- a/control/config.py +++ b/control/config.py @@ -76,7 +76,6 @@ def set_defaults(module, **keywords): >>> ct.defaults['freqplot.number_of_samples'] 100 >>> # do some customized freqplotting - >>> ct.reset_defaults() """ if not isinstance(module, str): @@ -209,7 +208,6 @@ def use_matlab_defaults(): -------- >>> ct.use_matlab_defaults() >>> # do some matlab style plotting - >>> ct.reset_defaults() """ set_defaults('freqplot', dB=True, deg=True, Hz=False, grid=True) @@ -229,7 +227,6 @@ def use_fbs_defaults(): -------- >>> ct.use_fbs_defaults() >>> # do some FBS style plotting - >>> ct.reset_defaults() """ set_defaults('freqplot', dB=False, deg=True, Hz=False, grid=False) @@ -263,7 +260,6 @@ class and functions. If flat is `False`, then matrices are -------- >>> ct.use_numpy_matrix(True, False) >>> # do some legacy calculations using np.matrix - >>> ct.reset_defaults() """ if flag and warn: @@ -285,7 +281,6 @@ def use_legacy_defaults(version): >>> ct.use_legacy_defaults("0.9.0") (0, 9, 0) >>> # do some legacy style plotting - >>> ct.reset_defaults() """ import re diff --git a/control/ctrlutil.py b/control/ctrlutil.py index fa7b91ee5..425812dc1 100644 --- a/control/ctrlutil.py +++ b/control/ctrlutil.py @@ -44,6 +44,7 @@ from . import lti import numpy as np import math +import warnings __all__ = ['unwrap', 'issys', 'db2mag', 'mag2db'] @@ -99,6 +100,8 @@ def issys(obj): False """ + warnings.warn("issys() is deprecated; use isinstance(obj, ct.LTI)", + FutureWarning, stacklevel=2) return isinstance(obj, lti.LTI) def db2mag(db): diff --git a/control/freqplot.py b/control/freqplot.py index c20fb189e..25de3c11b 100644 --- a/control/freqplot.py +++ b/control/freqplot.py @@ -558,21 +558,21 @@ def nyquist_plot( List of linear input/output systems (single system is OK). Nyquist curves for each system are plotted on the same graph. - plot : boolean - If True, plot magnitude - - omega : array_like + omega : array_like, optional Set of frequencies to be evaluated, in rad/sec. - omega_limits : array_like of two values + omega_limits : array_like of two values, optional Limits to the range of frequencies. Ignored if omega is provided, and auto-generated if omitted. - omega_num : int + omega_num : int, optional Number of frequency samples to plot. Defaults to config.defaults['freqplot.number_of_samples']. - color : string + plot : boolean, optional + If True (default), plot the Nyquist plot. + + color : string, optional Used to specify the color of the line and arrowhead. return_contour : bool, optional @@ -690,6 +690,13 @@ def nyquist_plot( to `none` will turn off indentation. If `return_contour` is True, the exact contour used for evaluation is returned. + 3. For those portions of the Nyquist plot in which the contour is + indented to avoid poles, resuling in a scaling of the Nyquist plot, + the line styles are according to the settings of the `primary_style` + and `mirror_style` keywords. By default the scaled portions of the + primary curve use a dotted line style and the scaled portion of the + mirror image use a dashdot line style. + Examples -------- >>> G = ct.zpk([], [-1, -2, -3], gain=100) diff --git a/control/matlab/wrappers.py b/control/matlab/wrappers.py index 1c4ee053a..e7d757248 100644 --- a/control/matlab/wrappers.py +++ b/control/matlab/wrappers.py @@ -5,7 +5,7 @@ import numpy as np from ..iosys import ss from ..xferfcn import tf -from ..ctrlutil import issys +from ..lti import LTI from ..exception import ControlArgument from scipy.signal import zpk2tf from warnings import warn @@ -124,7 +124,7 @@ def _parse_freqplot_args(*args): i = 0 while i < len(args): # Check to see if this is a system of some sort - if issys(args[i]): + if isinstance(args[i], LTI): # Append the system to our list of systems syslist.append(args[i]) i += 1 diff --git a/control/tests/ctrlutil_test.py b/control/tests/ctrlutil_test.py index 460ff601c..758c98b66 100644 --- a/control/tests/ctrlutil_test.py +++ b/control/tests/ctrlutil_test.py @@ -1,7 +1,8 @@ """ctrlutil_test.py""" import numpy as np - +import pytest +import control as ct from control.ctrlutil import db2mag, mag2db, unwrap class TestUtils: @@ -58,3 +59,8 @@ def test_mag2db(self): def test_mag2db_array(self): db_array = mag2db(self.mag) np.testing.assert_array_almost_equal(db_array, self.db) + + def test_issys(self): + sys = ct.rss(2, 1, 1) + with pytest.warns(FutureWarning, match="deprecated; use isinstance"): + ct.issys(sys) diff --git a/control/tests/flatsys_test.py b/control/tests/flatsys_test.py index 95fb8cf7c..7f480f43a 100644 --- a/control/tests/flatsys_test.py +++ b/control/tests/flatsys_test.py @@ -212,7 +212,7 @@ def test_kinematic_car_ocp( elif re.match("Iteration limit.*", traj_ocp.message) and \ re.match( "conda ubuntu-3.* Generic", os.getenv('JOBNAME', '')) and \ - re.match("1.24.[01]", np.__version__): + re.match("1.24.[012]", np.__version__): pytest.xfail("gh820: iteration limit exceeded") else: diff --git a/doc/conf.py b/doc/conf.py index a76b1c486..5fb7342f4 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -37,11 +37,12 @@ import re import control +# Get the version number for this commmit (including alpha/beta/rc tags) +release = re.sub('^v', '', os.popen('git describe').read().strip()) + # The short X.Y.Z version -version = re.sub(r'(\d+\.\d+\.\d+)(.*)', r'\1', control.__version__) +version = re.sub(r'(\d+\.\d+\.\d+(.post\d+)?)(.*)', r'\1', release) -# The full version, including alpha/beta/rc tags -release = control.__version__ print("version %s, release %s" % (version, release)) # -- General configuration --------------------------------------------------- @@ -206,11 +207,10 @@ def linkcode_resolve(domain, info): linespec = "" base_url = "https://github.com/python-control/python-control/blob/" - if 'dev' in control.__version__: + if release != version: # development release return base_url + "main/control/%s%s" % (fn, linespec) - else: - return base_url + "%s/control/%s%s" % ( - control.__version__, fn, linespec) + else: # specific version + return base_url + "%s/control/%s%s" % (version, fn, linespec) # Don't automaticall show all members of class in Methods & Attributes section numpydoc_show_class_members = False @@ -282,4 +282,5 @@ def linkcode_resolve(domain, info): import control as ct import control.optimal as obc import control.flatsys as fs +ct.reset_defaults() """ diff --git a/doc/conventions.rst b/doc/conventions.rst index 476366714..5dc2e3d76 100644 --- a/doc/conventions.rst +++ b/doc/conventions.rst @@ -6,8 +6,11 @@ Library conventions ******************* -The python-control library uses a set of standard conventions for the way -that different types of standard information used by the library. +The python-control library uses a set of standard conventions for the +way that different types of standard information used by the library. +Throughout this manual, we assume the `control` package has been +imported as `ct`. + LTI system representation ========================= @@ -29,7 +32,7 @@ of linear time-invariant (LTI) systems: where u is the input, y is the output, and x is the state. -To create a state space system, use the :func:`ss` function: +To create a state space system, use the :func:`ss` function:: sys = ct.ss(A, B, C, D) @@ -51,7 +54,7 @@ transfer functions where n is generally greater than or equal to m (for a proper transfer function). -To create a transfer function, use the :func:`tf` function: +To create a transfer function, use the :func:`tf` function:: sys = ct.tf(num, den) @@ -77,7 +80,7 @@ performed. The FRD class is also used as the return type for the :func:`frequency_response` function (and the equivalent method for the :class:`StateSpace` and :class:`TransferFunction` classes). This -object can be assigned to a tuple using +object can be assigned to a tuple using:: mag, phase, omega = response @@ -91,7 +94,7 @@ is not SISO or `squeeze` is False, the array is 3D, indexed by the output, input, and frequency. If `squeeze` is True then single-dimensional axes are removed. The processing of the `squeeze` keyword can be changed by calling the response function with a new -argument: +argument:: mag, phase, omega = response(squeeze=False) @@ -101,10 +104,10 @@ Discrete time systems A discrete time system is created by specifying a nonzero 'timebase', dt. The timebase argument can be given when a system is constructed: -* dt = 0: continuous time system (default) -* dt > 0: discrete time system with sampling period 'dt' -* dt = True: discrete time with unspecified sampling period -* dt = None: no timebase specified +* `dt = 0`: continuous time system (default) +* `dt > 0`: discrete time system with sampling period 'dt' +* `dt = True`: discrete time with unspecified sampling period +* `dt = None`: no timebase specified Only the :class:`StateSpace`, :class:`TransferFunction`, and :class:`InputOutputSystem` classes allow explicit representation of @@ -119,8 +122,8 @@ result will have the timebase of the latter system. For continuous time systems, the :func:`sample_system` function or the :meth:`StateSpace.sample` and :meth:`TransferFunction.sample` methods can be used to create a discrete time system from a continuous time system. See -:ref:`utility-and-conversions`. The default value of 'dt' can be changed by -changing the value of ``control.config.defaults['control.default_dt']``. +:ref:`utility-and-conversions`. The default value of `dt` can be changed by +changing the value of `control.config.defaults['control.default_dt']`. Conversion between representations ---------------------------------- @@ -165,10 +168,9 @@ points in time, rows are different components:: ... [ui(t1), ui(t2), ui(t3), ..., ui(tn)]] - Same for X, Y - -So, U[:,2] is the system's input at the third point in time; and U[1] or U[1,:] -is the sequence of values for the system's second input. +(and similarly for `X`, `Y`). So, `U[:, 2]` is the system's input at the +third point in time; and `U[1]` or `U[1, :]` is the sequence of values for +the system's second input. When there is only one row, a 1D object is accepted or returned, which adds convenience for SISO systems: @@ -185,8 +187,10 @@ Functions that return time responses (e.g., :func:`forced_response`, :func:`impulse_response`, :func:`input_output_response`, :func:`initial_response`, and :func:`step_response`) return a :class:`TimeResponseData` object that contains the data for the time -response. These data can be accessed via the ``time``, ``outputs``, -``states`` and ``inputs`` properties:: +response. These data can be accessed via the +:attr:`~TimeResponseData.time`, :attr:`~TimeResponseData.outputs`, +:attr:`~TimeResponseData.states` and :attr:`~TimeResponseData.inputs` +properties:: sys = ct.rss(4, 1, 1) response = ct.step_response(sys) @@ -213,13 +217,13 @@ The output of a MIMO LTI system can be plotted like this:: plot(t, y[1], label='y_1') The convention also works well with the state space form of linear -systems. If ``D`` is the feedthrough matrix (2D array) of a linear system, -and ``U`` is its input (array), then the feedthrough part of the system's +systems. If `D` is the feedthrough matrix (2D array) of a linear system, +and `U` is its input (array), then the feedthrough part of the system's response, can be computed like this:: ft = D @ U -Finally, the `to_pandas()` function can be used to create a pandas dataframe: +Finally, the `to_pandas()` function can be used to create a pandas dataframe:: df = response.to_pandas() @@ -242,16 +246,12 @@ for various types of plots and establishing the underlying representation for state space matrices. To set the default value of a configuration variable, set the appropriate -element of the `control.config.defaults` dictionary: - -.. code-block:: python +element of the `control.config.defaults` dictionary:: ct.config.defaults['module.parameter'] = value The `~control.config.set_defaults` function can also be used to set multiple -configuration parameters at the same time: - -.. code-block:: python +configuration parameters at the same time:: ct.config.set_defaults('module', param1=val1, param2=val2, ...] diff --git a/doc/intro.rst b/doc/intro.rst index ce01aca15..9d4198c56 100644 --- a/doc/intro.rst +++ b/doc/intro.rst @@ -31,23 +31,43 @@ some thing to keep in mind: * You must include commas in vectors. So [1 2 3] must be [1, 2, 3]. * Functions that return multiple arguments use tuples. * You cannot use braces for collections; use tuples instead. +* Time series data have time as the final index (see + :ref:`time-series-convention`). Installation ============ -The `python-control` package can be installed using pip, conda or the -standard setuptools mechanisms. The package requires `numpy`_ and -`scipy`_, and the plotting routines require `matplotlib -`_. In addition, some routines require the `slycot -`_ library in order to implement -more advanced features (including some MIMO functionality). +The `python-control` package can be installed using conda or pip. The +package requires `NumPy`_ and `SciPy`_, and the plotting routines +require `Matplotlib `_. In addition, some +routines require the `Slycot +`_ library in order to +implement more advanced features (including some MIMO functionality). +For users with the Anaconda distribution of Python, the following +command can be used:: + + conda install -c conda-forge control slycot + +This installs `slycot` and `python-control` from conda-forge, including the +`openblas` package. NumPy, SciPy, and Matplotlib will also be installed if +they are not already present. + +.. note:: + Mixing packages from conda-forge and the default conda channel + can sometimes cause problems with dependencies, so it is usually best to + instally NumPy, SciPy, and Matplotlib from conda-forge as well.) To install using pip:: pip install slycot # optional pip install control +.. note:: + If you install Slycot using pip you'll need a development + environment (e.g., Python development files, C and Fortran compilers). + Pip installation can be particularly complicated for Windows. + Many parts of `python-control` will work without `slycot`, but some functionality is limited or absent, and installation of `slycot` is recommended. Users can check to insure that slycot is installed @@ -56,28 +76,14 @@ correctly by running the command:: python -c "import slycot" and verifying that no error message appears. More information on the -slycot package can be obtained from the `slycot project page +Slycot package can be obtained from the `Slycot project page `_. -For users with the Anaconda distribution of Python, the following -commands can be used:: - - conda install numpy scipy matplotlib # if not yet installed - conda install -c conda-forge control slycot - -This installs `slycot` and `python-control` from conda-forge, including the -`openblas` package. - -Alternatively, to use setuptools, first `download the source +Alternatively, to install from source, first `download the source `_ and unpack it. To install in your home directory, use:: - python setup.py install --user - -or to install for all users (on Linux or Mac OS):: - - python setup.py build - sudo python setup.py install + pip install . Getting started =============== @@ -85,7 +91,7 @@ Getting started There are two different ways to use the package. For the default interface described in :ref:`function-ref`, simply import the control package as follows:: - >>> import control + >>> import control as ct If you want to have a MATLAB-like environment, use the :ref:`matlab-module`:: diff --git a/examples/kincar-flatsys.py b/examples/kincar-flatsys.py index 2ebee3133..b61a9e1c5 100644 --- a/examples/kincar-flatsys.py +++ b/examples/kincar-flatsys.py @@ -10,7 +10,7 @@ import matplotlib.pyplot as plt import control as ct import control.flatsys as fs -import control.optimal as opt +import control.optimal as obc # # System model and utility functions @@ -147,7 +147,7 @@ def plot_results(t, x, ud, rescale=True): basis = fs.PolyFamily(8) # Define the cost function (penalize lateral error and steering) -traj_cost = opt.quadratic_cost( +traj_cost = obc.quadratic_cost( vehicle_flat, np.diag([0, 0.1, 0]), np.diag([0.1, 1]), x0=xf, u0=uf) # Solve for an optimal solution @@ -168,7 +168,7 @@ def plot_results(t, x, ud, rescale=True): # Constraint the input values constraints = [ - opt.input_range_constraint(vehicle_flat, [8, -0.1], [12, 0.1]) ] + obc.input_range_constraint(vehicle_flat, [8, -0.1], [12, 0.1]) ] # TEST: Change the basis to use B-splines basis = fs.BSplineFamily([0, Tf/2, Tf], 6) @@ -198,11 +198,11 @@ def plot_results(t, x, ud, rescale=True): # # Define the cost function (mainly penalize steering angle) -traj_cost = opt.quadratic_cost( +traj_cost = obc.quadratic_cost( vehicle_flat, None, np.diag([0.1, 10]), x0=xf, u0=uf) # Set terminal cost to bring us close to xf -terminal_cost = opt.quadratic_cost(vehicle_flat, 1e3 * np.eye(3), None, x0=xf) +terminal_cost = obc.quadratic_cost(vehicle_flat, 1e3 * np.eye(3), None, x0=xf) # Change the basis to use B-splines basis = fs.BSplineFamily([0, Tf/2, Tf], [4, 6], vars=2) diff --git a/examples/mpc_aircraft.ipynb b/examples/mpc_aircraft.ipynb index 5da812eb0..a1edf3ebb 100644 --- a/examples/mpc_aircraft.ipynb +++ b/examples/mpc_aircraft.ipynb @@ -19,7 +19,7 @@ "source": [ "import control as ct\n", "import numpy as np\n", - "import control.optimal as opt\n", + "import control.optimal as obc\n", "import matplotlib.pyplot as plt" ] }, @@ -70,15 +70,15 @@ "# model.y.reference = ys;\n", "\n", "# provide constraints on the system signals\n", - "constraints = [opt.input_range_constraint(sys, [-5, -6], [5, 6])]\n", + "constraints = [obc.input_range_constraint(sys, [-5, -6], [5, 6])]\n", "\n", "# provide penalties on the system signals\n", "Q = model.C.transpose() @ np.diag([10, 10, 10, 10]) @ model.C\n", "R = np.diag([3, 2])\n", - "cost = opt.quadratic_cost(model, Q, R, x0=xd, u0=ud)\n", + "cost = obc.quadratic_cost(model, Q, R, x0=xd, u0=ud)\n", "\n", "# online MPC controller object is constructed with a horizon 6\n", - "ctrl = opt.create_mpc_iosystem(model, np.arange(0, 6) * 0.2, cost, constraints)" + "ctrl = obc.create_mpc_iosystem(model, np.arange(0, 6) * 0.2, cost, constraints)" ] }, { diff --git a/examples/steering-optimal.py b/examples/steering-optimal.py index 778ac3c25..d9bad608e 100644 --- a/examples/steering-optimal.py +++ b/examples/steering-optimal.py @@ -8,7 +8,7 @@ import numpy as np import math import control as ct -import control.optimal as opt +import control.optimal as obc import matplotlib.pyplot as plt import logging import time @@ -106,7 +106,7 @@ def plot_lanechange(t, y, u, yf=None, figure=None): # Set up the cost functions Q = np.diag([.1, 10, .1]) # keep lateral error low R = np.diag([.1, 1]) # minimize applied inputs -quad_cost = opt.quadratic_cost(vehicle, Q, R, x0=xf, u0=uf) +quad_cost = obc.quadratic_cost(vehicle, Q, R, x0=xf, u0=uf) # Define the time horizon (and spacing) for the optimization timepts = np.linspace(0, Tf, 20, endpoint=True) @@ -124,7 +124,7 @@ def plot_lanechange(t, y, u, yf=None, figure=None): # Compute the optimal control, setting step size for gradient calculation (eps) start_time = time.process_time() -result1 = opt.solve_ocp( +result1 = obc.solve_ocp( vehicle, timepts, x0, quad_cost, initial_guess=straight_line, log=True, # minimize_method='trust-constr', # minimize_options={'finite_diff_rel_step': 0.01}, @@ -158,9 +158,9 @@ def plot_lanechange(t, y, u, yf=None, figure=None): print("\nApproach 2: input cost and constraints plus terminal cost") # Add input constraint, input cost, terminal cost -constraints = [ opt.input_range_constraint(vehicle, [8, -0.1], [12, 0.1]) ] -traj_cost = opt.quadratic_cost(vehicle, None, np.diag([0.1, 1]), u0=uf) -term_cost = opt.quadratic_cost(vehicle, np.diag([1, 10, 10]), None, x0=xf) +constraints = [ obc.input_range_constraint(vehicle, [8, -0.1], [12, 0.1]) ] +traj_cost = obc.quadratic_cost(vehicle, None, np.diag([0.1, 1]), u0=uf) +term_cost = obc.quadratic_cost(vehicle, np.diag([1, 10, 10]), None, x0=xf) # Change logging to keep less information logging.basicConfig( @@ -175,7 +175,7 @@ def plot_lanechange(t, y, u, yf=None, figure=None): # Compute the optimal control start_time = time.process_time() -result2 = opt.solve_ocp( +result2 = obc.solve_ocp( vehicle, timepts, x0, traj_cost, constraints, terminal_cost=term_cost, initial_guess=straight_line, log=True, # minimize_method='SLSQP', minimize_options={'eps': 0.01} @@ -207,10 +207,10 @@ def plot_lanechange(t, y, u, yf=None, figure=None): # Input cost and terminal constraints R = np.diag([1, 1]) # minimize applied inputs -cost3 = opt.quadratic_cost(vehicle, np.zeros((3,3)), R, u0=uf) +cost3 = obc.quadratic_cost(vehicle, np.zeros((3,3)), R, u0=uf) constraints = [ - opt.input_range_constraint(vehicle, [8, -0.1], [12, 0.1]) ] -terminal = [ opt.state_range_constraint(vehicle, xf, xf) ] + obc.input_range_constraint(vehicle, [8, -0.1], [12, 0.1]) ] +terminal = [ obc.state_range_constraint(vehicle, xf, xf) ] # Reset logging to its default values logging.basicConfig( @@ -219,7 +219,7 @@ def plot_lanechange(t, y, u, yf=None, figure=None): # Compute the optimal control start_time = time.process_time() -result3 = opt.solve_ocp( +result3 = obc.solve_ocp( vehicle, timepts, x0, cost3, constraints, terminal_constraints=terminal, initial_guess=straight_line, log=False, # solve_ivp_kwargs={'atol': 1e-3, 'rtol': 1e-2}, @@ -254,7 +254,7 @@ def plot_lanechange(t, y, u, yf=None, figure=None): # Compute the optimal control start_time = time.process_time() -result4 = opt.solve_ocp( +result4 = obc.solve_ocp( vehicle, timepts, x0, quad_cost, constraints, terminal_constraints=terminal, pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy