@@ -799,18 +799,18 @@ def unicycle_output(t, x, u, params):
799
799
800
800
from math import pi
801
801
802
- @pytest .mark .parametrize ("method" , [None , 'nearest' ])
802
+ @pytest .mark .parametrize ("method" , [None , 'nearest' , 'linear' , 'cubic' ])
803
803
def test_gainsched_unicycle (unicycle , method ):
804
804
# Speeds and angles at which to compute the gains
805
805
speeds = [1 , 5 , 10 ]
806
- angles = - pi + np .linspace (0 , 2 * pi , 10 )
806
+ angles = np .linspace (0 , pi / 2 , 4 )
807
807
points = list (itertools .product (speeds , angles ))
808
808
809
809
# Gains for each speed (using LQR controller)
810
810
Q = np .identity (unicycle .nstates )
811
811
R = np .identity (unicycle .ninputs )
812
- gains = [ct .lqr (unicycle .linearize (
813
- [0 , 0 , angle ], [speed , 0 ]), Q , R )[0 ] for speed , angle in points ]
812
+ gains = [np . array ( ct .lqr (unicycle .linearize (
813
+ [0 , 0 , angle ], [speed , 0 ]), Q , R )[0 ]) for speed , angle in points ]
814
814
815
815
#
816
816
# Schedule on desired speed and angle
@@ -836,13 +836,28 @@ def test_gainsched_unicycle(unicycle, method):
836
836
837
837
# Check the closed loop system at the scheduling points
838
838
clsys_lin = clsys .linearize (xe , [xd , ud ])
839
- np .testing .assert_allclose (np .sort (
840
- clsys_lin .poles ()), np .sort (E ), rtol = 1e-2 )
839
+ np .testing .assert_allclose (
840
+ np .sort (clsys_lin .poles ()), np .sort (E ), rtol = 1e-2 )
841
+
842
+ # Check the gain at an intermediate point and confirm stability
843
+ speed , angle = 2 , pi / 3
844
+ xe , ue = np .array ([0 , 0 , angle ]), np .array ([speed , 0 ])
845
+ xd , ud = np .array ([0 , 0 , angle ]), np .array ([speed , 0 ])
846
+ clsys_lin = clsys .linearize (xe , [xd , ud ])
847
+ assert np .all (np .real (clsys_lin .poles ()) < 0 )
848
+
849
+ # Make sure that gains are different from 'nearest'
850
+ if method is not None and method != 'nearest' :
851
+ ctrl_nearest , clsys_nearest = ct .create_statefbk_iosystem (
852
+ unicycle , (gains , points , 'nearest' ), gainsched_indices = [3 , 2 ])
853
+ nearest_lin = clsys_nearest .linearize (xe , [xd , ud ])
854
+ assert not np .allclose (
855
+ np .sort (clsys_lin .poles ()), np .sort (nearest_lin .poles ()), rtol = 1e-2 )
841
856
842
857
# Run a simulation following a curved path
843
858
T = 10 # length of the trajectory [sec]
844
859
r = 10 # radius of the circle [m]
845
- timepts = np .linspace (0 , T , 100 )
860
+ timepts = np .linspace (0 , T , 50 )
846
861
Xd = np .vstack ([
847
862
r * np .cos (timepts / T * pi / 2 + 3 * pi / 2 ),
848
863
r * np .sin (timepts / T * pi / 2 + 3 * pi / 2 ) + r ,
@@ -885,20 +900,6 @@ def test_gainsched_unicycle(unicycle, method):
885
900
clsys_lin .poles ()), np .sort (E ), rtol = 1e-2 )
886
901
887
902
# Run a simulation following a curved path
888
- T = 10 # length of the trajectory [sec]
889
- r = 10 # radius of the circle [m]
890
- timepts = np .linspace (0 , T , 100 )
891
- Xd = np .vstack ([
892
- r * np .cos (timepts / T * pi / 2 + 3 * pi / 2 ),
893
- r * np .sin (timepts / T * pi / 2 + 3 * pi / 2 ) + r ,
894
- timepts / T * pi / 2
895
- ])
896
- Ud = np .vstack ([
897
- np .ones_like (timepts ) * (r * pi / 2 ) / T ,
898
- np .ones_like (timepts ) * (pi / 2 ) / T
899
- ])
900
- X0 = Xd [:, 0 ] + np .array ([- 0.1 , - 0.1 , - 0.1 ])
901
-
902
903
resp = ct .input_output_response (clsys , timepts , [Xd , Ud ], X0 )
903
904
np .testing .assert_allclose (
904
905
resp .states [:, - 1 ], Xd [:, - 1 ], atol = 1e-2 , rtol = 1e-2 )
0 commit comments