Skip to content

Commit dd1e2d3

Browse files
committed
add support for 1D gain scheduling
1 parent c90781d commit dd1e2d3

File tree

2 files changed

+39
-2
lines changed

2 files changed

+39
-2
lines changed

control/statefbk.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -826,6 +826,10 @@ def create_statefbk_iosystem(
826826
gainsched_indices = range(sys.nstates) if gainsched_indices is None \
827827
else list(gainsched_indices)
828828

829+
# If points is a 1D list, convert to 2D
830+
if points.ndim == 1:
831+
points = points.reshape(-1, 1)
832+
829833
# Make sure the scheduling variable indices are the right length
830834
if len(gainsched_indices) != points.shape[1]:
831835
raise ControlArgument(
@@ -838,7 +842,12 @@ def create_statefbk_iosystem(
838842
gainsched_indices[i] = inputs.index(gainsched_indices[i])
839843

840844
# Create interpolating function
841-
if gainsched_method == 'nearest':
845+
if points.shape[1] < 2:
846+
_interp = sp.interpolate.interp1d(
847+
points[:, 0], gains, axis=0, kind=gainsched_method)
848+
_nearest = sp.interpolate.interp1d(
849+
points[:, 0], gains, axis=0, kind='nearest')
850+
elif gainsched_method == 'nearest':
842851
_interp = sp.interpolate.NearestNDInterpolator(points, gains)
843852
def _nearest(mu):
844853
raise SystemError(f"could not find nearest gain at mu = {mu}")

control/tests/statefbk_test.py

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -924,6 +924,34 @@ def test_gainsched_unicycle(unicycle, method):
924924
resp.states[:, -1], Xd[:, -1], atol=1e-2, rtol=1e-2)
925925

926926

927+
@pytest.mark.parametrize("method", ['nearest', 'linear', 'cubic'])
928+
def test_gainsched_1d(method):
929+
# Define a linear system to test
930+
sys = ct.ss([[-1, 0.1], [0, -2]], [[0], [1]], np.eye(2), 0)
931+
932+
# Define gains for the first state only
933+
points = [-1, 0, 1]
934+
935+
# Define gain to be constant
936+
K, _, _ = ct.lqr(sys, np.eye(sys.nstates), np.eye(sys.ninputs))
937+
gains = [K for p in points]
938+
939+
# Define the paramters for the simulations
940+
timepts = np.linspace(0, 10, 100)
941+
X0 = np.ones(sys.nstates) * 1.1 # Start outside defined range
942+
943+
# Create a controller and simulate the initial response
944+
gs_ctrl, gs_clsys = ct.create_statefbk_iosystem(
945+
sys, (gains, points), gainsched_indices=[0])
946+
gs_resp = ct.input_output_response(gs_clsys, timepts, 0, X0)
947+
948+
# Verify that we get the same result as a constant gain
949+
ck_clsys = ct.ss(sys.A - sys.B @ K, sys.B, sys.C, 0)
950+
ck_resp = ct.input_output_response(ck_clsys, timepts, 0, X0)
951+
952+
np.testing.assert_allclose(gs_resp.states, ck_resp.states)
953+
954+
927955
def test_gainsched_default_indices():
928956
# Define a linear system to test
929957
sys = ct.ss([[-1, 0.1], [0, -2]], [[0], [1]], np.eye(2), 0)
@@ -937,7 +965,7 @@ def test_gainsched_default_indices():
937965

938966
# Define the paramters for the simulations
939967
timepts = np.linspace(0, 10, 100)
940-
X0 = np.ones(sys.nstates) * 0.9
968+
X0 = np.ones(sys.nstates) * 1.1 # Start outside defined range
941969

942970
# Create a controller and simulate the initial response
943971
gs_ctrl, gs_clsys = ct.create_statefbk_iosystem(sys, (gains, points))

0 commit comments

Comments
 (0)