7
7
# road. The controller compensates for these unknowns by measuring the speed
8
8
# of the car and adjusting the throttle appropriately.
9
9
#
10
- # This file explore the dynamics and control of the cruise control system,
11
- # following the material presenting in Feedback Systems by Astrom and Murray.
10
+ # This file explores the dynamics and control of the cruise control system,
11
+ # following the material presented in Feedback Systems by Astrom and Murray.
12
12
# A full nonlinear model of the vehicle dynamics is used, with both PI and
13
13
# state space control laws. Different methods of constructing control systems
14
- # are show , all using the InputOutputSystem class (and subclasses).
14
+ # are shown , all using the InputOutputSystem class (and subclasses).
15
15
16
16
import numpy as np
17
17
import matplotlib .pyplot as plt
@@ -87,7 +87,7 @@ def vehicle_update(t, x, u, params={}):
87
87
# the coefficient of rolling friction and sgn(v) is the sign of v (+/- 1) or
88
88
# zero if v = 0.
89
89
90
- Fr = m * g * Cr * sign (v )
90
+ Fr = m * g * Cr * sign (v )
91
91
92
92
# The aerodynamic drag is proportional to the square of the speed: Fa =
93
93
# 1/\rho Cd A |v| v, where \rho is the density of air, Cd is the
@@ -120,7 +120,7 @@ def motor_torque(omega, params={}):
120
120
# Define the input/output system for the vehicle
121
121
vehicle = ct .NonlinearIOSystem (
122
122
vehicle_update , None , name = 'vehicle' ,
123
- inputs = ('u' , 'gear' , 'theta' ), outputs = ('v' ), states = ('v' ))
123
+ inputs = ('u' , 'gear' , 'theta' ), outputs = ('v' ), states = ('v' ))
124
124
125
125
# Figure 1.11: A feedback system for controlling the speed of a vehicle. In
126
126
# this example, the speed of the vehicle is measured and compared to the
@@ -140,13 +140,13 @@ def motor_torque(omega, params={}):
140
140
# Outputs: v (vehicle velocity)
141
141
cruise_tf = ct .InterconnectedSystem (
142
142
(control_tf , vehicle ), name = 'cruise' ,
143
- connections = (
143
+ connections = (
144
144
['control.u' , '-vehicle.v' ],
145
145
['vehicle.u' , 'control.y' ]),
146
- inplist = ('control.u' , 'vehicle.gear' , 'vehicle.theta' ),
147
- inputs = ('vref' , 'gear' , 'theta' ),
148
- outlist = ('vehicle.v' , 'vehicle.u' ),
149
- outputs = ('v' , 'u' ))
146
+ inplist = ('control.u' , 'vehicle.gear' , 'vehicle.theta' ),
147
+ inputs = ('vref' , 'gear' , 'theta' ),
148
+ outlist = ('vehicle.v' , 'vehicle.u' ),
149
+ outputs = ('v' , 'u' ))
150
150
151
151
# Define the time and input vectors
152
152
T = np .linspace (0 , 25 , 101 )
@@ -168,10 +168,10 @@ def motor_torque(omega, params={}):
168
168
# Compute the equilibrium state for the system
169
169
X0 , U0 = ct .find_eqpt (
170
170
cruise_tf , [0 , vref [0 ]], [vref [0 ], gear [0 ], theta0 [0 ]],
171
- iu = [1 , 2 ], y0 = [vref [0 ], 0 ], iy = [0 ], params = {'m' :m })
171
+ iu = [1 , 2 ], y0 = [vref [0 ], 0 ], iy = [0 ], params = {'m' : m })
172
172
173
173
t , y = ct .input_output_response (
174
- cruise_tf , T , [vref , gear , theta_hill ], X0 , params = {'m' :m })
174
+ cruise_tf , T , [vref , gear , theta_hill ], X0 , params = {'m' : m })
175
175
176
176
# Plot the velocity
177
177
plt .sca (vel_axes )
@@ -202,7 +202,7 @@ def motor_torque(omega, params={}):
202
202
omega_range = np .linspace (0 , 700 , 701 )
203
203
plt .subplot (2 , 2 , 1 )
204
204
plt .plot (omega_range , [motor_torque (w ) for w in omega_range ])
205
- plt .xlabel ('Angular velocity $\omega$ [rad/s]' )
205
+ plt .xlabel (r 'Angular velocity $\omega$ [rad/s]' )
206
206
plt .ylabel ('Torque $T$ [Nm]' )
207
207
plt .grid (True , linestyle = 'dotted' )
208
208
@@ -228,6 +228,7 @@ def motor_torque(omega, params={}):
228
228
plt .xlabel ('Velocity $v$ [m/s]' )
229
229
plt .ylabel ('Torque $T$ [Nm]' )
230
230
231
+ plt .tight_layout (rect = [0 , 0.03 , 1 , 0.95 ]) # Make space for suptitle
231
232
plt .show (block = False )
232
233
233
234
# Figure 4.3: Car with cruise control encountering a sloping road
@@ -272,8 +273,8 @@ def pi_output(t, x, u, params={}):
272
273
273
274
control_pi = ct .NonlinearIOSystem (
274
275
pi_update , pi_output , name = 'control' ,
275
- inputs = ['v' , 'vref' ], outputs = ['u' ], states = ['z' ],
276
- params = {'kp' :0.5 , 'ki' :0.1 })
276
+ inputs = ['v' , 'vref' ], outputs = ['u' ], states = ['z' ],
277
+ params = {'kp' : 0.5 , 'ki' : 0.1 })
277
278
278
279
# Create the closed loop system
279
280
cruise_pi = ct .InterconnectedSystem (
@@ -290,8 +291,10 @@ def pi_output(t, x, u, params={}):
290
291
# desired velocity is recovered after 20 s.
291
292
292
293
# Define a function for creating a "standard" cruise control plot
293
- def cruise_plot (sys , t , y , t_hill = 5 , vref = 20 , antiwindup = False ,
294
- linetype = 'b-' , subplots = [None , None ]):
294
+ def cruise_plot (sys , t , y , label = None , t_hill = None , vref = 20 , antiwindup = False ,
295
+ linetype = 'b-' , subplots = None , legend = None ):
296
+ if subplots is None :
297
+ subplots = [None , None ]
295
298
# Figure out the plot bounds and indices
296
299
v_min = vref - 1.2 ; v_max = vref + 0.5 ; v_ind = sys .find_output ('v' )
297
300
u_min = 0 ; u_max = 2 if antiwindup else 1 ; u_ind = sys .find_output ('u' )
@@ -310,7 +313,8 @@ def cruise_plot(sys, t, y, t_hill=5, vref=20, antiwindup=False,
310
313
plt .sca (subplots [0 ])
311
314
plt .plot (t , y [v_ind ], linetype )
312
315
plt .plot (t , vref * np .ones (t .shape ), 'k-' )
313
- plt .plot ([t_hill , t_hill ], [v_min , v_max ], 'k--' )
316
+ if t_hill :
317
+ plt .axvline (t_hill , color = 'k' , linestyle = '--' , label = 't hill' )
314
318
plt .axis ([0 , t [- 1 ], v_min , v_max ])
315
319
plt .xlabel ('Time $t$ [s]' )
316
320
plt .ylabel ('Velocity $v$ [m/s]' )
@@ -320,17 +324,18 @@ def cruise_plot(sys, t, y, t_hill=5, vref=20, antiwindup=False,
320
324
subplot_axes [1 ] = plt .subplot (2 , 1 , 2 )
321
325
else :
322
326
plt .sca (subplots [1 ])
323
- plt .plot (t , y [u_ind ], 'r--' if antiwindup else linetype )
324
- plt .plot ([t_hill , t_hill ], [u_min , u_max ], 'k--' )
325
- plt .axis ([0 , t [- 1 ], u_min , u_max ])
326
- plt .xlabel ('Time $t$ [s]' )
327
- plt .ylabel ('Throttle $u$' )
328
-
327
+ plt .plot (t , y [u_ind ], 'r--' if antiwindup else linetype , label = label )
329
328
# Applied input profile
330
329
if antiwindup :
331
330
# TODO: plot the actual signal from the process?
332
- plt .plot (t , np .clip (y [u_ind ], 0 , 1 ), linetype )
333
- plt .legend (['Commanded' , 'Applied' ], frameon = False )
331
+ plt .plot (t , np .clip (y [u_ind ], 0 , 1 ), linetype , label = 'Applied' )
332
+ if t_hill :
333
+ plt .axvline (t_hill , color = 'k' , linestyle = '--' )
334
+ if legend :
335
+ plt .legend (frameon = False )
336
+ plt .axis ([0 , t [- 1 ], u_min , u_max ])
337
+ plt .xlabel ('Time $t$ [s]' )
338
+ plt .ylabel ('Throttle $u$' )
334
339
335
340
return subplot_axes
336
341
@@ -354,7 +359,7 @@ def cruise_plot(sys, t, y, t_hill=5, vref=20, antiwindup=False,
354
359
4. / 180. * pi * (t - 5 ) if t <= 6 else
355
360
4. / 180. * pi for t in T ]
356
361
t , y = ct .input_output_response (cruise_pi , T , [vref , gear , theta_hill ], X0 )
357
- cruise_plot (cruise_pi , t , y )
362
+ cruise_plot (cruise_pi , t , y , t_hill = 5 )
358
363
359
364
#
360
365
# Example 7.8: State space feedback with integral action
@@ -435,17 +440,15 @@ def sf_output(t, z, u, params={}):
435
440
4. / 180. * pi for t in T ]
436
441
t , y = ct .input_output_response (
437
442
cruise_sf , T , [vref , gear , theta_hill ], [X0 [0 ], 0 ],
438
- params = {'K' :K , 'kf' :kf , 'ki' :0.0 , 'kf' :kf , 'xd' :xd , 'ud' :ud , 'yd' :yd })
439
- subplots = cruise_plot (cruise_sf , t , y , t_hill = 8 , linetype = 'b--' )
443
+ params = {'K' : K , 'kf' : kf , 'ki' : 0.0 , 'kf' : kf , 'xd' : xd , 'ud' : ud , 'yd' : yd })
444
+ subplots = cruise_plot (cruise_sf , t , y , label = 'Proportional' , linetype = 'b--' )
440
445
441
446
# Response of the system with state feedback + integral action
442
447
t , y = ct .input_output_response (
443
448
cruise_sf , T , [vref , gear , theta_hill ], [X0 [0 ], 0 ],
444
- params = {'K' :K , 'kf' :kf , 'ki' :0.1 , 'kf' :kf , 'xd' :xd , 'ud' :ud , 'yd' :yd })
445
- cruise_plot (cruise_sf , t , y , t_hill = 8 , linetype = 'b-' , subplots = subplots )
446
-
447
- # Add a legend
448
- plt .legend (['Proportional' , 'PI control' ], frameon = False )
449
+ params = {'K' : K , 'kf' : kf , 'ki' : 0.1 , 'kf' : kf , 'xd' : xd , 'ud' : ud , 'yd' : yd })
450
+ cruise_plot (cruise_sf , t , y , label = 'PI control' , t_hill = 8 , linetype = 'b-' ,
451
+ subplots = subplots , legend = True )
449
452
450
453
# Example 11.5: simulate the effect of a (steeper) hill at t = 5 seconds
451
454
#
@@ -463,8 +466,9 @@ def sf_output(t, z, u, params={}):
463
466
6. / 180. * pi for t in T ]
464
467
t , y = ct .input_output_response (
465
468
cruise_pi , T , [vref , gear , theta_hill ], X0 ,
466
- params = {'kaw' :0 })
467
- cruise_plot (cruise_pi , t , y , antiwindup = True )
469
+ params = {'kaw' : 0 })
470
+ cruise_plot (cruise_pi , t , y , label = 'Commanded' , t_hill = 5 , antiwindup = True ,
471
+ legend = True )
468
472
469
473
# Example 11.6: add anti-windup compensation
470
474
#
@@ -477,8 +481,9 @@ def sf_output(t, z, u, params={}):
477
481
plt .suptitle ('Cruise control with integrator anti-windup protection' )
478
482
t , y = ct .input_output_response (
479
483
cruise_pi , T , [vref , gear , theta_hill ], X0 ,
480
- params = {'kaw' :2. })
481
- cruise_plot (cruise_pi , t , y , antiwindup = True )
484
+ params = {'kaw' : 2. })
485
+ cruise_plot (cruise_pi , t , y , label = 'Commanded' , t_hill = 5 , antiwindup = True ,
486
+ legend = True )
482
487
483
488
# If running as a standalone program, show plots and wait before closing
484
489
import os
0 commit comments