|
| 1 | +..currentmodule control |
| 2 | + |
1 | 3 | Nonlinear system models |
2 | 4 | ======================= |
3 | 5 |
|
| 6 | +Nonlinear input/output systems are represented as state space systems |
| 7 | +of the form |
| 8 | + |
| 9 | +.. math:: |
| 10 | +
|
| 11 | + \frac{dx}{dt} &= f(t, x, u, \theta) \\ |
| 12 | + y &= h(t, x, u, \theta) |
| 13 | +
|
| 14 | +where :math:`t` represents the current time, :math:`x` is the system |
| 15 | +state, math:`u` is the system input, :math:`y` is the system output, |
| 16 | +and :math:`\theta` represents a set of parameters. |
| 17 | + |
| 18 | +Discrete time systems are also supported and have dynamics of the form |
| 19 | + |
4 | 20 | Creating nonlinear models |
5 | 21 | ------------------------- |
6 | 22 |
|
| 23 | +A nonlinear system is created using the :func:`nlsys` factory function:: |
| 24 | + |
| 25 | + sys = ct.nlsys( |
| 26 | + updfcn[, outfcn], inputs=m, states=n, outputs=p, [, params=params]) |
| 27 | + |
| 28 | +The `updfcn` argument is a function returning the state update function:: |
| 29 | + |
| 30 | + updfcn(t, x, u, params) -> array |
| 31 | + |
| 32 | +where `x` is a 1-D array with shape (n,), `u` is a 1-D array |
| 33 | +with shape (m,), `t` is a float representing the currrent time, |
| 34 | +and `params` is a dict containing the values of parameters used by the |
| 35 | +function. The dynamics of the system can be in continuous or discrete |
| 36 | +time (use the `dt` keyword to create a discrte time system). |
| 37 | + |
| 38 | +The output function `outfcn` is used to specify the outputs of the |
| 39 | +system and has the same calling signature as `updfcn`. If it is not |
| 40 | +specified, then the output of the system is set equal to the system |
| 41 | +state. Otherwise, it should return an output of shape (p,). |
| 42 | + |
| 43 | +Note that the number of states, inputs, and outputs should generally |
| 44 | +be explicitly specified, although some operations can infer the |
| 45 | +dimensions if they are not given when the system is created. The |
| 46 | +`inputs`, `outputs`, and `states` keywords can also be given as lists |
| 47 | +of strings, in which case the various signals will be given the |
| 48 | +appropriate names. |
| 49 | + |
| 50 | +To illustrate the creation of a nonlinear I/O system model, consider a |
| 51 | +simple model of a spring loaded arm driven by a motor: |
| 52 | + |
| 53 | +.. image:: figures/servomech-diagram.png |
| 54 | + :width: 240 |
| 55 | + |
| 56 | +The dynamics of this system can be modeling using the following code:: |
| 57 | + |
| 58 | + # Parameter values |
| 59 | + servomech_params = { |
| 60 | + 'J': 100, # Moment of inertia of the motor |
| 61 | + 'b': 10, # Angular damping of the arm |
| 62 | + 'k': 1, # Spring constant |
| 63 | + 'r': 1, # Location of spring contact on arm |
| 64 | + 'l': 2, # Distance to the read head |
| 65 | + 'eps': 0.01, # Magnitude of velocity-dependent perturbation |
| 66 | + } |
| 67 | + |
| 68 | + # State derivative |
| 69 | + def servomech_update(t, x, u, params): |
| 70 | + # Extract the configuration and velocity variables from the state vector |
| 71 | + theta = x[0] # Angular position of the disk drive arm |
| 72 | + thetadot = x[1] # Angular velocity of the disk drive arm |
| 73 | + tau = u[0] # Torque applied at the base of the arm |
| 74 | + |
| 75 | + # Get the parameter values |
| 76 | + J, b, k, r = map(params.get, ['J', 'b', 'k', 'r']) |
| 77 | + |
| 78 | + # Compute the angular acceleration |
| 79 | + dthetadot = 1/J * ( |
| 80 | + -b * thetadot - k * r * np.sin(theta) + tau) |
| 81 | + |
| 82 | + # Return the state update law |
| 83 | + return np.array([thetadot, dthetadot]) |
| 84 | + |
| 85 | + # System output (tip radial position + angular velocity) |
| 86 | + def servomech_output(t, x, u, params): |
| 87 | + l = params['l'] |
| 88 | + return np.array([l * x[0], x[1]]) |
| 89 | + |
| 90 | + # System dynamics |
| 91 | + servomech = ct.nlsys( |
| 92 | + servomech_update, servomech_output, name='servomech', |
| 93 | + params=servomech_params, states=['theta', 'thdot'], |
| 94 | + outputs=['y', 'thdot'], inputs=['tau']) |
| 95 | + |
| 96 | +A summary of the model can be obtained using the string representation |
| 97 | +of the model (via the Python `print()` function):: |
| 98 | + |
| 99 | + >>> print(servomech) |
| 100 | + <NonlinearIOSystem>: servomech |
| 101 | + Inputs (1): ['tau'] |
| 102 | + Outputs (2): ['y', 'thdot'] |
| 103 | + States (2): ['theta', 'thdot'] |
| 104 | + Parameters: ['J', 'b', 'k', 'r', 'l', 'eps'] |
| 105 | + |
| 106 | + Update: <function servomech_update at 0x117a17f60> |
| 107 | + Output: <function servomech_output at 0x1354e3d80> |
| 108 | + |
| 109 | + |
7 | 110 | Operating points and linearization |
8 | 111 | ---------------------------------- |
9 | 112 |
|
| 113 | +A nonlinear input/output system can be linearized around an equilibrium point |
| 114 | +to obtain a :class:`~control.StateSpace` linear system:: |
| 115 | + |
| 116 | + sys_ss = ct.linearize(sys_nl, xeq, ueq) |
| 117 | + |
| 118 | +If the equilibrium point is not known, the |
| 119 | +:func:`find_operating_point` function can be used to obtain an |
| 120 | +equilibrium point. In its simplest form, `find_operating_point` finds |
| 121 | +an equilibrium point given either the desired input or desired |
| 122 | +output:: |
| 123 | + |
| 124 | + xeq, ueq = find_operating_point(sys, x0, u0) |
| 125 | + xeq, ueq = find_operating_point(sys, x0, u0, y0) |
| 126 | + |
| 127 | +The first form finds an equilibrium point for a given input `u0` based |
| 128 | +on an initial guess `x0`. The second form fixes the desired output |
| 129 | +values `y0` and uses x0 and u0 as an initial guess to find the |
| 130 | +equilibrium point. If no equilibrium point can be found, the function |
| 131 | +returns the operating point that minimizes the state update (state |
| 132 | +derivative for continuous time systems, state difference for discrete |
| 133 | +time systems). |
| 134 | + |
| 135 | +More complex operating points can be found by specifying which states, |
| 136 | +inputs, or outputs should be used in computing the operating point, as |
| 137 | +well as desired values of the states, inputs, outputs, or state |
| 138 | +updates. See the :func:`find_operating_point` documentation for more deatils. |
| 139 | + |
| 140 | + |
10 | 141 | Simulations and plotting |
11 | 142 | ------------------------ |
| 143 | + |
| 144 | +To simulate an input/output system, use the |
| 145 | +:func:`~control.input_output_response` function:: |
| 146 | + |
| 147 | + resp = ct.input_output_response(io_sys, T, U, x0, params) |
| 148 | + t, y, x = resp.time, resp.outputs, resp.states |
| 149 | + |
| 150 | +Time responses can be plotted using the :func:`time_response_plot` |
| 151 | +function or (equivalently) the :func:`TimeResponseData.plot` |
| 152 | +method:: |
| 153 | + |
| 154 | + cplt = ct.time_response_plot(resp) |
| 155 | + |
| 156 | +The resulting :class:`ControlPlot` object can be used to access |
| 157 | +different plot elements. The :func:`combine_time_responses` function |
| 158 | +an be used to combine multiple time responses into a single |
| 159 | +`TimeResponseData` object. See the :ref:`response-chapter` chapter |
| 160 | +for more information on this functionality. |
0 commit comments