@@ -799,18 +799,18 @@ def unicycle_output(t, x, u, params):
799799
800800from math import pi
801801
802- @pytest .mark .parametrize ("method" , [None , 'nearest' ])
802+ @pytest .mark .parametrize ("method" , [None , 'nearest' , 'linear' , 'cubic' ])
803803def test_gainsched_unicycle (unicycle , method ):
804804 # Speeds and angles at which to compute the gains
805805 speeds = [1 , 5 , 10 ]
806- angles = - pi + np .linspace (0 , 2 * pi , 10 )
806+ angles = np .linspace (0 , pi / 2 , 4 )
807807 points = list (itertools .product (speeds , angles ))
808808
809809 # Gains for each speed (using LQR controller)
810810 Q = np .identity (unicycle .nstates )
811811 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 ]
814814
815815 #
816816 # Schedule on desired speed and angle
@@ -836,13 +836,28 @@ def test_gainsched_unicycle(unicycle, method):
836836
837837 # Check the closed loop system at the scheduling points
838838 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 )
841856
842857 # Run a simulation following a curved path
843858 T = 10 # length of the trajectory [sec]
844859 r = 10 # radius of the circle [m]
845- timepts = np .linspace (0 , T , 100 )
860+ timepts = np .linspace (0 , T , 50 )
846861 Xd = np .vstack ([
847862 r * np .cos (timepts / T * pi / 2 + 3 * pi / 2 ),
848863 r * np .sin (timepts / T * pi / 2 + 3 * pi / 2 ) + r ,
@@ -885,20 +900,6 @@ def test_gainsched_unicycle(unicycle, method):
885900 clsys_lin .poles ()), np .sort (E ), rtol = 1e-2 )
886901
887902 # 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-
902903 resp = ct .input_output_response (clsys , timepts , [Xd , Ud ], X0 )
903904 np .testing .assert_allclose (
904905 resp .states [:, - 1 ], Xd [:, - 1 ], atol = 1e-2 , rtol = 1e-2 )
0 commit comments