@@ -66,7 +66,7 @@ class OptimalControlProblem():
66
66
`(fun, lb, ub)`. The constraints will be applied at each time point
67
67
along the trajectory.
68
68
terminal_cost : callable, optional
69
- Function that returns the terminal cost given the current state
69
+ Function that returns the terminal cost given the final state
70
70
and input. Called as terminal_cost(x, u).
71
71
trajectory_method : string, optional
72
72
Method to use for carrying out the optimization. Currently supported
@@ -287,12 +287,16 @@ def __init__(
287
287
# time point and we use a trapezoidal approximation to compute the
288
288
# integral cost, then add on the terminal cost.
289
289
#
290
- # For shooting methods, given the input U = [u[0 ], ... u[N ]] we need to
290
+ # For shooting methods, given the input U = [u[t_0 ], ... u[t_N ]] we need to
291
291
# compute the cost of the trajectory generated by that input. This
292
292
# means we have to simulate the system to get the state trajectory X =
293
- # [x[0 ], ..., x[N ]] and then compute the cost at each point:
293
+ # [x[t_0 ], ..., x[t_N ]] and then compute the cost at each point:
294
294
#
295
- # cost = sum_k integral_cost(x[k], u[k]) + terminal_cost(x[N], u[N])
295
+ # cost = sum_k integral_cost(x[t_k], u[t_k])
296
+ # + terminal_cost(x[t_N], u[t_N])
297
+ #
298
+ # The actual calculation is a bit more complex: for continuous time
299
+ # systems, we use a trapezoidal approximation for the integral cost.
296
300
#
297
301
# The initial state used for generating the simulation is stored in the
298
302
# class parameter `x` prior to calling the optimization algorithm.
@@ -325,8 +329,8 @@ def _cost_function(self, coeffs):
325
329
# Sum the integral cost over the time (second) indices
326
330
# cost += self.integral_cost(states[:,i], inputs[:,i])
327
331
cost = sum (map (
328
- self .integral_cost , np . transpose ( states [:, :- 1 ]),
329
- np . transpose ( inputs [:, :- 1 ])))
332
+ self .integral_cost , states [:, :- 1 ]. transpose ( ),
333
+ inputs [:, :- 1 ]. transpose ( )))
330
334
331
335
# Terminal cost
332
336
if self .terminal_cost is not None :
@@ -954,7 +958,22 @@ def solve_ocp(
954
958
transpose = None , return_states = True , print_summary = True , log = False ,
955
959
** kwargs ):
956
960
957
- """Compute the solution to an optimal control problem
961
+ """Compute the solution to an optimal control problem.
962
+
963
+ The optimal trajectory (states and inputs) is computed so as to
964
+ approximately mimimize a cost function of the following form (for
965
+ continuous time systems):
966
+
967
+ J(x(.), u(.)) = \int_0^T L(x(t), u(t)) dt + V(x(T)),
968
+
969
+ where T is the time horizon.
970
+
971
+ Discrete time systems use a similar formulation, with the integral
972
+ replaced by a sum:
973
+
974
+ J(x[.], u[.]) = \sum_0^{N-1} L(x_k, u_k) + V(x_N),
975
+
976
+ where N is the time horizon (corresponding to timepts[-1]).
958
977
959
978
Parameters
960
979
----------
@@ -968,7 +987,7 @@ def solve_ocp(
968
987
Initial condition (default = 0).
969
988
970
989
cost : callable
971
- Function that returns the integral cost given the current state
990
+ Function that returns the integral cost (L) given the current state
972
991
and input. Called as `cost(x, u)`.
973
992
974
993
trajectory_constraints : list of tuples, optional
@@ -990,8 +1009,10 @@ def solve_ocp(
990
1009
The constraints are applied at each time point along the trajectory.
991
1010
992
1011
terminal_cost : callable, optional
993
- Function that returns the terminal cost given the current state
994
- and input. Called as terminal_cost(x, u).
1012
+ Function that returns the terminal cost (V) given the final state
1013
+ and input. Called as terminal_cost(x, u). (For compatibility with
1014
+ the form of the cost function, u is passed even though it is often
1015
+ not part of the terminal cost.)
995
1016
996
1017
terminal_constraints : list of tuples, optional
997
1018
List of constraints that should hold at the end of the trajectory.
@@ -1044,9 +1065,19 @@ def solve_ocp(
1044
1065
1045
1066
Notes
1046
1067
-----
1047
- Additional keyword parameters can be used to fine-tune the behavior of
1048
- the underlying optimization and integration functions. See
1049
- :func:`OptimalControlProblem` for more information.
1068
+ 1. For discrete time systems, the final value of the timepts vector
1069
+ specifies the final time t_N, and the trajectory cost is computed
1070
+ from time t_0 to t_{N-1}. Note that the input u_N does not affect
1071
+ the state x_N and so it should always be returned as 0. Further, if
1072
+ neither a terminal cost nor a terminal constraint is given, then the
1073
+ input at time point t_{N-1} does not affect the cost function and
1074
+ hence u_{N-1} will also be returned as zero. If you want the
1075
+ trajectory cost to include state costs at time t_{N}, then you can
1076
+ set `terminal_cost` to be the same function as `cost`.
1077
+
1078
+ 2. Additional keyword parameters can be used to fine-tune the behavior
1079
+ of the underlying optimization and integration functions. See
1080
+ :func:`OptimalControlProblem` for more information.
1050
1081
1051
1082
"""
1052
1083
# Process keyword arguments
@@ -1116,15 +1147,16 @@ def create_mpc_iosystem(
1116
1147
See :func:`~control.optimal.solve_ocp` for more details.
1117
1148
1118
1149
terminal_cost : callable, optional
1119
- Function that returns the terminal cost given the current state
1150
+ Function that returns the terminal cost given the final state
1120
1151
and input. Called as terminal_cost(x, u).
1121
1152
1122
1153
terminal_constraints : list of tuples, optional
1123
1154
List of constraints that should hold at the end of the trajectory.
1124
1155
Same format as `constraints`.
1125
1156
1126
- kwargs : dict, optional
1127
- Additional parameters (passed to :func:`scipy.optimal.minimize`).
1157
+ **kwargs
1158
+ Additional parameters, passed to :func:`scipy.optimal.minimize` and
1159
+ :class:`NonlinearIOSystem`.
1128
1160
1129
1161
Returns
1130
1162
-------
@@ -1149,14 +1181,22 @@ def create_mpc_iosystem(
1149
1181
:func:`OptimalControlProblem` for more information.
1150
1182
1151
1183
"""
1184
+ from .iosys import InputOutputSystem
1185
+
1186
+ # Grab the keyword arguments known by this function
1187
+ iosys_kwargs = {}
1188
+ for kw in InputOutputSystem .kwargs_list :
1189
+ if kw in kwargs :
1190
+ iosys_kwargs [kw ] = kwargs .pop (kw )
1191
+
1152
1192
# Set up the optimal control problem
1153
1193
ocp = OptimalControlProblem (
1154
1194
sys , timepts , cost , trajectory_constraints = constraints ,
1155
1195
terminal_cost = terminal_cost , terminal_constraints = terminal_constraints ,
1156
- log = log , kwargs_check = False , ** kwargs )
1196
+ log = log , ** kwargs )
1157
1197
1158
1198
# Return an I/O system implementing the model predictive controller
1159
- return ocp .create_mpc_iosystem (** kwargs )
1199
+ return ocp .create_mpc_iosystem (** iosys_kwargs )
1160
1200
1161
1201
1162
1202
#
0 commit comments