Skip to content

Commit 7741fe9

Browse files
committed
add basis functions, solver options, examples/tests
1 parent 980fa5f commit 7741fe9

File tree

7 files changed

+456
-97
lines changed

7 files changed

+456
-97
lines changed

control/flatsys/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@
5353
# Basis function families
5454
from .basis import BasisFamily
5555
from .poly import PolyFamily
56+
from .bezier import BezierFamily
5657

5758
# Classes
5859
from .systraj import SystemTrajectory

control/flatsys/basis.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,3 +51,10 @@ class BasisFamily:
5151
def __init__(self, N):
5252
"""Create a basis family of order N."""
5353
self.N = N # save number of basis functions
54+
55+
def __call__(self, i, t):
56+
"""Evaluate the ith basis function at a point in time"""
57+
return self.eval_deriv(i, 0, t)
58+
59+
def eval_deriv(self, i, j, t):
60+
raise NotImplementedError("Internal error; improper basis functions")

control/flatsys/bezier.py

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
# bezier.m - 1D Bezier curve basis functions
2+
# RMM, 24 Feb 2021
3+
#
4+
# This class implements a set of basis functions based on Bezier curves:
5+
#
6+
# \phi_i(t) = \sum_{i=0}^n {n \choose i} (T - t)^{n-i} t^i
7+
#
8+
9+
# Copyright (c) 2012 by California Institute of Technology
10+
# All rights reserved.
11+
#
12+
# Redistribution and use in source and binary forms, with or without
13+
# modification, are permitted provided that the following conditions
14+
# are met:
15+
#
16+
# 1. Redistributions of source code must retain the above copyright
17+
# notice, this list of conditions and the following disclaimer.
18+
#
19+
# 2. Redistributions in binary form must reproduce the above copyright
20+
# notice, this list of conditions and the following disclaimer in the
21+
# documentation and/or other materials provided with the distribution.
22+
#
23+
# 3. Neither the name of the California Institute of Technology nor
24+
# the names of its contributors may be used to endorse or promote
25+
# products derived from this software without specific prior
26+
# written permission.
27+
#
28+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
29+
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
30+
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
31+
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL CALTECH
32+
# OR THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
33+
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
34+
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
35+
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
36+
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
37+
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
38+
# OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
39+
# SUCH DAMAGE.
40+
41+
import numpy as np
42+
from scipy.special import binom
43+
from .basis import BasisFamily
44+
45+
class BezierFamily(BasisFamily):
46+
r"""Polynomial basis functions.
47+
48+
This class represents the family of polynomials of the form
49+
50+
.. math::
51+
\phi_i(t) = \sum_{i=0}^n {n \choose i} (T - t)^{n-i} t^i
52+
53+
"""
54+
def __init__(self, N, T=1):
55+
"""Create a polynomial basis of order N."""
56+
self.N = N # save number of basis functions
57+
self.T = T # save end of time interval
58+
59+
# Compute the kth derivative of the ith basis function at time t
60+
def eval_deriv(self, i, k, t):
61+
"""Evaluate the kth derivative of the ith basis function at time t."""
62+
if k > 0:
63+
raise NotImplementedError("Bezier derivatives not yet available")
64+
elif i > self.N:
65+
raise ValueError("Basis function index too high")
66+
67+
# Return the Bezier basis function (note N = # basis functions)
68+
return binom(self.N - 1, i) * \
69+
(t/self.T)**i * (1 - t/self.T)**(self.N - i - 1)

control/iosys.py

Lines changed: 33 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1412,9 +1412,10 @@ def __init__(self, io_sys, ss_sys=None):
14121412
raise TypeError("Second argument must be a state space system.")
14131413

14141414

1415-
def input_output_response(sys, T, U=0., X0=0, params={}, method='RK45',
1416-
transpose=False, return_x=False, squeeze=None):
1417-
1415+
def input_output_response(
1416+
sys, T, U=0., X0=0, params={},
1417+
transpose=False, return_x=False, squeeze=None,
1418+
solve_ivp_kwargs={}, **kwargs):
14181419
"""Compute the output response of a system to a given input.
14191420
14201421
Simulate a dynamical system with a given input and return its output
@@ -1457,7 +1458,33 @@ def input_output_response(sys, T, U=0., X0=0, params={}, method='RK45',
14571458
ValueError
14581459
If time step does not match sampling time (for discrete time systems)
14591460
1461+
Additional parameters
1462+
---------------------
1463+
solve_ivp_method : str, optional
1464+
Set the method used by :func:`scipy.integrate.solve_ivp`. Defaults
1465+
to 'RK45'.
1466+
solve_ivp_kwargs : str, optional
1467+
Pass additional keywords to :func:`scipy.integrate.solve_ivp`.
1468+
14601469
"""
1470+
#
1471+
# Process keyword arguments
1472+
#
1473+
1474+
# Allow method as an alternative to solve_ivp_method
1475+
if kwargs.get('method', None):
1476+
solve_ivp_kwargs['method'] = kwargs.pop('method')
1477+
1478+
# Figure out the method to be used
1479+
if kwargs.get('solve_ivp_method', None):
1480+
if kwargs.get('method', None):
1481+
raise ValueError("ivp_method specified more than once")
1482+
solve_ivp_kwargs['method'] = kwargs['solve_ivp_method']
1483+
1484+
# Set the default method to 'RK45'
1485+
if solve_ivp_kwargs.get('method', None) is None:
1486+
solve_ivp_kwargs['method'] = 'RK45'
1487+
14611488
# Sanity checking on the input
14621489
if not isinstance(sys, InputOutputSystem):
14631490
raise TypeError("System of type ", type(sys), " not valid")
@@ -1504,8 +1531,9 @@ def ivp_rhs(t, x): return sys._rhs(t, x, u(t))
15041531
if not hasattr(sp.integrate, 'solve_ivp'):
15051532
raise NameError("scipy.integrate.solve_ivp not found; "
15061533
"use SciPy 1.0 or greater")
1507-
soln = sp.integrate.solve_ivp(ivp_rhs, (T0, Tf), X0, t_eval=T,
1508-
method=method, vectorized=False)
1534+
soln = sp.integrate.solve_ivp(
1535+
ivp_rhs, (T0, Tf), X0, t_eval=T,
1536+
vectorized=False, **solve_ivp_kwargs)
15091537

15101538
# Compute the output associated with the state (and use sys.out to
15111539
# figure out the number of outputs just in case it wasn't specified)

0 commit comments

Comments
 (0)