@@ -50,44 +50,48 @@ class OptimalControlProblem():
50
50
integral_cost : callable
51
51
Function that returns the integral cost given the current state
52
52
and input. Called as integral_cost(x, u).
53
- trajectory_constraints : list oftuples , optional
53
+ trajectory_constraints : list ofconstraints , optional
54
54
List of constraints that should hold at each point in the time
55
- vector. Each element of the list should consist of a tuple with
56
- first element given by :meth:`~scipy.optimize.LinearConstraint` or
57
- :meth:`~scipy.optimize.NonlinearConstraint` and the remaining
58
- elements of the tuple are the arguments that would be passed to
59
- those functions. The constraints will be applied at each time
60
- point along the trajectory.
55
+ vector. Each element of the list should be an object of type
56
+ :class:`~scipy.optimize.LinearConstraint` with arguments `(A, lb,
57
+ ub)` or :class:`~scipy.optimize.NonlinearConstraint` with arguments
58
+ `(fun, lb, ub)`. The constraints will be applied at each time point
59
+ along the trajectory.
61
60
terminal_cost : callable, optional
62
61
Function that returns the terminal cost given the current state
63
62
and input. Called as terminal_cost(x, u).
64
- initial_guess : 1D or 2D array_like
65
- Initial inputs to use as a guess for the optimal input. The
66
- inputs should either be a 2D vector of shape (ninputs, horizon)
67
- or a 1D input of shape (ninputs,) that will be broadcast by
68
- extension of the time axis.
69
63
trajectory_method : string, optional
70
64
Method to use for carrying out the optimization. Currently supported
71
65
methods are 'shooting' and 'collocation' (continuous time only). The
72
66
default value is 'shooting' for discrete time systems and
73
67
'collocation' for continuous time systems
68
+ initial_guess : (tuple of) 1D or 2D array_like
69
+ Initial states and/or inputs to use as a guess for the optimal
70
+ trajectory. For shooting methods, an array of inputs for each time
71
+ point should be specified. For collocation methods, the initial
72
+ guess is either the input vector or a tuple consisting guesses for
73
+ the state and the input. Guess should either be a 2D vector of
74
+ shape (ninputs, ntimepts) or a 1D input of shape (ninputs,) that
75
+ will be broadcast by extension of the time axis.
74
76
log : bool, optional
75
77
If `True`, turn on logging messages (using Python logging module).
76
- Use ``logging.basicConfig`` to enable logging output (e.g., to a file).
77
- kwargs : dict, optional
78
- Additional parameters (passed to :func:`scipy.optimal.minimize`).
78
+ Use :py:func:`logging.basicConfig` to enable logging output
79
+ (e.g., to a file).
79
80
80
81
Returns
81
82
-------
82
83
ocp : OptimalControlProblem
83
84
Optimal control problem object, to be used in computing optimal
84
85
controllers.
85
86
86
- Additional parameters
87
- ---------------------
87
+ Other Parameters
88
+ ----------------
88
89
basis : BasisFamily, optional
89
90
Use the given set of basis functions for the inputs instead of
90
91
setting the value of the input at each point in the timepts vector.
92
+ terminal_constraints : list of constraints, optional
93
+ List of constraints that should hold at the terminal point in time,
94
+ in the same form as `trajectory_constraints`.
91
95
solve_ivp_method : str, optional
92
96
Set the method used by :func:`scipy.integrate.solve_ivp`.
93
97
solve_ivp_kwargs : str, optional
@@ -182,20 +186,6 @@ def __init__(
182
186
if kwargs :
183
187
raise TypeError ("unrecognized keyword(s): " ,str (kwargs ))
184
188
185
- # Process trajectory constraints
186
- def _process_constraints (constraint_list ,name ):
187
- if isinstance (constraint_list ,tuple ):
188
- constraint_list = [constraint_list ]
189
- elif not isinstance (constraint_list ,list ):
190
- raise TypeError (f"{ name } constraints must be a list" )
191
-
192
- # Make sure that we recognize all of the constraint types
193
- for ctype ,fun ,lb ,ub in constraint_list :
194
- if not ctype in [opt .LinearConstraint ,opt .NonlinearConstraint ]:
195
- raise TypeError (f"unknown{ name } constraint type{ ctype } " )
196
-
197
- return constraint_list
198
-
199
189
self .trajectory_constraints = _process_constraints (
200
190
trajectory_constraints ,"trajectory" )
201
191
self .terminal_constraints = _process_constraints (
@@ -1017,9 +1007,6 @@ def solve_ocp(
1017
1007
If True, assume that 2D input arrays are transposed from the standard
1018
1008
format. Used to convert MATLAB-style inputs to our format.
1019
1009
1020
- kwargs : dict, optional
1021
- Additional parameters (passed to :func:`scipy.optimal.minimize`).
1022
-
1023
1010
Returns
1024
1011
-------
1025
1012
res : OptimalControlResult
@@ -1456,3 +1443,45 @@ def _evaluate_output_range_constraint(x, u):
1456
1443
1457
1444
# Return a nonlinear constraint object based on the polynomial
1458
1445
return (opt .NonlinearConstraint ,_evaluate_output_range_constraint ,lb ,ub )
1446
+
1447
+ #
1448
+ # Utility functions
1449
+ #
1450
+
1451
+ #
1452
+ # Process trajectory constraints
1453
+ #
1454
+ # Constraints were originally specified as a tuple with the type of
1455
+ # constraint followed by the arguments. However, they are now specified
1456
+ # directly as SciPy constraint objects.
1457
+ #
1458
+ # The _process_constraints() function will covert everything to a consistent
1459
+ # internal representation (currently a tuple with the constraint type as the
1460
+ # first element.
1461
+ #
1462
+ def _process_constraints (clist ,name ):
1463
+ if isinstance (
1464
+ clist , (tuple ,opt .LinearConstraint ,opt .NonlinearConstraint )):
1465
+ clist = [clist ]
1466
+ elif not isinstance (clist ,list ):
1467
+ raise TypeError (f"{ name } constraints must be a list" )
1468
+
1469
+ # Process individual list elements
1470
+ constraint_list = []
1471
+ for constraint in clist :
1472
+ if isinstance (constraint ,tuple ):
1473
+ # Original style of constraint
1474
+ ctype ,fun ,lb ,ub = constraint
1475
+ if not ctype in [opt .LinearConstraint ,opt .NonlinearConstraint ]:
1476
+ raise TypeError (f"unknown{ name } constraint type{ ctype } " )
1477
+ constraint_list .append (constraint )
1478
+ elif isinstance (constraint ,opt .LinearConstraint ):
1479
+ constraint_list .append (
1480
+ (opt .LinearConstraint ,constraint .A ,
1481
+ constraint .lb ,constraint .ub ))
1482
+ elif isinstance (constraint ,opt .NonlinearConstraint ):
1483
+ constraint_list .append (
1484
+ (opt .NonlinearConstraint ,constraint .fun ,
1485
+ constraint .lb ,constraint .ub ))
1486
+
1487
+ return constraint_list