@@ -92,60 +92,101 @@ def plot_results(t, y, u, figure=None, yf=None):
9292xf = [100. , 2. , 0. ]; uf = [10. , 0. ]
9393Tf = 10
9494
95- # Set up the cost functions
96- Q = np .diag ([0.1 , 1 , 0.1 ]) # keep lateral error low
97- R = np .eye (2 ) # minimize applied inputs
98- cost = obc .quadratic_cost (vehicle , Q , R , x0 = xf , u0 = uf )
99-
10095#
101- # Set up different types of constraints to demonstrate
96+ # Approach 1: standard quadratic cost
97+ #
98+ # We can set up the optimal control problem as trying to minimize the
99+ # distance form the desired final point while at the same time as not
100+ # exerting too much control effort to achieve our goal.
101+ #
102+ # Note: depending on what version of SciPy you are using, you might get a
103+ # warning message about precision loss, but the solution is pretty good.
102104#
103105
104- # Input constraints
105- constraints = [ obc .input_range_constraint (vehicle , [8 , - 0.1 ], [12 , 0.1 ]) ]
106-
107- # Terminal constraints (optional)
108- terminal = [ obc .state_range_constraint (vehicle , xf , xf ) ]
106+ # Set up the cost functions
107+ Q = np .diag ([1 , 10 , 1 ]) # keep lateral error low
108+ R = np .diag ([1 , 1 ]) # minimize applied inputs
109+ cost1 = obc .quadratic_cost (vehicle , Q , R , x0 = xf , u0 = uf )
109110
110- # Time horizon and possible initial guessses
111+ # Define the time horizon ( and spacing) for the optimization
111112horizon = np .linspace (0 , Tf , 10 , endpoint = True )
112- straight = [10 , 0 ] # straight trajectory
113- bend_left = [10 , 0.01 ] # slight left veer
114113
115- #
116- # Solve the optimal control problem in dififerent ways
117- #
114+ # Provide an intial guess (will be extended to entire horizon)
115+ bend_left = [10 , 0.01 ] # slight left veer
118116
119- # Basic setup: quadratic cost, no terminal constraint, straight initial path
117+ # Turn on debug level logging so that we can see what the optimizer is doing
120118logging .basicConfig (
121- level = logging .DEBUG , filename = "steering-straight .log" ,
119+ level = logging .DEBUG , filename = "steering-integral_cost .log" ,
122120 filemode = 'w' , force = True )
123- result = obc .compute_optimal_input (
124- vehicle , horizon , x0 , cost , initial_guess = straight ,
125- log = True , options = {'eps' : 0.01 })
126- t1 , u1 = result .time , result .inputs
121+
122+ # Compute the optimal control, setting step size for gradient calculation (eps)
123+ result1 = obc .compute_optimal_input (
124+ vehicle , horizon , x0 , cost1 , initial_guess = bend_left , log = True ,
125+ options = {'eps' : 0.01 })
126+
127+ # Extract and plot the results (+ state trajectory)
128+ t1 , u1 = result1 .time , result1 .inputs
127129t1 , y1 = ct .input_output_response (vehicle , horizon , u1 , x0 )
128130plot_results (t1 , y1 , u1 , figure = 1 , yf = xf [0 :2 ])
129131
130- # Add constraint on the input to avoid high steering angles
132+ #
133+ # Approach 2: input cost, input constraints, terminal cost
134+ #
135+ # The previous solution integrates the position error for the entire
136+ # horizon, and so the car changes lanes very quickly (at the cost of larger
137+ # inputs). Instead, we can penalize the final state and impose a higher
138+ # cost on the inputs, resuling in a more graduate lane change.
139+ #
140+ # We also set the solver explicitly (its actually the default one, but shows
141+ # how to do this).
142+ #
143+
144+ # Add input constraint, input cost, terminal cost
145+ constraints = [ obc .input_range_constraint (vehicle , [8 , - 0.1 ], [12 , 0.1 ]) ]
146+ traj_cost = obc .quadratic_cost (vehicle , None , np .diag ([0.1 , 1 ]), u0 = uf )
147+ term_cost = obc .quadratic_cost (vehicle , np .diag ([1 , 10 , 10 ]), None , x0 = xf )
148+
149+ # Change logging to keep less information
131150logging .basicConfig (
132- level = logging .INFO , filename = "./steering-bendleft .log" ,
151+ level = logging .INFO , filename = "./steering-terminal_cost .log" ,
133152 filemode = 'w' , force = True )
134- result = obc .compute_optimal_input (
135- vehicle , horizon , x0 , cost , constraints , initial_guess = bend_left ,
136- log = True , options = {'eps' : 0.01 })
137- t2 , u2 = result .time , result .inputs
153+
154+ # Compute the optimal control
155+ result2 = obc .compute_optimal_input (
156+ vehicle , horizon , x0 , traj_cost , constraints , terminal_cost = term_cost ,
157+ initial_guess = bend_left , log = True ,
158+ method = 'SLSQP' , options = {'eps' : 0.01 })
159+
160+ # Extract and plot the results (+ state trajectory)
161+ t2 , u2 = result2 .time , result2 .inputs
138162t2 , y2 = ct .input_output_response (vehicle , horizon , u2 , x0 )
139163plot_results (t2 , y2 , u2 , figure = 2 , yf = xf [0 :2 ])
140164
141- # Resolve with a terminal constraint (starting with previous result)
142- logging .basicConfig (
143- level = logging .WARN , filename = "./steering-terminal.log" ,
144- filemode = 'w' , force = True )
145- result = obc .compute_optimal_input (
146- vehicle , horizon , x0 , cost , constraints ,
147- terminal_constraints = terminal , initial_guess = u2 ,
148- log = True , options = {'eps' : 0.01 })
149- t3 , u3 = result .time , result .inputs
165+ #
166+ # Approach 3: terminal constraints and new solver
167+ #
168+ # As a final example, we can remove the cost function on the state and
169+ # replace it with a terminal *constraint* on the state. If a solution is
170+ # found, it guarantees we get to exactly the final state.
171+ #
172+ # To speeds things up a bit, we initalize the problem using the previous
173+ # optimal controller (which didn't quite hit the final value).
174+ #
175+
176+ # Input cost and terminal constraints
177+ cost3 = obc .quadratic_cost (vehicle , np .zeros ((3 ,3 )), R , u0 = uf )
178+ terminal = [ obc .state_range_constraint (vehicle , xf , xf ) ]
179+
180+ # Reset logging to its default values
181+ logging .basicConfig (level = logging .WARN , force = True )
182+
183+ # Compute the optimal control
184+ result3 = obc .compute_optimal_input (
185+ vehicle , horizon , x0 , cost3 , constraints ,
186+ terminal_constraints = terminal , initial_guess = u2 , log = True ,
187+ options = {'eps' : 0.01 })
188+
189+ # Extract and plot the results (+ state trajectory)
190+ t3 , u3 = result3 .time , result3 .inputs
150191t3 , y3 = ct .input_output_response (vehicle , horizon , u3 , x0 )
151192plot_results (t3 , y3 , u3 , figure = 3 , yf = xf [0 :2 ])
0 commit comments