<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>http://fbswiki.org/wiki/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Murray</id>
	<title>FBSwiki - User contributions [en]</title>
	<link rel="self" type="application/atom+xml" href="http://fbswiki.org/wiki/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Murray"/>
	<link rel="alternate" type="text/html" href="http://fbswiki.org/wiki/index.php/Special:Contributions/Murray"/>
	<updated>2026-04-24T21:24:55Z</updated>
	<subtitle>User contributions</subtitle>
	<generator>MediaWiki 1.35.1</generator>
	<entry>
		<id>http://fbswiki.org/wiki/index.php?title=Figure_7.7:_Simulation_results_for_the_controlled_predator%E2%80%93prey_system&amp;diff=1375</id>
		<title>Figure 7.7: Simulation results for the controlled predator–prey system</title>
		<link rel="alternate" type="text/html" href="http://fbswiki.org/wiki/index.php?title=Figure_7.7:_Simulation_results_for_the_controlled_predator%E2%80%93prey_system&amp;diff=1375"/>
		<updated>2026-01-19T01:51:51Z</updated>

		<summary type="html">&lt;p&gt;Murray: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Figure&lt;br /&gt;
|Chapter=State Feedback&lt;br /&gt;
|Figure number=7&lt;br /&gt;
|Sort key=707&lt;br /&gt;
|Figure title=Simulation results for the controlled predator–prey system&lt;br /&gt;
|GitHub URL=https://github.com/murrayrm/fbs2e-python/blob/main/figure-7.7-predprey_place.py&lt;br /&gt;
|Requires=predprey.py&lt;br /&gt;
}}&lt;br /&gt;
[[Image:figure-7.7-predprey_place-time.png]] &amp;amp;nbsp;&lt;br /&gt;
[[Image:figure-7.7-predprey_place-pp.png]]&lt;br /&gt;
&lt;br /&gt;
'''Figure 7.7:''' Simulation results for the controlled predator–prey system. The population of lynxes and hares as a function of time is shown in (a), and a phase portrait for the controlled system is shown in (b). Feedback is used to make the population stable at He = 20.6 and Le = 30.&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&lt;br /&gt;
# predprey_place.py - stabilization via state space feedback&lt;br /&gt;
# RMM, 18 Jan 2026&lt;br /&gt;
&lt;br /&gt;
import matplotlib.pyplot as plt&lt;br /&gt;
import numpy as np&lt;br /&gt;
import control as ct&lt;br /&gt;
import fbs                      # FBS plotting customizations&lt;br /&gt;
&lt;br /&gt;
# System definition&lt;br /&gt;
from predprey import predprey&lt;br /&gt;
&lt;br /&gt;
# Find the equilibrium point and linearize&lt;br /&gt;
xe, ue = ct.find_eqpt(predprey, [20, 30], 0)&lt;br /&gt;
sys = predprey.linearize(xe, ue)&lt;br /&gt;
&lt;br /&gt;
# Eigenvalue placement&lt;br /&gt;
K = ct.place(sys.A, sys.B, [-0.1, -0.2])&lt;br /&gt;
kf = -1 / (sys.C[1] @ np.linalg.inv(sys.A - sys.B @ K) @ sys.B)&lt;br /&gt;
&lt;br /&gt;
ctrl = ct.nlsys(&lt;br /&gt;
    None, lambda t, x, u, params: -K @ (u[0:2] - xe) + kf * (u[2] - xe[1]),&lt;br /&gt;
    inputs=['H', 'L', 'r'], outputs=['u'],&lt;br /&gt;
)&lt;br /&gt;
clsys = ct.interconnect(&lt;br /&gt;
    [predprey, ctrl], inputs=['r'], outputs=['H', 'L', 'u'],&lt;br /&gt;
    name='predprey_clsys'&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
# Compute initial condition response&lt;br /&gt;
T = np.linspace(0, 100, 1000)&lt;br /&gt;
response = ct.input_output_response(clsys, T, 30, [20, 15])&lt;br /&gt;
&lt;br /&gt;
# Plot results&lt;br /&gt;
fbs.figure('mlh')               # Create figure using FBS defaults&lt;br /&gt;
plt.plot(response.time, response.outputs[0], 'b-', label=&amp;quot;Hare&amp;quot;)&lt;br /&gt;
plt.plot(response.time, response.outputs[1], 'r--', label=&amp;quot;Lynx&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
plt.xlabel(&amp;quot;Time [years]&amp;quot;)&lt;br /&gt;
plt.ylabel(&amp;quot;Population&amp;quot;)&lt;br /&gt;
plt.legend(frameon=False)&lt;br /&gt;
&lt;br /&gt;
plt.tight_layout()&lt;br /&gt;
fbs.savefig('figure-7.7-predprey_place-time.png')&lt;br /&gt;
&lt;br /&gt;
# Generate a phase portrait&lt;br /&gt;
fbs.figure('mlh')               # Create figure using FBS defaults&lt;br /&gt;
&lt;br /&gt;
# Create a function for the phase portrait that includes the reference input&lt;br /&gt;
ppfcn = lambda t, x: clsys.dynamics(t, x, [30], {})&lt;br /&gt;
&lt;br /&gt;
ct.phase_plane_plot(&lt;br /&gt;
    ppfcn, [1, 100, 1, 100], 30, plot_separatrices=False)&lt;br /&gt;
# Turn off vector field lines since phase_plan_plot has arrows&lt;br /&gt;
# ct.phaseplot.vectorfield(ppfcn, [0, 100, 0, 100], gridspec=[20, 20])&lt;br /&gt;
&lt;br /&gt;
plt.xlabel(&amp;quot;Hares&amp;quot;)&lt;br /&gt;
plt.ylabel(&amp;quot;Lynxes&amp;quot;)&lt;br /&gt;
plt.suptitle(&amp;quot;&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
plt.tight_layout()&lt;br /&gt;
fbs.savefig('figure-7.7-predprey_place-pp.png')&lt;br /&gt;
&amp;lt;/nowiki&amp;gt;&lt;/div&gt;</summary>
		<author><name>Murray</name></author>
	</entry>
	<entry>
		<id>http://fbswiki.org/wiki/index.php?title=File:Figure-7.7-predprey_place-pp.png&amp;diff=1374</id>
		<title>File:Figure-7.7-predprey place-pp.png</title>
		<link rel="alternate" type="text/html" href="http://fbswiki.org/wiki/index.php?title=File:Figure-7.7-predprey_place-pp.png&amp;diff=1374"/>
		<updated>2026-01-18T16:37:46Z</updated>

		<summary type="html">&lt;p&gt;Murray: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Murray</name></author>
	</entry>
	<entry>
		<id>http://fbswiki.org/wiki/index.php?title=File:Figure-7.7-predprey_place-time.png&amp;diff=1371</id>
		<title>File:Figure-7.7-predprey place-time.png</title>
		<link rel="alternate" type="text/html" href="http://fbswiki.org/wiki/index.php?title=File:Figure-7.7-predprey_place-time.png&amp;diff=1371"/>
		<updated>2026-01-18T16:33:03Z</updated>

		<summary type="html">&lt;p&gt;Murray: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Murray</name></author>
	</entry>
	<entry>
		<id>http://fbswiki.org/wiki/index.php?title=Figure_7.7:_Simulation_results_for_the_controlled_predator%E2%80%93prey_system&amp;diff=1370</id>
		<title>Figure 7.7: Simulation results for the controlled predator–prey system</title>
		<link rel="alternate" type="text/html" href="http://fbswiki.org/wiki/index.php?title=Figure_7.7:_Simulation_results_for_the_controlled_predator%E2%80%93prey_system&amp;diff=1370"/>
		<updated>2026-01-18T16:32:24Z</updated>

		<summary type="html">&lt;p&gt;Murray: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Figure&lt;br /&gt;
|Chapter=7&lt;br /&gt;
|Figure number=7&lt;br /&gt;
|Sort key=707&lt;br /&gt;
|Figure title=Simulation results for the controlled predator–prey system&lt;br /&gt;
|GitHub URL=https://github.com/murrayrm/fbs2e-python/blob/main/figure-7.7-predprey_place.py&lt;br /&gt;
|Requires=predprey.py&lt;br /&gt;
}}&lt;br /&gt;
[[Image:figure-7.7-predprey_place-time.png]] &amp;amp;nbsp;&lt;br /&gt;
[[Image:figure-7.7-predprey_place-pp.png]]&lt;br /&gt;
&lt;br /&gt;
'''Figure 7.7:''' Simulation results for the controlled predator–prey system. The population of lynxes and hares as a function of time is shown in (a), and a phase portrait for the controlled system is shown in (b). Feedback is used to make the population stable at He = 20.6 and Le = 30.&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&lt;br /&gt;
# predprey_place.py - stabilization via state space feedback&lt;br /&gt;
# RMM, 18 Jan 2026&lt;br /&gt;
&lt;br /&gt;
import matplotlib.pyplot as plt&lt;br /&gt;
import numpy as np&lt;br /&gt;
import control as ct&lt;br /&gt;
import fbs                      # FBS plotting customizations&lt;br /&gt;
&lt;br /&gt;
# System definition&lt;br /&gt;
from predprey import predprey&lt;br /&gt;
&lt;br /&gt;
# Find the equilibrium point and linearize&lt;br /&gt;
xe, ue = ct.find_eqpt(predprey, [20, 30], 0)&lt;br /&gt;
sys = predprey.linearize(xe, ue)&lt;br /&gt;
&lt;br /&gt;
# Eigenvalue placement&lt;br /&gt;
K = ct.place(sys.A, sys.B, [-0.1, -0.2])&lt;br /&gt;
kf = -1 / (sys.C[1] @ np.linalg.inv(sys.A - sys.B @ K) @ sys.B)&lt;br /&gt;
&lt;br /&gt;
ctrl = ct.nlsys(&lt;br /&gt;
    None, lambda t, x, u, params: -K @ (u[0:2] - xe) + kf * (u[2] - xe[1]),&lt;br /&gt;
    inputs=['H', 'L', 'r'], outputs=['u'],&lt;br /&gt;
)&lt;br /&gt;
clsys = ct.interconnect(&lt;br /&gt;
    [predprey, ctrl], inputs=['r'], outputs=['H', 'L', 'u'],&lt;br /&gt;
    name='predprey_clsys'&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
# Compute initial condition response&lt;br /&gt;
T = np.linspace(0, 100, 1000)&lt;br /&gt;
response = ct.input_output_response(clsys, T, 30, [20, 15])&lt;br /&gt;
&lt;br /&gt;
# Plot results&lt;br /&gt;
fbs.figure('mlh')               # Create figure using FBS defaults&lt;br /&gt;
plt.plot(response.time, response.outputs[0], 'b-', label=&amp;quot;Hare&amp;quot;)&lt;br /&gt;
plt.plot(response.time, response.outputs[1], 'r--', label=&amp;quot;Lynx&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
plt.xlabel(&amp;quot;Time [years]&amp;quot;)&lt;br /&gt;
plt.ylabel(&amp;quot;Population&amp;quot;)&lt;br /&gt;
plt.legend(frameon=False)&lt;br /&gt;
&lt;br /&gt;
plt.tight_layout()&lt;br /&gt;
fbs.savefig('figure-7.7-predprey_place-time.png')&lt;br /&gt;
&lt;br /&gt;
# Generate a phase portrait&lt;br /&gt;
fbs.figure('mlh')               # Create figure using FBS defaults&lt;br /&gt;
&lt;br /&gt;
# Create a function for the phase portrait that includes the reference input&lt;br /&gt;
ppfcn = lambda t, x: clsys.dynamics(t, x, [30], {})&lt;br /&gt;
&lt;br /&gt;
ct.phase_plane_plot(&lt;br /&gt;
    ppfcn, [1, 100, 1, 100], 30, plot_separatrices=False)&lt;br /&gt;
# Turn off vector field lines since phase_plan_plot has arrows&lt;br /&gt;
# ct.phaseplot.vectorfield(ppfcn, [0, 100, 0, 100], gridspec=[20, 20])&lt;br /&gt;
&lt;br /&gt;
plt.xlabel(&amp;quot;Hares&amp;quot;)&lt;br /&gt;
plt.ylabel(&amp;quot;Lynxes&amp;quot;)&lt;br /&gt;
plt.suptitle(&amp;quot;&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
plt.tight_layout()&lt;br /&gt;
fbs.savefig('figure-7.7-predprey_place-pp.png')&lt;br /&gt;
&amp;lt;/nowiki&amp;gt;&lt;/div&gt;</summary>
		<author><name>Murray</name></author>
	</entry>
	<entry>
		<id>http://fbswiki.org/wiki/index.php?title=Figure_7.7:_Simulation_results_for_the_controlled_predator%E2%80%93prey_system&amp;diff=1369</id>
		<title>Figure 7.7: Simulation results for the controlled predator–prey system</title>
		<link rel="alternate" type="text/html" href="http://fbswiki.org/wiki/index.php?title=Figure_7.7:_Simulation_results_for_the_controlled_predator%E2%80%93prey_system&amp;diff=1369"/>
		<updated>2026-01-18T16:27:33Z</updated>

		<summary type="html">&lt;p&gt;Murray: Created page with &amp;quot;{{Figure |Chapter=7 |Figure number=7 |Sort key=707 |Figure title=Simulation results for the controlled predator–prey system |GitHub URL=https://github.com/murrayrm/fbs2e-pyt...&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Figure&lt;br /&gt;
|Chapter=7&lt;br /&gt;
|Figure number=7&lt;br /&gt;
|Sort key=707&lt;br /&gt;
|Figure title=Simulation results for the controlled predator–prey system&lt;br /&gt;
|GitHub URL=https://github.com/murrayrm/fbs2e-python/blob/main/figure-7.7-predprey_place.py&lt;br /&gt;
|Requires=predprey.py&lt;br /&gt;
}}&lt;/div&gt;</summary>
		<author><name>Murray</name></author>
	</entry>
	<entry>
		<id>http://fbswiki.org/wiki/index.php?title=Biomolecular_Feedback_Systems/index.php/Main_Page&amp;diff=1368</id>
		<title>Biomolecular Feedback Systems/index.php/Main Page</title>
		<link rel="alternate" type="text/html" href="http://fbswiki.org/wiki/index.php?title=Biomolecular_Feedback_Systems/index.php/Main_Page&amp;diff=1368"/>
		<updated>2025-07-03T12:04:04Z</updated>

		<summary type="html">&lt;p&gt;Murray: Redirected page to Biomolecular Feedback Systems&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;#REDIRECT [[Biomolecular Feedback Systems]]&lt;/div&gt;</summary>
		<author><name>Murray</name></author>
	</entry>
	<entry>
		<id>http://fbswiki.org/wiki/index.php?title=Biomolecular_Feedback_Systems/&amp;diff=1367</id>
		<title>Biomolecular Feedback Systems/</title>
		<link rel="alternate" type="text/html" href="http://fbswiki.org/wiki/index.php?title=Biomolecular_Feedback_Systems/&amp;diff=1367"/>
		<updated>2025-07-03T12:02:27Z</updated>

		<summary type="html">&lt;p&gt;Murray: Redirected page to Biomolecular Feedback Systems&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;#REDIRECT [[Biomolecular Feedback Systems]]&lt;/div&gt;</summary>
		<author><name>Murray</name></author>
	</entry>
	<entry>
		<id>http://fbswiki.org/wiki/index.php?title=Biomolecular_Feedback_Systems&amp;diff=1366</id>
		<title>Biomolecular Feedback Systems</title>
		<link rel="alternate" type="text/html" href="http://fbswiki.org/wiki/index.php?title=Biomolecular_Feedback_Systems&amp;diff=1366"/>
		<updated>2025-06-24T13:24:51Z</updated>

		<summary type="html">&lt;p&gt;Murray: /* Contents */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{BFS quick links}}&lt;br /&gt;
&amp;lt;font size='+1'&amp;gt;[[Domitilla Del Vecchio]] and [[User:murray|Richard M. Murray]]&amp;lt;/font&amp;gt;&lt;br /&gt;
__NOTOC__&lt;br /&gt;
This page contains information on the text ''Biomolecular Feedback Systems'' by Domitilla Del Vecchio and Richard M. Murray.  &lt;br /&gt;
&lt;br /&gt;
Copyright in this book is held by Princeton University Press, who have kindly agreed to allow us to keep the book available on the web.&lt;br /&gt;
&lt;br /&gt;
This textbook is intended for researchers interested in the application of feedback and control to biomolecular systems.  The material has been designed so that it can be used in parallel with ''Feedback Systems'' as part of a course on biomolecular feedback and control systems, or as a standalone reference for readers who have had a basic course in feedback and control theory.&lt;br /&gt;
&lt;br /&gt;
===== News =====&lt;br /&gt;
{{#ask: [[Category:Announcements]] [[Book::BFS]] &lt;br /&gt;
| sort=Date | order=descening&lt;br /&gt;
| format=ul | limit=6&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== Contents ===&lt;br /&gt;
{| width=100% border=1 &lt;br /&gt;
|- valign=top&lt;br /&gt;
| width=50% |&lt;br /&gt;
* '''{{BFS pdf|Preface and Contents|bfs-frontmatter|14Sep14}}'''&lt;br /&gt;
* '''Chapter 1: {{BFS pdf|Introductory Concepts|bfs-intro|14Sep14}}'''&lt;br /&gt;
** Systems Biology: Modeling, Analysis and the Role of Feedback&lt;br /&gt;
** The Cell as a System&lt;br /&gt;
** Control and Dynamical Systems Tools&lt;br /&gt;
** Input/Output Modeling&lt;br /&gt;
** From Systems to Synthetic Biology&lt;br /&gt;
* '''Chapter 2: {{BFS pdf|Core Processes|bfs-coreproc|14Sep14}}'''&lt;br /&gt;
** Modeling Chemical Reactions&lt;br /&gt;
** Transcription and Translation&lt;br /&gt;
** Transcriptional Regulation&lt;br /&gt;
** Post-Transcriptional Regulation&lt;br /&gt;
** Cellular Subsystems&lt;br /&gt;
* '''Chapter 3: {{BFS pdf|Dynamic Behavior|bfs-dynamics|14Sep14}}'''&lt;br /&gt;
** Analysis Near Equilibria&lt;br /&gt;
** Robustness&lt;br /&gt;
** Oscillatory Behavior&lt;br /&gt;
** Bifurcations&lt;br /&gt;
** Model Reduction Techniques&lt;br /&gt;
* '''Chapter 4: {{BFS pdf|Stochastic Modeling and Analysis|bfs-stochastic|14Sep14}}'''&lt;br /&gt;
** Stochastic Modeling of Biochemical Systems&lt;br /&gt;
** Simulation of Stochastic Systems&lt;br /&gt;
** Input/Output Linear Stochastic Systems&lt;br /&gt;
| width=50% |&lt;br /&gt;
* '''Chapter 5: {{BFS pdf|Biological Circuit Components|bfs-circuits|14Sep14}}'''&lt;br /&gt;
** Introduction to Biological Circuit Design&lt;br /&gt;
** Negative Autoregulation&lt;br /&gt;
** The Toggle Switch&lt;br /&gt;
** The Repressilator&lt;br /&gt;
** Activator-repressor Clock&lt;br /&gt;
** An Incoherent Feedforward Loop (IFFL)&lt;br /&gt;
** Bacterial Chemotaxis&lt;br /&gt;
* '''Chapter 6: {{BFS pdf|Interconnecting Components|bfs-modules|14Sep14}}'''&lt;br /&gt;
** Input/Output Modeling and the Modularity Assumption&lt;br /&gt;
** Introduction to Retroactivity&lt;br /&gt;
** Retroactivity in Gene Circuits&lt;br /&gt;
** Retroactivity in Signaling Systems&lt;br /&gt;
** Insulation Devices: Retroactivity Attentuation &lt;br /&gt;
** A Case Study on the Use of Insulation Devices&lt;br /&gt;
* '''Chapter 7: {{BFS pdf|Design Tradeoffs|bfs-tradeoffs|14Sep14}}'''&lt;br /&gt;
** Competition for Shared Cellular Resources&lt;br /&gt;
** Stochastic Effects: Design Tradeoffs in Systems with Large Gains&lt;br /&gt;
* '''Chapter 8: {{BFS pdf|Resource Competition|bfs-competition|24Jun2025}}'''&lt;br /&gt;
** Resource competition in bacterial genetic circuits&lt;br /&gt;
** Resource competition in mammalian genetic circuits&lt;br /&gt;
** Feedforward control to mitigate the effects of resource sharing&lt;br /&gt;
** Feedback control to mitigate the effects of resource sharing&lt;br /&gt;
* '''{{BFS pdf|Bibliography and Index|bfs-backmatter|14Sep14}}'''&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Resources ===&lt;br /&gt;
* [[BFS errata|List of errata]]&lt;/div&gt;</summary>
		<author><name>Murray</name></author>
	</entry>
	<entry>
		<id>http://fbswiki.org/wiki/index.php?title=Figure_6.13:_AFM_frequency_response&amp;diff=1365</id>
		<title>Figure 6.13: AFM frequency response</title>
		<link rel="alternate" type="text/html" href="http://fbswiki.org/wiki/index.php?title=Figure_6.13:_AFM_frequency_response&amp;diff=1365"/>
		<updated>2025-02-19T06:09:09Z</updated>

		<summary type="html">&lt;p&gt;Murray: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Figure&lt;br /&gt;
|Chapter=Linear Systems&lt;br /&gt;
|Figure number=6.13&lt;br /&gt;
|Sort key=613&lt;br /&gt;
|Figure title=AFM frequency response&lt;br /&gt;
|GitHub URL=https://github.com/murrayrm/fbs2e-python/blob/main/example-6.9-afm_freqresp.py&lt;br /&gt;
}}&lt;br /&gt;
[[Image:figure-6.13-afm_freqresp.png]]&lt;br /&gt;
&lt;br /&gt;
'''Figure 6.13:''' AFM frequency response. (a) A block diagram for the vertical dynamics of an atomic force microscope in contact mode [not shown]. The plot in (b) shows the gain and phase for the piezo stack. The response contains two frequency peaks at resonances of the system, along with an antiresonance at &amp;lt;math&amp;gt;\omega = 268&amp;lt;/math&amp;gt; krad/s. The combination of a resonant peak followed by an antiresonance is common for systems with multiple lightly damped modes. The dashed horizontal line represents the gain equal to the zero frequency gain divided by &amp;lt;math&amp;gt;\sqrt{2}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&lt;br /&gt;
# example-6.9-afm_freqresp.py - &amp;lt;Short description&amp;gt;&lt;br /&gt;
# RMM, 28 Nov 2024&lt;br /&gt;
#&lt;br /&gt;
# Figure 6.13: AFM frequency response. (a) A block diagram for the vertical&lt;br /&gt;
# dynamics of an atomic force microscope in contact mode. The plot in (b)&lt;br /&gt;
# shows the gain and phase for the piezo stack. The response contains two&lt;br /&gt;
# frequency peaks at resonances of the system, along with an antiresonance&lt;br /&gt;
# at ω = 268 krad/s.  The combination of a resonant peak followed by an&lt;br /&gt;
# antiresonance is common for systems with multiple lightly damped&lt;br /&gt;
# modes. The dashed horizontal line represents the gain equal to the zero&lt;br /&gt;
# frequency gain divided by sqrt(2).&lt;br /&gt;
&lt;br /&gt;
import control as ct&lt;br /&gt;
import numpy as np&lt;br /&gt;
import matplotlib.pyplot as plt&lt;br /&gt;
from math import sqrt&lt;br /&gt;
ct.use_fbs_defaults()&lt;br /&gt;
&lt;br /&gt;
#&lt;br /&gt;
# System dynamics&lt;br /&gt;
#&lt;br /&gt;
&lt;br /&gt;
# System parameters&lt;br /&gt;
m1 = 0.15e-3&lt;br /&gt;
m2 = 1e-3&lt;br /&gt;
f1 = 40.9e3&lt;br /&gt;
f2 = 41.6e3&lt;br /&gt;
f3 = 120e3&lt;br /&gt;
&lt;br /&gt;
# Derived quantities&lt;br /&gt;
w1 = 2 * np.pi * f1&lt;br /&gt;
w2 = 2 * np.pi * f2&lt;br /&gt;
w3 = 2 * np.pi * f3&lt;br /&gt;
z1 = 0.1&lt;br /&gt;
z3 = 0.1&lt;br /&gt;
k2 = w2**2 * m2&lt;br /&gt;
c2 = m2 * z1 * w1&lt;br /&gt;
&lt;br /&gt;
# System matrices&lt;br /&gt;
A = np.array([[0, 1, 0, 0],&lt;br /&gt;
              [-k2 / (m1 + m2), -c2 / (m1 + m2), 1 / m2, 0],&lt;br /&gt;
              [0, 0, 0, 1],&lt;br /&gt;
              [0, 0, -w3**2, -2 * z3 * w3]])&lt;br /&gt;
B = np.array([[0], [0], [0], [w3**2]])&lt;br /&gt;
C = (m2 / (m1 + m2)) * np.array([m1 * k2 / (m1 + m2), m1 * c2 / (m1 + m2), 1, 0])&lt;br /&gt;
D = np.array([[0]])&lt;br /&gt;
&lt;br /&gt;
# Create the state-space system&lt;br /&gt;
sys = ct.ss(A, B, C, D)&lt;br /&gt;
&lt;br /&gt;
#&lt;br /&gt;
# (b) Frequency response&lt;br /&gt;
#&lt;br /&gt;
&lt;br /&gt;
# Generate the frequency response&lt;br /&gt;
omega = np.logspace(4, 7, 10000)  # Frequency range from 10^4 to 10^7 rad/s&lt;br /&gt;
freqresp = ct.frequency_response(sys, omega)&lt;br /&gt;
mag, phase = freqresp.magnitude, freqresp.phase&lt;br /&gt;
&lt;br /&gt;
# Create the Bode plot&lt;br /&gt;
cplt = freqresp.plot()&lt;br /&gt;
mag_ax, phase_ax = cplt.axes[:, 0]&lt;br /&gt;
cplt.set_plot_title(&amp;quot;(b) Frequency response&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
# Locate peaks and valleys&lt;br /&gt;
# Find the first peak&lt;br /&gt;
max_mag = 0&lt;br /&gt;
omega1 = None&lt;br /&gt;
for i, m in enumerate(mag):&lt;br /&gt;
    if m &amp;gt; max_mag:&lt;br /&gt;
        max_mag = m&lt;br /&gt;
        omega1 = omega[i]&lt;br /&gt;
    elif m &amp;lt; max_mag:&lt;br /&gt;
        break&lt;br /&gt;
&lt;br /&gt;
# Find the first valley&lt;br /&gt;
min_mag = max_mag&lt;br /&gt;
omega2 = None&lt;br /&gt;
for i, m in enumerate(mag):&lt;br /&gt;
    if m &amp;lt; min_mag:&lt;br /&gt;
        min_mag = m&lt;br /&gt;
        omega2 = omega[i]&lt;br /&gt;
    elif m &amp;gt; min_mag:&lt;br /&gt;
        break&lt;br /&gt;
&lt;br /&gt;
# Find the second peak (must be higher than first)&lt;br /&gt;
omega3 = None&lt;br /&gt;
for i, m in enumerate(mag):&lt;br /&gt;
    if m &amp;gt; max_mag:&lt;br /&gt;
        max_mag = m&lt;br /&gt;
        omega3 = omega[i]&lt;br /&gt;
    elif m &amp;lt; max_mag and omega3 is not None:&lt;br /&gt;
        break&lt;br /&gt;
&lt;br /&gt;
# Print peaks and valley frequencies&lt;br /&gt;
print(f&amp;quot;Peaks at {omega1:.2e}, {omega3:.2e}; valley at {omega2:.2e}&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
# Add lines to mark the frequencies&lt;br /&gt;
mag_ax.axhline(1 / sqrt(2), color='r', linestyle='--', linewidth=0.5)&lt;br /&gt;
&lt;br /&gt;
mag_ax.axvline(omega1, color='k', linestyle='--', linewidth=0.5)&lt;br /&gt;
mag_ax.text(omega1 * 1.1, 2, r&amp;quot;$M_{r1}$&amp;quot;)&lt;br /&gt;
mag_ax.text(omega1 * 1.1, 0.07, r&amp;quot;$\omega = \omega_{r1}$&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
mag_ax.axvline(omega3, color='k', linestyle='--', linewidth=0.5)&lt;br /&gt;
mag_ax.text(omega3 * 1.2, 3, r&amp;quot;$M_{r2}$&amp;quot;)&lt;br /&gt;
mag_ax.text(omega3 * 1.1, 0.07, r&amp;quot;$\omega = \omega_{r2}$&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
phase_ax.axvline(omega1, color='k', linestyle='--', linewidth=0.5)&lt;br /&gt;
phase_ax.axvline(omega3, color='k', linestyle='--', linewidth=0.5)&lt;br /&gt;
&lt;br /&gt;
# Save the figure&lt;br /&gt;
plt.savefig(&amp;quot;figure-6.9-afm_freqresp.png&amp;quot;, bbox_inches='tight')&lt;br /&gt;
&amp;lt;/nowiki&amp;gt;&lt;/div&gt;</summary>
		<author><name>Murray</name></author>
	</entry>
	<entry>
		<id>http://fbswiki.org/wiki/index.php?title=File:Springmass-coupled.png&amp;diff=1364</id>
		<title>File:Springmass-coupled.png</title>
		<link rel="alternate" type="text/html" href="http://fbswiki.org/wiki/index.php?title=File:Springmass-coupled.png&amp;diff=1364"/>
		<updated>2024-12-26T17:05:30Z</updated>

		<summary type="html">&lt;p&gt;Murray: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Murray</name></author>
	</entry>
	<entry>
		<id>http://fbswiki.org/wiki/index.php?title=File:Figure-7.6-steering_place.png&amp;diff=1363</id>
		<title>File:Figure-7.6-steering place.png</title>
		<link rel="alternate" type="text/html" href="http://fbswiki.org/wiki/index.php?title=File:Figure-7.6-steering_place.png&amp;diff=1363"/>
		<updated>2024-11-29T01:45:21Z</updated>

		<summary type="html">&lt;p&gt;Murray: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Murray</name></author>
	</entry>
	<entry>
		<id>http://fbswiki.org/wiki/index.php?title=Figure_7.6:_State_feedback_control_of_a_steering_system&amp;diff=1362</id>
		<title>Figure 7.6: State feedback control of a steering system</title>
		<link rel="alternate" type="text/html" href="http://fbswiki.org/wiki/index.php?title=Figure_7.6:_State_feedback_control_of_a_steering_system&amp;diff=1362"/>
		<updated>2024-11-29T01:44:24Z</updated>

		<summary type="html">&lt;p&gt;Murray: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Figure&lt;br /&gt;
|Chapter=State Feedback&lt;br /&gt;
|Figure number=7.6&lt;br /&gt;
|Sort key=706&lt;br /&gt;
|Figure title=State feedback control of a steering system&lt;br /&gt;
|GitHub URL=https://github.com/murrayrm/fbs2e-python/blob/main/example-7.4-steering_place.py&lt;br /&gt;
}}&lt;br /&gt;
[[Image:figure-7.6-steering_place.png]]&lt;br /&gt;
&lt;br /&gt;
'''Figure 7.6:''' State feedback control of a steering system. Unit step responses (from zero initial condition) obtained with controllers designed with &amp;lt;math&amp;gt;\zeta_c = 0.7&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;\omega_c = 0.5&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;0.7&amp;lt;/math&amp;gt;, and &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt; [rad/s] are shown in (a). The dashed lines indicate &amp;lt;math&amp;gt;\pm 5&amp;lt;/math&amp;gt;% deviations from the setpoint. Notice that response speed increases with increasing &amp;lt;math&amp;gt;\omega_c&amp;lt;/math&amp;gt;, but that large &amp;lt;math&amp;gt;\omega_c&amp;lt;/math&amp;gt; also give large initial control actions. Unit step responses obtained with a controller designed with &amp;lt;math&amp;gt;\omega_c = 0.7&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;\zeta_c = 0.5&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;0.7&amp;lt;/math&amp;gt;, and &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt; are shown in (b).&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&lt;br /&gt;
# example-7.4-steering_place.py - &amp;lt;Short description&amp;gt;&lt;br /&gt;
# RMM, 28 Nov 2024&lt;br /&gt;
#&lt;br /&gt;
# Figure 7.6: State feedback control of a steering system. Unit step&lt;br /&gt;
# responses (from zero initial condition) obtained with controllers&lt;br /&gt;
# designed with zeta_c = 0.7 and omega_c = 0.5, 0.7, and 1 [rad/s] are&lt;br /&gt;
# shown in (a). The dashed lines indicate ±5% deviations from the&lt;br /&gt;
# setpoint. Notice that response speed increases with increasing omega_c,&lt;br /&gt;
# but that large omega_c also give large initial control actions. Unit step&lt;br /&gt;
# responses obtained with a controller designed with omega_c = 0.7 and&lt;br /&gt;
# zeta_c = 0.5, 0.7, and 1 are shown 2in (b).&lt;br /&gt;
&lt;br /&gt;
import control as ct&lt;br /&gt;
import numpy as np&lt;br /&gt;
import matplotlib.pyplot as plt&lt;br /&gt;
ct.use_fbs_defaults()&lt;br /&gt;
&lt;br /&gt;
#&lt;br /&gt;
# System dynamics&lt;br /&gt;
#&lt;br /&gt;
&lt;br /&gt;
# Get the normalized linear dynamics&lt;br /&gt;
from steering import linearize_lateral&lt;br /&gt;
sys = linearize_lateral(normalize=True, output_full_state=True)&lt;br /&gt;
&lt;br /&gt;
# Function to place the poles at desired values&lt;br /&gt;
def steering_place(sys, omega, zeta):&lt;br /&gt;
    # Get the pole locations based on omega and zeta&lt;br /&gt;
    desired_poly = np.polynomial.Polynomial([omega**2, 2 * zeta * omega, 1])&lt;br /&gt;
    return ct.place(sys.A, sys.B, desired_poly.roots())&lt;br /&gt;
&lt;br /&gt;
# Set up the plotting grid to match the layout in the book&lt;br /&gt;
fig = plt.figure(constrained_layout=True)&lt;br /&gt;
gs = fig.add_gridspec(3, 14)     # allow some space in the middle&lt;br /&gt;
left = slice(0, 6)&lt;br /&gt;
right = slice(7, 13)&lt;br /&gt;
&lt;br /&gt;
#&lt;br /&gt;
# (a) Unit step response for varying omega_c&lt;br /&gt;
#&lt;br /&gt;
&lt;br /&gt;
ax_pos = fig.add_subplot(gs[0, left])&lt;br /&gt;
ax_delta = fig.add_subplot(gs[1, left])&lt;br /&gt;
ax_pos.set_title(&lt;br /&gt;
    r&amp;quot;(a) Unit step response for varying $\omega_c$&amp;quot;, size='medium')&lt;br /&gt;
&lt;br /&gt;
timepts = np.linspace(0, 20)&lt;br /&gt;
zeta_c = 0.7&lt;br /&gt;
for omega_c in [0.5, 0.7, 1]:&lt;br /&gt;
    # Compute the gains for the controller&lt;br /&gt;
    K = steering_place(sys, omega_c, zeta_c)&lt;br /&gt;
    kf = omega_c**2&lt;br /&gt;
&lt;br /&gt;
    # Compute the closed loop system&lt;br /&gt;
    clsys = ct.feedback(sys, K) * kf&lt;br /&gt;
&lt;br /&gt;
    # Simulate the closed loop dynamics&lt;br /&gt;
    response = ct.forced_response(clsys, timepts, 1, X0=0)&lt;br /&gt;
&lt;br /&gt;
    ax_pos.plot(response.time, response.states[0], 'b')&lt;br /&gt;
    ax_delta.plot(response.time, (kf - K @ response.states)[0], 'b')&lt;br /&gt;
&lt;br /&gt;
# Label the plot&lt;br /&gt;
ax_pos.set_ylabel(r&amp;quot;Lateral position $y/b$&amp;quot;)&lt;br /&gt;
ax_delta.set_xlabel(r&amp;quot;Normalized time $v_0 t/b$&amp;quot;)&lt;br /&gt;
ax_delta.set_ylabel(r&amp;quot;Steering angle $\delta$ [rad]&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
ax_pos.axhline(0.95, color='k', linestyle='--', linewidth=0.5)&lt;br /&gt;
ax_pos.axhline(1.05, color='k', linestyle='--', linewidth=0.5)&lt;br /&gt;
ax_pos.annotate(&lt;br /&gt;
    &amp;quot;&amp;quot;, xy=(3.5, 0.3), xytext=[0, 0.8], arrowprops={'arrowstyle': '&amp;lt;-'})&lt;br /&gt;
ax_pos.text(3.6, 0.25, r&amp;quot;$\omega_c$&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
ax_delta.annotate(&lt;br /&gt;
    &amp;quot;&amp;quot;, xy=(4, 0.1), xytext=(0, -0.2), arrowprops={'arrowstyle': '&amp;lt;-'})&lt;br /&gt;
ax_delta.text(3.5, 0.15, r&amp;quot;$\omega_c$&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
#&lt;br /&gt;
# (b) Unit step response for varying zeta_c&lt;br /&gt;
#&lt;br /&gt;
&lt;br /&gt;
ax_pos = fig.add_subplot(gs[0, right], sharey=ax_pos)&lt;br /&gt;
ax_delta = fig.add_subplot(gs[1, right], sharey=ax_delta)&lt;br /&gt;
ax_pos.set_title(&lt;br /&gt;
    r&amp;quot;(b) Unit step response for varying $\zeta_c$&amp;quot;, size='medium')&lt;br /&gt;
&lt;br /&gt;
timepts = np.linspace(0, 20)&lt;br /&gt;
omega_c = 0.7&lt;br /&gt;
for zeta_c in [0.5, 0.7, 1]:&lt;br /&gt;
    # Compute the gains for the controller&lt;br /&gt;
    K = steering_place(sys, omega_c, zeta_c)&lt;br /&gt;
    kf = omega_c**2&lt;br /&gt;
&lt;br /&gt;
    # Compute the closed loop system&lt;br /&gt;
    clsys = ct.feedback(sys, K) * kf&lt;br /&gt;
&lt;br /&gt;
    # Simulate the closed loop dynamics&lt;br /&gt;
    response = ct.forced_response(clsys, timepts, 1, X0=0)&lt;br /&gt;
&lt;br /&gt;
    ax_pos.plot(response.time, response.states[0], 'b')&lt;br /&gt;
    ax_delta.plot(response.time, (kf - K @ response.states)[0], 'b')&lt;br /&gt;
&lt;br /&gt;
# Label the plot&lt;br /&gt;
ax_pos.set_ylabel(r&amp;quot;Lateral position $y/b$&amp;quot;)&lt;br /&gt;
ax_delta.set_xlabel(r&amp;quot;Normalized time $v_0 t/b$&amp;quot;)&lt;br /&gt;
ax_delta.set_ylabel(r&amp;quot;Steering angle $\delta$ [rad]&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
ax_pos.axhline(0.95, color='k', linestyle='--', linewidth=0.5)&lt;br /&gt;
ax_pos.axhline(1.05, color='k', linestyle='--', linewidth=0.5)&lt;br /&gt;
ax_pos.annotate(&lt;br /&gt;
    &amp;quot;&amp;quot;, xy=(1.7, 1.15), xytext=(5.2, 0.6), arrowprops={'arrowstyle': '&amp;lt;-'})&lt;br /&gt;
ax_pos.text(5.5, 0.5, r&amp;quot;$\zeta_c$&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
ax_delta.annotate(&lt;br /&gt;
    &amp;quot;&amp;quot;, xy=(5.5, -0.2), xytext=(3.5, 0.15), arrowprops={'arrowstyle': '&amp;lt;-'})&lt;br /&gt;
ax_delta.text(3, 0.2, r&amp;quot;$\zeta_c$&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
# Save the figure&lt;br /&gt;
fig.align_ylabels()&lt;br /&gt;
plt.savefig(&amp;quot;figure-7.4-steering_place.png&amp;quot;, bbox_inches='tight')&lt;br /&gt;
&amp;lt;/nowiki&amp;gt;&lt;/div&gt;</summary>
		<author><name>Murray</name></author>
	</entry>
	<entry>
		<id>http://fbswiki.org/wiki/index.php?title=Figure_7.6:_State_feedback_control_of_a_steering_system&amp;diff=1361</id>
		<title>Figure 7.6: State feedback control of a steering system</title>
		<link rel="alternate" type="text/html" href="http://fbswiki.org/wiki/index.php?title=Figure_7.6:_State_feedback_control_of_a_steering_system&amp;diff=1361"/>
		<updated>2024-11-29T01:42:30Z</updated>

		<summary type="html">&lt;p&gt;Murray: Created page with &amp;quot;{{Figure |Chapter=State Feedback |Figure number=7.6 |Sort key=706 |Figure title=State feedback control of a steering system |GitHub URL=https://github.com/murrayrm/fbs2e-pytho...&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Figure&lt;br /&gt;
|Chapter=State Feedback&lt;br /&gt;
|Figure number=7.6&lt;br /&gt;
|Sort key=706&lt;br /&gt;
|Figure title=State feedback control of a steering system&lt;br /&gt;
|GitHub URL=https://github.com/murrayrm/fbs2e-python/blob/main/example-7.4-steering_place.py&lt;br /&gt;
}}&lt;br /&gt;
[[Image:figure-7.6-steering_place.png]]&lt;br /&gt;
&lt;br /&gt;
'''Figure 7.6:''' State feedback control of a steering system. Unit step responses (from zero initial condition) obtained with controllers designed with &amp;lt;math&amp;gt;\zeta_c = 0.7&amp;lt;/math) and &amp;lt;math&amp;gt;\omega_c = 0.5&amp;lt;/math&amp;gt;, 0.7, and 1 [rad/s] are shown in (a). The dashed lines indicate &amp;lt;math&amp;gt;\pm 5&amp;lt;/math&amp;gt;% deviations from the setpoint. Notice that response speed increases with increasing &amp;lt;math&amp;gt;\omega_c&amp;lt;/math&amp;gt;, but that large &amp;lt;math&amp;gt;\omega_c&amp;lt;/math&amp;gt; also give large initial control actions. Unit step responses obtained with a controller designed with &amp;lt;math&amp;gt;\omega_c = 0.7&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;\zeta_c = 0.5&amp;lt;/math&amp;gt;, 0.7, and 1 are shown in (b).&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&lt;br /&gt;
# example-7.4-steering_place.py - &amp;lt;Short description&amp;gt;&lt;br /&gt;
# RMM, 28 Nov 2024&lt;br /&gt;
#&lt;br /&gt;
# Figure 7.6: State feedback control of a steering system. Unit step&lt;br /&gt;
# responses (from zero initial condition) obtained with controllers&lt;br /&gt;
# designed with zeta_c = 0.7 and omega_c = 0.5, 0.7, and 1 [rad/s] are&lt;br /&gt;
# shown in (a). The dashed lines indicate ±5% deviations from the&lt;br /&gt;
# setpoint. Notice that response speed increases with increasing omega_c,&lt;br /&gt;
# but that large omega_c also give large initial control actions. Unit step&lt;br /&gt;
# responses obtained with a controller designed with omega_c = 0.7 and&lt;br /&gt;
# zeta_c = 0.5, 0.7, and 1 are shown 2in (b).&lt;br /&gt;
&lt;br /&gt;
import control as ct&lt;br /&gt;
import numpy as np&lt;br /&gt;
import matplotlib.pyplot as plt&lt;br /&gt;
ct.use_fbs_defaults()&lt;br /&gt;
&lt;br /&gt;
#&lt;br /&gt;
# System dynamics&lt;br /&gt;
#&lt;br /&gt;
&lt;br /&gt;
# Get the normalized linear dynamics&lt;br /&gt;
from steering import linearize_lateral&lt;br /&gt;
sys = linearize_lateral(normalize=True, output_full_state=True)&lt;br /&gt;
&lt;br /&gt;
# Function to place the poles at desired values&lt;br /&gt;
def steering_place(sys, omega, zeta):&lt;br /&gt;
    # Get the pole locations based on omega and zeta&lt;br /&gt;
    desired_poly = np.polynomial.Polynomial([omega**2, 2 * zeta * omega, 1])&lt;br /&gt;
    return ct.place(sys.A, sys.B, desired_poly.roots())&lt;br /&gt;
&lt;br /&gt;
# Set up the plotting grid to match the layout in the book&lt;br /&gt;
fig = plt.figure(constrained_layout=True)&lt;br /&gt;
gs = fig.add_gridspec(3, 14)     # allow some space in the middle&lt;br /&gt;
left = slice(0, 6)&lt;br /&gt;
right = slice(7, 13)&lt;br /&gt;
&lt;br /&gt;
#&lt;br /&gt;
# (a) Unit step response for varying omega_c&lt;br /&gt;
#&lt;br /&gt;
&lt;br /&gt;
ax_pos = fig.add_subplot(gs[0, left])&lt;br /&gt;
ax_delta = fig.add_subplot(gs[1, left])&lt;br /&gt;
ax_pos.set_title(&lt;br /&gt;
    r&amp;quot;(a) Unit step response for varying $\omega_c$&amp;quot;, size='medium')&lt;br /&gt;
&lt;br /&gt;
timepts = np.linspace(0, 20)&lt;br /&gt;
zeta_c = 0.7&lt;br /&gt;
for omega_c in [0.5, 0.7, 1]:&lt;br /&gt;
    # Compute the gains for the controller&lt;br /&gt;
    K = steering_place(sys, omega_c, zeta_c)&lt;br /&gt;
    kf = omega_c**2&lt;br /&gt;
&lt;br /&gt;
    # Compute the closed loop system&lt;br /&gt;
    clsys = ct.feedback(sys, K) * kf&lt;br /&gt;
&lt;br /&gt;
    # Simulate the closed loop dynamics&lt;br /&gt;
    response = ct.forced_response(clsys, timepts, 1, X0=0)&lt;br /&gt;
&lt;br /&gt;
    ax_pos.plot(response.time, response.states[0], 'b')&lt;br /&gt;
    ax_delta.plot(response.time, (kf - K @ response.states)[0], 'b')&lt;br /&gt;
&lt;br /&gt;
# Label the plot&lt;br /&gt;
ax_pos.set_ylabel(r&amp;quot;Lateral position $y/b$&amp;quot;)&lt;br /&gt;
ax_delta.set_xlabel(r&amp;quot;Normalized time $v_0 t/b$&amp;quot;)&lt;br /&gt;
ax_delta.set_ylabel(r&amp;quot;Steering angle $\delta$ [rad]&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
ax_pos.axhline(0.95, color='k', linestyle='--', linewidth=0.5)&lt;br /&gt;
ax_pos.axhline(1.05, color='k', linestyle='--', linewidth=0.5)&lt;br /&gt;
ax_pos.annotate(&lt;br /&gt;
    &amp;quot;&amp;quot;, xy=(3.5, 0.3), xytext=[0, 0.8], arrowprops={'arrowstyle': '&amp;lt;-'})&lt;br /&gt;
ax_pos.text(3.6, 0.25, r&amp;quot;$\omega_c$&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
ax_delta.annotate(&lt;br /&gt;
    &amp;quot;&amp;quot;, xy=(4, 0.1), xytext=(0, -0.2), arrowprops={'arrowstyle': '&amp;lt;-'})&lt;br /&gt;
ax_delta.text(3.5, 0.15, r&amp;quot;$\omega_c$&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
#&lt;br /&gt;
# (b) Unit step response for varying zeta_c&lt;br /&gt;
#&lt;br /&gt;
&lt;br /&gt;
ax_pos = fig.add_subplot(gs[0, right], sharey=ax_pos)&lt;br /&gt;
ax_delta = fig.add_subplot(gs[1, right], sharey=ax_delta)&lt;br /&gt;
ax_pos.set_title(&lt;br /&gt;
    r&amp;quot;(b) Unit step response for varying $\zeta_c$&amp;quot;, size='medium')&lt;br /&gt;
&lt;br /&gt;
timepts = np.linspace(0, 20)&lt;br /&gt;
omega_c = 0.7&lt;br /&gt;
for zeta_c in [0.5, 0.7, 1]:&lt;br /&gt;
    # Compute the gains for the controller&lt;br /&gt;
    K = steering_place(sys, omega_c, zeta_c)&lt;br /&gt;
    kf = omega_c**2&lt;br /&gt;
&lt;br /&gt;
    # Compute the closed loop system&lt;br /&gt;
    clsys = ct.feedback(sys, K) * kf&lt;br /&gt;
&lt;br /&gt;
    # Simulate the closed loop dynamics&lt;br /&gt;
    response = ct.forced_response(clsys, timepts, 1, X0=0)&lt;br /&gt;
&lt;br /&gt;
    ax_pos.plot(response.time, response.states[0], 'b')&lt;br /&gt;
    ax_delta.plot(response.time, (kf - K @ response.states)[0], 'b')&lt;br /&gt;
&lt;br /&gt;
# Label the plot&lt;br /&gt;
ax_pos.set_ylabel(r&amp;quot;Lateral position $y/b$&amp;quot;)&lt;br /&gt;
ax_delta.set_xlabel(r&amp;quot;Normalized time $v_0 t/b$&amp;quot;)&lt;br /&gt;
ax_delta.set_ylabel(r&amp;quot;Steering angle $\delta$ [rad]&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
ax_pos.axhline(0.95, color='k', linestyle='--', linewidth=0.5)&lt;br /&gt;
ax_pos.axhline(1.05, color='k', linestyle='--', linewidth=0.5)&lt;br /&gt;
ax_pos.annotate(&lt;br /&gt;
    &amp;quot;&amp;quot;, xy=(1.7, 1.15), xytext=(5.2, 0.6), arrowprops={'arrowstyle': '&amp;lt;-'})&lt;br /&gt;
ax_pos.text(5.5, 0.5, r&amp;quot;$\zeta_c$&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
ax_delta.annotate(&lt;br /&gt;
    &amp;quot;&amp;quot;, xy=(5.5, -0.2), xytext=(3.5, 0.15), arrowprops={'arrowstyle': '&amp;lt;-'})&lt;br /&gt;
ax_delta.text(3, 0.2, r&amp;quot;$\zeta_c$&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
# Save the figure&lt;br /&gt;
fig.align_ylabels()&lt;br /&gt;
plt.savefig(&amp;quot;figure-7.4-steering_place.png&amp;quot;, bbox_inches='tight')&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;/div&gt;</summary>
		<author><name>Murray</name></author>
	</entry>
	<entry>
		<id>http://fbswiki.org/wiki/index.php?title=File:Figure-6.13-afm_freqresp.png&amp;diff=1360</id>
		<title>File:Figure-6.13-afm freqresp.png</title>
		<link rel="alternate" type="text/html" href="http://fbswiki.org/wiki/index.php?title=File:Figure-6.13-afm_freqresp.png&amp;diff=1360"/>
		<updated>2024-11-28T19:22:02Z</updated>

		<summary type="html">&lt;p&gt;Murray: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Murray</name></author>
	</entry>
	<entry>
		<id>http://fbswiki.org/wiki/index.php?title=Figure_6.13:_AFM_frequency_response&amp;diff=1359</id>
		<title>Figure 6.13: AFM frequency response</title>
		<link rel="alternate" type="text/html" href="http://fbswiki.org/wiki/index.php?title=Figure_6.13:_AFM_frequency_response&amp;diff=1359"/>
		<updated>2024-11-28T19:21:38Z</updated>

		<summary type="html">&lt;p&gt;Murray: Created page with &amp;quot;{{Figure |Chapter=Linear Systems |Figure number=6.13 |Sort key=613 |Figure title=AFM frequency response |GitHub URL=https://github.com/murrayrm/fbs2e-python/blob/main/example-...&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Figure&lt;br /&gt;
|Chapter=Linear Systems&lt;br /&gt;
|Figure number=6.13&lt;br /&gt;
|Sort key=613&lt;br /&gt;
|Figure title=AFM frequency response&lt;br /&gt;
|GitHub URL=https://github.com/murrayrm/fbs2e-python/blob/main/example-6.9-afm_freqresp.py&lt;br /&gt;
}}&lt;br /&gt;
[[Image:figure-6.13-afm_freqresp.png]]&lt;br /&gt;
&lt;br /&gt;
'''Figure 6.13:''' AFM frequency response. (a) A block diagram for the vertical dynamics of an atomic force microscope in contact mode [not shown]. The plot in (b) shows the gain and phase for the piezo stack. The response contains two frequency peaks at resonances of the system, along with an antiresonance at &amp;lt;math&amp;gt;\omega = 268&amp;lt;/math&amp;gt; krad/s. The combination of a resonant peak followed by an antiresonance is common for systems with multiple lightly damped modes. The dashed horizontal line represents the gain equal to the zero frequency gain divided by &amp;lt;math&amp;gt;\sqrt{2}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&lt;br /&gt;
# example-6.9-afm_freqresp.py - &amp;lt;Short description&amp;gt;&lt;br /&gt;
# RMM, 28 Nov 2024&lt;br /&gt;
#&lt;br /&gt;
# Figure 6.13: AFM frequency response. (a) A block diagram for the vertical&lt;br /&gt;
# dynamics of an atomic force microscope in contact mode. The plot in (b)&lt;br /&gt;
# shows the gain and phase for the piezo stack. The response contains two&lt;br /&gt;
# frequency peaks at resonances of the system, along with an antiresonance&lt;br /&gt;
# at ω = 268 krad/s.  The combination of a resonant peak followed by an&lt;br /&gt;
# antiresonance is common for systems with multiple lightly damped&lt;br /&gt;
# modes. The dashed horizontal line represents the gain equal to the zero&lt;br /&gt;
# frequency gain divided by sqrt(2).&lt;br /&gt;
&lt;br /&gt;
import control as ct&lt;br /&gt;
import numpy as np&lt;br /&gt;
import matplotlib.pyplot as plt&lt;br /&gt;
from math import sqrt&lt;br /&gt;
ct.use_fbs_defaults()&lt;br /&gt;
&lt;br /&gt;
#&lt;br /&gt;
# System dynamics&lt;br /&gt;
#&lt;br /&gt;
&lt;br /&gt;
# System parameters&lt;br /&gt;
m1 = 0.15e-3&lt;br /&gt;
m2 = 1e-3&lt;br /&gt;
f1 = 40.9e3&lt;br /&gt;
f2 = 41.6e3&lt;br /&gt;
f3 = 120e3&lt;br /&gt;
&lt;br /&gt;
# Derived quantities&lt;br /&gt;
w1 = 2 * np.pi * f1&lt;br /&gt;
w2 = 2 * np.pi * f2&lt;br /&gt;
w3 = 2 * np.pi * f3&lt;br /&gt;
z1 = 0.1&lt;br /&gt;
z3 = 0.1&lt;br /&gt;
k2 = w2**2 * m2&lt;br /&gt;
c2 = m2 * z1 * w1&lt;br /&gt;
&lt;br /&gt;
# System matrices&lt;br /&gt;
A = np.array([[0, 1, 0, 0],&lt;br /&gt;
              [-k2 / (m1 + m2), -c2 / (m1 + m2), 1 / m2, 0],&lt;br /&gt;
              [0, 0, 0, 1],&lt;br /&gt;
              [0, 0, -w3**2, -2 * z3 * w3]])&lt;br /&gt;
B = np.array([[0], [0], [0], [w3**2]])&lt;br /&gt;
C = (m2 / (m1 + m2)) * np.array([m1 * k2 / (m1 + m2), m1 * c2 / (m1 + m2), 1, 0])&lt;br /&gt;
D = np.array([[0]])&lt;br /&gt;
&lt;br /&gt;
# Create the state-space system&lt;br /&gt;
sys = ct.ss(A, B, C, D)&lt;br /&gt;
&lt;br /&gt;
#&lt;br /&gt;
# (b) Frequency response&lt;br /&gt;
#&lt;br /&gt;
&lt;br /&gt;
# Generate the frequency response&lt;br /&gt;
omega = np.logspace(4, 7, 10000)  # Frequency range from 10^4 to 10^7 rad/s&lt;br /&gt;
freqresp = ct.frequency_response(sys, omega)&lt;br /&gt;
mag, phase = freqresp.magnitude[0, 0], freqresp.phase[0, 0]&lt;br /&gt;
&lt;br /&gt;
# Create the Bode plot&lt;br /&gt;
cplt = freqresp.plot()&lt;br /&gt;
mag_ax, phase_ax = cplt.axes[:, 0]&lt;br /&gt;
cplt.set_plot_title(&amp;quot;(b) Frequency response&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
# Locate peaks and valleys&lt;br /&gt;
# Find the first peak&lt;br /&gt;
max_mag = 0&lt;br /&gt;
omega1 = None&lt;br /&gt;
for i, m in enumerate(mag):&lt;br /&gt;
    if m &amp;gt; max_mag:&lt;br /&gt;
        max_mag = m&lt;br /&gt;
        omega1 = omega[i]&lt;br /&gt;
    elif m &amp;lt; max_mag:&lt;br /&gt;
        break&lt;br /&gt;
&lt;br /&gt;
# Find the first valley&lt;br /&gt;
min_mag = max_mag&lt;br /&gt;
omega2 = None&lt;br /&gt;
for i, m in enumerate(mag):&lt;br /&gt;
    if m &amp;lt; min_mag:&lt;br /&gt;
        min_mag = m&lt;br /&gt;
        omega2 = omega[i]&lt;br /&gt;
    elif m &amp;gt; min_mag:&lt;br /&gt;
        break&lt;br /&gt;
&lt;br /&gt;
# Find the second peak (must be higher than first)&lt;br /&gt;
omega3 = None&lt;br /&gt;
for i, m in enumerate(mag):&lt;br /&gt;
    if m &amp;gt; max_mag:&lt;br /&gt;
        max_mag = m&lt;br /&gt;
        omega3 = omega[i]&lt;br /&gt;
    elif m &amp;lt; max_mag and omega3 is not None:&lt;br /&gt;
        break&lt;br /&gt;
&lt;br /&gt;
# Print peaks and valley frequencies&lt;br /&gt;
print(f&amp;quot;Peaks at {omega1:.2e}, {omega3:.2e}; valley at {omega2:.2e}&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
# Add lines to mark the frequencies&lt;br /&gt;
mag_ax.axhline(1 / sqrt(2), color='r', linestyle='--', linewidth=0.5)&lt;br /&gt;
&lt;br /&gt;
mag_ax.axvline(omega1, color='k', linestyle='--', linewidth=0.5)&lt;br /&gt;
mag_ax.text(omega1 * 1.1, 2, r&amp;quot;$M_{r1}$&amp;quot;)&lt;br /&gt;
mag_ax.text(omega1 * 1.1, 0.07, r&amp;quot;$\omega = \omega_{r1}$&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
mag_ax.axvline(omega3, color='k', linestyle='--', linewidth=0.5)&lt;br /&gt;
mag_ax.text(omega3 * 1.2, 3, r&amp;quot;$M_{r2}$&amp;quot;)&lt;br /&gt;
mag_ax.text(omega3 * 1.1, 0.07, r&amp;quot;$\omega = \omega_{r2}$&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
phase_ax.axvline(omega1, color='k', linestyle='--', linewidth=0.5)&lt;br /&gt;
phase_ax.axvline(omega3, color='k', linestyle='--', linewidth=0.5)&lt;br /&gt;
&lt;br /&gt;
# Save the figure&lt;br /&gt;
plt.savefig(&amp;quot;figure-6.9-afm_freqresp.png&amp;quot;, bbox_inches='tight')&lt;br /&gt;
&amp;lt;/nowiki&amp;gt;&lt;/div&gt;</summary>
		<author><name>Murray</name></author>
	</entry>
	<entry>
		<id>http://fbswiki.org/wiki/index.php?title=File:Figure-6.12-opamp_bandpass.png&amp;diff=1358</id>
		<title>File:Figure-6.12-opamp bandpass.png</title>
		<link rel="alternate" type="text/html" href="http://fbswiki.org/wiki/index.php?title=File:Figure-6.12-opamp_bandpass.png&amp;diff=1358"/>
		<updated>2024-11-28T18:49:39Z</updated>

		<summary type="html">&lt;p&gt;Murray: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Murray</name></author>
	</entry>
	<entry>
		<id>http://fbswiki.org/wiki/index.php?title=Figure_6.12:_Active_band-pass_filter&amp;diff=1357</id>
		<title>Figure 6.12: Active band-pass filter</title>
		<link rel="alternate" type="text/html" href="http://fbswiki.org/wiki/index.php?title=Figure_6.12:_Active_band-pass_filter&amp;diff=1357"/>
		<updated>2024-11-28T18:49:06Z</updated>

		<summary type="html">&lt;p&gt;Murray: Created page with &amp;quot;{{Figure |Chapter=Linear Systems |Figure number=6.12 |Sort key=612 |Figure title=Active band-pass filter |GitHub URL=https://github.com/murrayrm/fbs2e-python/blob/main/example...&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Figure&lt;br /&gt;
|Chapter=Linear Systems&lt;br /&gt;
|Figure number=6.12&lt;br /&gt;
|Sort key=612&lt;br /&gt;
|Figure title=Active band-pass filter&lt;br /&gt;
|GitHub URL=https://github.com/murrayrm/fbs2e-python/blob/main/example-6.8-opamp_bandpass.py&lt;br /&gt;
}}&lt;br /&gt;
[[Image:figure-6.12-opamp_bandpass.png]]&lt;br /&gt;
&lt;br /&gt;
'''Figure 6.12:''' Active band-pass filter. The circuit diagram (a) [not shown] shows an op amp with two RC filters arranged to provide a band-pass filter. The plot in (b) shows the gain and phase of the filter as a function of frequency. Note that the phase starts at &amp;lt;math&amp;gt;-90^\circ&amp;lt;/math&amp;gt; due to the negative gain of the operational amplifier.&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&lt;br /&gt;
# example-6.8-opamp_bandpass.py - &amp;lt;Short description&amp;gt;&lt;br /&gt;
# RMM, 28 Nov 2028&lt;br /&gt;
#&lt;br /&gt;
# Figure 6.12: Active band-pass filter. The circuit diagram (a) shows an op&lt;br /&gt;
# amp with two RC filters arranged to provide a band-pass filter.  The plot&lt;br /&gt;
# in (b) shows the gain and phase of the filter as a function of frequency.&lt;br /&gt;
# Note that the phase starts at -90 deg due to the negative gain of the&lt;br /&gt;
# operational amplifier.&lt;br /&gt;
&lt;br /&gt;
import control as ct&lt;br /&gt;
import numpy as np&lt;br /&gt;
import matplotlib.pyplot as plt&lt;br /&gt;
ct.use_fbs_defaults()&lt;br /&gt;
&lt;br /&gt;
#&lt;br /&gt;
# System dynamics&lt;br /&gt;
#&lt;br /&gt;
&lt;br /&gt;
# Filter parameters&lt;br /&gt;
R1 = 100&lt;br /&gt;
R2 = 5000&lt;br /&gt;
C1 = 100e-6&lt;br /&gt;
C2 = 100e-6&lt;br /&gt;
&lt;br /&gt;
# State-space form of the solution&lt;br /&gt;
A = np.array([[-1 / (R1 * C1), 0],&lt;br /&gt;
              [1 / (R1 * C2), -1 / (R2 * C2)]])&lt;br /&gt;
B = np.array([[1 / (R1 * C1)],&lt;br /&gt;
              [-1 / (R1 * C2)]])&lt;br /&gt;
C = np.array([[0, 1]])&lt;br /&gt;
D = np.array([[0]])&lt;br /&gt;
&lt;br /&gt;
# Create the state-space system&lt;br /&gt;
sys = ct.ss(A, B, C, D)&lt;br /&gt;
&lt;br /&gt;
#&lt;br /&gt;
# (b) Frequency response&lt;br /&gt;
#&lt;br /&gt;
&lt;br /&gt;
# Generate the Bode plot data&lt;br /&gt;
omega = np.logspace(-1, 3, 100)  # Frequency range from 0.1 to 1000 rad/s&lt;br /&gt;
cplt = ct.frequency_response(sys, omega).plot(&lt;br /&gt;
    initial_phase=-90, title=&amp;quot;(b) Frequency response&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
# Plot the unit gain line&lt;br /&gt;
cplt.axes[0, 0].axhline(1, color='k', linestyle='-', linewidth=0.5)&lt;br /&gt;
&lt;br /&gt;
# Print system poles for bandwidth computation&lt;br /&gt;
print(&amp;quot;System poles (for computing bandwidth):&amp;quot;)&lt;br /&gt;
print(sys.poles())&lt;br /&gt;
&lt;br /&gt;
# Save the figure&lt;br /&gt;
plt.savefig(&amp;quot;figure-6.8-opamp_bandpass.png&amp;quot;, bbox_inches='tight')&lt;br /&gt;
&amp;lt;/nowiki&amp;gt;&lt;/div&gt;</summary>
		<author><name>Murray</name></author>
	</entry>
	<entry>
		<id>http://fbswiki.org/wiki/index.php?title=Errata:_sign_errors_in_Example_5.18_(noise_cancellation)&amp;diff=1356</id>
		<title>Errata: sign errors in Example 5.18 (noise cancellation)</title>
		<link rel="alternate" type="text/html" href="http://fbswiki.org/wiki/index.php?title=Errata:_sign_errors_in_Example_5.18_(noise_cancellation)&amp;diff=1356"/>
		<updated>2024-11-27T00:44:16Z</updated>

		<summary type="html">&lt;p&gt;Murray: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Errata&lt;br /&gt;
|Chapter=Dynamic Behavior&lt;br /&gt;
|Page number=5-34&lt;br /&gt;
|Line=8&lt;br /&gt;
|Version=3.1.5&lt;br /&gt;
|Date=26 Nov 2024&lt;br /&gt;
}}&lt;br /&gt;
In Example 5.18 (noise cancellation), there are two sign errors in equation (5.26) that are propagated through the next several lines.  The corrected text should read (with changes in red):&lt;br /&gt;
&lt;br /&gt;
Assuming for simplicity that &amp;lt;math&amp;gt;S=0&amp;lt;/math&amp;gt;, introduce &amp;lt;math&amp;gt;x_1=e=z-w&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;x_2 = a - a_0&amp;lt;/math&amp;gt;, and &amp;lt;math&amp;gt;x_3 = b - b_0&amp;lt;/math&amp;gt;.  Then&lt;br /&gt;
{| width=100%&lt;br /&gt;
|-&lt;br /&gt;
|&lt;br /&gt;
&amp;lt;center&amp;gt;&amp;lt;math&amp;gt;&lt;br /&gt;
\frac{dx_1}{dt} = a_0 (z - w) + (a - a_0) w + (b - b_0) n&lt;br /&gt;
    = a_0 x_1 {\color{red} \boldsymbol{-}} x_2 w {\color{red} \boldsymbol{-}} x_3 n.&lt;br /&gt;
&amp;lt;/math&amp;gt;&amp;lt;/center&amp;gt;&lt;br /&gt;
| (5.26)&lt;br /&gt;
|}&lt;br /&gt;
We will achieve noise cancellation if we can find a feedback law for changing the parameters &amp;lt;math&amp;gt;a&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;b&amp;lt;/math&amp;gt; so that the error &amp;lt;math&amp;gt;e&amp;lt;/math&amp;gt; goes to zero. To do this we choose&lt;br /&gt;
&amp;lt;center&amp;gt;&amp;lt;math&amp;gt;&lt;br /&gt;
  V(x_1,x_2,x_3)=\frac{1}{2} \bigl( \alpha x_1^2+x_2^2+x_3^2 \bigr)&lt;br /&gt;
&amp;lt;/math&amp;gt;&amp;lt;/center&amp;gt;&lt;br /&gt;
as a candidate Lyapunov function for&lt;br /&gt;
equation (5.26). The derivative of &amp;lt;math&amp;gt;V&amp;lt;/math&amp;gt; is&lt;br /&gt;
&amp;lt;center&amp;gt;&amp;lt;math&amp;gt;&lt;br /&gt;
  \dot V = \alpha x_1 \dot x_1 + x_2 \dot x_2 + x_3 \dot x_3&lt;br /&gt;
  = \alpha a_0 x_1^2 + x_2 (\dot x_2 {\color{red} \boldsymbol{-}} \alpha w x_1) + x_3 (\dot x_3 {\color{red} \boldsymbol{-}}&lt;br /&gt;
    \alpha n x_1).&lt;br /&gt;
&amp;lt;/math&amp;gt;&amp;lt;/center&amp;gt;&lt;br /&gt;
Choosing&lt;br /&gt;
{| width=100%&lt;br /&gt;
|-&lt;br /&gt;
|&lt;br /&gt;
&amp;lt;center&amp;gt;&amp;lt;math&amp;gt;&lt;br /&gt;
  \dot a = \dot x_2 = {\color{red}+} \alpha w x_1 = {\color{red}+} \alpha w e,\qquad &lt;br /&gt;
  \dot b =\dot x_3 = {\color{red}+} \alpha n x_1 = {\color{red}+} \alpha n e,&lt;br /&gt;
&amp;lt;/math&amp;gt;&amp;lt;/center&amp;gt;&lt;br /&gt;
| (5.27)&lt;br /&gt;
|}&lt;br /&gt;
we find that &amp;lt;math&amp;gt;\dot V = \alpha a_0 x_1^2 &amp;lt; 0&amp;lt;/math&amp;gt;, and it follows that the&lt;br /&gt;
quadratic function will decrease as long as &amp;lt;math&amp;gt;e = x_1 = w - z \neq 0&amp;lt;/math&amp;gt;.&lt;/div&gt;</summary>
		<author><name>Murray</name></author>
	</entry>
	<entry>
		<id>http://fbswiki.org/wiki/index.php?title=Errata:_sign_errors_in_Example_5.18_(noise_cancellation)&amp;diff=1355</id>
		<title>Errata: sign errors in Example 5.18 (noise cancellation)</title>
		<link rel="alternate" type="text/html" href="http://fbswiki.org/wiki/index.php?title=Errata:_sign_errors_in_Example_5.18_(noise_cancellation)&amp;diff=1355"/>
		<updated>2024-11-27T00:43:17Z</updated>

		<summary type="html">&lt;p&gt;Murray: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Errata&lt;br /&gt;
|Chapter=Dynamic Behavior&lt;br /&gt;
|Page number=5-34&lt;br /&gt;
|Line=8&lt;br /&gt;
|Version=3.1.5&lt;br /&gt;
|Date=26 Nov 2024&lt;br /&gt;
}}&lt;br /&gt;
In Example 5.18 (noise cancellation), there are two sign errors in equation (5.26) that are propagated through the next several lines.  The corrected text should read (with changes in red):&lt;br /&gt;
&lt;br /&gt;
Assuming for simplicity that &amp;lt;math&amp;gt;S=0&amp;lt;/math&amp;gt;, introduce &amp;lt;math&amp;gt;x_1=e=z-w&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;x_2 = a - a_0&amp;lt;/math&amp;gt;, and &amp;lt;math&amp;gt;x_3 = b - b_0&amp;lt;/math&amp;gt;.  Then&lt;br /&gt;
{| width=100%&lt;br /&gt;
|-&lt;br /&gt;
|&lt;br /&gt;
&amp;lt;center&amp;gt;&amp;lt;math&amp;gt;&lt;br /&gt;
\frac{dx_1}{dt} = a_0 (z - w) + (a - a_0) w + (b - b_0) n&lt;br /&gt;
    = a_0 x_1 {\color{red} \boldsymbol{-}} x_2 w {\color{blue}-} x_3 n.&lt;br /&gt;
&amp;lt;/math&amp;gt;&amp;lt;/center&amp;gt;&lt;br /&gt;
| (5.26)&lt;br /&gt;
|}&lt;br /&gt;
We will achieve noise cancellation if we can find a feedback law for changing the parameters &amp;lt;math&amp;gt;a&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;b&amp;lt;/math&amp;gt; so that the error &amp;lt;math&amp;gt;e&amp;lt;/math&amp;gt; goes to zero. To do this we choose&lt;br /&gt;
&amp;lt;center&amp;gt;&amp;lt;math&amp;gt;&lt;br /&gt;
  V(x_1,x_2,x_3)=\frac{1}{2} \bigl( \alpha x_1^2+x_2^2+x_3^2 \bigr)&lt;br /&gt;
&amp;lt;/math&amp;gt;&amp;lt;/center&amp;gt;&lt;br /&gt;
as a candidate Lyapunov function for&lt;br /&gt;
equation (5.26). The derivative of &amp;lt;math&amp;gt;V&amp;lt;/math&amp;gt; is&lt;br /&gt;
&amp;lt;center&amp;gt;&amp;lt;math&amp;gt;&lt;br /&gt;
  \dot V = \alpha x_1 \dot x_1 + x_2 \dot x_2 + x_3 \dot x_3&lt;br /&gt;
  = \alpha a_0 x_1^2 + x_2 (\dot x_2 {\color{red}-} \alpha w x_1) + x_3 (\dot x_3 {\color{red}-}&lt;br /&gt;
    \alpha n x_1).&lt;br /&gt;
&amp;lt;/math&amp;gt;&amp;lt;/center&amp;gt;&lt;br /&gt;
Choosing&lt;br /&gt;
{| width=100%&lt;br /&gt;
|-&lt;br /&gt;
|&lt;br /&gt;
&amp;lt;center&amp;gt;&amp;lt;math&amp;gt;&lt;br /&gt;
  \dot a = \dot x_2 = {\color{red}+} \alpha w x_1 = {\color{red}+} \alpha w e,\qquad &lt;br /&gt;
  \dot b =\dot x_3 = {\color{red}+} \alpha n x_1 = {\color{red}+} \alpha n e,&lt;br /&gt;
&amp;lt;/math&amp;gt;&amp;lt;/center&amp;gt;&lt;br /&gt;
| (5.27)&lt;br /&gt;
|}&lt;br /&gt;
we find that &amp;lt;math&amp;gt;\dot V = \alpha a_0 x_1^2 &amp;lt; 0&amp;lt;/math&amp;gt;, and it follows that the&lt;br /&gt;
quadratic function will decrease as long as &amp;lt;math&amp;gt;e = x_1 = w - z \neq 0&amp;lt;/math&amp;gt;.&lt;/div&gt;</summary>
		<author><name>Murray</name></author>
	</entry>
	<entry>
		<id>http://fbswiki.org/wiki/index.php?title=Errata:_sign_errors_in_Example_5.18_(noise_cancellation)&amp;diff=1354</id>
		<title>Errata: sign errors in Example 5.18 (noise cancellation)</title>
		<link rel="alternate" type="text/html" href="http://fbswiki.org/wiki/index.php?title=Errata:_sign_errors_in_Example_5.18_(noise_cancellation)&amp;diff=1354"/>
		<updated>2024-11-27T00:40:38Z</updated>

		<summary type="html">&lt;p&gt;Murray: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Errata&lt;br /&gt;
|Chapter=Dynamic Behavior&lt;br /&gt;
|Page number=5-34&lt;br /&gt;
|Line=8&lt;br /&gt;
|Version=3.1.5&lt;br /&gt;
|Date=26 Nov 2024&lt;br /&gt;
}}&lt;br /&gt;
In Example 5.18 (noise cancellation), there are two sign errors in equation (5.26) that are propagated through the next several lines.  The corrected text should read (with changes in red):&lt;br /&gt;
&lt;br /&gt;
Assuming for simplicity that &amp;lt;math&amp;gt;S=0&amp;lt;/math&amp;gt;, introduce &amp;lt;math&amp;gt;x_1=e=z-w&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;x_2 = a - a_0&amp;lt;/math&amp;gt;, and &amp;lt;math&amp;gt;x_3 = b - b_0&amp;lt;/math&amp;gt;.  Then&lt;br /&gt;
{| width=100%&lt;br /&gt;
|-&lt;br /&gt;
|&lt;br /&gt;
&amp;lt;center&amp;gt;&amp;lt;math&amp;gt;&lt;br /&gt;
\frac{dx_1}{dt} = a_0 (z - w) + (a - a_0) w + (b - b_0) n&lt;br /&gt;
    = a_0 x_1 {\color{red} \boldsymbol{-}} x_2 w {\color{red}-} x_3 n.&lt;br /&gt;
&amp;lt;/math&amp;gt;&amp;lt;/center&amp;gt;&lt;br /&gt;
| (5.26)&lt;br /&gt;
|}&lt;br /&gt;
We will achieve noise cancellation if we can find a feedback law for changing the parameters &amp;lt;math&amp;gt;a&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;b&amp;lt;/math&amp;gt; so that the error &amp;lt;math&amp;gt;e&amp;lt;/math&amp;gt; goes to zero. To do this we choose&lt;br /&gt;
&amp;lt;center&amp;gt;&amp;lt;math&amp;gt;&lt;br /&gt;
  V(x_1,x_2,x_3)=\frac{1}{2} \bigl( \alpha x_1^2+x_2^2+x_3^2 \bigr)&lt;br /&gt;
&amp;lt;/math&amp;gt;&amp;lt;/center&amp;gt;&lt;br /&gt;
as a candidate Lyapunov function for&lt;br /&gt;
equation (5.26). The derivative of &amp;lt;math&amp;gt;V&amp;lt;/math&amp;gt; is&lt;br /&gt;
&amp;lt;center&amp;gt;&amp;lt;math&amp;gt;&lt;br /&gt;
  \dot V = \alpha x_1 \dot x_1 + x_2 \dot x_2 + x_3 \dot x_3&lt;br /&gt;
  = \alpha a_0 x_1^2 + x_2 (\dot x_2 {\color{red}-} \alpha w x_1) + x_3 (\dot x_3 {\color{red}-}&lt;br /&gt;
    \alpha n x_1).&lt;br /&gt;
&amp;lt;/math&amp;gt;&amp;lt;/center&amp;gt;&lt;br /&gt;
Choosing&lt;br /&gt;
{| width=100%&lt;br /&gt;
|-&lt;br /&gt;
|&lt;br /&gt;
&amp;lt;center&amp;gt;&amp;lt;math&amp;gt;&lt;br /&gt;
  \dot a = \dot x_2 = {\color{red}+} \alpha w x_1 = {\color{red}+} \alpha w e,\qquad &lt;br /&gt;
  \dot b =\dot x_3 = {\color{red}+} \alpha n x_1 = {\color{red}+} \alpha n e,&lt;br /&gt;
&amp;lt;/math&amp;gt;&amp;lt;/center&amp;gt;&lt;br /&gt;
| (5.27)&lt;br /&gt;
|}&lt;br /&gt;
we find that &amp;lt;math&amp;gt;\dot V = \alpha a_0 x_1^2 &amp;lt; 0&amp;lt;/math&amp;gt;, and it follows that the&lt;br /&gt;
quadratic function will decrease as long as &amp;lt;math&amp;gt;e = x_1 = w - z \neq 0&amp;lt;/math&amp;gt;.&lt;/div&gt;</summary>
		<author><name>Murray</name></author>
	</entry>
	<entry>
		<id>http://fbswiki.org/wiki/index.php?title=Errata:_sign_errors_in_Example_5.18_(noise_cancellation)&amp;diff=1353</id>
		<title>Errata: sign errors in Example 5.18 (noise cancellation)</title>
		<link rel="alternate" type="text/html" href="http://fbswiki.org/wiki/index.php?title=Errata:_sign_errors_in_Example_5.18_(noise_cancellation)&amp;diff=1353"/>
		<updated>2024-11-27T00:39:11Z</updated>

		<summary type="html">&lt;p&gt;Murray: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Errata&lt;br /&gt;
|Chapter=Dynamic Behavior&lt;br /&gt;
|Page number=5-34&lt;br /&gt;
|Line=8&lt;br /&gt;
|Version=3.1.5&lt;br /&gt;
|Date=26 Nov 2024&lt;br /&gt;
}}&lt;br /&gt;
In Example 5.18 (noise cancellation), there are two sign errors in equation (5.26) that are propagated through the next several lines.  The corrected text should read (with changes in red):&lt;br /&gt;
&lt;br /&gt;
Assuming for simplicity that &amp;lt;math&amp;gt;S=0&amp;lt;/math&amp;gt;, introduce &amp;lt;math&amp;gt;x_1=e=z-w&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;x_2 = a - a_0&amp;lt;/math&amp;gt;, and &amp;lt;math&amp;gt;x_3 = b - b_0&amp;lt;/math&amp;gt;.  Then&lt;br /&gt;
{| width=100%&lt;br /&gt;
|-&lt;br /&gt;
|&lt;br /&gt;
&amp;lt;center&amp;gt;&amp;lt;math&amp;gt;&lt;br /&gt;
\frac{dx_1}{dt} = a_0 (z - w) + (a - a_0) w + (b - b_0) n&lt;br /&gt;
    = a_0 x_1 {\color{red}-} x_2 w {\color{red}-} x_3 n.&lt;br /&gt;
&amp;lt;/math&amp;gt;&amp;lt;/center&amp;gt;&lt;br /&gt;
| (5.26)&lt;br /&gt;
|}&lt;br /&gt;
We will achieve noise cancellation if we can find a feedback law for changing the parameters &amp;lt;math&amp;gt;a&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;b&amp;lt;/math&amp;gt; so that the error &amp;lt;math&amp;gt;e&amp;lt;/math&amp;gt; goes to zero. To do this we choose&lt;br /&gt;
&amp;lt;center&amp;gt;&amp;lt;math&amp;gt;&lt;br /&gt;
  V(x_1,x_2,x_3)=\frac{1}{2} \bigl( \alpha x_1^2+x_2^2+x_3^2 \bigr)&lt;br /&gt;
&amp;lt;/math&amp;gt;&amp;lt;/center&amp;gt;&lt;br /&gt;
as a candidate Lyapunov function for&lt;br /&gt;
equation (5.26). The derivative of &amp;lt;math&amp;gt;V&amp;lt;/math&amp;gt; is&lt;br /&gt;
&amp;lt;center&amp;gt;&amp;lt;math&amp;gt;&lt;br /&gt;
  \dot V = \alpha x_1 \dot x_1 + x_2 \dot x_2 + x_3 \dot x_3&lt;br /&gt;
  = \alpha a_0 x_1^2 + x_2 (\dot x_2 {\color{red}-} \alpha w x_1) + x_3 (\dot x_3 {\color{red}-}&lt;br /&gt;
    \alpha n x_1).&lt;br /&gt;
&amp;lt;/math&amp;gt;&amp;lt;/center&amp;gt;&lt;br /&gt;
Choosing&lt;br /&gt;
{| width=100%&lt;br /&gt;
|-&lt;br /&gt;
|&lt;br /&gt;
&amp;lt;center&amp;gt;&amp;lt;math&amp;gt;&lt;br /&gt;
  \dot a = \dot x_2 = {\color{red}+} \alpha w x_1 = {\color{red}+} \alpha w e,\qquad &lt;br /&gt;
  \dot b =\dot x_3 = {\color{red}+} \alpha n x_1 = {\color{red}+} \alpha n e,&lt;br /&gt;
&amp;lt;/math&amp;gt;&amp;lt;/center&amp;gt;&lt;br /&gt;
| (5.27)&lt;br /&gt;
|}&lt;br /&gt;
we find that &amp;lt;math&amp;gt;\dot V = \alpha a_0 x_1^2 &amp;lt; 0&amp;lt;/math&amp;gt;, and it follows that the&lt;br /&gt;
quadratic function will decrease as long as &amp;lt;math&amp;gt;e = x_1 = w - z \neq 0&amp;lt;/math&amp;gt;.&lt;/div&gt;</summary>
		<author><name>Murray</name></author>
	</entry>
	<entry>
		<id>http://fbswiki.org/wiki/index.php?title=Errata:_sign_errors_in_Example_5.18_(noise_cancellation)&amp;diff=1352</id>
		<title>Errata: sign errors in Example 5.18 (noise cancellation)</title>
		<link rel="alternate" type="text/html" href="http://fbswiki.org/wiki/index.php?title=Errata:_sign_errors_in_Example_5.18_(noise_cancellation)&amp;diff=1352"/>
		<updated>2024-11-27T00:30:05Z</updated>

		<summary type="html">&lt;p&gt;Murray: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Errata&lt;br /&gt;
|Chapter=Dynamic Behavior&lt;br /&gt;
|Page number=5-34&lt;br /&gt;
|Line=8&lt;br /&gt;
|Version=3.1.5&lt;br /&gt;
|Date=26 Nov 2024&lt;br /&gt;
}}&lt;br /&gt;
In Example 5.18 (noise cancellation), there are two sign errors in equation (5.26) that are propagated through the next several lines.  The corrected text should read (with changes in red):&lt;br /&gt;
&lt;br /&gt;
Assuming for simplicity that &amp;lt;math&amp;gt;S=0&amp;lt;/math&amp;gt;, introduce &amp;lt;math&amp;gt;x_1=e=z-w&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;x_2 = a - a_0&amp;lt;/math&amp;gt;, and &amp;lt;math&amp;gt;x_3 = b - b_0&amp;lt;/math&amp;gt;.  Then&lt;br /&gt;
{| width=100%&lt;br /&gt;
|-&lt;br /&gt;
|&lt;br /&gt;
&amp;lt;center&amp;gt;&amp;lt;math&amp;gt;&lt;br /&gt;
\frac{dx_1}{dt} = a_0 (z - w) + (a - a_0) w + (b - b_0) n&lt;br /&gt;
    = a_0 x_1 - x_2 w - x_3 n.&lt;br /&gt;
&amp;lt;/math&amp;gt;&amp;lt;/center&amp;gt;&lt;br /&gt;
| (5.26)&lt;br /&gt;
|}&lt;br /&gt;
We will achieve noise cancellation if we can find a feedback law for changing the parameters &amp;lt;math&amp;gt;a&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;b&amp;lt;/math&amp;gt; so that the error &amp;lt;math&amp;gt;e&amp;lt;/math&amp;gt; goes to zero. To do this we choose&lt;br /&gt;
&amp;lt;center&amp;gt;&amp;lt;math&amp;gt;&lt;br /&gt;
  V(x_1,x_2,x_3)=\frac{1}{2} \bigl( \alpha x_1^2+x_2^2+x_3^2 \bigr)&lt;br /&gt;
&amp;lt;/math&amp;gt;&amp;lt;/center&amp;gt;&lt;br /&gt;
as a candidate Lyapunov function for&lt;br /&gt;
equation (5.26). The derivative of &amp;lt;math&amp;gt;V&amp;lt;/math&amp;gt; is&lt;br /&gt;
&amp;lt;center&amp;gt;&amp;lt;math&amp;gt;&lt;br /&gt;
  \dot V = \alpha x_1 \dot x_1 + x_2 \dot x_2 + x_3 \dot x_3&lt;br /&gt;
  = \alpha a_0 x_1^2 + x_2 (\dot x_2 - \alpha w x_1) + x_3 (\dot x_3 -&lt;br /&gt;
    \alpha n x_1).&lt;br /&gt;
&amp;lt;/math&amp;gt;&amp;lt;/center&amp;gt;&lt;br /&gt;
Choosing&lt;br /&gt;
{| width=100%&lt;br /&gt;
|-&lt;br /&gt;
|&lt;br /&gt;
&amp;lt;center&amp;gt;&amp;lt;math&amp;gt;&lt;br /&gt;
  \dot a = \dot x_2 = \alpha w x_1 = \alpha w e,\qquad &lt;br /&gt;
  \dot b =\dot x_3 = \alpha n x_1 = \alpha n e,&lt;br /&gt;
&amp;lt;/math&amp;gt;&amp;lt;/center&amp;gt;&lt;br /&gt;
| (5.27)&lt;br /&gt;
|}&lt;br /&gt;
we find that &amp;lt;math&amp;gt;\dot V = \alpha a_0 x_1^2 &amp;lt; 0&amp;lt;/math&amp;gt;, and it follows that the&lt;br /&gt;
quadratic function will decrease as long as &amp;lt;math&amp;gt;e = x_1 = w - z \neq 0&amp;lt;/math&amp;gt;.&lt;/div&gt;</summary>
		<author><name>Murray</name></author>
	</entry>
	<entry>
		<id>http://fbswiki.org/wiki/index.php?title=Errata:_sign_errors_in_Example_5.18_(noise_cancellation)&amp;diff=1351</id>
		<title>Errata: sign errors in Example 5.18 (noise cancellation)</title>
		<link rel="alternate" type="text/html" href="http://fbswiki.org/wiki/index.php?title=Errata:_sign_errors_in_Example_5.18_(noise_cancellation)&amp;diff=1351"/>
		<updated>2024-11-27T00:29:24Z</updated>

		<summary type="html">&lt;p&gt;Murray: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Errata&lt;br /&gt;
|Chapter=Dynamic Behavior&lt;br /&gt;
|Page number=5-34&lt;br /&gt;
|Line=8&lt;br /&gt;
|Version=3.1.5&lt;br /&gt;
|Date=26 Nov 2024&lt;br /&gt;
}}&lt;br /&gt;
In Example 5.18 (noise cancellation), there are two sign errors in equation (5.26) that are propagated through the next several lines.  The corrected text should read (with changes in red):&lt;br /&gt;
&lt;br /&gt;
Assuming for simplicity that &amp;lt;math&amp;gt;S=0&amp;lt;/math&amp;gt;, introduce &amp;lt;math&amp;gt;x_1=e=z-w&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;x_2 = a - a_0&amp;lt;/math&amp;gt;, and &amp;lt;math&amp;gt;x_3 = b - b_0&amp;lt;/math&amp;gt;.  Then&lt;br /&gt;
{| width=100%&lt;br /&gt;
|-&lt;br /&gt;
|&lt;br /&gt;
&amp;lt;center&amp;gt;&amp;lt;math&amp;gt;&lt;br /&gt;
\frac{dx_1}{dt} = a_0 (z - w) + (a - a_0) w + (b - b_0) n&lt;br /&gt;
    = a_0 x_1 - x_2 w - x_3 n.&lt;br /&gt;
&amp;lt;/math&amp;gt;&amp;lt;/center&amp;gt;&lt;br /&gt;
| (5.26)&lt;br /&gt;
|}&lt;br /&gt;
We will achieve noise cancellation if we can find a feedback law for changing the parameters &amp;lt;math&amp;gt;a&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;b&amp;lt;/math&amp;gt; so that the error &amp;lt;math&amp;gt;e&amp;lt;/math&amp;gt; goes to zero. To do this we choose&lt;br /&gt;
&amp;lt;center&amp;gt;&amp;lt;math&amp;gt;&lt;br /&gt;
  V(x_1,x_2,x_3)=\frac{1}{2} \bigl( \alpha x_1^2+x_2^2+x_3^2 \bigr)&lt;br /&gt;
&amp;lt;/math&amp;gt;&amp;lt;/center&amp;gt;&lt;br /&gt;
as a candidate Lyapunov function for&lt;br /&gt;
equation (5.26). The derivative of &amp;lt;math&amp;gt;V&amp;lt;/math&amp;gt; is&lt;br /&gt;
&amp;lt;center&amp;gt;&amp;lt;math&amp;gt;&lt;br /&gt;
  \dot V = \alpha x_1 \dot x_1 + x_2 \dot x_2 + x_3 \dot x_3&lt;br /&gt;
  = \alpha a_0 x_1^2 + x_2 (\dot x_2 - \alpha w x_1) + x_3 (\dot x_3 -&lt;br /&gt;
    \alpha n x_1).&lt;br /&gt;
&amp;lt;/math&amp;gt;&amp;lt;/center&amp;gt;&lt;br /&gt;
Choosing&lt;br /&gt;
&amp;lt;center&amp;gt;&amp;lt;math&amp;gt;&lt;br /&gt;
  \dot a = \dot x_2 = \alpha w x_1 = \alpha w e,\qquad &lt;br /&gt;
  \dot b =\dot x_3 = \alpha n x_1 = \alpha n e,&lt;br /&gt;
&amp;lt;/math&amp;gt;&amp;lt;/center&amp;gt;&lt;br /&gt;
we find that &amp;lt;math&amp;gt;\dot V = \alpha a_0 x_1^2 &amp;lt; 0&amp;lt;/math&amp;gt;, and it follows that the&lt;br /&gt;
quadratic function will decrease as long as &amp;lt;math&amp;gt;e = x_1 = w - z \neq 0&amp;lt;/math&amp;gt;.&lt;/div&gt;</summary>
		<author><name>Murray</name></author>
	</entry>
	<entry>
		<id>http://fbswiki.org/wiki/index.php?title=Errata:_sign_errors_in_Example_5.18_(noise_cancellation)&amp;diff=1350</id>
		<title>Errata: sign errors in Example 5.18 (noise cancellation)</title>
		<link rel="alternate" type="text/html" href="http://fbswiki.org/wiki/index.php?title=Errata:_sign_errors_in_Example_5.18_(noise_cancellation)&amp;diff=1350"/>
		<updated>2024-11-27T00:27:57Z</updated>

		<summary type="html">&lt;p&gt;Murray: Created page with &amp;quot;{{Errata |Chapter=Dynamic Behavior |Page number=5-34 |Line=8 |Version=3.1.5 |Date=26 Nov 2024 }} In Example 5.18 (noise cancellation), there are two sign errors in equation (5...&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Errata&lt;br /&gt;
|Chapter=Dynamic Behavior&lt;br /&gt;
|Page number=5-34&lt;br /&gt;
|Line=8&lt;br /&gt;
|Version=3.1.5&lt;br /&gt;
|Date=26 Nov 2024&lt;br /&gt;
}}&lt;br /&gt;
In Example 5.18 (noise cancellation), there are two sign errors in equation (5.26) that are propagated through the next several lines.  The corrected text should read (with changes in red):&lt;br /&gt;
&lt;br /&gt;
Assuming for simplicity that &amp;lt;math&amp;gt;S=0&amp;lt;/math&amp;gt;, introduce &amp;lt;math&amp;gt;x_1=e=z-w&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;x_2 = a - a_0&amp;lt;/math&amp;gt;, and &amp;lt;math&amp;gt;x_3 = b - b_0&amp;lt;/math&amp;gt;.  Then&lt;br /&gt;
&amp;lt;center&amp;gt;&amp;lt;math&amp;gt;&lt;br /&gt;
\hfill \frac{dx_1}{dt} = a_0 (z - w) + (a - a_0) w + (b - b_0) n&lt;br /&gt;
    = a_0 x_1 - x_2 w - x_3 n. \hfill (5.26)&lt;br /&gt;
&amp;lt;/math&amp;gt;&amp;lt;/center&amp;gt;&lt;br /&gt;
We will achieve noise cancellation if we can find a feedback law for changing the parameters &amp;lt;math&amp;gt;a&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;b&amp;lt;/math&amp;gt; so that the error &amp;lt;math&amp;gt;e&amp;lt;/math&amp;gt; goes to zero. To do this we choose&lt;br /&gt;
&amp;lt;center&amp;gt;&amp;lt;math&amp;gt;&lt;br /&gt;
  V(x_1,x_2,x_3)=\frac{1}{2} \bigl( \alpha x_1^2+x_2^2+x_3^2 \bigr)&lt;br /&gt;
&amp;lt;/math&amp;gt;&amp;lt;/center&amp;gt;&lt;br /&gt;
as a candidate Lyapunov function for&lt;br /&gt;
equation (5.26). The derivative of &amp;lt;math&amp;gt;V&amp;lt;/math&amp;gt; is&lt;br /&gt;
&amp;lt;center&amp;gt;&amp;lt;math&amp;gt;&lt;br /&gt;
  \dot V = \alpha x_1 \dot x_1 + x_2 \dot x_2 + x_3 \dot x_3&lt;br /&gt;
  = \alpha a_0 x_1^2 + x_2 (\dot x_2 - \alpha w x_1) + x_3 (\dot x_3 -&lt;br /&gt;
    \alpha n x_1).&lt;br /&gt;
&amp;lt;/math&amp;gt;&amp;lt;/center&amp;gt;&lt;br /&gt;
Choosing&lt;br /&gt;
&amp;lt;center&amp;gt;&amp;lt;math&amp;gt;&lt;br /&gt;
  \dot a = \dot x_2 = \alpha w x_1 = \alpha w e,\qquad &lt;br /&gt;
  \dot b =\dot x_3 = \alpha n x_1 = \alpha n e,&lt;br /&gt;
  \label{ex:dynamics:noisecancu}&lt;br /&gt;
&amp;lt;/math&amp;gt;&amp;lt;/center&amp;gt;&lt;br /&gt;
we find that &amp;lt;math&amp;gt;\dot V = \alpha a_0 x_1^2 &amp;lt; 0&amp;lt;/math&amp;gt;, and it follows that the&lt;br /&gt;
quadratic function will decrease as long as &amp;lt;math&amp;gt;e = x_1 = w - z \neq 0&amp;lt;/math&amp;gt;.&lt;/div&gt;</summary>
		<author><name>Murray</name></author>
	</entry>
	<entry>
		<id>http://fbswiki.org/wiki/index.php?title=File:Figure-6.10-compartment_response.png&amp;diff=1349</id>
		<title>File:Figure-6.10-compartment response.png</title>
		<link rel="alternate" type="text/html" href="http://fbswiki.org/wiki/index.php?title=File:Figure-6.10-compartment_response.png&amp;diff=1349"/>
		<updated>2024-11-25T06:29:37Z</updated>

		<summary type="html">&lt;p&gt;Murray: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Murray</name></author>
	</entry>
	<entry>
		<id>http://fbswiki.org/wiki/index.php?title=Figure_6.10:_Response_of_a_compartment_model_to_a_constant_drug_infusion&amp;diff=1348</id>
		<title>Figure 6.10: Response of a compartment model to a constant drug infusion</title>
		<link rel="alternate" type="text/html" href="http://fbswiki.org/wiki/index.php?title=Figure_6.10:_Response_of_a_compartment_model_to_a_constant_drug_infusion&amp;diff=1348"/>
		<updated>2024-11-25T06:29:20Z</updated>

		<summary type="html">&lt;p&gt;Murray: Created page with &amp;quot;{{Figure |Chapter=Linear Systems |Figure number=6.10 |Sort key=610 |Figure title=Response of a compartment model to a constant drug infusion |GitHub URL=https://github.com/mur...&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Figure&lt;br /&gt;
|Chapter=Linear Systems&lt;br /&gt;
|Figure number=6.10&lt;br /&gt;
|Sort key=610&lt;br /&gt;
|Figure title=Response of a compartment model to a constant drug infusion&lt;br /&gt;
|GitHub URL=https://github.com/murrayrm/fbs2e-python/blob/main/example-6.10-compartment_response.py&lt;br /&gt;
}}&lt;br /&gt;
[[Image:figure-6.10-compartment_response.png]]&lt;br /&gt;
&lt;br /&gt;
'''Figure 6.10''': Response of a compartment model to a constant drug infusion. A simple diagram of the system is shown in (a). The step response (b) shows the rate of concentration buildup in compartment 2. In (c) a pulse of initial concentration is used to speed up the response.&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&lt;br /&gt;
# example-6.7-compartment_response.py - Compartment model response&lt;br /&gt;
# RMM, 24 Nov 2024&lt;br /&gt;
#&lt;br /&gt;
# Figure 6.10: Response of a compartment model to a constant drug&lt;br /&gt;
# infusion. A simple diagram of the system is shown in (a). The step&lt;br /&gt;
# response (b) shows the rate of concentration buildup in compartment 2. In&lt;br /&gt;
# (c) a pulse of initial concentration is used to speed up the response.&lt;br /&gt;
&lt;br /&gt;
import control as ct&lt;br /&gt;
import numpy as np&lt;br /&gt;
import matplotlib.pyplot as plt&lt;br /&gt;
ct.use_fbs_defaults()&lt;br /&gt;
&lt;br /&gt;
#&lt;br /&gt;
# System dynamics&lt;br /&gt;
#&lt;br /&gt;
&lt;br /&gt;
# Parameter settings for the model&lt;br /&gt;
k0 = 0.1&lt;br /&gt;
k1 = 0.1&lt;br /&gt;
k2 = 0.5&lt;br /&gt;
b0 = 1.5&lt;br /&gt;
&lt;br /&gt;
# Compartment model definition&lt;br /&gt;
Ada = np.array([[-k0 - k1, k1], [k2, -k2]])&lt;br /&gt;
Bda = np.array([[b0], [0]])&lt;br /&gt;
Cda = np.array([[0, 1]])&lt;br /&gt;
compartment = ct.ss(Ada, Bda, Cda, 0);&lt;br /&gt;
&lt;br /&gt;
# Set up the plotting grid to match the layout in the book&lt;br /&gt;
fig = plt.figure(constrained_layout=True)&lt;br /&gt;
gs = fig.add_gridspec(4, 3)&lt;br /&gt;
&lt;br /&gt;
#&lt;br /&gt;
# (a) Step input&lt;br /&gt;
#&lt;br /&gt;
&lt;br /&gt;
timepts = np.linspace(0, 50)&lt;br /&gt;
input = np.ones(timepts.size) * 0.1&lt;br /&gt;
response = ct.forced_response(compartment, timepts, input)&lt;br /&gt;
&lt;br /&gt;
ax = fig.add_subplot(gs[0, 1])&lt;br /&gt;
ax.set_title(&amp;quot;(b) Step input&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
ax.plot(response.time, response.outputs)&lt;br /&gt;
ax.axhline(response.outputs[-1], color='k', linewidth=0.5)&lt;br /&gt;
ax.set_ylabel(&amp;quot;Concentration $C_2$&amp;quot;)&lt;br /&gt;
ax.axis('tight')&lt;br /&gt;
ax.axis([0, 50, 0, 2])&lt;br /&gt;
&lt;br /&gt;
ax = fig.add_subplot(gs[1, 1])&lt;br /&gt;
ax.plot(response.time, response.inputs)&lt;br /&gt;
ax.set_xlabel(&amp;quot;Time $t$ [min]&amp;quot;)&lt;br /&gt;
ax.set_ylabel(&amp;quot;Input dosage&amp;quot;)&lt;br /&gt;
ax.axis('tight')&lt;br /&gt;
ax.axis([0, 50, 0, 0.4])&lt;br /&gt;
&lt;br /&gt;
#&lt;br /&gt;
# (b) Pulse input&lt;br /&gt;
#&lt;br /&gt;
&lt;br /&gt;
timepts = np.linspace(0, 50, 200)&lt;br /&gt;
input = np.ones(timepts.size) * 0.1&lt;br /&gt;
input[:20] = 0.3                # Increase value for first 5 seconds&lt;br /&gt;
response = ct.forced_response(compartment, timepts, input)&lt;br /&gt;
&lt;br /&gt;
ax = fig.add_subplot(gs[0, 2])&lt;br /&gt;
ax.set_title(&amp;quot;(c) Pulse input&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
ax.plot(response.time, response.outputs)&lt;br /&gt;
ax.axhline(response.outputs[-1], color='k', linewidth=0.5)&lt;br /&gt;
ax.set_ylabel(&amp;quot;Concentration $C_2$&amp;quot;)&lt;br /&gt;
ax.axis('tight')&lt;br /&gt;
ax.axis([0, 50, 0, 2])&lt;br /&gt;
&lt;br /&gt;
ax = fig.add_subplot(gs[1, 2])&lt;br /&gt;
ax.plot(response.time, response.inputs)&lt;br /&gt;
ax.set_xlabel(&amp;quot;Time $t$ [min]&amp;quot;)&lt;br /&gt;
ax.set_ylabel(&amp;quot;Input dosage&amp;quot;)&lt;br /&gt;
ax.axis('tight')&lt;br /&gt;
ax.axis([0, 50, 0, 0.4])&lt;br /&gt;
&lt;br /&gt;
# Save the figure&lt;br /&gt;
fig.align_ylabels()&lt;br /&gt;
plt.savefig(&amp;quot;figure-6.10-compartment_response.png&amp;quot;, bbox_inches='tight')&lt;br /&gt;
&amp;lt;/nowiki&amp;gt;&lt;/div&gt;</summary>
		<author><name>Murray</name></author>
	</entry>
	<entry>
		<id>http://fbswiki.org/wiki/index.php?title=Spring-mass_system&amp;diff=1347</id>
		<title>Spring-mass system</title>
		<link rel="alternate" type="text/html" href="http://fbswiki.org/wiki/index.php?title=Spring-mass_system&amp;diff=1347"/>
		<updated>2024-11-25T05:59:57Z</updated>

		<summary type="html">&lt;p&gt;Murray: Created page with &amp;quot;{| class=&amp;quot;wikitable&amp;quot; |- ! GitHub URL | https://github.com/murrayrm/fbs2e-python/blob/main/springmass.py |- ! Requires | python-control |}  This pa...&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! GitHub URL&lt;br /&gt;
| https://github.com/murrayrm/fbs2e-python/blob/main/springmass.py&lt;br /&gt;
|-&lt;br /&gt;
! Requires&lt;br /&gt;
| [[https:python-control.org|python-control]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
This page documents the spring-mass system that is used as a running example throughout the text.  A description of the dynamics of this system are presented in {{chapter link|System Modeling}}, Example 3.1.  This page contains a description of the system, including the models and commands used to generate some of the plots in the text.&lt;br /&gt;
&lt;br /&gt;
Note: the Python code on this page makes use of the [https://python-control.org Python Control Systems Library], an open source software toolbox for control systems analysis.&lt;br /&gt;
&lt;br /&gt;
Figures making use of this model:&lt;br /&gt;
{{#ask: [[Category:Figures]] [[Requires::springmass.py]] | format=ul | sort=Sort key}}&lt;/div&gt;</summary>
		<author><name>Murray</name></author>
	</entry>
	<entry>
		<id>http://fbswiki.org/wiki/index.php?title=Springmass.py&amp;diff=1346</id>
		<title>Springmass.py</title>
		<link rel="alternate" type="text/html" href="http://fbswiki.org/wiki/index.php?title=Springmass.py&amp;diff=1346"/>
		<updated>2024-11-25T05:58:19Z</updated>

		<summary type="html">&lt;p&gt;Murray: Redirected page to Spring-mass system&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;#REDIRECT [[Spring-mass system]]&lt;/div&gt;</summary>
		<author><name>Murray</name></author>
	</entry>
	<entry>
		<id>http://fbswiki.org/wiki/index.php?title=Figure_6.5:_Modes_for_a_second-order_system_with_real_eigenvalues&amp;diff=1345</id>
		<title>Figure 6.5: Modes for a second-order system with real eigenvalues</title>
		<link rel="alternate" type="text/html" href="http://fbswiki.org/wiki/index.php?title=Figure_6.5:_Modes_for_a_second-order_system_with_real_eigenvalues&amp;diff=1345"/>
		<updated>2024-11-25T05:55:44Z</updated>

		<summary type="html">&lt;p&gt;Murray: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Figure&lt;br /&gt;
|Chapter=Linear Systems&lt;br /&gt;
|Figure number=6.5&lt;br /&gt;
|Sort key=605&lt;br /&gt;
|Figure title=The notion of modes for a second-order system with real eigenvalues.&lt;br /&gt;
|GitHub URL=https://github.com/murrayrm/fbs2e-python/blob/main/figure-6.5-modes.py&lt;br /&gt;
|Requires=springmass.py&lt;br /&gt;
}}&lt;br /&gt;
{| border=0&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=2 | [[Image:figure-6.5-springmass_modes-pp.png]] &lt;br /&gt;
| [[Image:figure-6.5-springmass_modes-slow.png]] &lt;br /&gt;
|-&lt;br /&gt;
| [[Image:figure-6.5-springmass_modes-fast.png]] &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
'''Figure 6.5''': The notion of modes for a second-order system with real eigenvalues. The left figure shows the phase portrait and the modes corresponding to solutions that start on the eigenvectors (bold lines). The corresponding time functions are shown on the right.&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&lt;br /&gt;
# superposition.py - superposition of homogeneous and particular solutions&lt;br /&gt;
# RMM, 19 Apr 2024&lt;br /&gt;
&lt;br /&gt;
import matplotlib.pyplot as plt&lt;br /&gt;
import numpy as np&lt;br /&gt;
import control as ct&lt;br /&gt;
import fbs                      # FBS plotting customizations&lt;br /&gt;
&lt;br /&gt;
# Spring mass system&lt;br /&gt;
from springmass import springmass       # use spring mass dynamics&lt;br /&gt;
sys = springmass / springmass(0).real   # normalize the response to 1&lt;br /&gt;
X0 = [2, -1]                            # initial condition&lt;br /&gt;
&lt;br /&gt;
# Create input vectors&lt;br /&gt;
tvec = np.linspace(0, 60, 100)&lt;br /&gt;
u1 = 0 * tvec&lt;br /&gt;
u2 = np.hstack([tvec[0:50]/tvec[50], 1 - tvec[0:50]/tvec[50]])&lt;br /&gt;
&lt;br /&gt;
# Run simulations for the different cases&lt;br /&gt;
homogeneous = ct.forced_response(sys, tvec, u1, X0=X0)&lt;br /&gt;
particular = ct.forced_response(sys, tvec, u2)&lt;br /&gt;
complete = ct.forced_response(sys, tvec, u1 + u2, X0=X0)&lt;br /&gt;
&lt;br /&gt;
# Plot results&lt;br /&gt;
fig, axs = plt.subplots(3, 3, figsize=[8, 4], layout='tight')&lt;br /&gt;
for i, resp in enumerate([homogeneous, particular, complete]):&lt;br /&gt;
    axs[i, 0].plot(resp.time, resp.inputs)&lt;br /&gt;
    axs[0, 0].set_title(&amp;quot;Input $u$&amp;quot;)&lt;br /&gt;
    axs[i, 0].set_ylim(-2, 2)&lt;br /&gt;
    &lt;br /&gt;
    axs[i, 1].plot(&lt;br /&gt;
        resp.time, resp.states[0], 'b',&lt;br /&gt;
        resp.time, resp.states[1], 'r--')&lt;br /&gt;
    axs[0, 1].set_title(&amp;quot;States $x_1$, $x_2$&amp;quot;)&lt;br /&gt;
    axs[i, 1].set_ylim(-2, 2)&lt;br /&gt;
&lt;br /&gt;
    axs[i, 2].plot(resp.time, resp.outputs)&lt;br /&gt;
    axs[0, 2].set_title(&amp;quot;Output $y$&amp;quot;)&lt;br /&gt;
    axs[i, 2].set_ylim(-2, 2)&lt;br /&gt;
&lt;br /&gt;
# Label the plots&lt;br /&gt;
axs[0, 0].set_ylabel(&amp;quot;Homogeneous&amp;quot;)&lt;br /&gt;
axs[1, 0].set_ylabel(&amp;quot;Particular&amp;quot;)&lt;br /&gt;
axs[2, 0].set_ylabel(&amp;quot;Complete&amp;quot;)&lt;br /&gt;
for i in range(3):&lt;br /&gt;
    axs[2, i].set_xlabel(&amp;quot;Time $t$ [s]&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
# Save the figure&lt;br /&gt;
fbs.savefig('figure-6.1-superposition.png')&lt;br /&gt;
&amp;lt;/nowiki&amp;gt;&lt;/div&gt;</summary>
		<author><name>Murray</name></author>
	</entry>
	<entry>
		<id>http://fbswiki.org/wiki/index.php?title=Figure_6.5:_Modes_for_a_second-order_system_with_real_eigenvalues&amp;diff=1344</id>
		<title>Figure 6.5: Modes for a second-order system with real eigenvalues</title>
		<link rel="alternate" type="text/html" href="http://fbswiki.org/wiki/index.php?title=Figure_6.5:_Modes_for_a_second-order_system_with_real_eigenvalues&amp;diff=1344"/>
		<updated>2024-11-25T05:55:00Z</updated>

		<summary type="html">&lt;p&gt;Murray: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Figure&lt;br /&gt;
|Chapter=Linear Systems&lt;br /&gt;
|Figure number=6.5&lt;br /&gt;
|Sort key=605&lt;br /&gt;
|Figure title=The notion of modes for a second-order system with real eigenvalues.&lt;br /&gt;
|GitHub URL=https://github.com/murrayrm/fbs2e-python/blob/main/figure-6.5-superposition.py&lt;br /&gt;
|Requires=springmass.py&lt;br /&gt;
}}&lt;br /&gt;
{| border=0&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=2 | [[Image:figure-6.5-springmass_modes-pp.png]] &lt;br /&gt;
| [[Image:figure-6.5-springmass_modes-slow.png]] &lt;br /&gt;
|-&lt;br /&gt;
| [[Image:figure-6.5-springmass_modes-fast.png]] &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
'''Figure 6.5''': The notion of modes for a second-order system with real eigenvalues. The left figure shows the phase portrait and the modes corresponding to solutions that start on the eigenvectors (bold lines). The corresponding time functions are shown on the right.&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&lt;br /&gt;
# superposition.py - superposition of homogeneous and particular solutions&lt;br /&gt;
# RMM, 19 Apr 2024&lt;br /&gt;
&lt;br /&gt;
import matplotlib.pyplot as plt&lt;br /&gt;
import numpy as np&lt;br /&gt;
import control as ct&lt;br /&gt;
import fbs                      # FBS plotting customizations&lt;br /&gt;
&lt;br /&gt;
# Spring mass system&lt;br /&gt;
from springmass import springmass       # use spring mass dynamics&lt;br /&gt;
sys = springmass / springmass(0).real   # normalize the response to 1&lt;br /&gt;
X0 = [2, -1]                            # initial condition&lt;br /&gt;
&lt;br /&gt;
# Create input vectors&lt;br /&gt;
tvec = np.linspace(0, 60, 100)&lt;br /&gt;
u1 = 0 * tvec&lt;br /&gt;
u2 = np.hstack([tvec[0:50]/tvec[50], 1 - tvec[0:50]/tvec[50]])&lt;br /&gt;
&lt;br /&gt;
# Run simulations for the different cases&lt;br /&gt;
homogeneous = ct.forced_response(sys, tvec, u1, X0=X0)&lt;br /&gt;
particular = ct.forced_response(sys, tvec, u2)&lt;br /&gt;
complete = ct.forced_response(sys, tvec, u1 + u2, X0=X0)&lt;br /&gt;
&lt;br /&gt;
# Plot results&lt;br /&gt;
fig, axs = plt.subplots(3, 3, figsize=[8, 4], layout='tight')&lt;br /&gt;
for i, resp in enumerate([homogeneous, particular, complete]):&lt;br /&gt;
    axs[i, 0].plot(resp.time, resp.inputs)&lt;br /&gt;
    axs[0, 0].set_title(&amp;quot;Input $u$&amp;quot;)&lt;br /&gt;
    axs[i, 0].set_ylim(-2, 2)&lt;br /&gt;
    &lt;br /&gt;
    axs[i, 1].plot(&lt;br /&gt;
        resp.time, resp.states[0], 'b',&lt;br /&gt;
        resp.time, resp.states[1], 'r--')&lt;br /&gt;
    axs[0, 1].set_title(&amp;quot;States $x_1$, $x_2$&amp;quot;)&lt;br /&gt;
    axs[i, 1].set_ylim(-2, 2)&lt;br /&gt;
&lt;br /&gt;
    axs[i, 2].plot(resp.time, resp.outputs)&lt;br /&gt;
    axs[0, 2].set_title(&amp;quot;Output $y$&amp;quot;)&lt;br /&gt;
    axs[i, 2].set_ylim(-2, 2)&lt;br /&gt;
&lt;br /&gt;
# Label the plots&lt;br /&gt;
axs[0, 0].set_ylabel(&amp;quot;Homogeneous&amp;quot;)&lt;br /&gt;
axs[1, 0].set_ylabel(&amp;quot;Particular&amp;quot;)&lt;br /&gt;
axs[2, 0].set_ylabel(&amp;quot;Complete&amp;quot;)&lt;br /&gt;
for i in range(3):&lt;br /&gt;
    axs[2, i].set_xlabel(&amp;quot;Time $t$ [s]&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
# Save the figure&lt;br /&gt;
fbs.savefig('figure-6.1-superposition.png')&lt;br /&gt;
&amp;lt;/nowiki&amp;gt;&lt;/div&gt;</summary>
		<author><name>Murray</name></author>
	</entry>
	<entry>
		<id>http://fbswiki.org/wiki/index.php?title=Figure_6.5:_Modes_for_a_second-order_system_with_real_eigenvalues&amp;diff=1343</id>
		<title>Figure 6.5: Modes for a second-order system with real eigenvalues</title>
		<link rel="alternate" type="text/html" href="http://fbswiki.org/wiki/index.php?title=Figure_6.5:_Modes_for_a_second-order_system_with_real_eigenvalues&amp;diff=1343"/>
		<updated>2024-11-25T05:54:29Z</updated>

		<summary type="html">&lt;p&gt;Murray: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Figure&lt;br /&gt;
|Chapter=Linear Systems&lt;br /&gt;
|Figure number=6.5&lt;br /&gt;
|Sort key=605&lt;br /&gt;
|Figure title=The notion of modes for a second-order system with real eigenvalues.&lt;br /&gt;
|GitHub URL=https://github.com/murrayrm/fbs2e-python/blob/main/figure-6.5-springmass_modes.py&lt;br /&gt;
|Requires=springmass.py&lt;br /&gt;
}}&lt;br /&gt;
{| border=0&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=2 | [[Image:figure-6.5-springmass_modes-pp.png]] &lt;br /&gt;
| [[Image:figure-6.5-springmass_modes-slow.png]] &lt;br /&gt;
|-&lt;br /&gt;
| [[Image:figure-6.5-springmass_modes-fast.png]] &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
'''Figure 6.5''': The notion of modes for a second-order system with real eigenvalues. The left figure shows the phase portrait and the modes corresponding to solutions that start on the eigenvectors (bold lines). The corresponding time functions are shown on the right.&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&lt;br /&gt;
# superposition.py - superposition of homogeneous and particular solutions&lt;br /&gt;
# RMM, 19 Apr 2024&lt;br /&gt;
&lt;br /&gt;
import matplotlib.pyplot as plt&lt;br /&gt;
import numpy as np&lt;br /&gt;
import control as ct&lt;br /&gt;
import fbs                      # FBS plotting customizations&lt;br /&gt;
&lt;br /&gt;
# Spring mass system&lt;br /&gt;
from springmass import springmass       # use spring mass dynamics&lt;br /&gt;
sys = springmass / springmass(0).real   # normalize the response to 1&lt;br /&gt;
X0 = [2, -1]                            # initial condition&lt;br /&gt;
&lt;br /&gt;
# Create input vectors&lt;br /&gt;
tvec = np.linspace(0, 60, 100)&lt;br /&gt;
u1 = 0 * tvec&lt;br /&gt;
u2 = np.hstack([tvec[0:50]/tvec[50], 1 - tvec[0:50]/tvec[50]])&lt;br /&gt;
&lt;br /&gt;
# Run simulations for the different cases&lt;br /&gt;
homogeneous = ct.forced_response(sys, tvec, u1, X0=X0)&lt;br /&gt;
particular = ct.forced_response(sys, tvec, u2)&lt;br /&gt;
complete = ct.forced_response(sys, tvec, u1 + u2, X0=X0)&lt;br /&gt;
&lt;br /&gt;
# Plot results&lt;br /&gt;
fig, axs = plt.subplots(3, 3, figsize=[8, 4], layout='tight')&lt;br /&gt;
for i, resp in enumerate([homogeneous, particular, complete]):&lt;br /&gt;
    axs[i, 0].plot(resp.time, resp.inputs)&lt;br /&gt;
    axs[0, 0].set_title(&amp;quot;Input $u$&amp;quot;)&lt;br /&gt;
    axs[i, 0].set_ylim(-2, 2)&lt;br /&gt;
    &lt;br /&gt;
    axs[i, 1].plot(&lt;br /&gt;
        resp.time, resp.states[0], 'b',&lt;br /&gt;
        resp.time, resp.states[1], 'r--')&lt;br /&gt;
    axs[0, 1].set_title(&amp;quot;States $x_1$, $x_2$&amp;quot;)&lt;br /&gt;
    axs[i, 1].set_ylim(-2, 2)&lt;br /&gt;
&lt;br /&gt;
    axs[i, 2].plot(resp.time, resp.outputs)&lt;br /&gt;
    axs[0, 2].set_title(&amp;quot;Output $y$&amp;quot;)&lt;br /&gt;
    axs[i, 2].set_ylim(-2, 2)&lt;br /&gt;
&lt;br /&gt;
# Label the plots&lt;br /&gt;
axs[0, 0].set_ylabel(&amp;quot;Homogeneous&amp;quot;)&lt;br /&gt;
axs[1, 0].set_ylabel(&amp;quot;Particular&amp;quot;)&lt;br /&gt;
axs[2, 0].set_ylabel(&amp;quot;Complete&amp;quot;)&lt;br /&gt;
for i in range(3):&lt;br /&gt;
    axs[2, i].set_xlabel(&amp;quot;Time $t$ [s]&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
# Save the figure&lt;br /&gt;
fbs.savefig('figure-6.1-superposition.png')&lt;br /&gt;
&amp;lt;/nowiki&amp;gt;&lt;/div&gt;</summary>
		<author><name>Murray</name></author>
	</entry>
	<entry>
		<id>http://fbswiki.org/wiki/index.php?title=File:Figure-5.21-noise_cancel.png&amp;diff=1342</id>
		<title>File:Figure-5.21-noise cancel.png</title>
		<link rel="alternate" type="text/html" href="http://fbswiki.org/wiki/index.php?title=File:Figure-5.21-noise_cancel.png&amp;diff=1342"/>
		<updated>2024-11-25T00:48:45Z</updated>

		<summary type="html">&lt;p&gt;Murray: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Murray</name></author>
	</entry>
	<entry>
		<id>http://fbswiki.org/wiki/index.php?title=Figure_5.21:_Simulation_of_noise_cancellation&amp;diff=1341</id>
		<title>Figure 5.21: Simulation of noise cancellation</title>
		<link rel="alternate" type="text/html" href="http://fbswiki.org/wiki/index.php?title=Figure_5.21:_Simulation_of_noise_cancellation&amp;diff=1341"/>
		<updated>2024-11-25T00:48:30Z</updated>

		<summary type="html">&lt;p&gt;Murray: Created page with &amp;quot;{{Figure |Chapter=Dynamic Behavior |Figure number=5.21 |Sort key=521 |Figure title=Simulation of noise cancellation |GitHub URL=https://github.com/murrayrm/fbs2e-python/blob/m...&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Figure&lt;br /&gt;
|Chapter=Dynamic Behavior&lt;br /&gt;
|Figure number=5.21&lt;br /&gt;
|Sort key=521&lt;br /&gt;
|Figure title=Simulation of noise cancellation&lt;br /&gt;
|GitHub URL=https://github.com/murrayrm/fbs2e-python/blob/main/example-5.18-noise_cancel.py&lt;br /&gt;
}}&lt;br /&gt;
[[Image:figure-5.21-noise_cancel.png]]&lt;br /&gt;
&lt;br /&gt;
Figure 5.21: Simulation of noise cancellation. The upper left figure shows the headphone signal without noise cancellation, and the lower left figure shows the signal with noise cancellation. The right figures show the parameters &amp;lt;math&amp;gt;a&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;b&amp;lt;/math&amp;gt; of the filter.&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&lt;br /&gt;
# example-5.18-noise_cancel.py - Noise cancellation&lt;br /&gt;
# RMM, 24 Nov 2024&lt;br /&gt;
#&lt;br /&gt;
# Figure 5.21: Simulation of noise cancellation. The upper left figure&lt;br /&gt;
# shows the headphone signal without noise cancellation, and the lower left&lt;br /&gt;
# figure shows the signal with noise cancellation. The right figures show&lt;br /&gt;
# the parameters a and b of the filter.&lt;br /&gt;
&lt;br /&gt;
import control as ct&lt;br /&gt;
import numpy as np&lt;br /&gt;
import matplotlib.pyplot as plt&lt;br /&gt;
from math import pi&lt;br /&gt;
ct.use_fbs_defaults()&lt;br /&gt;
&lt;br /&gt;
#&lt;br /&gt;
# System dynamics&lt;br /&gt;
#&lt;br /&gt;
&lt;br /&gt;
# Headphone dynamics&lt;br /&gt;
headphone_params = {'a0': -0.75, 'b0': 0.9}&lt;br /&gt;
def headphone_update(t, z, n, params):&lt;br /&gt;
    return params['a0'] * z[0] + params['b0'] * n[0]&lt;br /&gt;
headphone = ct.nlsys(&lt;br /&gt;
    headphone_update, inputs='n', states='z', params=headphone_params,&lt;br /&gt;
    name='headphone')&lt;br /&gt;
&lt;br /&gt;
# Filter dynamics&lt;br /&gt;
def filter_update(t, w, u, params):&lt;br /&gt;
    n, a, b = u&lt;br /&gt;
    return a * w + b * n&lt;br /&gt;
filter = ct.nlsys(&lt;br /&gt;
    filter_update, inputs=['n', 'a', 'b'], states='w', name='filter')&lt;br /&gt;
&lt;br /&gt;
# Controller dynamics&lt;br /&gt;
control_params = {'alpha': 1}&lt;br /&gt;
def control_update(t, x, u, params):&lt;br /&gt;
    n, e, w = u&lt;br /&gt;
    a, b = x&lt;br /&gt;
    return [&lt;br /&gt;
        params['alpha'] * w * e,&lt;br /&gt;
        params['alpha'] * n * e&lt;br /&gt;
    ]&lt;br /&gt;
control = ct.nlsys(&lt;br /&gt;
    control_update, inputs=['n', 'e', 'w'], states=['a', 'b'], name='control',&lt;br /&gt;
    params=control_params)&lt;br /&gt;
&lt;br /&gt;
# Create summing junction to add all of the signal together&lt;br /&gt;
summer = ct.summing_junction(inputs=['z', 'S', '-w'], outputs='e')&lt;br /&gt;
&lt;br /&gt;
# Interconnected system&lt;br /&gt;
sys = ct.interconnect(&lt;br /&gt;
    [headphone, filter, control, summer], name='noise_cancel',&lt;br /&gt;
    inputs=['S', 'n'], outputs=['e', 'a', 'b'])&lt;br /&gt;
&lt;br /&gt;
# Create the signal and noise&lt;br /&gt;
timepts = np.linspace(0, 200, 2000)&lt;br /&gt;
signal = np.sin(0.1 * 2 * pi * timepts) # sinewave with frequency 0.1 Hz&lt;br /&gt;
noise = ct.white_noise(timepts, 5)      # white noise with covariance 5&lt;br /&gt;
&lt;br /&gt;
# Set up the plotting grid to match the layout in the book&lt;br /&gt;
fig = plt.figure(constrained_layout=True)&lt;br /&gt;
gs = fig.add_gridspec(3, 2)&lt;br /&gt;
&lt;br /&gt;
# No noise cancellation&lt;br /&gt;
resp_off = ct.input_output_response(&lt;br /&gt;
    sys, timepts, [signal, noise], params={'alpha': 0})&lt;br /&gt;
&lt;br /&gt;
ax = fig.add_subplot(gs[0, 0])&lt;br /&gt;
ax.plot(resp_off.time, resp_off.outputs[0])&lt;br /&gt;
ax.axis('tight')&lt;br /&gt;
ax.axis([0, 200, -5, 5])&lt;br /&gt;
ax.set_ylabel(&amp;quot;No cancellation&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
resp_on = ct.input_output_response(&lt;br /&gt;
    sys, timepts, [signal, noise], params={'alpha': 1e-2})&lt;br /&gt;
&lt;br /&gt;
ax = fig.add_subplot(gs[1, 0])&lt;br /&gt;
ax.plot(resp_on.time, resp_on.outputs[0], label='e')&lt;br /&gt;
# ax.plot(resp_on.time, signal, label='S')&lt;br /&gt;
ax.axis('tight')&lt;br /&gt;
ax.axis([0, 200, -5, 5])&lt;br /&gt;
ax.set_ylabel(&amp;quot;Cancellation&amp;quot;)&lt;br /&gt;
ax.set_xlabel(&amp;quot;Time $t$ [s]&amp;quot;)&lt;br /&gt;
# ax.legend()&lt;br /&gt;
&lt;br /&gt;
ax = fig.add_subplot(gs[0, 1])&lt;br /&gt;
ax.plot(resp_on.time, resp_on.outputs[1])&lt;br /&gt;
ax.axis('tight')&lt;br /&gt;
ax.axis([0, 200, -1.1, 0])&lt;br /&gt;
ax.set_ylabel(&amp;quot;$a$&amp;quot;, rotation=0)&lt;br /&gt;
&lt;br /&gt;
ax = fig.add_subplot(gs[1, 1])&lt;br /&gt;
ax.plot(resp_on.time, resp_on.outputs[2])&lt;br /&gt;
ax.axis('tight')&lt;br /&gt;
ax.axis([0, 200, 0, 1.1])&lt;br /&gt;
ax.set_ylabel(&amp;quot;$b$&amp;quot;, rotation=0)&lt;br /&gt;
ax.set_xlabel(&amp;quot;Time $t$ [s]&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
# Save the figure&lt;br /&gt;
plt.savefig(&amp;quot;figure-5.21-noise_cancel.png&amp;quot;, bbox_inches='tight')&lt;br /&gt;
&amp;lt;/nowiki&amp;gt;&lt;/div&gt;</summary>
		<author><name>Murray</name></author>
	</entry>
	<entry>
		<id>http://fbswiki.org/wiki/index.php?title=Bicycle_dynamics&amp;diff=1340</id>
		<title>Bicycle dynamics</title>
		<link rel="alternate" type="text/html" href="http://fbswiki.org/wiki/index.php?title=Bicycle_dynamics&amp;diff=1340"/>
		<updated>2024-11-24T20:45:11Z</updated>

		<summary type="html">&lt;p&gt;Murray: Created page with &amp;quot;{{righttoc}}  {| class=&amp;quot;wikitable&amp;quot; |- ! GitHub URL | https://github.com/murrayrm/fbs2e-python/blob/main/bicycle.py |- ! Requires | python-control...&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{righttoc}}&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! GitHub URL&lt;br /&gt;
| https://github.com/murrayrm/fbs2e-python/blob/main/bicycle.py&lt;br /&gt;
|-&lt;br /&gt;
! Requires&lt;br /&gt;
| [[https:python-control.org|python-control]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
This page documents the bicycle dynamics model that is used as a running example throughout the text.  A detailed description of the dynamics of this system are presented in {{chapter link|Examples}}.  This page contains a description of the system, including the models and commands used to generate some of the plots in the text.&lt;br /&gt;
&lt;br /&gt;
Note: the Python code on this page makes use of the [https://python-control.org Python Control Systems Library], an open source software toolbox for control systems analysis.&lt;br /&gt;
&lt;br /&gt;
Figures making use of this model:&lt;br /&gt;
{{#ask: [[Category:Figures]] [[Requires::bicycle.py]] | format=ul | sort=Sort key}}&lt;/div&gt;</summary>
		<author><name>Murray</name></author>
	</entry>
	<entry>
		<id>http://fbswiki.org/wiki/index.php?title=Examples&amp;diff=1339</id>
		<title>Examples</title>
		<link rel="alternate" type="text/html" href="http://fbswiki.org/wiki/index.php?title=Examples&amp;diff=1339"/>
		<updated>2024-11-24T20:44:18Z</updated>

		<summary type="html">&lt;p&gt;Murray: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Chapter&lt;br /&gt;
|Chapter number=4&lt;br /&gt;
|Short name=examples&lt;br /&gt;
|Previous chapter=System Modeling&lt;br /&gt;
|Next chapter=Dynamic Behavior&lt;br /&gt;
|First edition URL=https://www.cds.caltech.edu/~murray/amwiki/index.php?title=Examples&lt;br /&gt;
|Chapter summary=In this chapter we present a collection of examples spanning many different fields of science and engineering. These examples are used throughout the text and in exercises to illustrate different concepts. First-time readers may wish to focus on only a few examples with which they have had the most prior experience or insight to understand the concepts of state, input, output, and dynamics in a familiar setting.&lt;br /&gt;
|Chapter contents=# [[Cruise control|Cruise Control]]&lt;br /&gt;
# [[Bicycle dynamics|Bicycle Dynamics]]&lt;br /&gt;
# Operational Amplifier Circuits&lt;br /&gt;
# Computing Systems and Networks&lt;br /&gt;
#* Web Server Control&lt;br /&gt;
#* Congestion Control&lt;br /&gt;
# Atomic Force Microscopy&lt;br /&gt;
# Drug Administration&lt;br /&gt;
#* Compartment Models&lt;br /&gt;
#* Insulin--Glucose Dynamics&lt;br /&gt;
# Population Dynamics&lt;br /&gt;
#* Logistic Growth Model&lt;br /&gt;
#* [[Predator-prey dynamics|Predator-Prey Models]]&lt;br /&gt;
:: Exercises&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
{{Chapter footer&lt;br /&gt;
| First edition URL=https://www.cds.caltech.edu/~murray/amwiki/Examples.html&lt;br /&gt;
}}&lt;/div&gt;</summary>
		<author><name>Murray</name></author>
	</entry>
	<entry>
		<id>http://fbswiki.org/wiki/index.php?title=Bicycle.py&amp;diff=1338</id>
		<title>Bicycle.py</title>
		<link rel="alternate" type="text/html" href="http://fbswiki.org/wiki/index.php?title=Bicycle.py&amp;diff=1338"/>
		<updated>2024-11-24T20:43:50Z</updated>

		<summary type="html">&lt;p&gt;Murray: Redirected page to Bicycle dynamics&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;#REDIRECT [[Bicycle dynamics]]&lt;/div&gt;</summary>
		<author><name>Murray</name></author>
	</entry>
	<entry>
		<id>http://fbswiki.org/wiki/index.php?title=File:Figure-5.19-bicycle_stability.png&amp;diff=1337</id>
		<title>File:Figure-5.19-bicycle stability.png</title>
		<link rel="alternate" type="text/html" href="http://fbswiki.org/wiki/index.php?title=File:Figure-5.19-bicycle_stability.png&amp;diff=1337"/>
		<updated>2024-11-24T20:43:13Z</updated>

		<summary type="html">&lt;p&gt;Murray: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Murray</name></author>
	</entry>
	<entry>
		<id>http://fbswiki.org/wiki/index.php?title=Figure_5.19:_Stability_plots_for_a_bicycle_moving_at_constant_velocity&amp;diff=1336</id>
		<title>Figure 5.19: Stability plots for a bicycle moving at constant velocity</title>
		<link rel="alternate" type="text/html" href="http://fbswiki.org/wiki/index.php?title=Figure_5.19:_Stability_plots_for_a_bicycle_moving_at_constant_velocity&amp;diff=1336"/>
		<updated>2024-11-24T20:42:56Z</updated>

		<summary type="html">&lt;p&gt;Murray: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Figure&lt;br /&gt;
|Chapter=Dynamic Behavior&lt;br /&gt;
|Figure number=5.19&lt;br /&gt;
|Sort key=519&lt;br /&gt;
|Figure title=Stability plots for a bicycle moving at constant velocity&lt;br /&gt;
|GitHub URL=https://github.com/murrayrm/fbs2e-python/blob/main/example-5.17-bicycle_stability.py&lt;br /&gt;
|Requires=bicycle.py&lt;br /&gt;
}}&lt;br /&gt;
[[Image:figure-5.19-bicycle_stability.png]]&lt;br /&gt;
&lt;br /&gt;
'''Figure 5.19''': Stability plots for a bicycle moving at constant velocity. The plot in (a) shows the real part of the system eigenvalues as a function of the bicycle velocity &amp;lt;math&amp;gt;v_0&amp;lt;/math&amp;gt;. The system is stable when all eigenvalues have negative real part (shaded region). The plot in (b) shows the locus of eigenvalues on the complex plane as the velocity &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; is varied and gives a different view of the stability of the system. This type of plot is called a ''root locus diagram''.&lt;br /&gt;
&lt;br /&gt;
Note: The line styles used in this plot are slightly different than in the book.  Solid lines are used for real-valued eigenvalues and dashed lines are used for the real part of complex-valued eigenvalues.&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&lt;br /&gt;
# example-5.17-bicycle_stability.py - Root locus diagram for a bicycle model&lt;br /&gt;
# RMM, 24 Nov 2024&lt;br /&gt;
#&lt;br /&gt;
# Figure 5.19: Stability plots for a bicycle moving at constant&lt;br /&gt;
# velocity. The plot in (a) shows the real part of the system eigenvalues&lt;br /&gt;
# as a function of the bicycle velocity v0. The system is stable when all&lt;br /&gt;
# eigenvalues have negative real part (shaded region). The plot in (b)&lt;br /&gt;
# shows the locus of eigenvalues on the complex plane as the velocity v is&lt;br /&gt;
# varied and gives a different view of the stability of the system. This&lt;br /&gt;
# type of plot is called a root locus diagram.&lt;br /&gt;
#&lt;br /&gt;
# Notes:&lt;br /&gt;
#&lt;br /&gt;
# 1. The line styles used in this plot are slightly different than in the&lt;br /&gt;
#    book.  Solid lines are used for real-valued eigenvalues and dashed&lt;br /&gt;
#    lines are used for the real part of complex-valued eigenvalues.&lt;br /&gt;
#&lt;br /&gt;
# 2. This code relies on features on python-control-0.10.2, which is&lt;br /&gt;
#    currently under development.&lt;br /&gt;
&lt;br /&gt;
import control as ct&lt;br /&gt;
import numpy as np&lt;br /&gt;
import matplotlib.pyplot as plt&lt;br /&gt;
from math import isclose&lt;br /&gt;
ct.use_fbs_defaults()&lt;br /&gt;
&lt;br /&gt;
#&lt;br /&gt;
# System dynamics&lt;br /&gt;
#&lt;br /&gt;
&lt;br /&gt;
from bicycle import whipple_A&lt;br /&gt;
&lt;br /&gt;
# Set up the plotting grid to match the layout in the book&lt;br /&gt;
fig = plt.figure(constrained_layout=True)&lt;br /&gt;
gs = fig.add_gridspec(2, 2)&lt;br /&gt;
&lt;br /&gt;
#&lt;br /&gt;
# (a) Stability diagram&lt;br /&gt;
#&lt;br /&gt;
&lt;br /&gt;
ax = fig.add_subplot(gs[0, 0])  # first row, first column&lt;br /&gt;
ax.set_title(&amp;quot;(a) Stability diagram&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
# Compute the eigenvalues as a function of velocity&lt;br /&gt;
v0_vals = np.linspace(-15, 15, 500)&lt;br /&gt;
eig_vals = []&lt;br /&gt;
for v0 in v0_vals:&lt;br /&gt;
    A = whipple_A(v0)&lt;br /&gt;
    eig_vals.append(np.sort(np.linalg.eig(A).eigenvalues))&lt;br /&gt;
&lt;br /&gt;
# Initialize lists to categorize eigenvalues&lt;br /&gt;
eigs_real_stable = []&lt;br /&gt;
eigs_complex_stable = []&lt;br /&gt;
eigs_real_unstable = []&lt;br /&gt;
eigs_complex_unstable = []&lt;br /&gt;
&lt;br /&gt;
# Keep track of region in which all eigenvalues are stable&lt;br /&gt;
stable_beg = stable_end = None&lt;br /&gt;
&lt;br /&gt;
# Process each set of eigenvalues&lt;br /&gt;
for i, eig_set in enumerate(eig_vals):&lt;br /&gt;
    # Create arrays filled with NaN for each category&lt;br /&gt;
    real_stable = np.full(eig_set.shape, np.nan)&lt;br /&gt;
    complex_stable = np.full(eig_set.shape, np.nan)&lt;br /&gt;
    real_unstable = np.full(eig_set.shape, np.nan)&lt;br /&gt;
    complex_unstable = np.full(eig_set.shape, np.nan)&lt;br /&gt;
    &lt;br /&gt;
    # Classify eigenvalues&lt;br /&gt;
    for j, eig in enumerate(eig_set):&lt;br /&gt;
        if isclose(eig.imag, 0):  # Real eigenvalue&lt;br /&gt;
            if eig.real &amp;lt; 0:&lt;br /&gt;
                real_stable[j] = eig.real&lt;br /&gt;
            else:&lt;br /&gt;
                real_unstable[j] = eig.real&lt;br /&gt;
        else:  # Complex eigenvalue&lt;br /&gt;
            if eig.real &amp;lt; 0:&lt;br /&gt;
                complex_stable[j] = eig.real&lt;br /&gt;
            else:&lt;br /&gt;
                complex_unstable[j] = eig.real&lt;br /&gt;
&lt;br /&gt;
    # Append categorized arrays to respective lists&lt;br /&gt;
    eigs_real_stable.append(real_stable)&lt;br /&gt;
    eigs_complex_stable.append(complex_stable)&lt;br /&gt;
    eigs_real_unstable.append(real_unstable)&lt;br /&gt;
    eigs_complex_unstable.append(complex_unstable)&lt;br /&gt;
&lt;br /&gt;
    # Look for regions where everything is stable&lt;br /&gt;
    if stable_beg is None and all(eig_set.real &amp;lt; 0):&lt;br /&gt;
        stable_beg = i&lt;br /&gt;
    elif stable_beg and stable_end is None and any(eig_set.real &amp;gt; 0):&lt;br /&gt;
        stable_end = i&lt;br /&gt;
&lt;br /&gt;
# Plot the stability diagram&lt;br /&gt;
ax.plot(v0_vals, eigs_real_stable, 'b-')&lt;br /&gt;
ax.plot(v0_vals, eigs_real_unstable, 'r-')&lt;br /&gt;
ax.plot(v0_vals, eigs_complex_stable, 'b--')&lt;br /&gt;
ax.plot(v0_vals, eigs_complex_unstable, 'r--')&lt;br /&gt;
&lt;br /&gt;
# Add in the coordinate axes&lt;br /&gt;
ax.axhline(color='k', linewidth=0.5)&lt;br /&gt;
ax.axvline(color='k', linewidth=0.5)&lt;br /&gt;
&lt;br /&gt;
# Label and shade stable and unstable regions&lt;br /&gt;
ax.text(-12, 8, &amp;quot;Unstable&amp;quot;)&lt;br /&gt;
ax.fill_betweenx(&lt;br /&gt;
    [-15, 15], [v0_vals[stable_beg], v0_vals[stable_beg]],&lt;br /&gt;
    [v0_vals[stable_end], v0_vals[stable_end]], color='0.9')&lt;br /&gt;
ax.text(7.2, 6, &amp;quot;Stable&amp;quot;, rotation=90)&lt;br /&gt;
ax.text(11.7, 5, &amp;quot;Unstable&amp;quot;, rotation=90)&lt;br /&gt;
&lt;br /&gt;
# Label the axes&lt;br /&gt;
ax.set_xlabel(r&amp;quot;Velocity $v_0$ [m/s]&amp;quot;)&lt;br /&gt;
ax.set_ylabel(r&amp;quot;$\text{Re}\,\lambda$&amp;quot;)&lt;br /&gt;
ax.axis('scaled')&lt;br /&gt;
ax.axis([-15, 15, -15, 15])&lt;br /&gt;
&lt;br /&gt;
#&lt;br /&gt;
# (b) Root locus diagram&lt;br /&gt;
#&lt;br /&gt;
&lt;br /&gt;
ax = fig.add_subplot(gs[0, 1])  # first row, second column&lt;br /&gt;
ax.set_title(&amp;quot;(b) Root locus diagram&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
# Generate the root locus diagram via the root_locus_plot functionality&lt;br /&gt;
pos_idx = np.argmax(v0_vals &amp;gt;= 0)&lt;br /&gt;
poles = eig_vals[pos_idx]&lt;br /&gt;
loci = np.array(eig_vals[pos_idx:])&lt;br /&gt;
rl_map = ct.PoleZeroData(poles, [], v0_vals[pos_idx:], loci)&lt;br /&gt;
rl_map.plot(ax=ax)&lt;br /&gt;
&lt;br /&gt;
# Add in the coordinate axes&lt;br /&gt;
ax.axhline(color='k', linewidth=0.5)&lt;br /&gt;
ax.axvline(color='k', linewidth=0.5)&lt;br /&gt;
&lt;br /&gt;
# Label the real axes of the plot&lt;br /&gt;
ax.text(-12.5, -2, r&amp;quot;$\leftarrow v_0$&amp;quot;)&lt;br /&gt;
ax.text(-3.5, -2, r&amp;quot;$v_0 \rightarrow$&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
# Label the crossover points&lt;br /&gt;
xo_idx = np.argmax(rl_map.loci[:, 3].real &amp;lt; 0)&lt;br /&gt;
ax.plot(0, rl_map.loci[xo_idx, 2].imag, 'bo', markersize=3)&lt;br /&gt;
ax.plot(0, rl_map.loci[xo_idx, 3].imag, 'bo', markersize=3)&lt;br /&gt;
ax.text(1, rl_map.loci[xo_idx, 2].imag, r&amp;quot;$v_0 = 6.1$&amp;quot;)&lt;br /&gt;
ax.text(1, rl_map.loci[xo_idx, 3].imag, r&amp;quot;$v_0 = 6.1$&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
# Label the axes&lt;br /&gt;
ax.set_xlabel(r&amp;quot;$\text{Re}\,\lambda$&amp;quot;)&lt;br /&gt;
ax.set_ylabel(r&amp;quot;$\text{Im}\,\lambda$&amp;quot;)&lt;br /&gt;
ax.set_box_aspect(1)&lt;br /&gt;
ax.axis([-15, 15, -10, 10])&lt;br /&gt;
&lt;br /&gt;
# Save the figure&lt;br /&gt;
plt.savefig(&amp;quot;figure-5.19-bicycle_stability.png&amp;quot;, bbox_inches='tight')&lt;br /&gt;
&amp;lt;/nowiki&amp;gt;&lt;/div&gt;</summary>
		<author><name>Murray</name></author>
	</entry>
	<entry>
		<id>http://fbswiki.org/wiki/index.php?title=Figure_5.19:_Stability_plots_for_a_bicycle_moving_at_constant_velocity&amp;diff=1335</id>
		<title>Figure 5.19: Stability plots for a bicycle moving at constant velocity</title>
		<link rel="alternate" type="text/html" href="http://fbswiki.org/wiki/index.php?title=Figure_5.19:_Stability_plots_for_a_bicycle_moving_at_constant_velocity&amp;diff=1335"/>
		<updated>2024-11-24T20:42:40Z</updated>

		<summary type="html">&lt;p&gt;Murray: Created page with &amp;quot;{{Figure |Chapter=Dynamic Behavior |Figure number=5.19 |Sort key=519 |Figure title=Stability plots for a bicycle moving at constant velocity |GitHub URL=https://github.com/mur...&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Figure&lt;br /&gt;
|Chapter=Dynamic Behavior&lt;br /&gt;
|Figure number=5.19&lt;br /&gt;
|Sort key=519&lt;br /&gt;
|Figure title=Stability plots for a bicycle moving at constant velocity&lt;br /&gt;
|GitHub URL=https://github.com/murrayrm/fbs2e-python/blob/main/example-5.17-bicycle_stability.py&lt;br /&gt;
|Requires=bicycle.py&lt;br /&gt;
}}&lt;br /&gt;
[[Image:figure-5.18-predprey_bif.png]]&lt;br /&gt;
&lt;br /&gt;
'''Figure 5.19''': Stability plots for a bicycle moving at constant velocity. The plot in (a) shows the real part of the system eigenvalues as a function of the bicycle velocity &amp;lt;math&amp;gt;v_0&amp;lt;/math&amp;gt;. The system is stable when all eigenvalues have negative real part (shaded region). The plot in (b) shows the locus of eigenvalues on the complex plane as the velocity &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; is varied and gives a different view of the stability of the system. This type of plot is called a ''root locus diagram''.&lt;br /&gt;
&lt;br /&gt;
Note: The line styles used in this plot are slightly different than in the book.  Solid lines are used for real-valued eigenvalues and dashed lines are used for the real part of complex-valued eigenvalues.&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&lt;br /&gt;
# example-5.17-bicycle_stability.py - Root locus diagram for a bicycle model&lt;br /&gt;
# RMM, 24 Nov 2024&lt;br /&gt;
#&lt;br /&gt;
# Figure 5.19: Stability plots for a bicycle moving at constant&lt;br /&gt;
# velocity. The plot in (a) shows the real part of the system eigenvalues&lt;br /&gt;
# as a function of the bicycle velocity v0. The system is stable when all&lt;br /&gt;
# eigenvalues have negative real part (shaded region). The plot in (b)&lt;br /&gt;
# shows the locus of eigenvalues on the complex plane as the velocity v is&lt;br /&gt;
# varied and gives a different view of the stability of the system. This&lt;br /&gt;
# type of plot is called a root locus diagram.&lt;br /&gt;
#&lt;br /&gt;
# Notes:&lt;br /&gt;
#&lt;br /&gt;
# 1. The line styles used in this plot are slightly different than in the&lt;br /&gt;
#    book.  Solid lines are used for real-valued eigenvalues and dashed&lt;br /&gt;
#    lines are used for the real part of complex-valued eigenvalues.&lt;br /&gt;
#&lt;br /&gt;
# 2. This code relies on features on python-control-0.10.2, which is&lt;br /&gt;
#    currently under development.&lt;br /&gt;
&lt;br /&gt;
import control as ct&lt;br /&gt;
import numpy as np&lt;br /&gt;
import matplotlib.pyplot as plt&lt;br /&gt;
from math import isclose&lt;br /&gt;
ct.use_fbs_defaults()&lt;br /&gt;
&lt;br /&gt;
#&lt;br /&gt;
# System dynamics&lt;br /&gt;
#&lt;br /&gt;
&lt;br /&gt;
from bicycle import whipple_A&lt;br /&gt;
&lt;br /&gt;
# Set up the plotting grid to match the layout in the book&lt;br /&gt;
fig = plt.figure(constrained_layout=True)&lt;br /&gt;
gs = fig.add_gridspec(2, 2)&lt;br /&gt;
&lt;br /&gt;
#&lt;br /&gt;
# (a) Stability diagram&lt;br /&gt;
#&lt;br /&gt;
&lt;br /&gt;
ax = fig.add_subplot(gs[0, 0])  # first row, first column&lt;br /&gt;
ax.set_title(&amp;quot;(a) Stability diagram&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
# Compute the eigenvalues as a function of velocity&lt;br /&gt;
v0_vals = np.linspace(-15, 15, 500)&lt;br /&gt;
eig_vals = []&lt;br /&gt;
for v0 in v0_vals:&lt;br /&gt;
    A = whipple_A(v0)&lt;br /&gt;
    eig_vals.append(np.sort(np.linalg.eig(A).eigenvalues))&lt;br /&gt;
&lt;br /&gt;
# Initialize lists to categorize eigenvalues&lt;br /&gt;
eigs_real_stable = []&lt;br /&gt;
eigs_complex_stable = []&lt;br /&gt;
eigs_real_unstable = []&lt;br /&gt;
eigs_complex_unstable = []&lt;br /&gt;
&lt;br /&gt;
# Keep track of region in which all eigenvalues are stable&lt;br /&gt;
stable_beg = stable_end = None&lt;br /&gt;
&lt;br /&gt;
# Process each set of eigenvalues&lt;br /&gt;
for i, eig_set in enumerate(eig_vals):&lt;br /&gt;
    # Create arrays filled with NaN for each category&lt;br /&gt;
    real_stable = np.full(eig_set.shape, np.nan)&lt;br /&gt;
    complex_stable = np.full(eig_set.shape, np.nan)&lt;br /&gt;
    real_unstable = np.full(eig_set.shape, np.nan)&lt;br /&gt;
    complex_unstable = np.full(eig_set.shape, np.nan)&lt;br /&gt;
    &lt;br /&gt;
    # Classify eigenvalues&lt;br /&gt;
    for j, eig in enumerate(eig_set):&lt;br /&gt;
        if isclose(eig.imag, 0):  # Real eigenvalue&lt;br /&gt;
            if eig.real &amp;lt; 0:&lt;br /&gt;
                real_stable[j] = eig.real&lt;br /&gt;
            else:&lt;br /&gt;
                real_unstable[j] = eig.real&lt;br /&gt;
        else:  # Complex eigenvalue&lt;br /&gt;
            if eig.real &amp;lt; 0:&lt;br /&gt;
                complex_stable[j] = eig.real&lt;br /&gt;
            else:&lt;br /&gt;
                complex_unstable[j] = eig.real&lt;br /&gt;
&lt;br /&gt;
    # Append categorized arrays to respective lists&lt;br /&gt;
    eigs_real_stable.append(real_stable)&lt;br /&gt;
    eigs_complex_stable.append(complex_stable)&lt;br /&gt;
    eigs_real_unstable.append(real_unstable)&lt;br /&gt;
    eigs_complex_unstable.append(complex_unstable)&lt;br /&gt;
&lt;br /&gt;
    # Look for regions where everything is stable&lt;br /&gt;
    if stable_beg is None and all(eig_set.real &amp;lt; 0):&lt;br /&gt;
        stable_beg = i&lt;br /&gt;
    elif stable_beg and stable_end is None and any(eig_set.real &amp;gt; 0):&lt;br /&gt;
        stable_end = i&lt;br /&gt;
&lt;br /&gt;
# Plot the stability diagram&lt;br /&gt;
ax.plot(v0_vals, eigs_real_stable, 'b-')&lt;br /&gt;
ax.plot(v0_vals, eigs_real_unstable, 'r-')&lt;br /&gt;
ax.plot(v0_vals, eigs_complex_stable, 'b--')&lt;br /&gt;
ax.plot(v0_vals, eigs_complex_unstable, 'r--')&lt;br /&gt;
&lt;br /&gt;
# Add in the coordinate axes&lt;br /&gt;
ax.axhline(color='k', linewidth=0.5)&lt;br /&gt;
ax.axvline(color='k', linewidth=0.5)&lt;br /&gt;
&lt;br /&gt;
# Label and shade stable and unstable regions&lt;br /&gt;
ax.text(-12, 8, &amp;quot;Unstable&amp;quot;)&lt;br /&gt;
ax.fill_betweenx(&lt;br /&gt;
    [-15, 15], [v0_vals[stable_beg], v0_vals[stable_beg]],&lt;br /&gt;
    [v0_vals[stable_end], v0_vals[stable_end]], color='0.9')&lt;br /&gt;
ax.text(7.2, 6, &amp;quot;Stable&amp;quot;, rotation=90)&lt;br /&gt;
ax.text(11.7, 5, &amp;quot;Unstable&amp;quot;, rotation=90)&lt;br /&gt;
&lt;br /&gt;
# Label the axes&lt;br /&gt;
ax.set_xlabel(r&amp;quot;Velocity $v_0$ [m/s]&amp;quot;)&lt;br /&gt;
ax.set_ylabel(r&amp;quot;$\text{Re}\,\lambda$&amp;quot;)&lt;br /&gt;
ax.axis('scaled')&lt;br /&gt;
ax.axis([-15, 15, -15, 15])&lt;br /&gt;
&lt;br /&gt;
#&lt;br /&gt;
# (b) Root locus diagram&lt;br /&gt;
#&lt;br /&gt;
&lt;br /&gt;
ax = fig.add_subplot(gs[0, 1])  # first row, second column&lt;br /&gt;
ax.set_title(&amp;quot;(b) Root locus diagram&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
# Generate the root locus diagram via the root_locus_plot functionality&lt;br /&gt;
pos_idx = np.argmax(v0_vals &amp;gt;= 0)&lt;br /&gt;
poles = eig_vals[pos_idx]&lt;br /&gt;
loci = np.array(eig_vals[pos_idx:])&lt;br /&gt;
rl_map = ct.PoleZeroData(poles, [], v0_vals[pos_idx:], loci)&lt;br /&gt;
rl_map.plot(ax=ax)&lt;br /&gt;
&lt;br /&gt;
# Add in the coordinate axes&lt;br /&gt;
ax.axhline(color='k', linewidth=0.5)&lt;br /&gt;
ax.axvline(color='k', linewidth=0.5)&lt;br /&gt;
&lt;br /&gt;
# Label the real axes of the plot&lt;br /&gt;
ax.text(-12.5, -2, r&amp;quot;$\leftarrow v_0$&amp;quot;)&lt;br /&gt;
ax.text(-3.5, -2, r&amp;quot;$v_0 \rightarrow$&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
# Label the crossover points&lt;br /&gt;
xo_idx = np.argmax(rl_map.loci[:, 3].real &amp;lt; 0)&lt;br /&gt;
ax.plot(0, rl_map.loci[xo_idx, 2].imag, 'bo', markersize=3)&lt;br /&gt;
ax.plot(0, rl_map.loci[xo_idx, 3].imag, 'bo', markersize=3)&lt;br /&gt;
ax.text(1, rl_map.loci[xo_idx, 2].imag, r&amp;quot;$v_0 = 6.1$&amp;quot;)&lt;br /&gt;
ax.text(1, rl_map.loci[xo_idx, 3].imag, r&amp;quot;$v_0 = 6.1$&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
# Label the axes&lt;br /&gt;
ax.set_xlabel(r&amp;quot;$\text{Re}\,\lambda$&amp;quot;)&lt;br /&gt;
ax.set_ylabel(r&amp;quot;$\text{Im}\,\lambda$&amp;quot;)&lt;br /&gt;
ax.set_box_aspect(1)&lt;br /&gt;
ax.axis([-15, 15, -10, 10])&lt;br /&gt;
&lt;br /&gt;
# Save the figure&lt;br /&gt;
plt.savefig(&amp;quot;figure-5.19-bicycle_stability.png&amp;quot;, bbox_inches='tight')&lt;br /&gt;
&amp;lt;/nowiki&amp;gt;&lt;/div&gt;</summary>
		<author><name>Murray</name></author>
	</entry>
	<entry>
		<id>http://fbswiki.org/wiki/index.php?title=Dynamic_Behavior&amp;diff=1334</id>
		<title>Dynamic Behavior</title>
		<link rel="alternate" type="text/html" href="http://fbswiki.org/wiki/index.php?title=Dynamic_Behavior&amp;diff=1334"/>
		<updated>2024-11-24T16:53:35Z</updated>

		<summary type="html">&lt;p&gt;Murray: /* Chapter Summary */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Chapter&lt;br /&gt;
|Chapter number=5&lt;br /&gt;
|Short name=dynamics&lt;br /&gt;
|Previous chapter=Examples&lt;br /&gt;
|Next chapter=Linear Systems&lt;br /&gt;
|First edition URL=https://www.cds.caltech.edu/~murray/amwiki/index.php?title=Dynamic_Behavior&lt;br /&gt;
|Chapter summary=In this chapter we give a broad discussion of the behavior of dynamical systems, focused on systems modeled by nonlinear differential equations. This allows us to discuss equilibrium points, stability, limit cycles and other key concepts of dynamical systems. We also introduce some methods for analyzing global behavior of solutions.&lt;br /&gt;
|Chapter contents=# Solving Differential Equations&lt;br /&gt;
# Qualitative Analysis&lt;br /&gt;
# Stability&lt;br /&gt;
# Lyapunov Stability Analysis&lt;br /&gt;
# Parametric and Nonlocal Behavior&lt;br /&gt;
# Further Reading&lt;br /&gt;
:: Exercises&lt;br /&gt;
}}&lt;br /&gt;
== Chapter Summary ==&lt;br /&gt;
&amp;lt;ol&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt; &amp;lt;p&amp;gt; We say that &amp;lt;math&amp;gt;x(t)&amp;lt;/math&amp;gt; is a solution of a differential equation on the time interval &amp;lt;math&amp;gt;t_0&amp;lt;/math&amp;gt; to &amp;lt;math&amp;gt;t_\text{f}&amp;lt;/math&amp;gt; with initial value &amp;lt;math&amp;gt;x_0&amp;lt;/math&amp;gt;  if it satisfies&lt;br /&gt;
&amp;lt;center&amp;gt;&amp;lt;math&amp;gt;&lt;br /&gt;
  x(t_0) = x_0 \quad\text{and}\quad  \dot x(t) = F(x(t)) \quad\text{for all}\quad t_0 \leq t \leq t_\text{f}.&lt;br /&gt;
&amp;lt;/math&amp;gt;&amp;lt;/center&amp;gt;&lt;br /&gt;
We will usually assume &amp;lt;math&amp;gt;t_0 = 0&amp;lt;/math&amp;gt;.  For most differential equations we will encounter, there is a unique solution for a given initial condition.  Numerical tools such as MATLAB and Mathematica can be used to obtain numerical solutions for &amp;lt;math&amp;gt;x(t)&amp;lt;/math&amp;gt; given the function &amp;lt;math&amp;gt;F(x)&amp;lt;/math&amp;gt;.&amp;lt;/p&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;&amp;lt;p&amp;gt; An ''equilibrium point'' for a dynamical system represents a point &amp;lt;math&amp;gt;x_{e}&amp;lt;/math&amp;gt; such that if &amp;lt;math&amp;gt;x(0) = x_\text{e}&amp;lt;/math&amp;gt; then &amp;lt;math&amp;gt;x(t) = x_\text{e}&amp;lt;/math&amp;gt; for all &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;.   Equilibrium points represent stationary conditions for the dynamics of a system.  A ''limit cycle'' for a dynamical system is a solution &amp;lt;math&amp;gt;x(t)&amp;lt;/math&amp;gt; which is periodic with some period &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;, so that &amp;lt;math&amp;gt;x(t + T) = x(t)&amp;lt;/math&amp;gt; for all &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;.&amp;lt;/p&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;&amp;lt;p&amp;gt;&amp;lt;span id=stability&amp;gt;An equilibrium point is (locally) ''stable'' if initial conditions that start near an equilibrium point stay near that equilibrium point.  A equilibrium point is (locally) ''asymptotically stable'' if it is stable and, in addition, the state of the system converges to the equilibrium point as time increases.  An equilibrium point is ''unstable'' if it is not stable.  Similar definitions can be used to define the stability of a limit cycle.&amp;lt;/span&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;&amp;lt;p&amp;gt; Phase portraits provide a convenient way to understand the behavior of 2-dimensional dynamical systems.  A phase portrait is a graphical representation of the dynamics obtained by plotting the state &amp;lt;math&amp;gt;x(t) = (x_1(t), x_2(t))&amp;lt;/math&amp;gt; in the plane.  This portrait is often augmented by plotting an arrow in the plane corresponding to &amp;lt;math&amp;gt;F(x)&amp;lt;/math&amp;gt;, which shows the rate of change of the state.  The following diagrams illustrate the basic features of a dynamical systems:&lt;br /&gt;
&amp;lt;table border=0&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td align=center width=33%&amp;gt; [[Image:doscpp.png|180px]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td align=center width=33%&amp;gt; [[Image:oscpp.png|180px]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td align=center width=33%&amp;gt; [[Image:stablepp.png|180px]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td align=center&amp;gt;An asymptotically stable equilibrium point at &amp;lt;math&amp;gt;x = (0, 0)&amp;lt;/math&amp;gt;.&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td align=center&amp;gt;A limit cycle of radius one, with an unstable equilibrium point at &amp;lt;math&amp;gt;x = (0, 0)&amp;lt;/math&amp;gt;.&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td align=center&amp;gt;A stable equlibirum point at &amp;lt;math&amp;gt;x = (0, 0)&amp;lt;/math&amp;gt; (nearby initial conditions stay nearby).&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&amp;lt;/table&amp;gt;&lt;br /&gt;
&amp;lt;/p&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;&amp;lt;p&amp;gt;A linear system&lt;br /&gt;
&amp;lt;center&amp;gt;&amp;lt;math&amp;gt;&lt;br /&gt;
  \dfrac{dx}{dt} = A x&lt;br /&gt;
&amp;lt;/math&amp;gt;&amp;lt;/center&amp;gt;&lt;br /&gt;
is asymptotically stable if and only if all eigenvalues of &amp;lt;math&amp;gt;A&amp;lt;/math&amp;gt; all have strictly negative real part and is unstable if any eigenvalue of &amp;lt;math&amp;gt;A&amp;lt;/math&amp;gt; has strictly positive real part.  A nonlinear system can be approximated by a linear system around an equilibrium point by using the relationship&lt;br /&gt;
&amp;lt;center&amp;gt;&amp;lt;math&amp;gt;&lt;br /&gt;
  \dot x = F(x_\text{e}) + \left.\frac{\partial F}{\partial x}\right|_{x_\text{e}} (x - x_\text{e}) + \text{higher order terms in}\, (x-x_\text{e}).&lt;br /&gt;
&amp;lt;/math&amp;gt;&amp;lt;/center&amp;gt;&lt;br /&gt;
Since &amp;lt;math&amp;gt;F(x_e) = 0&amp;lt;/math&amp;gt;, we can approximate the system by choosing a new&lt;br /&gt;
state variable &amp;lt;math&amp;gt;z = x - x_\text{e}&amp;lt;/math&amp;gt; and writing the dynamics as &amp;lt;math&amp;gt;\dot z = A z&amp;lt;/math&amp;gt;.  The stability of the nonlinear system can be determined in a local neighborhood of the equilibrium point through its linearization.&lt;br /&gt;
&amp;lt;/p&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;&amp;lt;p&amp;gt;A ''Lyapunov function'' is an energy-like function &amp;lt;math&amp;gt;V:\mathbb{R}^n \to \mathbb{R}&amp;lt;/math&amp;gt; that can be used to reason about the stability of an equilibrium point.  We define the derivative of &amp;lt;math&amp;gt;V&amp;lt;/math&amp;gt; along the trajectory of the system as&lt;br /&gt;
&amp;lt;center&amp;gt;&amp;lt;math&amp;gt;&lt;br /&gt;
\dot V(x) = \dfrac{\partial V}{\partial x} \dot x =  \dfrac{\partial V}{\partial x} F(x)&lt;br /&gt;
&amp;lt;/math&amp;gt;&amp;lt;/center&amp;gt;&lt;br /&gt;
Assuming &amp;lt;math&amp;gt;x_\text{e} = 0&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;V(0) = 0&amp;lt;/math&amp;gt;, the following conditions hold:&lt;br /&gt;
&amp;lt;center&amp;gt;&lt;br /&gt;
{| border=1 style=&amp;quot;border-spacing: 2px;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| Condition on &amp;lt;math&amp;gt;V&amp;lt;/math&amp;gt; || Condition on &amp;lt;math&amp;gt;\dot V&amp;lt;/math&amp;gt; || Stability&lt;br /&gt;
|-&lt;br /&gt;
| align=center | &amp;lt;math&amp;gt; V(x) &amp;gt; 0, x \neq 0&amp;lt;/math&amp;gt; &lt;br /&gt;
| align=center | &amp;lt;math&amp;gt;\dot V(x) \leq 0&amp;lt;/math&amp;gt; for all &amp;lt;math&amp;gt;x&amp;lt;/math&amp;gt;&lt;br /&gt;
| align=left | &amp;lt;math&amp;gt;x_e&amp;lt;/math&amp;gt; stable&lt;br /&gt;
|-&lt;br /&gt;
| align=center | &amp;lt;math&amp;gt;V(x) &amp;gt; 0, x \neq 0 &amp;lt;/math&amp;gt; &lt;br /&gt;
| align=center | &amp;lt;math&amp;gt;\dot V(x) &amp;lt; 0, x \neq 0&amp;lt;/math&amp;gt; &lt;br /&gt;
| align=left | &amp;lt;math&amp;gt;x_e&amp;lt;/math&amp;gt; asymptotically stable&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
Stability of limit cycles can also be studied using Lyapunov functions.&lt;br /&gt;
&amp;lt;/p&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;&amp;lt;p&amp;gt;The ''Krasovskii-LaSalle Principle'' allows one to reason about asymptotic stability even if the time derivative of &amp;lt;math&amp;gt;V&amp;lt;/math&amp;gt; is only negative semi-definite (&amp;lt;math&amp;gt;\leq 0&amp;lt;/math&amp;gt; rather than &amp;lt;math&amp;gt;&amp;lt; 0&amp;lt;/math&amp;gt;).  Let &amp;lt;math&amp;gt;V: \mathbb{R}^n \to \mathbb{R}&amp;lt;/math&amp;gt; be a ''positive definite function'', &amp;lt;math&amp;gt;V(x) &amp;gt; 0&amp;lt;/math&amp;gt; for all &amp;lt;math&amp;gt; x \neq 0&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;V(0) = 0&amp;lt;/math&amp;gt;, such that&lt;br /&gt;
&amp;lt;center&amp;gt;&lt;br /&gt;
&amp;lt;math&amp;gt;\dot V(x) \leq 0&amp;lt;/math&amp;gt; on the compact set &amp;lt;math&amp;gt;\Omega_c = \{x \in \mathbb{R}^n:V(x) \leq c\}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
Then as &amp;lt;math&amp;gt;t \to \infty&amp;lt;/math&amp;gt;, the trajectory of the system will converge to the largest invariant set inside&lt;br /&gt;
&amp;lt;center&amp;gt;&amp;lt;math&amp;gt;S = \{x \in \Omega_c:\dot V(x) = 0\}&amp;lt;/math&amp;gt;.&amp;lt;/center&amp;gt;&lt;br /&gt;
In particular, if &amp;lt;math&amp;gt;S&amp;lt;/math&amp;gt; contains no invariant sets other than &amp;lt;math&amp;gt;x = 0&amp;lt;/math&amp;gt;, then 0 is asymptotically stable.&lt;br /&gt;
&amp;lt;/p&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;&amp;lt;p&amp;gt;The ''global behavior'' of a nonlinear system refers to dynamics of the system far away from equilibrium points.  The ''region of attraction'' of an asymptotically stable equilirium point refers to the set of all initial conditions that converge to that equilibrium point.  An equilibrium point is said to be ''globally asymptotically stable'' if all initial conditions converge to that equilibrium point.  Global stability can be checked by finding a Lyapunov function that is globally positive definition with time derivative globally negative definite.&amp;lt;/p&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ol&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{{Chapter footer&lt;br /&gt;
|First edition URL=https://www.cds.caltech.edu/~murray/amwiki/Dynamic_Behavior.html&lt;br /&gt;
}}&lt;/div&gt;</summary>
		<author><name>Murray</name></author>
	</entry>
	<entry>
		<id>http://fbswiki.org/wiki/index.php?title=Bibliography&amp;diff=1333</id>
		<title>Bibliography</title>
		<link rel="alternate" type="text/html" href="http://fbswiki.org/wiki/index.php?title=Bibliography&amp;diff=1333"/>
		<updated>2024-11-24T16:44:50Z</updated>

		<summary type="html">&lt;p&gt;Murray: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Chapter header&lt;br /&gt;
|Chapter number=&lt;br /&gt;
|Chapter title={{PAGENAME}}&lt;br /&gt;
|Previous chapter=Architecture and System Design&lt;br /&gt;
|Next chapter=FBS&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
The bibliography contains information on all citations in the book.  Note that the bibliography uses an alphanumberic tag format, so that the bibliography can be updated as the text is written.&lt;br /&gt;
&lt;br /&gt;
{{FBS pdf|PDF|fbs-backmatter}} ({{FBS date}})&lt;br /&gt;
&lt;br /&gt;
== Online References ==&lt;br /&gt;
&lt;br /&gt;
The following notes, reports and books are available online.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=Bec05&amp;gt;&lt;br /&gt;
'''[Bec05]''' [http://dx.doi.org/10.1103/RevModPhys.77.783 &amp;quot;Feedback for physicists: A tutorial essay on control&amp;quot;], ''Review of Modern Physics'', 77:783, 2005 ([http://www.sfu.ca/chaos/Publications/papers/rmp_reprint05.pdf reprint]).&lt;br /&gt;
&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=BFS&amp;gt;&lt;br /&gt;
'''[BFS]''' ''[[Biomolecular Feedback Systems]]'', D. Del Vecchio and R. M. Murray, 2014.&lt;br /&gt;
&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=DFT92&amp;gt;&lt;br /&gt;
'''[DFT92]''' [https://www.control.utoronto.ca/people/profs/francis/dft.pdf ''Feedback Control Theory''], J. C. Doyle, B. Francis, A. Tannenbaum, MacMillan, 1992.&lt;br /&gt;
&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=Fra15&amp;gt;&lt;br /&gt;
'''[Fra15]''' [https://web.archive.org/web/20220423093734/http://www.scg.utoronto.ca/~francis/main.pdf ''Classical Control]'', B. A. Francis, University of Toronto, 2015.&lt;br /&gt;
&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=Lew03&amp;gt;&lt;br /&gt;
'''[Lew03]''' [https://mast.queensu.ca/~andrew/teaching/pdf/332-notes.pdf ''A Mathematical Approach to Classical Control''], A. D. Lewis, 2003.&lt;br /&gt;
&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=Lew03&amp;gt;&lt;br /&gt;
'''[LST]''' ''[[Supplement: Linear Systems Theory|Linear Systems Theory]]'' (supplemental notes), R. M. Murray, 2020.&lt;br /&gt;
&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=OBC&amp;gt;&lt;br /&gt;
'''[OBC]''' [[Supplement: Optimization-Based Control|''Optimization-Based Control'']] (supplemental notes), R. M. Murray, 2010.&lt;br /&gt;
&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=Mur03&amp;gt;&lt;br /&gt;
'''[Mur03]''' [http://www.cds.caltech.edu/~murray/cdspanel ''Control in an Information Rich World''], R. M. Murray (ed), SIAM, 2003.&lt;br /&gt;
&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=Son98&amp;gt;&lt;br /&gt;
'''[Son98]''' [http://www.sontaglab.org/FTPDIR/sontag_mathematical_control_theory_springer98.pdf ''Mathematical Control Theory''], E. D. Sontag, Springer, 1998.&lt;br /&gt;
&amp;lt;/span&amp;gt;&lt;/div&gt;</summary>
		<author><name>Murray</name></author>
	</entry>
	<entry>
		<id>http://fbswiki.org/wiki/index.php?title=Bibliography&amp;diff=1332</id>
		<title>Bibliography</title>
		<link rel="alternate" type="text/html" href="http://fbswiki.org/wiki/index.php?title=Bibliography&amp;diff=1332"/>
		<updated>2024-11-24T16:44:31Z</updated>

		<summary type="html">&lt;p&gt;Murray: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Chapter header&lt;br /&gt;
|Chapter number=&lt;br /&gt;
|Chapter title={{PAGENAME}}&lt;br /&gt;
|Previous chapter=Architecture and System Design&lt;br /&gt;
|Next chapter=FBS&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
The bibliography contains information on all citations in the book.  Note that the bibliography uses an alphanumberic tag format, so that the bibliography can be updated as the text is written.&lt;br /&gt;
&lt;br /&gt;
{{FBS pdf|PDF|fbs-backmatter}} ({{FBS date}})&lt;br /&gt;
&lt;br /&gt;
== Online References ==&lt;br /&gt;
&lt;br /&gt;
The following notes, reports and books are available online.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=Bec05&amp;gt;&lt;br /&gt;
'''[Bec05]''' [http://dx.doi.org/10.1103/RevModPhys.77.783 &amp;quot;Feedback for physicists: A tutorial essay on control&amp;quot;], ''Review of Modern Physics'', 77:783, 2005 ([http://www.sfu.ca/chaos/Publications/papers/rmp_reprint05.pdf reprint]).&lt;br /&gt;
&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=BFS&amp;gt;&lt;br /&gt;
'''[BFS]''' ''[[Biomolecular Feedback Systems]]'', D. Del Vecchio and R. M. Murray, 2014.&lt;br /&gt;
&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=DFT92&amp;gt;&lt;br /&gt;
'''[DFT92]''' [https://www.control.utoronto.ca/people/profs/francis/dft.pdf ''Feedback Control Theory''], J. C. Doyle, B. Francis, A. Tannenbaum, MacMillan, 1992.&lt;br /&gt;
&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=Fra15&amp;gt;&lt;br /&gt;
'''[Fra15]''' [http:web.archive.org/web/20220423093734/http://www.scg.utoronto.ca/~francis/main.pdf ''Classical Control]'', B. A. Francis, University of Toronto, 2015.&lt;br /&gt;
&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=Lew03&amp;gt;&lt;br /&gt;
'''[Lew03]''' [https://mast.queensu.ca/~andrew/teaching/pdf/332-notes.pdf ''A Mathematical Approach to Classical Control''], A. D. Lewis, 2003.&lt;br /&gt;
&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=Lew03&amp;gt;&lt;br /&gt;
'''[LST]''' ''[[Supplement: Linear Systems Theory|Linear Systems Theory]]'' (supplemental notes), R. M. Murray, 2020.&lt;br /&gt;
&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=OBC&amp;gt;&lt;br /&gt;
'''[OBC]''' [[Supplement: Optimization-Based Control|''Optimization-Based Control'']] (supplemental notes), R. M. Murray, 2010.&lt;br /&gt;
&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=Mur03&amp;gt;&lt;br /&gt;
'''[Mur03]''' [http://www.cds.caltech.edu/~murray/cdspanel ''Control in an Information Rich World''], R. M. Murray (ed), SIAM, 2003.&lt;br /&gt;
&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=Son98&amp;gt;&lt;br /&gt;
'''[Son98]''' [http://www.sontaglab.org/FTPDIR/sontag_mathematical_control_theory_springer98.pdf ''Mathematical Control Theory''], E. D. Sontag, Springer, 1998.&lt;br /&gt;
&amp;lt;/span&amp;gt;&lt;/div&gt;</summary>
		<author><name>Murray</name></author>
	</entry>
	<entry>
		<id>http://fbswiki.org/wiki/index.php?title=Predator-prey_dynamics&amp;diff=1331</id>
		<title>Predator-prey dynamics</title>
		<link rel="alternate" type="text/html" href="http://fbswiki.org/wiki/index.php?title=Predator-prey_dynamics&amp;diff=1331"/>
		<updated>2024-11-24T16:40:07Z</updated>

		<summary type="html">&lt;p&gt;Murray: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! GitHub URL&lt;br /&gt;
| https://github.com/murrayrm/fbs2e-python/blob/main/predprey.py&lt;br /&gt;
|-&lt;br /&gt;
! Requires&lt;br /&gt;
| [[https:python-control.org|python-control]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
This page documents the predator-prey model that is used as a running example throughout the text.  A detailed description of the dynamics of this system are presented in {{chapter link|Examples}}.  This page contains a description of the system, including the models and commands used to generate some of the plots in the text.&lt;br /&gt;
&lt;br /&gt;
Note: the Python code on this page makes use of the [https://python-control.org Python Control Systems Library], an open source software toolbox for control systems analysis.&lt;br /&gt;
&lt;br /&gt;
Figures making use of this model:&lt;br /&gt;
{{#ask: [[Category:Figures]] [[Requires::predprey.py]] | format=ul | sort=Sort key}}&lt;/div&gt;</summary>
		<author><name>Murray</name></author>
	</entry>
	<entry>
		<id>http://fbswiki.org/wiki/index.php?title=Cruise_control&amp;diff=1330</id>
		<title>Cruise control</title>
		<link rel="alternate" type="text/html" href="http://fbswiki.org/wiki/index.php?title=Cruise_control&amp;diff=1330"/>
		<updated>2024-11-24T16:39:50Z</updated>

		<summary type="html">&lt;p&gt;Murray: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{righttoc}}&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! GitHub URL&lt;br /&gt;
| https://github.com/murrayrm/fbs2e-python/blob/main/cruise.py&lt;br /&gt;
|-&lt;br /&gt;
! Requires&lt;br /&gt;
| [[https:python-control.org|python-control]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
This page documents the cruise control system that is used as a running example throughout the text.  A detailed description of the dynamics of this system are presented in {{chapter link|Examples}}.  This page contains a description of the system, including the models and commands used to generate some of the plots in the text.&lt;br /&gt;
&lt;br /&gt;
Note: the Python code on this page makes use of the [https://python-control.org Python Control Systems Library], an open source software toolbox for control systems analysis.&lt;br /&gt;
&lt;br /&gt;
Figures making use of this model:&lt;br /&gt;
{{#ask: [[Category:Figures]] [[Requires::cruise.py]] | format=ul | sort=Sort key}}&lt;br /&gt;
&lt;br /&gt;
== Introduction ==&lt;br /&gt;
&lt;br /&gt;
[[Image:cruise-block.png|right|300px|border|A feedback system for controlling the velocity of a vehicle. In the block diagram, the velocity of the vehicle is measured and compared to the desired velocity within the “Compute” block. Based on the difference in the actual and desired velocities, the throttle (or brake) is used to modify the force applied to the vehicle by the engine, drivetrain, and wheels.]]&lt;br /&gt;
Cruise control is the term used to describe a control system that regulates the speed of an automobile.  Cruise control was commercially introduced in 1958 as an option on the Chrysler Imperial.  The basic operation of a cruise controller is to sense the speed of the vehicle, compare this speed to a desired reference, and then accelerate or decelerate the car as required.  The figure to the right shows a block diagram of this feedback system.&lt;br /&gt;
&lt;br /&gt;
[[Image:cruise-speedresp.png|right|300px|border|Response to a disturbance. The car travels on a horizontal road and the slope of the road changes to a constant uphill slope. The three different curves correspond to differing masses of the vehicle, between 1200 and 2000 kg, demonstrating that feedback can indeed compensate for the changing slope and that the closed loop system is robust to a large change in the vehicle characteristics.&lt;br /&gt;
]]&lt;br /&gt;
A simple control algorithm for controlling the speed is to use a &amp;quot;proportional plus integral&amp;quot; feedback.  In this algorithm, we choose the amount of gas flowing to the engine based  on both the error between the current and desired speed, and the integral of that error.  The plot on the right shows the results of this feedback for a step change in the desired speed and a variety of different masses for the car (which might result from having a different number of passengers or towing a trailer).  Notice that independent of the mass (which varies by 25% of the total weight of the car), the steady state speed of the vehicle always approaches the desired speed and achieves that speed within approximately 10-15 seconds.  Thus the performance of the system is robust with respect to this uncertainty.&lt;br /&gt;
&lt;br /&gt;
== Dynamic model ==&lt;br /&gt;
&lt;br /&gt;
To develop a mathematical model we start with a force balance for the car body. Let &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; be the speed of the car, &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; the total mass (including passengers), &amp;lt;math&amp;gt;F&amp;lt;/math&amp;gt; the force generated by the contact of the wheels with the road, and &amp;lt;math&amp;gt;F_\text{d}&amp;lt;/math&amp;gt; the disturbance force due to gravity, friction and aerodynamic drag. The equation of motion of the car is simply &lt;br /&gt;
&amp;lt;center&amp;gt;&amp;lt;math&amp;gt; &lt;br /&gt;
  m \frac{dv}{dt} = F - F_\text{d}.&lt;br /&gt;
&amp;lt;/math&amp;gt;&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The force &amp;lt;math&amp;gt;F&amp;lt;/math&amp;gt; is generated by the engine, whose torque is proportional to the rate of fuel injection, which is itself proportional to a control signal &amp;lt;math&amp;gt;0 \leq u \leq 1&amp;lt;/math&amp;gt; that controls the throttle position. The torque also depends on engine speed &amp;lt;math&amp;gt;\omega&amp;lt;/math&amp;gt;. A simple representation of the torque at full throttle is given by the torque curve &lt;br /&gt;
&amp;lt;center&amp;gt;&amp;lt;math&amp;gt;&lt;br /&gt;
  T(\omega) = T_\text{m} \left(1 - \beta \biggl(\frac{\omega}{\omega_\text{m}} - 1\biggr)^2&lt;br /&gt;
  \right),&lt;br /&gt;
&amp;lt;/math&amp;gt;&amp;lt;/center&amp;gt;&lt;br /&gt;
where the maximum torque &amp;lt;math&amp;gt;T_\text{m}&amp;lt;/math&amp;gt; is obtained at engine speed &amp;lt;math&amp;gt;\omega_\text{m}&amp;lt;/math&amp;gt;.  Typical parameters are &amp;lt;math&amp;gt;T_m = 190&amp;lt;/math&amp;gt; Nm, &amp;lt;math&amp;gt;\omega_m&amp;lt;/math&amp;gt; = 420 rad/s (about 4000 RPM) and &amp;lt;math&amp;gt;\beta = 0.4&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Let &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; be the gear ratio and &amp;lt;math&amp;gt;r&amp;lt;/math&amp;gt; the wheel radius. The engine speed is related to the velocity through the expression&lt;br /&gt;
&amp;lt;center&amp;gt;&amp;lt;math&amp;gt;&lt;br /&gt;
  \omega = \frac{n}{r} v =: \alpha_n v,&lt;br /&gt;
&amp;lt;/math&amp;gt;&amp;lt;/center&amp;gt;&lt;br /&gt;
and the driving force can be written as&lt;br /&gt;
&amp;lt;center&amp;gt;&amp;lt;math&amp;gt;&lt;br /&gt;
  F = \frac{nu}{r} T(\omega) = \alpha_n u T(\alpha_n v).&lt;br /&gt;
&amp;lt;/math&amp;gt;&amp;lt;/center&amp;gt;&lt;br /&gt;
[[Image:cruise-gearcurves.png|right|400px|border|Torque curves for typical car engine. The graph on the left shows the torque generated by the engine as a function of the angular velocity of the engine, while the curve on the right shows torque as a function of car speed for different gears.]]&lt;br /&gt;
Typical values of &amp;lt;math&amp;gt;\alpha_n&amp;lt;/math&amp;gt; for gears 1 through 5 are &amp;lt;math&amp;gt;\alpha_1 = 40&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;\alpha_2 = 25&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;\alpha_3 = 16&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;\alpha_4 = 12&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;\alpha_5 = 10&amp;lt;/math&amp;gt;. The inverse of &amp;lt;math&amp;gt;\alpha_n&amp;lt;/math&amp;gt; has a physical interpretation as the effective wheel radius.  The figure to the right shows the torque as a function of vehicle speed. The figure shows that the effect of the gear is to &amp;quot;flatten&amp;quot; the torque curve so that an almost full torque can be obtained almost over the whole speed range.&lt;br /&gt;
&lt;br /&gt;
The disturbance force &amp;lt;math&amp;gt;F_\text{d}&amp;lt;/math&amp;gt; has three major components: &amp;lt;math&amp;gt;F_\text{g}&amp;lt;/math&amp;gt;, the forces due to gravity; &amp;lt;math&amp;gt;F_\text{r}&amp;lt;/math&amp;gt;, the forces due to rolling friction; and &amp;lt;math&amp;gt;F_\text{a}&amp;lt;/math&amp;gt;, the aerodynamic drag.  Letting the slope of the&lt;br /&gt;
road be &amp;lt;math&amp;gt;\theta&amp;lt;/math&amp;gt;, gravity gives the force &amp;lt;math&amp;gt;F_\text{g} = m g \sin\theta&amp;lt;/math&amp;gt;, where &amp;lt;math&amp;gt;g = 9.8\,&lt;br /&gt;
\text{m}/\text{s}^2&amp;lt;/math&amp;gt; is the gravitational constant.  A simple model of rolling friction is&lt;br /&gt;
&amp;lt;center&amp;gt;&amp;lt;math&amp;gt;&lt;br /&gt;
  F_\text{r} = m g C_\text{r}\, \text{sgn}(v),&lt;br /&gt;
&amp;lt;/math&amp;gt;&amp;lt;/center&amp;gt;&lt;br /&gt;
where &amp;lt;math&amp;gt;C_\text{r}&amp;lt;/math&amp;gt; is the coefficient of rolling friction and sgn(&amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;) is the sign of &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; or zero if &amp;lt;math&amp;gt;v = 0&amp;lt;/math&amp;gt;.  A typical value for the coefficient of rolling friction is &amp;lt;math&amp;gt;C_\text{r} = 0.01&amp;lt;/math&amp;gt;.  Finally, the aerodynamic drag is proportional to the square of the speed:&lt;br /&gt;
&amp;lt;center&amp;gt;&amp;lt;math&amp;gt;&lt;br /&gt;
  F_\text{a} = \frac{1}{2} \rho C_\text{d} A v^2,&lt;br /&gt;
&amp;lt;/math&amp;gt;&amp;lt;/center&amp;gt;&lt;br /&gt;
where &amp;lt;math&amp;gt;\rho&amp;lt;/math&amp;gt; is the density of air, &amp;lt;math&amp;gt;C_d&amp;lt;/math&amp;gt; is the shape-dependent aerodynamic drag coefficient and &amp;lt;math&amp;gt;A&amp;lt;/math&amp;gt; is the frontal area of the car.  Typical parameters are &amp;lt;math&amp;gt;\rho = &amp;lt;/math&amp;gt; 1.3 k/m&amp;lt;math&amp;gt;{}^3&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;C_\text{d} = 0.32&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;A =&amp;lt;/math&amp;gt; 2.4 m&amp;lt;math&amp;gt;{}^2&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Python code ===&lt;br /&gt;
&lt;br /&gt;
The model for the system above can be built using the [[https:python-control.org|Python Control Toolbox]].  The code blocks in this section can be used to generate the plots above.  The vehicle dynamics and PI controller are defined in [[https:github.com/murrayrm/fbs2e-python/blob/main/cruise.py|cruise.py]] (via GitHub).&lt;br /&gt;
&lt;br /&gt;
{{Code block|Package initialization}}&lt;br /&gt;
{{Code start}}&lt;br /&gt;
import numpy as np&lt;br /&gt;
import matplotlib as mpl&lt;br /&gt;
import matplotlib.pyplot as plt&lt;br /&gt;
from math import pi&lt;br /&gt;
import control as ct&lt;br /&gt;
{{Code end}}&lt;br /&gt;
&lt;br /&gt;
{{Code block|Vehicle model}}&lt;br /&gt;
{{Code start}}&lt;br /&gt;
def vehicle_update(t, x, u, params={}):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;Vehicle dynamics for cruise control system.&lt;br /&gt;
&lt;br /&gt;
    Parameters&lt;br /&gt;
    ----------&lt;br /&gt;
    x : array&lt;br /&gt;
         System state: car velocity in m/s&lt;br /&gt;
    u : array&lt;br /&gt;
         System input: [throttle, gear, road_slope], where throttle is&lt;br /&gt;
         a float between 0 and 1, gear is an integer between 1 and 5,&lt;br /&gt;
         and road_slope is in rad.&lt;br /&gt;
&lt;br /&gt;
    Returns&lt;br /&gt;
    -------&lt;br /&gt;
    float&lt;br /&gt;
        Vehicle acceleration&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
    from math import copysign, sin&lt;br /&gt;
    sign = lambda x: copysign(1, x)         # define the sign() function&lt;br /&gt;
    &lt;br /&gt;
    # Set up the system parameters&lt;br /&gt;
    m = params.get('m', 1600.)              # vehicle mass, kg&lt;br /&gt;
    g = params.get('g', 9.8)                # gravitational constant, m/s^2&lt;br /&gt;
    Cr = params.get('Cr', 0.01)             # coefficient of rolling friction&lt;br /&gt;
    Cd = params.get('Cd', 0.32)             # drag coefficient&lt;br /&gt;
    rho = params.get('rho', 1.3)            # density of air, kg/m^3&lt;br /&gt;
    A = params.get('A', 2.4)                # car area, m^2&lt;br /&gt;
    alpha = params.get(&lt;br /&gt;
        'alpha', [40, 25, 16, 12, 10])      # gear ratio / wheel radius&lt;br /&gt;
&lt;br /&gt;
    # Define variables for vehicle state and inputs&lt;br /&gt;
    v = x[0]                           # vehicle velocity&lt;br /&gt;
    throttle = np.clip(u[0], 0, 1)     # vehicle throttle&lt;br /&gt;
    gear = u[1]                        # vehicle gear&lt;br /&gt;
    theta = u[2]                       # road slope&lt;br /&gt;
&lt;br /&gt;
    # Force generated by the engine&lt;br /&gt;
    omega = alpha[int(gear)-1] * v      # engine angular speed&lt;br /&gt;
    F = alpha[int(gear)-1] * motor_torque(omega, params) * throttle&lt;br /&gt;
&lt;br /&gt;
    # Disturbance forces&lt;br /&gt;
    #&lt;br /&gt;
    # The disturbance force Fd has three major components: Fg, the forces due&lt;br /&gt;
    # to gravity; Fr, the forces due to rolling friction; and Fa, the&lt;br /&gt;
    # aerodynamic drag.&lt;br /&gt;
&lt;br /&gt;
    # Letting the slope of the road be \theta (theta), gravity gives the&lt;br /&gt;
    # force Fg = m g sin \theta.&lt;br /&gt;
    Fg = m * g * sin(theta)&lt;br /&gt;
&lt;br /&gt;
    # A simple model of rolling friction is Fr = m g Cr sgn(v), where Cr is&lt;br /&gt;
    # the coefficient of rolling friction and sgn(v) is the sign of v (±1) or&lt;br /&gt;
    # zero if v = 0.&lt;br /&gt;
    Fr  = m * g * Cr * sign(v)&lt;br /&gt;
&lt;br /&gt;
    # The aerodynamic drag is proportional to the square of the speed: Fa =&lt;br /&gt;
    # 1/2 \rho Cd A |v| v, where \rho is the density of air, Cd is the&lt;br /&gt;
    # shape-dependent aerodynamic drag coefficient, and A is the frontal area&lt;br /&gt;
    # of the car.&lt;br /&gt;
    Fa = 1/2 * rho * Cd * A * abs(v) * v&lt;br /&gt;
    &lt;br /&gt;
    # Final acceleration on the car&lt;br /&gt;
    Fd = Fg + Fr + Fa&lt;br /&gt;
    dv = (F - Fd) / m&lt;br /&gt;
    &lt;br /&gt;
    return dv&lt;br /&gt;
{{Code end}}&lt;br /&gt;
&lt;br /&gt;
{{Code block|Engine model}}&lt;br /&gt;
{{Code start}}&lt;br /&gt;
def motor_torque(omega, params={}):&lt;br /&gt;
    # Set up the system parameters&lt;br /&gt;
    Tm = params.get('Tm', 190.)             # engine torque constant&lt;br /&gt;
    omega_m = params.get('omega_m', 420.)   # peak engine angular speed&lt;br /&gt;
    beta = params.get('beta', 0.4)          # peak engine rolloff&lt;br /&gt;
&lt;br /&gt;
    return np.clip(Tm * (1 - beta * (omega/omega_m - 1)**2), 0, None)&lt;br /&gt;
{{Code end}}&lt;br /&gt;
&lt;br /&gt;
{{Code block|Input/output model for the vehicle system}}&lt;br /&gt;
{{Code start}}&lt;br /&gt;
vehicle = ct.NonlinearIOSystem(&lt;br /&gt;
    vehicle_update, None, name='vehicle',&lt;br /&gt;
    inputs = ('u', 'gear', 'theta'), outputs = ('v'), states=('v'))&lt;br /&gt;
{{Code end}}&lt;br /&gt;
&lt;br /&gt;
{{Code block|Input/output torque curves (plot)}}&lt;br /&gt;
{{Code start}}&lt;br /&gt;
# Figure 4.2a - single torque curve as function of omega&lt;br /&gt;
omega_range = np.linspace(0, 700, 701)&lt;br /&gt;
plt.subplot(2, 2, 1)&lt;br /&gt;
plt.plot(omega_range, [motor_torque(w) for w in omega_range])&lt;br /&gt;
plt.xlabel('Angular velocity $\omega$ [rad/s]')&lt;br /&gt;
plt.ylabel('Torque $T$ [Nm]')&lt;br /&gt;
plt.grid(True, linestyle='dotted')&lt;br /&gt;
&lt;br /&gt;
# Figure 4.2b - torque curves in different gears, as function of velocity&lt;br /&gt;
plt.subplot(2, 2, 2)&lt;br /&gt;
v_range = np.linspace(0, 70, 71)&lt;br /&gt;
alpha = [40, 25, 16, 12, 10]&lt;br /&gt;
for gear in range(5):&lt;br /&gt;
    omega_range = alpha[gear] * v_range&lt;br /&gt;
    plt.plot(v_range, [motor_torque(w) for w in omega_range],&lt;br /&gt;
             color='blue', linestyle='solid')&lt;br /&gt;
&lt;br /&gt;
# Set up the axes and style&lt;br /&gt;
plt.axis([0, 70, 100, 200])&lt;br /&gt;
plt.grid(True, linestyle='dotted')&lt;br /&gt;
&lt;br /&gt;
# Add labels&lt;br /&gt;
plt.text(11.5, 120, '$n$=1')&lt;br /&gt;
plt.text(24, 120, '$n$=2')&lt;br /&gt;
plt.text(42.5, 120, '$n$=3')&lt;br /&gt;
plt.text(58.5, 120, '$n$=4')&lt;br /&gt;
plt.text(58.5, 185, '$n$=5')&lt;br /&gt;
plt.xlabel('Velocity $v$ [m/s]')&lt;br /&gt;
plt.ylabel('Torque $T$ [Nm]')&lt;br /&gt;
&lt;br /&gt;
plt.suptitle('Torque curves for typical car engine');&lt;br /&gt;
plt.tight_layout()&lt;br /&gt;
plt.savefig(&amp;quot;cruise-gearcurves.png&amp;quot;, bbox_inches='tight')&lt;br /&gt;
{{Code end}}&lt;br /&gt;
&lt;br /&gt;
{{Code block|PI controller}}&lt;br /&gt;
{{Code start}}&lt;br /&gt;
# Construct a PI controller with rolloff, as a transfer function&lt;br /&gt;
Kp = 0.5                        # proportional gain&lt;br /&gt;
Ki = 0.1                        # integral gain&lt;br /&gt;
control_pi = ct.tf2io(&lt;br /&gt;
    ct.TransferFunction([Kp, Ki], [1, 0.01*Ki/Kp]),&lt;br /&gt;
    name='control', inputs='u', outputs='y')&lt;br /&gt;
&lt;br /&gt;
cruise_pi = ct.InterconnectedSystem(&lt;br /&gt;
    (vehicle, control_pi), name='cruise',&lt;br /&gt;
    connections = [('control.u', '-vehicle.v'), ('vehicle.u', 'control.y')],&lt;br /&gt;
    inplist = ('control.u', 'vehicle.gear', 'vehicle.theta'), inputs = ('vref', 'gear', 'theta'),&lt;br /&gt;
    outlist = ('vehicle.v', 'vehicle.u'), outputs = ('v', 'u'))&lt;br /&gt;
{{Code end}} &lt;br /&gt;
&lt;br /&gt;
{{Code block|Plotting function}}&lt;br /&gt;
{{Code start}}&lt;br /&gt;
# Define a generator for creating a &amp;quot;standard&amp;quot; cruise control plot&lt;br /&gt;
def cruise_plot(sys, t, y, t_hill=5, vref=20, antiwindup=False, linetype='b-',&lt;br /&gt;
               subplots=[None, None]):&lt;br /&gt;
    # Figure out the plot bounds and indices&lt;br /&gt;
    v_min = vref-1.2; v_max = vref+0.5; v_ind = sys.find_output('v')&lt;br /&gt;
    u_min = 0; u_max = 2 if antiwindup else 1; u_ind = sys.find_output('u')&lt;br /&gt;
&lt;br /&gt;
    # Make sure the upper and lower bounds on v are OK&lt;br /&gt;
    while max(y[v_ind]) &amp;gt; v_max: v_max += 1&lt;br /&gt;
    while min(y[v_ind]) &amp;lt; v_min: v_min -= 1&lt;br /&gt;
        &lt;br /&gt;
    # Create arrays for return values&lt;br /&gt;
    subplot_axes = subplots.copy()&lt;br /&gt;
&lt;br /&gt;
    # Velocity profile&lt;br /&gt;
    if subplot_axes[0] is None:&lt;br /&gt;
        subplot_axes[0] = plt.subplot(2, 1, 1)&lt;br /&gt;
    else:&lt;br /&gt;
        plt.sca(subplots[0])&lt;br /&gt;
    plt.plot(t, y[v_ind], linetype)&lt;br /&gt;
    plt.plot(t, vref*np.ones(t.shape), 'k-')&lt;br /&gt;
    plt.plot([t_hill, t_hill], [v_min, v_max], 'k--')&lt;br /&gt;
    plt.axis([0, t[-1], v_min, v_max])&lt;br /&gt;
    plt.xlabel('Time $t$ [s]')&lt;br /&gt;
    plt.ylabel('Velocity $v$ [m/s]')&lt;br /&gt;
&lt;br /&gt;
    # Commanded input profile&lt;br /&gt;
    if subplot_axes[1] is None:&lt;br /&gt;
        subplot_axes[1] = plt.subplot(2, 1, 2)&lt;br /&gt;
    else:&lt;br /&gt;
        plt.sca(subplots[1])&lt;br /&gt;
    plt.plot(t, y[u_ind], 'r--' if antiwindup else linetype)&lt;br /&gt;
    plt.plot([t_hill, t_hill], [u_min, u_max], 'k--')&lt;br /&gt;
    plt.axis([0, t[-1], u_min, u_max])&lt;br /&gt;
    plt.xlabel('Time $t$ [s]')&lt;br /&gt;
    plt.ylabel('Throttle $u$')&lt;br /&gt;
&lt;br /&gt;
    # Applied input profile&lt;br /&gt;
    if antiwindup:&lt;br /&gt;
        plt.plot(t, np.clip(y[u_ind], 0, 1), linetype)&lt;br /&gt;
        plt.legend(['Commanded', 'Applied'], frameon=False)&lt;br /&gt;
        &lt;br /&gt;
    return subplot_axes&lt;br /&gt;
{{Code end}}&lt;br /&gt;
&lt;br /&gt;
{{Code block|Trajectory description}}&lt;br /&gt;
{{Code start}}&lt;br /&gt;
# Define the time and input vectors&lt;br /&gt;
T = np.linspace(0, 25, 101)&lt;br /&gt;
vref = 20 * np.ones(T.shape)&lt;br /&gt;
gear = 4 * np.ones(T.shape)&lt;br /&gt;
theta0 = np.zeros(T.shape)&lt;br /&gt;
&lt;br /&gt;
# Effect of a hill at t = 5 seconds&lt;br /&gt;
theta_hill = np.array([&lt;br /&gt;
    0 if t &amp;lt;= 5 else&lt;br /&gt;
    4./180. * pi * (t-5) if t &amp;lt;= 6 else&lt;br /&gt;
    4./180. * pi for t in T])&lt;br /&gt;
{{Code end}}&lt;br /&gt;
&lt;br /&gt;
{{Code block|Simulated responses (plot)}}&lt;br /&gt;
{{Code start}}&lt;br /&gt;
# Simulate and plot&lt;br /&gt;
plt.figure()&lt;br /&gt;
plt.suptitle('Response to change in road slope')&lt;br /&gt;
&lt;br /&gt;
subplots = [None, None]&lt;br /&gt;
linecolor = ['red', 'blue', 'green']&lt;br /&gt;
handles = []&lt;br /&gt;
for i, m in enumerate([1200, 1600, 2000]):&lt;br /&gt;
    # Compute the equilibrium state for the system&lt;br /&gt;
    X0, U0 = ct.find_eqpt(&lt;br /&gt;
        cruise_pi, [vref[0], 0], [vref[0], gear[0], theta0[0]], &lt;br /&gt;
        iu=[1, 2], y0=[vref[0], 0], iy=[0], params={'m':m})&lt;br /&gt;
&lt;br /&gt;
    t, y = ct.input_output_response(&lt;br /&gt;
        cruise_pi, T, [vref, gear, theta_hill], X0, params={'m':m})&lt;br /&gt;
&lt;br /&gt;
    subplots = cruise_plot(cruise_pi, t, y, t_hill=5, subplots=subplots,&lt;br /&gt;
                           linetype=linecolor[i][0] + '-')&lt;br /&gt;
    handles.append(mpl.lines.Line2D([], [], color=linecolor[i], &lt;br /&gt;
                   linestyle='-', label=&amp;quot;m = %d&amp;quot; % m))&lt;br /&gt;
&lt;br /&gt;
# Add labels to the plots&lt;br /&gt;
plt.sca(subplots[0])&lt;br /&gt;
plt.ylabel('Speed [m/s]')&lt;br /&gt;
plt.legend(handles=handles, frameon=False, loc='lower right');&lt;br /&gt;
&lt;br /&gt;
plt.sca(subplots[1])&lt;br /&gt;
plt.ylabel('Throttle')&lt;br /&gt;
plt.xlabel('Time [s]');&lt;br /&gt;
plt.savefig(&amp;quot;cruise-speedresp.png&amp;quot;, bbox_inches='tight')&lt;br /&gt;
{{Code end}}&lt;br /&gt;
&lt;br /&gt;
=== Linearized Dynamics ===&lt;br /&gt;
&lt;br /&gt;
To explore the behavior of the cruise control system near the equilibrium point we&lt;br /&gt;
will linearize the system. A Taylor series expansion of&lt;br /&gt;
the dynamics around the equilibrium point gives&lt;br /&gt;
&amp;lt;center&amp;gt;&amp;lt;math&amp;gt;&lt;br /&gt;
  \frac{d(v-v_\text{e})}{dt} = -a (v - v_\text{e})&lt;br /&gt;
    - b_\text{g} (\theta - \theta_\text{e}) + b (u - u_\text{e}) &lt;br /&gt;
    + \text{higher-order terms,}&lt;br /&gt;
&amp;lt;/math&amp;gt;&amp;lt;/center&amp;gt;&lt;br /&gt;
where&lt;br /&gt;
&amp;lt;center&amp;gt;&amp;lt;math&amp;gt;&lt;br /&gt;
  a = \frac{\rho C_\text{d} A |v_\text{e}|&lt;br /&gt;
        - u_\text{e} \alpha_n^2 T'(\alpha_n v_\text{e})}{m}, \qquad&lt;br /&gt;
  b_\text{g} = g \cos{\theta_\text{e}}, \qquad &lt;br /&gt;
  b = \frac{\alpha_n T(\alpha_n v_\text{e})}{m}. &lt;br /&gt;
&amp;lt;/math&amp;gt;&amp;lt;/center&amp;gt;&lt;br /&gt;
[[Image:cruise-linear_vs_nonlinear.png|right|border|400px|Simulated response of a vehicle with PI cruise control as it climbs a hill with a slope of 4 degrees (smaller  velocity deviation/throttle) and a slope of 6 degrees (larger velocity deviation/throttle). The solid line is the simulation based on a  nonlinear model, and the dashed line shows the corresponding simulation using a linear model. The controller gains are kp = 0.5 and ki = 0.1 and include anti-windup compensation (described in more detail in below).]]&lt;br /&gt;
Notice that the term corresponding to rolling friction disappears if &amp;lt;math&amp;gt;v &amp;gt; 0&amp;lt;/math&amp;gt;.  For a car in fourth gear with &amp;lt;math&amp;gt;v_\text{e} = 20&amp;lt;/math&amp;gt; m/s, &amp;lt;math&amp;gt;\theta_\text{e} = 0&amp;lt;/math&amp;gt;, and the numerical values from above, the equilibrium value for the throttle is &amp;lt;math&amp;gt;u_\text{e}=0.1687&amp;lt;/math&amp;gt; and the parameters are &amp;lt;math&amp;gt;a = 0.01&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;b = 1.32&amp;lt;/math&amp;gt;, and &amp;lt;math&amp;gt;b_\text{g} = 9.8&amp;lt;/math&amp;gt;.  This linear model describes how small perturbations in the velocity about the nominal speed evolve in time.&lt;br /&gt;
&lt;br /&gt;
We apply the PI controller above to the case where the car is running with constant speed on a horizontal road and the system has stabilized so that the vehicle speed and the controller output are constant. The figure to the right shows what happens when the car encounters a hill with a slope of 4 degrees and a hill with a slope of 6 degrees at time &amp;lt;math&amp;gt;t =\,&amp;lt;/math&amp;gt; 5 seconds. The results for the nonlinear model are dashed curves and those for the linear model are solid curves.  The differences between the curves are very small (especially for &amp;lt;math&amp;gt;\theta =\,&amp;lt;/math&amp;gt; 4 degrees), and control design based on the linearized model is thus validated.&amp;lt;br clear=both&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Python code ===&lt;br /&gt;
&lt;br /&gt;
{{Code block|Linearized dynamics}}&lt;br /&gt;
{{Code start}}&lt;br /&gt;
# Find the equilibrium point for the system&lt;br /&gt;
Xeq, Ueq = ct.find_eqpt(&lt;br /&gt;
    vehicle, [vref[0]], [0, gear[0], theta0[0]], y0=[vref[0]], iu=[1, 2])&lt;br /&gt;
print(&amp;quot;Xeq = &amp;quot;, Xeq, &amp;quot;\nUeq = &amp;quot;, Ueq)&lt;br /&gt;
&lt;br /&gt;
# Compute the linearized system at the eq pt&lt;br /&gt;
vehlin = ct.linearize(vehicle, Xeq, [Ueq[0], gear[0], 0], name='vehlin', copy=True)&lt;br /&gt;
{{Code end}}&lt;br /&gt;
&lt;br /&gt;
{{Code block|PI controller with anti-windup compensation}}&lt;br /&gt;
{{Code start}}&lt;br /&gt;
def pi_update(t, x, u, params={}):&lt;br /&gt;
    # Get the controller parameters that we need&lt;br /&gt;
    ki = params.get('ki', 0.1)&lt;br /&gt;
    kaw = params.get('kaw', 2)  # anti-windup gain&lt;br /&gt;
&lt;br /&gt;
    # Assign variables for inputs and states (for readability)&lt;br /&gt;
    v = u[0]                    # current velocity&lt;br /&gt;
    vref = u[1]                 # reference velocity&lt;br /&gt;
    z = x[0]                    # integrated error&lt;br /&gt;
&lt;br /&gt;
    # Compute the nominal controller output (needed for anti-windup)&lt;br /&gt;
    u_a = pi_output(t, x, u, params)&lt;br /&gt;
&lt;br /&gt;
    # Compute anti-windup compensation (scale by ki to account for structure)&lt;br /&gt;
    u_aw = kaw/ki * (np.clip(u_a, 0, 1) - u_a) if ki != 0 else 0&lt;br /&gt;
&lt;br /&gt;
    # State is the integrated error, minus anti-windup compensation&lt;br /&gt;
    return (vref - v) + u_aw&lt;br /&gt;
&lt;br /&gt;
def pi_output(t, x, u, params={}):&lt;br /&gt;
    # Get the controller parameters that we need&lt;br /&gt;
    kp = params.get('kp', 0.5)&lt;br /&gt;
    ki = params.get('ki', 0.1)&lt;br /&gt;
&lt;br /&gt;
    # Assign variables for inputs and states (for readability)&lt;br /&gt;
    v = u[0]                    # current velocity&lt;br /&gt;
    vref = u[1]                 # reference velocity&lt;br /&gt;
    z = x[0]                    # integrated error&lt;br /&gt;
&lt;br /&gt;
    # PI controller&lt;br /&gt;
    return kp * (vref - v) + ki * z&lt;br /&gt;
&lt;br /&gt;
control_pi_aw = ct.NonlinearIOSystem(&lt;br /&gt;
    pi_update, pi_output, name='control',&lt;br /&gt;
    inputs = ['v', 'vref'], outputs = ['u'], states = ['z'],&lt;br /&gt;
    params = {'kp':0.5, 'ki':0.1})&lt;br /&gt;
&lt;br /&gt;
# Create a closed loop controller for the linear system&lt;br /&gt;
cruise_lin = ct.InterconnectedSystem(&lt;br /&gt;
    (vehlin, control_pi_aw), name='cruise_lin',&lt;br /&gt;
    connections=(&lt;br /&gt;
        ('vehlin.u', 'control.u'),&lt;br /&gt;
        ('control.v', 'vehlin.v')),&lt;br /&gt;
    inplist=('control.vref', 'vehlin.gear', 'vehlin.theta'),&lt;br /&gt;
    outlist=('control.u', 'vehlin.v'), outputs=['u', 'v'])&lt;br /&gt;
&lt;br /&gt;
# Create a closed loop controller for the nonlinear system&lt;br /&gt;
cruise_nonlin = ct.InterconnectedSystem(&lt;br /&gt;
    (vehicle, control_pi_aw), name='cruise_nonlin',&lt;br /&gt;
    connections=(&lt;br /&gt;
        ('vehicle.u', 'control.u'),&lt;br /&gt;
        ('control.v', 'vehicle.v')),&lt;br /&gt;
    inplist=('control.vref', 'vehicle.gear', 'vehicle.theta'),&lt;br /&gt;
    outlist=('control.u', 'vehicle.v'), outputs=['u', 'v'])&lt;br /&gt;
{{Code end}}&lt;br /&gt;
&lt;br /&gt;
{{Code block|Simulations}}&lt;br /&gt;
{{Code start}}&lt;br /&gt;
# Linear response&lt;br /&gt;
X0_lin, U0 = ct.find_eqpt(&lt;br /&gt;
    cruise_lin, [vref[0], 0], [vref[0], gear[0], theta0[0]],&lt;br /&gt;
    y0=[0, vref[0]], iu=[1, 2], iy=[1])&lt;br /&gt;
&lt;br /&gt;
t, y = ct.input_output_response(cruise_lin, T, [vref, gear, theta_hill], X0_lin)&lt;br /&gt;
subplots = cruise_plot(cruise_lin, t, y, t_hill=5, linetype='r-')&lt;br /&gt;
&lt;br /&gt;
# Nonlinear response&lt;br /&gt;
X0_nonlin, U0 = ct.find_eqpt(&lt;br /&gt;
    cruise_nonlin, [vref[0], 0], [vref[0], gear[0], theta0[0]],&lt;br /&gt;
    y0=[0, vref[0]], iu=[1, 2], iy=[1])&lt;br /&gt;
&lt;br /&gt;
t, y = ct.input_output_response(cruise_nonlin, T, [vref, gear, theta_hill], X0_nonlin)&lt;br /&gt;
subplots = cruise_plot(cruise_nonlin, t, y, t_hill=5, subplots=subplots, linetype='b--') &lt;br /&gt;
&lt;br /&gt;
# Add a legend to identify linear vs nonlinear&lt;br /&gt;
plt.legend(['linear', 'nonlinear'], frameon=False, loc='lower right')&lt;br /&gt;
&lt;br /&gt;
# Add two more simulations for 6 degree hill instead of 4 degree&lt;br /&gt;
t, y = ct.input_output_response(cruise_lin, T, [vref, gear, theta_hill*1.5], X0_lin)&lt;br /&gt;
subplots = cruise_plot(cruise_lin, t, y, t_hill=5, subplots=subplots, linetype='r-') &lt;br /&gt;
&lt;br /&gt;
t, y = ct.input_output_response(cruise_nonlin, T, [vref, gear, theta_hill*1.5], X0_nonlin)&lt;br /&gt;
subplots = cruise_plot(cruise_nonlin, t, y, t_hill=5, subplots=subplots, linetype='b--') &lt;br /&gt;
&lt;br /&gt;
# Add titles and legends and save the figure&lt;br /&gt;
plt.suptitle('Linear versus nonlinear model response')&lt;br /&gt;
plt.sca(subplots[1]); plt.axis([0, 25, 0, 1.2])&lt;br /&gt;
&lt;br /&gt;
plt.savefig(&amp;quot;cruise-linear_vs_nonlinear.png&amp;quot;, bbox_inches='tight')&lt;br /&gt;
{{Code end}}&lt;br /&gt;
&lt;br /&gt;
== State Space Control ==&lt;br /&gt;
&lt;br /&gt;
The linearized dynamics of&lt;br /&gt;
the process around an equilibrium point &amp;lt;math&amp;gt;v_\text{e}&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;u_\text{e}&amp;lt;/math&amp;gt; are given by&lt;br /&gt;
&amp;lt;center&amp;gt;&amp;lt;math&amp;gt;&lt;br /&gt;
  \begin{aligned}&lt;br /&gt;
    \frac{dx}{dt} &amp;amp;= -a x - b_\text{g} \theta + b w, \\&lt;br /&gt;
    y &amp;amp;= v = x + v_\text{e},&lt;br /&gt;
  \end{aligned}&lt;br /&gt;
&amp;lt;/math&amp;gt;&amp;lt;/center&amp;gt;&lt;br /&gt;
where &amp;lt;math&amp;gt;x = v - v_\text{e}&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt; = u - u_\text{e}&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; is the mass of&lt;br /&gt;
the car, and &amp;lt;math&amp;gt;\theta&amp;lt;/math&amp;gt; is the angle of the road.  The constants &amp;lt;math&amp;gt;a&amp;lt;/math&amp;gt;,&lt;br /&gt;
&amp;lt;math&amp;gt;b&amp;lt;/math&amp;gt;, and &amp;lt;math&amp;gt;b_\text{g}&amp;lt;/math&amp;gt; depend on the properties of the car and are&lt;br /&gt;
given above.&lt;br /&gt;
&lt;br /&gt;
If we augment the system with an integrator, the system dynamics become&lt;br /&gt;
&amp;lt;center&amp;gt;&amp;lt;math&amp;gt;&lt;br /&gt;
  \begin{aligned}&lt;br /&gt;
    \frac{dx}{dt} &amp;amp;= -a x - b_\text{g} \theta + b w, \\&lt;br /&gt;
    \frac{dz}{dt} &amp;amp;= y - v_\text{r} = v_\text{e} + x - v_\text{r},&lt;br /&gt;
  \end{aligned}&lt;br /&gt;
&amp;lt;/math&amp;gt;&amp;lt;/center&amp;gt;&lt;br /&gt;
or, in state space form,&lt;br /&gt;
&amp;lt;center&amp;gt;&amp;lt;math&amp;gt;&lt;br /&gt;
  \frac{d}{dt} \begin{bmatrix} x \\ z \end{bmatrix} = \begin{bmatrix}&lt;br /&gt;
    -a &amp;amp; 0 \\&lt;br /&gt;
    1 &amp;amp; 0&lt;br /&gt;
  \end{bmatrix} \begin{bmatrix} x \\ z \end{bmatrix} + &lt;br /&gt;
  \begin{bmatrix} b \\ 0 \end{bmatrix} w + &lt;br /&gt;
  \begin{bmatrix} -b_\text{g} \\ 0 \end{bmatrix} \theta + &lt;br /&gt;
  \begin{bmatrix} 0 \\ v_\text{e} - v_\text{r} \end{bmatrix}.&lt;br /&gt;
&amp;lt;/math&amp;gt;&amp;lt;/center&amp;gt;&lt;br /&gt;
Note that when the system is at equilibrium, we have that &amp;lt;math&amp;gt;\dot z = 0&amp;lt;/math&amp;gt;,&lt;br /&gt;
which implies that the vehicle speed &amp;lt;math&amp;gt;v = v_\text{e} + x&amp;lt;/math&amp;gt; should be&lt;br /&gt;
equal to the desired reference speed &amp;lt;math&amp;gt;v_\text{r}&amp;lt;/math&amp;gt;.  Our controller will be of&lt;br /&gt;
the form&lt;br /&gt;
&amp;lt;center&amp;gt;&amp;lt;math&amp;gt;&lt;br /&gt;
  \begin{aligned}&lt;br /&gt;
    \frac{dz}{dt} &amp;amp;= y - v_\text{r}, \\&lt;br /&gt;
    w &amp;amp;= -k_\text{p} x - k_\text{i} z + k_\text{f} v_\text{r},&lt;br /&gt;
  \end{aligned}&lt;br /&gt;
&amp;lt;/math&amp;gt;&amp;lt;/center&amp;gt;&lt;br /&gt;
and the gains &amp;lt;math&amp;gt;k_\text{p}&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;k_\text{i}&amp;lt;/math&amp;gt;, and &amp;lt;math&amp;gt;k_\text{f}&amp;lt;/math&amp;gt; will be chosen to&lt;br /&gt;
stabilize the system and provide the correct input for the reference speed.&lt;br /&gt;
&lt;br /&gt;
Assume that we wish to design the closed loop system to have the&lt;br /&gt;
characteristic polynomial&lt;br /&gt;
&amp;lt;center&amp;gt;&amp;lt;math&amp;gt;&lt;br /&gt;
  \lambda(s) = s^2 + a_1 s + a_2.&lt;br /&gt;
&amp;lt;/math&amp;gt;&amp;lt;/center&amp;gt;&lt;br /&gt;
Setting the disturbance &amp;lt;math&amp;gt;\theta = 0&amp;lt;/math&amp;gt;, &lt;br /&gt;
the characteristic polynomial of the closed loop system is given by&lt;br /&gt;
&amp;lt;center&amp;gt;&amp;lt;math&amp;gt;&lt;br /&gt;
  \det\bigl(sI - (A - BK)\bigr) = s^2 + (b k_\text{p} + a) s + b k_\text{i},&lt;br /&gt;
&amp;lt;/math&amp;gt;&amp;lt;/center&amp;gt;&lt;br /&gt;
and hence we set&lt;br /&gt;
&amp;lt;center&amp;gt;&amp;lt;math&amp;gt;&lt;br /&gt;
  k_\text{p} = \frac{a_1 - a}{b}, \qquad k_\text{i} = \frac{a_2}{b}, \qquad &lt;br /&gt;
  k_\text{f} = {-1}/\bigl(C (A-BK)^{-1} B\bigr) = \frac{a_1}{b}.&lt;br /&gt;
&amp;lt;/math&amp;gt;&amp;lt;/center&amp;gt;&lt;br /&gt;
[[Image:cruise-statefbk.png|right|border|400px|Velocity and throttle for a car with cruise control based on state feedback (dashed) and state feedback with integral action (solid). The controller with integral action is able to adjust the throttle to compensate for the effect of the hill and maintain the speed at the reference value of vr = 20 m/s. The controller gains are kp = 0.5 and ki = 0.1.]]&lt;br /&gt;
The resulting controller stabilizes the system and hence brings&lt;br /&gt;
&amp;lt;math&amp;gt;\dot z = y - v_\text{r}&amp;lt;/math&amp;gt; to zero, resulting in perfect tracking.&lt;br /&gt;
Notice that even if we have a small error in the values of the&lt;br /&gt;
parameters defining the system, as long as the closed loop eigenvalues&lt;br /&gt;
are still stable, then the tracking error will approach zero.  Thus&lt;br /&gt;
the exact calibration required in&lt;br /&gt;
our previous approach (using &amp;lt;math&amp;gt;k_\text{f}&amp;lt;/math&amp;gt;) is not needed here.&lt;br /&gt;
Indeed, we can even choose &amp;lt;math&amp;gt;k_\text{f} = 0&amp;lt;/math&amp;gt; and let the feedback&lt;br /&gt;
controller do all of the work.  However, &amp;lt;math&amp;gt;k_\text{f}&amp;lt;/math&amp;gt; does influence&lt;br /&gt;
the transient response to reference signals and setting it properly&lt;br /&gt;
will generally give a more favorable response.&lt;br /&gt;
&lt;br /&gt;
Integral feedback can also be used to compensate for constant&lt;br /&gt;
disturbances.  The figure to the right shows the results of&lt;br /&gt;
a simulation in which the car encounters a hill with angle&lt;br /&gt;
&amp;lt;math&amp;gt;\theta = 4&amp;lt;/math&amp;gt; degrees at &amp;lt;math&amp;gt;t = &amp;lt;/math&amp;gt; 5 seconds.  The&lt;br /&gt;
steady-state values of the throttle for a state feedback controller&lt;br /&gt;
and a controller with integral action are very close, but the&lt;br /&gt;
corresponding values of the car velocity are quite different.  The&lt;br /&gt;
reason for this is that the zero frequency gain from throttle to&lt;br /&gt;
velocity is &amp;lt;math&amp;gt;-b/a=130&amp;lt;/math&amp;gt; is high.&lt;br /&gt;
The stability of the system is not affected by this external&lt;br /&gt;
disturbance, and so we once again see that the car's velocity converges&lt;br /&gt;
to the reference speed.  This ability to handle constant disturbances&lt;br /&gt;
is a general property of controllers with integral feedback.&lt;br /&gt;
&lt;br /&gt;
=== Python code ===&lt;br /&gt;
&lt;br /&gt;
{{Code block|State feedback controller}}&lt;br /&gt;
{{Code start}}&lt;br /&gt;
def sf_update(t, z, u, params={}):&lt;br /&gt;
    y, r = u[1], u[2]&lt;br /&gt;
    return y - r&lt;br /&gt;
&lt;br /&gt;
def sf_output(t, z, u, params={}):&lt;br /&gt;
    # Get the controller parameters that we need&lt;br /&gt;
    K = params.get('K', 0)&lt;br /&gt;
    ki = params.get('ki', 0)&lt;br /&gt;
    kf = params.get('kf', 0)&lt;br /&gt;
    xd = params.get('xd', 0)&lt;br /&gt;
    yd = params.get('yd', 0)&lt;br /&gt;
    ud = params.get('ud', 0)&lt;br /&gt;
&lt;br /&gt;
    # Get the system state and reference input&lt;br /&gt;
    x, y, r = u[0], u[1], u[2]&lt;br /&gt;
&lt;br /&gt;
    return ud - K * (x - xd) - ki * z + kf * (r - yd)&lt;br /&gt;
&lt;br /&gt;
# Create the input/output system for the controller&lt;br /&gt;
control_sf = ct.NonlinearIOSystem(&lt;br /&gt;
    sf_update, sf_output, name='control',&lt;br /&gt;
    inputs=('x', 'y', 'r'),&lt;br /&gt;
    outputs=('u'),&lt;br /&gt;
    states=('z'))&lt;br /&gt;
&lt;br /&gt;
# Create the closed loop system for the state space controller&lt;br /&gt;
cruise_sf = ct.InterconnectedSystem(&lt;br /&gt;
    (vehicle, control_sf), name='cruise',&lt;br /&gt;
    connections=(&lt;br /&gt;
        ('vehicle.u', 'control.u'),&lt;br /&gt;
        ('control.x', 'vehicle.v'),&lt;br /&gt;
        ('control.y', 'vehicle.v')),&lt;br /&gt;
    inplist=('control.r', 'vehicle.gear', 'vehicle.theta'),&lt;br /&gt;
    outlist=('control.u', 'vehicle.v'), outputs=['u', 'v'])&lt;br /&gt;
&lt;br /&gt;
# Construct the gain matrices for the system&lt;br /&gt;
A, B, C = vehlin.A, vehlin.B[0, 0], vehlin.C&lt;br /&gt;
K = 0.5&lt;br /&gt;
kf = -1 / (C * np.linalg.inv(A - B * K) * B)&lt;br /&gt;
&lt;br /&gt;
# Compute the steady state velocity and throttle setting&lt;br /&gt;
xd = Xeq[0]&lt;br /&gt;
ud = Ueq[0]&lt;br /&gt;
yd = vref[-1]&lt;br /&gt;
{{Code end}}&lt;br /&gt;
&lt;br /&gt;
{{Code block|Simulation (plot)}}&lt;br /&gt;
{{Code start}}&lt;br /&gt;
# Response of the system with no integral feedback term&lt;br /&gt;
t, y_sfb = ct.input_output_response(&lt;br /&gt;
    cruise_sf, T, [vref, gear, theta_hill], [Xeq[0], 0],&lt;br /&gt;
    params={'K':K, 'ki':0.0, 'kf':kf, 'xd':xd, 'ud':ud, 'yd':yd})&lt;br /&gt;
subplots = cruise_plot(cruise_sf, t, y_sfb, t_hill=5, linetype='b--')&lt;br /&gt;
&lt;br /&gt;
# Response of the system with state feedback + integral action&lt;br /&gt;
t, y_sfb_int = ct.input_output_response(&lt;br /&gt;
    cruise_sf, T, [vref, gear, theta_hill], [Xeq[0], 0],&lt;br /&gt;
    params={'K':K, 'ki':0.1, 'kf':kf, 'xd':xd, 'ud':ud, 'yd':yd})&lt;br /&gt;
cruise_plot(cruise_sf, t, y_sfb_int, t_hill=5, linetype='b-', subplots=subplots)&lt;br /&gt;
&lt;br /&gt;
# Add title and legend&lt;br /&gt;
plt.suptitle('Cruise control with state feedback, integral action')&lt;br /&gt;
p_line = mpl.lines.Line2D([], [], color='blue', linestyle='--', label='State feedback')&lt;br /&gt;
pi_line = mpl.lines.Line2D([], [], color='blue', linestyle='-', label='w/ integral action')&lt;br /&gt;
plt.legend(handles=[p_line, pi_line], frameon=False, loc='lower right')&lt;br /&gt;
plt.savefig(&amp;quot;cruise-statefbk.png&amp;quot;, bbox_inches='tight')&lt;br /&gt;
{{Code end}}&lt;br /&gt;
&lt;br /&gt;
== PID Control ==&lt;br /&gt;
&lt;br /&gt;
The dynamics of the system are given by a first-order system with the transfer function&lt;br /&gt;
&amp;lt;center&amp;gt;&amp;lt;math&amp;gt;&lt;br /&gt;
  P(s)=\frac{b}{s+a}.&lt;br /&gt;
&amp;lt;/math&amp;gt;&amp;lt;/center&amp;gt;&lt;br /&gt;
With a PI controller the closed loop system has the characteristic polynomial&lt;br /&gt;
&amp;lt;center&amp;gt;&amp;lt;math&amp;gt;&lt;br /&gt;
  s (s + a) + b k_\text{p} s + b k_\text{i} = s^2 + (a + b k_\text{p}) s + b k_\text{i}.&lt;br /&gt;
&amp;lt;/math&amp;gt;&amp;lt;/center&amp;gt;&lt;br /&gt;
The closed loop poles can thus be assigned arbitrary values by proper &lt;br /&gt;
choice of the controller gains &amp;lt;math&amp;gt;k_\text{p}&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;k_\text{i}&amp;lt;/math&amp;gt;.&lt;br /&gt;
Requiring that the closed loop system have the characteristic polynomial&lt;br /&gt;
&amp;lt;center&amp;gt;&amp;lt;math&amp;gt;&lt;br /&gt;
  p(s) = s^2+a_1s+a_2,&lt;br /&gt;
&amp;lt;/math&amp;gt;&amp;lt;/center&amp;gt;&lt;br /&gt;
we find that the controller parameters are&lt;br /&gt;
&amp;lt;center&amp;gt;&amp;lt;math&amp;gt;&lt;br /&gt;
  k_\text{p}=\frac{a_1-a}{b},\qquad k_\text{i}=\frac{a_2}{b}.&lt;br /&gt;
&amp;lt;/math&amp;gt;&amp;lt;/center&amp;gt;&lt;br /&gt;
[[Image:cruise-pid-sweep-zeta.png|right|border|400px|Cruise control using PI feedback.  The step responses for the error and input illustrate the effect of parameters zeta_c and omega_c on the response of a car with cruise control.  The slope of the road changes linearly from 0 degrees to 4 degrees between t = 5 and 6 seconds.  Responses for omega_c = 0.5 and zeta_c = 0.5, 1, and 2.  Choosing&lt;br /&gt;
zeta_c &amp;gt;= 1 gives no overshoot in the velocity v.]]&lt;br /&gt;
If we require a response of the closed loop system that is slower than&lt;br /&gt;
that of the open loop system, a reasonable choice is &amp;lt;math&amp;gt;a_1 = a + \alpha&amp;lt;/math&amp;gt;&lt;br /&gt;
and &amp;lt;math&amp;gt;a_2 = \alpha a&amp;lt;/math&amp;gt;, where &amp;lt;math&amp;gt;\alpha &amp;lt; a&amp;lt;/math&amp;gt; determines the closed loop&lt;br /&gt;
response.  If a response faster than that of the open loop system is&lt;br /&gt;
required, a possible choice is &amp;lt;math&amp;gt;a_1 = 2 \zeta_\text{c} \omega_\text{c}&amp;lt;/math&amp;gt; and&lt;br /&gt;
&amp;lt;math&amp;gt;a_2 = \omega_\text{c}^2&amp;lt;/math&amp;gt;,&lt;br /&gt;
where &amp;lt;math&amp;gt;\omega_\text{c}&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;\zeta_\text{c}&amp;lt;/math&amp;gt; are the undamped natural&lt;br /&gt;
frequency and damping ratio of the dominant mode.&lt;br /&gt;
&lt;br /&gt;
To design a PI controller we choose &amp;lt;math&amp;gt;\zeta_\text{c} = 1&amp;lt;/math&amp;gt; to obtain a response without overshoot. The choice of &amp;lt;math&amp;gt;\omega_\text{c}&amp;lt;/math&amp;gt; is a compromise between response speed and control actions: a large value gives a fast response, but it requires fast control action. The largest velocity error decreases with increasing &amp;lt;math&amp;gt;\omega_\text{c}&amp;lt;/math&amp;gt;, but the control signal also changes more rapidly. In the simple model it was assumed that the force responds instantaneously to throttle commands. For rapid changes there may be additional dynamics that have to be accounted for.  There are also physical limits to the rate of change of the force, which also restricts the admissible value of &amp;lt;math&amp;gt;\omega_\text{c}&amp;lt;/math&amp;gt;. A reasonable choice of &amp;lt;math&amp;gt;\omega_\text{c}&amp;lt;/math&amp;gt; is in the range 0.5--1.0.&lt;br /&gt;
&lt;br /&gt;
=== Python code ===&lt;br /&gt;
&lt;br /&gt;
{{Code block|Get transfer function parameters}}&lt;br /&gt;
{{Code start}}&lt;br /&gt;
# Get the transfer function from throttle input + hill to vehicle speed&lt;br /&gt;
P = ct.ss2tf(vehlin[0, 0])&lt;br /&gt;
&lt;br /&gt;
# Construction a controller that cancels the pole&lt;br /&gt;
a = -P.pole()[0]&lt;br /&gt;
b = np.real(P(0)) * a&lt;br /&gt;
{{Code end}}&lt;br /&gt;
&lt;br /&gt;
{{Code block|Fix &amp;lt;math&amp;gt;\omega_\text{c}&amp;lt;/math&amp;gt; and vary &amp;lt;math&amp;gt;\zeta_\text{c} (plot)&amp;lt;/math&amp;gt;}}&lt;br /&gt;
{{Code start}}&lt;br /&gt;
# Fix \omega_0 and vary \zeta&lt;br /&gt;
w0 = 0.5&lt;br /&gt;
subplots = [None, None]&lt;br /&gt;
for zeta in [0.5, 1, 2]:&lt;br /&gt;
    # Create the controller transfer function (as an I/O system)&lt;br /&gt;
    kp = (2*zeta*w0 - a)/b&lt;br /&gt;
    ki = w0**2 / b&lt;br /&gt;
    control_tf = ct.tf2io(&lt;br /&gt;
        ct.TransferFunction([kp, ki], [1, 0.01*ki/kp]),&lt;br /&gt;
        name='control', inputs='u', outputs='y')&lt;br /&gt;
    &lt;br /&gt;
    # Construct the closed loop system by interconnecting process and controller&lt;br /&gt;
    cruise_tf = ct.InterconnectedSystem(&lt;br /&gt;
    (vehicle, control_tf), name='cruise',&lt;br /&gt;
    connections = [('control.u', '-vehicle.v'), ('vehicle.u', 'control.y')],&lt;br /&gt;
    inplist = ('control.u', 'vehicle.gear', 'vehicle.theta'), &lt;br /&gt;
        inputs = ('vref', 'gear', 'theta'),&lt;br /&gt;
    outlist = ('vehicle.v', 'vehicle.u'), outputs = ('v', 'u'))&lt;br /&gt;
&lt;br /&gt;
    # Plot the velocity response&lt;br /&gt;
    X0, U0 = ct.find_eqpt(&lt;br /&gt;
        cruise_tf, [vref[0], 0], [vref[0], gear[0], theta_hill[0]], &lt;br /&gt;
        iu=[1, 2], y0=[vref[0], 0], iy=[0])&lt;br /&gt;
&lt;br /&gt;
    t, y = ct.input_output_response(cruise_tf, T, [vref, gear, theta_hill], X0)&lt;br /&gt;
    subplots = cruise_plot(cruise_tf, t, y, t_hill=5, subplots=subplots)&lt;br /&gt;
&lt;br /&gt;
plt.suptitle('PID controller with varying zeta')&lt;br /&gt;
plt.savefig(&amp;quot;cruise-pid-sweep-zeta.png&amp;quot;, bbox_inches='tight')&lt;br /&gt;
{{Code end}}&lt;br /&gt;
&lt;br /&gt;
{{Code block|Fix vary &amp;lt;math&amp;gt;\zeta_\text{c}&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;\omega_\text{c} (plot)&amp;lt;/math&amp;gt;}}&lt;br /&gt;
{{Code start}}&lt;br /&gt;
zeta = 1&lt;br /&gt;
subplots = [None, None]&lt;br /&gt;
for w0 in [0.2, 0.5, 1]:&lt;br /&gt;
    # Create the controller transfer function (as an I/O system)&lt;br /&gt;
    kp = (2*zeta*w0 - a)/b&lt;br /&gt;
    ki = w0**2 / b&lt;br /&gt;
    control_tf = ct.tf2io(&lt;br /&gt;
        ct.TransferFunction([kp, ki], [1, 0.01*ki/kp]),&lt;br /&gt;
        name='control', inputs='u', outputs='y')&lt;br /&gt;
    &lt;br /&gt;
    # Construct the closed loop system by interconnecting process and controller&lt;br /&gt;
    cruise_tf = ct.InterconnectedSystem(&lt;br /&gt;
        (vehicle, control_tf), name='cruise',&lt;br /&gt;
        connections = [('control.u', '-vehicle.v'), ('vehicle.u', 'control.y')],&lt;br /&gt;
        inplist = ('control.u', 'vehicle.gear', 'vehicle.theta'), &lt;br /&gt;
        inputs = ('vref', 'gear', 'theta'),&lt;br /&gt;
        outlist = ('vehicle.v', 'vehicle.u'), outputs = ('v', 'u'))&lt;br /&gt;
&lt;br /&gt;
    # Plot the velocity response&lt;br /&gt;
    X0, U0 = ct.find_eqpt(&lt;br /&gt;
        cruise_tf, [vref[0], 0], [vref[0], gear[0], theta_hill[0]], &lt;br /&gt;
        iu=[1, 2], y0=[vref[0], 0], iy=[0])&lt;br /&gt;
&lt;br /&gt;
    t, y = ct.input_output_response(cruise_tf, T, [vref, gear, theta_hill], X0)&lt;br /&gt;
    subplots = cruise_plot(cruise_tf, t, y, t_hill=5, subplots=subplots)&lt;br /&gt;
&lt;br /&gt;
plt.suptitle('PID controller with varying omega')&lt;br /&gt;
plt.savefig(&amp;quot;cruise-pid-sweep-omega.png&amp;quot;, bbox_inches='tight')&lt;br /&gt;
{{Code end}}&lt;br /&gt;
&lt;br /&gt;
=== Anti-windup compensation ===&lt;br /&gt;
&lt;br /&gt;
The windup effect is illustrated in the left figure below, which shows what happens when a car encounters a hill that is so steep (6 degrees) that the throttle saturates when the cruise controller attempts to maintain speed. When encountering the slope at time &amp;lt;math&amp;gt;t=5&amp;lt;/math&amp;gt;, the velocity decreases and the throttle increases to generate more torque. However, the torque required is so large that the throttle saturates. The error decreases slowly because the torque generated by the engine is just a little larger than the torque required to compensate for gravity. The error is large and the integral continues to build up until the error reaches zero at time 25, but the controller output is still  larger than the saturation limit and the actuator remains saturated. The integral term starts to decrease and the velocity settles to the desired value at time &amp;lt;math&amp;gt;t = 40&amp;lt;/math&amp;gt;. Also notice the large overshoot.&lt;br /&gt;
&lt;br /&gt;
The right figure below shows what happens when a controller with anti-windup is applied to the system. Because of the feedback from the actuator model, the output of the integrator is quickly reset to a value such that the controller output is at the saturation limit.  The behavior is drastically different from that on the left and the large overshoot is avoided. The tracking gain used in the simulation is &amp;lt;math&amp;gt;k_\text{aw} = 2&amp;lt;/math&amp;gt; which is an order of magnitude larger than the integral gain &amp;lt;math&amp;gt;k_\text{i} = 0.2&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
{| align=center&lt;br /&gt;
|-&lt;br /&gt;
| width=45% |&lt;br /&gt;
[[Image:cruise-windup.png|border|400px|Simulation of PI cruise control with windup. The figure shows the speed v and the throttle u for a car that encounters a slope that is so steep that the throttle saturates. The controller output is a dashed line. The controller parameters are kp = 0.5 and ki = 0.1.]]&lt;br /&gt;
| width=10% |&lt;br /&gt;
| width=45% |&lt;br /&gt;
[[Image:cruise-antiwindup.png|border|400px|Simulation of PI cruise control with anti-windup. The figure shows the speed v and the throttle u for a car that encounters a slope that is so steep that the throttle saturates. The controller output is a dashed line. The controller parameters are kp = 0.5, ki = 0.1. and kaw =2. The anti-windup compensator eliminates the overshoot by preventing the error from building up in the integral term of the controller.]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Python code ===&lt;br /&gt;
&lt;br /&gt;
{{code block|Redefine input with longer duration}}&lt;br /&gt;
{{Code start}}&lt;br /&gt;
T = np.linspace(0, 50, 101)&lt;br /&gt;
vref = 20 * np.ones(T.shape)&lt;br /&gt;
theta_hill = [&lt;br /&gt;
    0 if t &amp;lt;= 5 else&lt;br /&gt;
    6./180. * pi * (t-5) if t &amp;lt;= 6 else&lt;br /&gt;
    6./180. * pi for t in T]&lt;br /&gt;
&lt;br /&gt;
# Compute the equilibrium throttle setting for the desired speed&lt;br /&gt;
X0, U0 = ct.find_eqpt(&lt;br /&gt;
    cruise_nonlin, [vref[0], 0], [vref[0], gear[0], theta0[0]],&lt;br /&gt;
    y0=[0, vref[0]], iu=[1, 2], iy=[1])&lt;br /&gt;
{{Code end}}&lt;br /&gt;
&lt;br /&gt;
{{Code block|Effects of windup (plot)}}&lt;br /&gt;
{{Code start}}&lt;br /&gt;
t, y = ct.input_output_response(&lt;br /&gt;
    cruise_nonlin, T, [vref, gear, theta_hill], X0,&lt;br /&gt;
    params={'kaw':0})&lt;br /&gt;
cruise_plot(cruise_nonlin, t, y, antiwindup=True);&lt;br /&gt;
&lt;br /&gt;
plt.suptitle('Cruise control with integrator windup')&lt;br /&gt;
plt.savefig(&amp;quot;cruise-windup.png&amp;quot;, bbox_inches='tight')&lt;br /&gt;
{{Code end}}&lt;br /&gt;
&lt;br /&gt;
{{Code block|Anti-windup compensation (plot)}}&lt;br /&gt;
{{Code start}}&lt;br /&gt;
t, y = ct.input_output_response(&lt;br /&gt;
    cruise_nonlin, T, [vref, gear, theta_hill], X0,&lt;br /&gt;
    params={'kaw':2})&lt;br /&gt;
cruise_plot(cruise_nonlin, t, y, antiwindup=True);&lt;br /&gt;
&lt;br /&gt;
plt.suptitle('Cruise control with anti-windup')&lt;br /&gt;
plt.savefig(&amp;quot;cruise-antiwindup.png&amp;quot;, bbox_inches='tight')&lt;br /&gt;
{{Code end}}&lt;br /&gt;
&lt;br /&gt;
== Further Reading ==&lt;br /&gt;
* How Stuff Works: [http://auto.howstuffworks.com/cruise-control.htm cruise control]&lt;br /&gt;
* Wikipedia: [[Wikipedia:Cruise control|cruise control]]&lt;br /&gt;
&lt;br /&gt;
[[Category:Running examples]]&lt;/div&gt;</summary>
		<author><name>Murray</name></author>
	</entry>
	<entry>
		<id>http://fbswiki.org/wiki/index.php?title=Cruise_control&amp;diff=1329</id>
		<title>Cruise control</title>
		<link rel="alternate" type="text/html" href="http://fbswiki.org/wiki/index.php?title=Cruise_control&amp;diff=1329"/>
		<updated>2024-11-24T16:39:36Z</updated>

		<summary type="html">&lt;p&gt;Murray: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{righttoc}}&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! GitHub URL&lt;br /&gt;
| https://github.com/murrayrm/fbs2e-python/blob/main/cruise.py&lt;br /&gt;
|-&lt;br /&gt;
! Requires&lt;br /&gt;
| [[https:python-control.org|python-control]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
This page documents the cruise control system that is used as a running example throughout the text.  A detailed description of the dynamics of this system are presented in {{chapter link|Examples}}.  This page contains a description of the system, including the models and commands used to generate some of the plots in the text.&lt;br /&gt;
&lt;br /&gt;
Note: the Python code on this page makes use of the [https://python-control.org Python Control Systems Library], an open source software toolbox for control systems analysis.&lt;br /&gt;
&lt;br /&gt;
Figures making use of this model:&lt;br /&gt;
{{#ask: [[Category:Figures]] [[Requires::cruise.py]] | format=ul | sort=Sort key}}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Introduction ==&lt;br /&gt;
&lt;br /&gt;
[[Image:cruise-block.png|right|300px|border|A feedback system for controlling the velocity of a vehicle. In the block diagram, the velocity of the vehicle is measured and compared to the desired velocity within the “Compute” block. Based on the difference in the actual and desired velocities, the throttle (or brake) is used to modify the force applied to the vehicle by the engine, drivetrain, and wheels.]]&lt;br /&gt;
Cruise control is the term used to describe a control system that regulates the speed of an automobile.  Cruise control was commercially introduced in 1958 as an option on the Chrysler Imperial.  The basic operation of a cruise controller is to sense the speed of the vehicle, compare this speed to a desired reference, and then accelerate or decelerate the car as required.  The figure to the right shows a block diagram of this feedback system.&lt;br /&gt;
&lt;br /&gt;
[[Image:cruise-speedresp.png|right|300px|border|Response to a disturbance. The car travels on a horizontal road and the slope of the road changes to a constant uphill slope. The three different curves correspond to differing masses of the vehicle, between 1200 and 2000 kg, demonstrating that feedback can indeed compensate for the changing slope and that the closed loop system is robust to a large change in the vehicle characteristics.&lt;br /&gt;
]]&lt;br /&gt;
A simple control algorithm for controlling the speed is to use a &amp;quot;proportional plus integral&amp;quot; feedback.  In this algorithm, we choose the amount of gas flowing to the engine based  on both the error between the current and desired speed, and the integral of that error.  The plot on the right shows the results of this feedback for a step change in the desired speed and a variety of different masses for the car (which might result from having a different number of passengers or towing a trailer).  Notice that independent of the mass (which varies by 25% of the total weight of the car), the steady state speed of the vehicle always approaches the desired speed and achieves that speed within approximately 10-15 seconds.  Thus the performance of the system is robust with respect to this uncertainty.&lt;br /&gt;
&lt;br /&gt;
== Dynamic model ==&lt;br /&gt;
&lt;br /&gt;
To develop a mathematical model we start with a force balance for the car body. Let &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; be the speed of the car, &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; the total mass (including passengers), &amp;lt;math&amp;gt;F&amp;lt;/math&amp;gt; the force generated by the contact of the wheels with the road, and &amp;lt;math&amp;gt;F_\text{d}&amp;lt;/math&amp;gt; the disturbance force due to gravity, friction and aerodynamic drag. The equation of motion of the car is simply &lt;br /&gt;
&amp;lt;center&amp;gt;&amp;lt;math&amp;gt; &lt;br /&gt;
  m \frac{dv}{dt} = F - F_\text{d}.&lt;br /&gt;
&amp;lt;/math&amp;gt;&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The force &amp;lt;math&amp;gt;F&amp;lt;/math&amp;gt; is generated by the engine, whose torque is proportional to the rate of fuel injection, which is itself proportional to a control signal &amp;lt;math&amp;gt;0 \leq u \leq 1&amp;lt;/math&amp;gt; that controls the throttle position. The torque also depends on engine speed &amp;lt;math&amp;gt;\omega&amp;lt;/math&amp;gt;. A simple representation of the torque at full throttle is given by the torque curve &lt;br /&gt;
&amp;lt;center&amp;gt;&amp;lt;math&amp;gt;&lt;br /&gt;
  T(\omega) = T_\text{m} \left(1 - \beta \biggl(\frac{\omega}{\omega_\text{m}} - 1\biggr)^2&lt;br /&gt;
  \right),&lt;br /&gt;
&amp;lt;/math&amp;gt;&amp;lt;/center&amp;gt;&lt;br /&gt;
where the maximum torque &amp;lt;math&amp;gt;T_\text{m}&amp;lt;/math&amp;gt; is obtained at engine speed &amp;lt;math&amp;gt;\omega_\text{m}&amp;lt;/math&amp;gt;.  Typical parameters are &amp;lt;math&amp;gt;T_m = 190&amp;lt;/math&amp;gt; Nm, &amp;lt;math&amp;gt;\omega_m&amp;lt;/math&amp;gt; = 420 rad/s (about 4000 RPM) and &amp;lt;math&amp;gt;\beta = 0.4&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Let &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; be the gear ratio and &amp;lt;math&amp;gt;r&amp;lt;/math&amp;gt; the wheel radius. The engine speed is related to the velocity through the expression&lt;br /&gt;
&amp;lt;center&amp;gt;&amp;lt;math&amp;gt;&lt;br /&gt;
  \omega = \frac{n}{r} v =: \alpha_n v,&lt;br /&gt;
&amp;lt;/math&amp;gt;&amp;lt;/center&amp;gt;&lt;br /&gt;
and the driving force can be written as&lt;br /&gt;
&amp;lt;center&amp;gt;&amp;lt;math&amp;gt;&lt;br /&gt;
  F = \frac{nu}{r} T(\omega) = \alpha_n u T(\alpha_n v).&lt;br /&gt;
&amp;lt;/math&amp;gt;&amp;lt;/center&amp;gt;&lt;br /&gt;
[[Image:cruise-gearcurves.png|right|400px|border|Torque curves for typical car engine. The graph on the left shows the torque generated by the engine as a function of the angular velocity of the engine, while the curve on the right shows torque as a function of car speed for different gears.]]&lt;br /&gt;
Typical values of &amp;lt;math&amp;gt;\alpha_n&amp;lt;/math&amp;gt; for gears 1 through 5 are &amp;lt;math&amp;gt;\alpha_1 = 40&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;\alpha_2 = 25&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;\alpha_3 = 16&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;\alpha_4 = 12&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;\alpha_5 = 10&amp;lt;/math&amp;gt;. The inverse of &amp;lt;math&amp;gt;\alpha_n&amp;lt;/math&amp;gt; has a physical interpretation as the effective wheel radius.  The figure to the right shows the torque as a function of vehicle speed. The figure shows that the effect of the gear is to &amp;quot;flatten&amp;quot; the torque curve so that an almost full torque can be obtained almost over the whole speed range.&lt;br /&gt;
&lt;br /&gt;
The disturbance force &amp;lt;math&amp;gt;F_\text{d}&amp;lt;/math&amp;gt; has three major components: &amp;lt;math&amp;gt;F_\text{g}&amp;lt;/math&amp;gt;, the forces due to gravity; &amp;lt;math&amp;gt;F_\text{r}&amp;lt;/math&amp;gt;, the forces due to rolling friction; and &amp;lt;math&amp;gt;F_\text{a}&amp;lt;/math&amp;gt;, the aerodynamic drag.  Letting the slope of the&lt;br /&gt;
road be &amp;lt;math&amp;gt;\theta&amp;lt;/math&amp;gt;, gravity gives the force &amp;lt;math&amp;gt;F_\text{g} = m g \sin\theta&amp;lt;/math&amp;gt;, where &amp;lt;math&amp;gt;g = 9.8\,&lt;br /&gt;
\text{m}/\text{s}^2&amp;lt;/math&amp;gt; is the gravitational constant.  A simple model of rolling friction is&lt;br /&gt;
&amp;lt;center&amp;gt;&amp;lt;math&amp;gt;&lt;br /&gt;
  F_\text{r} = m g C_\text{r}\, \text{sgn}(v),&lt;br /&gt;
&amp;lt;/math&amp;gt;&amp;lt;/center&amp;gt;&lt;br /&gt;
where &amp;lt;math&amp;gt;C_\text{r}&amp;lt;/math&amp;gt; is the coefficient of rolling friction and sgn(&amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;) is the sign of &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; or zero if &amp;lt;math&amp;gt;v = 0&amp;lt;/math&amp;gt;.  A typical value for the coefficient of rolling friction is &amp;lt;math&amp;gt;C_\text{r} = 0.01&amp;lt;/math&amp;gt;.  Finally, the aerodynamic drag is proportional to the square of the speed:&lt;br /&gt;
&amp;lt;center&amp;gt;&amp;lt;math&amp;gt;&lt;br /&gt;
  F_\text{a} = \frac{1}{2} \rho C_\text{d} A v^2,&lt;br /&gt;
&amp;lt;/math&amp;gt;&amp;lt;/center&amp;gt;&lt;br /&gt;
where &amp;lt;math&amp;gt;\rho&amp;lt;/math&amp;gt; is the density of air, &amp;lt;math&amp;gt;C_d&amp;lt;/math&amp;gt; is the shape-dependent aerodynamic drag coefficient and &amp;lt;math&amp;gt;A&amp;lt;/math&amp;gt; is the frontal area of the car.  Typical parameters are &amp;lt;math&amp;gt;\rho = &amp;lt;/math&amp;gt; 1.3 k/m&amp;lt;math&amp;gt;{}^3&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;C_\text{d} = 0.32&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;A =&amp;lt;/math&amp;gt; 2.4 m&amp;lt;math&amp;gt;{}^2&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Python code ===&lt;br /&gt;
&lt;br /&gt;
The model for the system above can be built using the [[https:python-control.org|Python Control Toolbox]].  The code blocks in this section can be used to generate the plots above.  The vehicle dynamics and PI controller are defined in [[https:github.com/murrayrm/fbs2e-python/blob/main/cruise.py|cruise.py]] (via GitHub).&lt;br /&gt;
&lt;br /&gt;
{{Code block|Package initialization}}&lt;br /&gt;
{{Code start}}&lt;br /&gt;
import numpy as np&lt;br /&gt;
import matplotlib as mpl&lt;br /&gt;
import matplotlib.pyplot as plt&lt;br /&gt;
from math import pi&lt;br /&gt;
import control as ct&lt;br /&gt;
{{Code end}}&lt;br /&gt;
&lt;br /&gt;
{{Code block|Vehicle model}}&lt;br /&gt;
{{Code start}}&lt;br /&gt;
def vehicle_update(t, x, u, params={}):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;Vehicle dynamics for cruise control system.&lt;br /&gt;
&lt;br /&gt;
    Parameters&lt;br /&gt;
    ----------&lt;br /&gt;
    x : array&lt;br /&gt;
         System state: car velocity in m/s&lt;br /&gt;
    u : array&lt;br /&gt;
         System input: [throttle, gear, road_slope], where throttle is&lt;br /&gt;
         a float between 0 and 1, gear is an integer between 1 and 5,&lt;br /&gt;
         and road_slope is in rad.&lt;br /&gt;
&lt;br /&gt;
    Returns&lt;br /&gt;
    -------&lt;br /&gt;
    float&lt;br /&gt;
        Vehicle acceleration&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
    from math import copysign, sin&lt;br /&gt;
    sign = lambda x: copysign(1, x)         # define the sign() function&lt;br /&gt;
    &lt;br /&gt;
    # Set up the system parameters&lt;br /&gt;
    m = params.get('m', 1600.)              # vehicle mass, kg&lt;br /&gt;
    g = params.get('g', 9.8)                # gravitational constant, m/s^2&lt;br /&gt;
    Cr = params.get('Cr', 0.01)             # coefficient of rolling friction&lt;br /&gt;
    Cd = params.get('Cd', 0.32)             # drag coefficient&lt;br /&gt;
    rho = params.get('rho', 1.3)            # density of air, kg/m^3&lt;br /&gt;
    A = params.get('A', 2.4)                # car area, m^2&lt;br /&gt;
    alpha = params.get(&lt;br /&gt;
        'alpha', [40, 25, 16, 12, 10])      # gear ratio / wheel radius&lt;br /&gt;
&lt;br /&gt;
    # Define variables for vehicle state and inputs&lt;br /&gt;
    v = x[0]                           # vehicle velocity&lt;br /&gt;
    throttle = np.clip(u[0], 0, 1)     # vehicle throttle&lt;br /&gt;
    gear = u[1]                        # vehicle gear&lt;br /&gt;
    theta = u[2]                       # road slope&lt;br /&gt;
&lt;br /&gt;
    # Force generated by the engine&lt;br /&gt;
    omega = alpha[int(gear)-1] * v      # engine angular speed&lt;br /&gt;
    F = alpha[int(gear)-1] * motor_torque(omega, params) * throttle&lt;br /&gt;
&lt;br /&gt;
    # Disturbance forces&lt;br /&gt;
    #&lt;br /&gt;
    # The disturbance force Fd has three major components: Fg, the forces due&lt;br /&gt;
    # to gravity; Fr, the forces due to rolling friction; and Fa, the&lt;br /&gt;
    # aerodynamic drag.&lt;br /&gt;
&lt;br /&gt;
    # Letting the slope of the road be \theta (theta), gravity gives the&lt;br /&gt;
    # force Fg = m g sin \theta.&lt;br /&gt;
    Fg = m * g * sin(theta)&lt;br /&gt;
&lt;br /&gt;
    # A simple model of rolling friction is Fr = m g Cr sgn(v), where Cr is&lt;br /&gt;
    # the coefficient of rolling friction and sgn(v) is the sign of v (±1) or&lt;br /&gt;
    # zero if v = 0.&lt;br /&gt;
    Fr  = m * g * Cr * sign(v)&lt;br /&gt;
&lt;br /&gt;
    # The aerodynamic drag is proportional to the square of the speed: Fa =&lt;br /&gt;
    # 1/2 \rho Cd A |v| v, where \rho is the density of air, Cd is the&lt;br /&gt;
    # shape-dependent aerodynamic drag coefficient, and A is the frontal area&lt;br /&gt;
    # of the car.&lt;br /&gt;
    Fa = 1/2 * rho * Cd * A * abs(v) * v&lt;br /&gt;
    &lt;br /&gt;
    # Final acceleration on the car&lt;br /&gt;
    Fd = Fg + Fr + Fa&lt;br /&gt;
    dv = (F - Fd) / m&lt;br /&gt;
    &lt;br /&gt;
    return dv&lt;br /&gt;
{{Code end}}&lt;br /&gt;
&lt;br /&gt;
{{Code block|Engine model}}&lt;br /&gt;
{{Code start}}&lt;br /&gt;
def motor_torque(omega, params={}):&lt;br /&gt;
    # Set up the system parameters&lt;br /&gt;
    Tm = params.get('Tm', 190.)             # engine torque constant&lt;br /&gt;
    omega_m = params.get('omega_m', 420.)   # peak engine angular speed&lt;br /&gt;
    beta = params.get('beta', 0.4)          # peak engine rolloff&lt;br /&gt;
&lt;br /&gt;
    return np.clip(Tm * (1 - beta * (omega/omega_m - 1)**2), 0, None)&lt;br /&gt;
{{Code end}}&lt;br /&gt;
&lt;br /&gt;
{{Code block|Input/output model for the vehicle system}}&lt;br /&gt;
{{Code start}}&lt;br /&gt;
vehicle = ct.NonlinearIOSystem(&lt;br /&gt;
    vehicle_update, None, name='vehicle',&lt;br /&gt;
    inputs = ('u', 'gear', 'theta'), outputs = ('v'), states=('v'))&lt;br /&gt;
{{Code end}}&lt;br /&gt;
&lt;br /&gt;
{{Code block|Input/output torque curves (plot)}}&lt;br /&gt;
{{Code start}}&lt;br /&gt;
# Figure 4.2a - single torque curve as function of omega&lt;br /&gt;
omega_range = np.linspace(0, 700, 701)&lt;br /&gt;
plt.subplot(2, 2, 1)&lt;br /&gt;
plt.plot(omega_range, [motor_torque(w) for w in omega_range])&lt;br /&gt;
plt.xlabel('Angular velocity $\omega$ [rad/s]')&lt;br /&gt;
plt.ylabel('Torque $T$ [Nm]')&lt;br /&gt;
plt.grid(True, linestyle='dotted')&lt;br /&gt;
&lt;br /&gt;
# Figure 4.2b - torque curves in different gears, as function of velocity&lt;br /&gt;
plt.subplot(2, 2, 2)&lt;br /&gt;
v_range = np.linspace(0, 70, 71)&lt;br /&gt;
alpha = [40, 25, 16, 12, 10]&lt;br /&gt;
for gear in range(5):&lt;br /&gt;
    omega_range = alpha[gear] * v_range&lt;br /&gt;
    plt.plot(v_range, [motor_torque(w) for w in omega_range],&lt;br /&gt;
             color='blue', linestyle='solid')&lt;br /&gt;
&lt;br /&gt;
# Set up the axes and style&lt;br /&gt;
plt.axis([0, 70, 100, 200])&lt;br /&gt;
plt.grid(True, linestyle='dotted')&lt;br /&gt;
&lt;br /&gt;
# Add labels&lt;br /&gt;
plt.text(11.5, 120, '$n$=1')&lt;br /&gt;
plt.text(24, 120, '$n$=2')&lt;br /&gt;
plt.text(42.5, 120, '$n$=3')&lt;br /&gt;
plt.text(58.5, 120, '$n$=4')&lt;br /&gt;
plt.text(58.5, 185, '$n$=5')&lt;br /&gt;
plt.xlabel('Velocity $v$ [m/s]')&lt;br /&gt;
plt.ylabel('Torque $T$ [Nm]')&lt;br /&gt;
&lt;br /&gt;
plt.suptitle('Torque curves for typical car engine');&lt;br /&gt;
plt.tight_layout()&lt;br /&gt;
plt.savefig(&amp;quot;cruise-gearcurves.png&amp;quot;, bbox_inches='tight')&lt;br /&gt;
{{Code end}}&lt;br /&gt;
&lt;br /&gt;
{{Code block|PI controller}}&lt;br /&gt;
{{Code start}}&lt;br /&gt;
# Construct a PI controller with rolloff, as a transfer function&lt;br /&gt;
Kp = 0.5                        # proportional gain&lt;br /&gt;
Ki = 0.1                        # integral gain&lt;br /&gt;
control_pi = ct.tf2io(&lt;br /&gt;
    ct.TransferFunction([Kp, Ki], [1, 0.01*Ki/Kp]),&lt;br /&gt;
    name='control', inputs='u', outputs='y')&lt;br /&gt;
&lt;br /&gt;
cruise_pi = ct.InterconnectedSystem(&lt;br /&gt;
    (vehicle, control_pi), name='cruise',&lt;br /&gt;
    connections = [('control.u', '-vehicle.v'), ('vehicle.u', 'control.y')],&lt;br /&gt;
    inplist = ('control.u', 'vehicle.gear', 'vehicle.theta'), inputs = ('vref', 'gear', 'theta'),&lt;br /&gt;
    outlist = ('vehicle.v', 'vehicle.u'), outputs = ('v', 'u'))&lt;br /&gt;
{{Code end}} &lt;br /&gt;
&lt;br /&gt;
{{Code block|Plotting function}}&lt;br /&gt;
{{Code start}}&lt;br /&gt;
# Define a generator for creating a &amp;quot;standard&amp;quot; cruise control plot&lt;br /&gt;
def cruise_plot(sys, t, y, t_hill=5, vref=20, antiwindup=False, linetype='b-',&lt;br /&gt;
               subplots=[None, None]):&lt;br /&gt;
    # Figure out the plot bounds and indices&lt;br /&gt;
    v_min = vref-1.2; v_max = vref+0.5; v_ind = sys.find_output('v')&lt;br /&gt;
    u_min = 0; u_max = 2 if antiwindup else 1; u_ind = sys.find_output('u')&lt;br /&gt;
&lt;br /&gt;
    # Make sure the upper and lower bounds on v are OK&lt;br /&gt;
    while max(y[v_ind]) &amp;gt; v_max: v_max += 1&lt;br /&gt;
    while min(y[v_ind]) &amp;lt; v_min: v_min -= 1&lt;br /&gt;
        &lt;br /&gt;
    # Create arrays for return values&lt;br /&gt;
    subplot_axes = subplots.copy()&lt;br /&gt;
&lt;br /&gt;
    # Velocity profile&lt;br /&gt;
    if subplot_axes[0] is None:&lt;br /&gt;
        subplot_axes[0] = plt.subplot(2, 1, 1)&lt;br /&gt;
    else:&lt;br /&gt;
        plt.sca(subplots[0])&lt;br /&gt;
    plt.plot(t, y[v_ind], linetype)&lt;br /&gt;
    plt.plot(t, vref*np.ones(t.shape), 'k-')&lt;br /&gt;
    plt.plot([t_hill, t_hill], [v_min, v_max], 'k--')&lt;br /&gt;
    plt.axis([0, t[-1], v_min, v_max])&lt;br /&gt;
    plt.xlabel('Time $t$ [s]')&lt;br /&gt;
    plt.ylabel('Velocity $v$ [m/s]')&lt;br /&gt;
&lt;br /&gt;
    # Commanded input profile&lt;br /&gt;
    if subplot_axes[1] is None:&lt;br /&gt;
        subplot_axes[1] = plt.subplot(2, 1, 2)&lt;br /&gt;
    else:&lt;br /&gt;
        plt.sca(subplots[1])&lt;br /&gt;
    plt.plot(t, y[u_ind], 'r--' if antiwindup else linetype)&lt;br /&gt;
    plt.plot([t_hill, t_hill], [u_min, u_max], 'k--')&lt;br /&gt;
    plt.axis([0, t[-1], u_min, u_max])&lt;br /&gt;
    plt.xlabel('Time $t$ [s]')&lt;br /&gt;
    plt.ylabel('Throttle $u$')&lt;br /&gt;
&lt;br /&gt;
    # Applied input profile&lt;br /&gt;
    if antiwindup:&lt;br /&gt;
        plt.plot(t, np.clip(y[u_ind], 0, 1), linetype)&lt;br /&gt;
        plt.legend(['Commanded', 'Applied'], frameon=False)&lt;br /&gt;
        &lt;br /&gt;
    return subplot_axes&lt;br /&gt;
{{Code end}}&lt;br /&gt;
&lt;br /&gt;
{{Code block|Trajectory description}}&lt;br /&gt;
{{Code start}}&lt;br /&gt;
# Define the time and input vectors&lt;br /&gt;
T = np.linspace(0, 25, 101)&lt;br /&gt;
vref = 20 * np.ones(T.shape)&lt;br /&gt;
gear = 4 * np.ones(T.shape)&lt;br /&gt;
theta0 = np.zeros(T.shape)&lt;br /&gt;
&lt;br /&gt;
# Effect of a hill at t = 5 seconds&lt;br /&gt;
theta_hill = np.array([&lt;br /&gt;
    0 if t &amp;lt;= 5 else&lt;br /&gt;
    4./180. * pi * (t-5) if t &amp;lt;= 6 else&lt;br /&gt;
    4./180. * pi for t in T])&lt;br /&gt;
{{Code end}}&lt;br /&gt;
&lt;br /&gt;
{{Code block|Simulated responses (plot)}}&lt;br /&gt;
{{Code start}}&lt;br /&gt;
# Simulate and plot&lt;br /&gt;
plt.figure()&lt;br /&gt;
plt.suptitle('Response to change in road slope')&lt;br /&gt;
&lt;br /&gt;
subplots = [None, None]&lt;br /&gt;
linecolor = ['red', 'blue', 'green']&lt;br /&gt;
handles = []&lt;br /&gt;
for i, m in enumerate([1200, 1600, 2000]):&lt;br /&gt;
    # Compute the equilibrium state for the system&lt;br /&gt;
    X0, U0 = ct.find_eqpt(&lt;br /&gt;
        cruise_pi, [vref[0], 0], [vref[0], gear[0], theta0[0]], &lt;br /&gt;
        iu=[1, 2], y0=[vref[0], 0], iy=[0], params={'m':m})&lt;br /&gt;
&lt;br /&gt;
    t, y = ct.input_output_response(&lt;br /&gt;
        cruise_pi, T, [vref, gear, theta_hill], X0, params={'m':m})&lt;br /&gt;
&lt;br /&gt;
    subplots = cruise_plot(cruise_pi, t, y, t_hill=5, subplots=subplots,&lt;br /&gt;
                           linetype=linecolor[i][0] + '-')&lt;br /&gt;
    handles.append(mpl.lines.Line2D([], [], color=linecolor[i], &lt;br /&gt;
                   linestyle='-', label=&amp;quot;m = %d&amp;quot; % m))&lt;br /&gt;
&lt;br /&gt;
# Add labels to the plots&lt;br /&gt;
plt.sca(subplots[0])&lt;br /&gt;
plt.ylabel('Speed [m/s]')&lt;br /&gt;
plt.legend(handles=handles, frameon=False, loc='lower right');&lt;br /&gt;
&lt;br /&gt;
plt.sca(subplots[1])&lt;br /&gt;
plt.ylabel('Throttle')&lt;br /&gt;
plt.xlabel('Time [s]');&lt;br /&gt;
plt.savefig(&amp;quot;cruise-speedresp.png&amp;quot;, bbox_inches='tight')&lt;br /&gt;
{{Code end}}&lt;br /&gt;
&lt;br /&gt;
=== Linearized Dynamics ===&lt;br /&gt;
&lt;br /&gt;
To explore the behavior of the cruise control system near the equilibrium point we&lt;br /&gt;
will linearize the system. A Taylor series expansion of&lt;br /&gt;
the dynamics around the equilibrium point gives&lt;br /&gt;
&amp;lt;center&amp;gt;&amp;lt;math&amp;gt;&lt;br /&gt;
  \frac{d(v-v_\text{e})}{dt} = -a (v - v_\text{e})&lt;br /&gt;
    - b_\text{g} (\theta - \theta_\text{e}) + b (u - u_\text{e}) &lt;br /&gt;
    + \text{higher-order terms,}&lt;br /&gt;
&amp;lt;/math&amp;gt;&amp;lt;/center&amp;gt;&lt;br /&gt;
where&lt;br /&gt;
&amp;lt;center&amp;gt;&amp;lt;math&amp;gt;&lt;br /&gt;
  a = \frac{\rho C_\text{d} A |v_\text{e}|&lt;br /&gt;
        - u_\text{e} \alpha_n^2 T'(\alpha_n v_\text{e})}{m}, \qquad&lt;br /&gt;
  b_\text{g} = g \cos{\theta_\text{e}}, \qquad &lt;br /&gt;
  b = \frac{\alpha_n T(\alpha_n v_\text{e})}{m}. &lt;br /&gt;
&amp;lt;/math&amp;gt;&amp;lt;/center&amp;gt;&lt;br /&gt;
[[Image:cruise-linear_vs_nonlinear.png|right|border|400px|Simulated response of a vehicle with PI cruise control as it climbs a hill with a slope of 4 degrees (smaller  velocity deviation/throttle) and a slope of 6 degrees (larger velocity deviation/throttle). The solid line is the simulation based on a  nonlinear model, and the dashed line shows the corresponding simulation using a linear model. The controller gains are kp = 0.5 and ki = 0.1 and include anti-windup compensation (described in more detail in below).]]&lt;br /&gt;
Notice that the term corresponding to rolling friction disappears if &amp;lt;math&amp;gt;v &amp;gt; 0&amp;lt;/math&amp;gt;.  For a car in fourth gear with &amp;lt;math&amp;gt;v_\text{e} = 20&amp;lt;/math&amp;gt; m/s, &amp;lt;math&amp;gt;\theta_\text{e} = 0&amp;lt;/math&amp;gt;, and the numerical values from above, the equilibrium value for the throttle is &amp;lt;math&amp;gt;u_\text{e}=0.1687&amp;lt;/math&amp;gt; and the parameters are &amp;lt;math&amp;gt;a = 0.01&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;b = 1.32&amp;lt;/math&amp;gt;, and &amp;lt;math&amp;gt;b_\text{g} = 9.8&amp;lt;/math&amp;gt;.  This linear model describes how small perturbations in the velocity about the nominal speed evolve in time.&lt;br /&gt;
&lt;br /&gt;
We apply the PI controller above to the case where the car is running with constant speed on a horizontal road and the system has stabilized so that the vehicle speed and the controller output are constant. The figure to the right shows what happens when the car encounters a hill with a slope of 4 degrees and a hill with a slope of 6 degrees at time &amp;lt;math&amp;gt;t =\,&amp;lt;/math&amp;gt; 5 seconds. The results for the nonlinear model are dashed curves and those for the linear model are solid curves.  The differences between the curves are very small (especially for &amp;lt;math&amp;gt;\theta =\,&amp;lt;/math&amp;gt; 4 degrees), and control design based on the linearized model is thus validated.&amp;lt;br clear=both&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Python code ===&lt;br /&gt;
&lt;br /&gt;
{{Code block|Linearized dynamics}}&lt;br /&gt;
{{Code start}}&lt;br /&gt;
# Find the equilibrium point for the system&lt;br /&gt;
Xeq, Ueq = ct.find_eqpt(&lt;br /&gt;
    vehicle, [vref[0]], [0, gear[0], theta0[0]], y0=[vref[0]], iu=[1, 2])&lt;br /&gt;
print(&amp;quot;Xeq = &amp;quot;, Xeq, &amp;quot;\nUeq = &amp;quot;, Ueq)&lt;br /&gt;
&lt;br /&gt;
# Compute the linearized system at the eq pt&lt;br /&gt;
vehlin = ct.linearize(vehicle, Xeq, [Ueq[0], gear[0], 0], name='vehlin', copy=True)&lt;br /&gt;
{{Code end}}&lt;br /&gt;
&lt;br /&gt;
{{Code block|PI controller with anti-windup compensation}}&lt;br /&gt;
{{Code start}}&lt;br /&gt;
def pi_update(t, x, u, params={}):&lt;br /&gt;
    # Get the controller parameters that we need&lt;br /&gt;
    ki = params.get('ki', 0.1)&lt;br /&gt;
    kaw = params.get('kaw', 2)  # anti-windup gain&lt;br /&gt;
&lt;br /&gt;
    # Assign variables for inputs and states (for readability)&lt;br /&gt;
    v = u[0]                    # current velocity&lt;br /&gt;
    vref = u[1]                 # reference velocity&lt;br /&gt;
    z = x[0]                    # integrated error&lt;br /&gt;
&lt;br /&gt;
    # Compute the nominal controller output (needed for anti-windup)&lt;br /&gt;
    u_a = pi_output(t, x, u, params)&lt;br /&gt;
&lt;br /&gt;
    # Compute anti-windup compensation (scale by ki to account for structure)&lt;br /&gt;
    u_aw = kaw/ki * (np.clip(u_a, 0, 1) - u_a) if ki != 0 else 0&lt;br /&gt;
&lt;br /&gt;
    # State is the integrated error, minus anti-windup compensation&lt;br /&gt;
    return (vref - v) + u_aw&lt;br /&gt;
&lt;br /&gt;
def pi_output(t, x, u, params={}):&lt;br /&gt;
    # Get the controller parameters that we need&lt;br /&gt;
    kp = params.get('kp', 0.5)&lt;br /&gt;
    ki = params.get('ki', 0.1)&lt;br /&gt;
&lt;br /&gt;
    # Assign variables for inputs and states (for readability)&lt;br /&gt;
    v = u[0]                    # current velocity&lt;br /&gt;
    vref = u[1]                 # reference velocity&lt;br /&gt;
    z = x[0]                    # integrated error&lt;br /&gt;
&lt;br /&gt;
    # PI controller&lt;br /&gt;
    return kp * (vref - v) + ki * z&lt;br /&gt;
&lt;br /&gt;
control_pi_aw = ct.NonlinearIOSystem(&lt;br /&gt;
    pi_update, pi_output, name='control',&lt;br /&gt;
    inputs = ['v', 'vref'], outputs = ['u'], states = ['z'],&lt;br /&gt;
    params = {'kp':0.5, 'ki':0.1})&lt;br /&gt;
&lt;br /&gt;
# Create a closed loop controller for the linear system&lt;br /&gt;
cruise_lin = ct.InterconnectedSystem(&lt;br /&gt;
    (vehlin, control_pi_aw), name='cruise_lin',&lt;br /&gt;
    connections=(&lt;br /&gt;
        ('vehlin.u', 'control.u'),&lt;br /&gt;
        ('control.v', 'vehlin.v')),&lt;br /&gt;
    inplist=('control.vref', 'vehlin.gear', 'vehlin.theta'),&lt;br /&gt;
    outlist=('control.u', 'vehlin.v'), outputs=['u', 'v'])&lt;br /&gt;
&lt;br /&gt;
# Create a closed loop controller for the nonlinear system&lt;br /&gt;
cruise_nonlin = ct.InterconnectedSystem(&lt;br /&gt;
    (vehicle, control_pi_aw), name='cruise_nonlin',&lt;br /&gt;
    connections=(&lt;br /&gt;
        ('vehicle.u', 'control.u'),&lt;br /&gt;
        ('control.v', 'vehicle.v')),&lt;br /&gt;
    inplist=('control.vref', 'vehicle.gear', 'vehicle.theta'),&lt;br /&gt;
    outlist=('control.u', 'vehicle.v'), outputs=['u', 'v'])&lt;br /&gt;
{{Code end}}&lt;br /&gt;
&lt;br /&gt;
{{Code block|Simulations}}&lt;br /&gt;
{{Code start}}&lt;br /&gt;
# Linear response&lt;br /&gt;
X0_lin, U0 = ct.find_eqpt(&lt;br /&gt;
    cruise_lin, [vref[0], 0], [vref[0], gear[0], theta0[0]],&lt;br /&gt;
    y0=[0, vref[0]], iu=[1, 2], iy=[1])&lt;br /&gt;
&lt;br /&gt;
t, y = ct.input_output_response(cruise_lin, T, [vref, gear, theta_hill], X0_lin)&lt;br /&gt;
subplots = cruise_plot(cruise_lin, t, y, t_hill=5, linetype='r-')&lt;br /&gt;
&lt;br /&gt;
# Nonlinear response&lt;br /&gt;
X0_nonlin, U0 = ct.find_eqpt(&lt;br /&gt;
    cruise_nonlin, [vref[0], 0], [vref[0], gear[0], theta0[0]],&lt;br /&gt;
    y0=[0, vref[0]], iu=[1, 2], iy=[1])&lt;br /&gt;
&lt;br /&gt;
t, y = ct.input_output_response(cruise_nonlin, T, [vref, gear, theta_hill], X0_nonlin)&lt;br /&gt;
subplots = cruise_plot(cruise_nonlin, t, y, t_hill=5, subplots=subplots, linetype='b--') &lt;br /&gt;
&lt;br /&gt;
# Add a legend to identify linear vs nonlinear&lt;br /&gt;
plt.legend(['linear', 'nonlinear'], frameon=False, loc='lower right')&lt;br /&gt;
&lt;br /&gt;
# Add two more simulations for 6 degree hill instead of 4 degree&lt;br /&gt;
t, y = ct.input_output_response(cruise_lin, T, [vref, gear, theta_hill*1.5], X0_lin)&lt;br /&gt;
subplots = cruise_plot(cruise_lin, t, y, t_hill=5, subplots=subplots, linetype='r-') &lt;br /&gt;
&lt;br /&gt;
t, y = ct.input_output_response(cruise_nonlin, T, [vref, gear, theta_hill*1.5], X0_nonlin)&lt;br /&gt;
subplots = cruise_plot(cruise_nonlin, t, y, t_hill=5, subplots=subplots, linetype='b--') &lt;br /&gt;
&lt;br /&gt;
# Add titles and legends and save the figure&lt;br /&gt;
plt.suptitle('Linear versus nonlinear model response')&lt;br /&gt;
plt.sca(subplots[1]); plt.axis([0, 25, 0, 1.2])&lt;br /&gt;
&lt;br /&gt;
plt.savefig(&amp;quot;cruise-linear_vs_nonlinear.png&amp;quot;, bbox_inches='tight')&lt;br /&gt;
{{Code end}}&lt;br /&gt;
&lt;br /&gt;
== State Space Control ==&lt;br /&gt;
&lt;br /&gt;
The linearized dynamics of&lt;br /&gt;
the process around an equilibrium point &amp;lt;math&amp;gt;v_\text{e}&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;u_\text{e}&amp;lt;/math&amp;gt; are given by&lt;br /&gt;
&amp;lt;center&amp;gt;&amp;lt;math&amp;gt;&lt;br /&gt;
  \begin{aligned}&lt;br /&gt;
    \frac{dx}{dt} &amp;amp;= -a x - b_\text{g} \theta + b w, \\&lt;br /&gt;
    y &amp;amp;= v = x + v_\text{e},&lt;br /&gt;
  \end{aligned}&lt;br /&gt;
&amp;lt;/math&amp;gt;&amp;lt;/center&amp;gt;&lt;br /&gt;
where &amp;lt;math&amp;gt;x = v - v_\text{e}&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt; = u - u_\text{e}&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; is the mass of&lt;br /&gt;
the car, and &amp;lt;math&amp;gt;\theta&amp;lt;/math&amp;gt; is the angle of the road.  The constants &amp;lt;math&amp;gt;a&amp;lt;/math&amp;gt;,&lt;br /&gt;
&amp;lt;math&amp;gt;b&amp;lt;/math&amp;gt;, and &amp;lt;math&amp;gt;b_\text{g}&amp;lt;/math&amp;gt; depend on the properties of the car and are&lt;br /&gt;
given above.&lt;br /&gt;
&lt;br /&gt;
If we augment the system with an integrator, the system dynamics become&lt;br /&gt;
&amp;lt;center&amp;gt;&amp;lt;math&amp;gt;&lt;br /&gt;
  \begin{aligned}&lt;br /&gt;
    \frac{dx}{dt} &amp;amp;= -a x - b_\text{g} \theta + b w, \\&lt;br /&gt;
    \frac{dz}{dt} &amp;amp;= y - v_\text{r} = v_\text{e} + x - v_\text{r},&lt;br /&gt;
  \end{aligned}&lt;br /&gt;
&amp;lt;/math&amp;gt;&amp;lt;/center&amp;gt;&lt;br /&gt;
or, in state space form,&lt;br /&gt;
&amp;lt;center&amp;gt;&amp;lt;math&amp;gt;&lt;br /&gt;
  \frac{d}{dt} \begin{bmatrix} x \\ z \end{bmatrix} = \begin{bmatrix}&lt;br /&gt;
    -a &amp;amp; 0 \\&lt;br /&gt;
    1 &amp;amp; 0&lt;br /&gt;
  \end{bmatrix} \begin{bmatrix} x \\ z \end{bmatrix} + &lt;br /&gt;
  \begin{bmatrix} b \\ 0 \end{bmatrix} w + &lt;br /&gt;
  \begin{bmatrix} -b_\text{g} \\ 0 \end{bmatrix} \theta + &lt;br /&gt;
  \begin{bmatrix} 0 \\ v_\text{e} - v_\text{r} \end{bmatrix}.&lt;br /&gt;
&amp;lt;/math&amp;gt;&amp;lt;/center&amp;gt;&lt;br /&gt;
Note that when the system is at equilibrium, we have that &amp;lt;math&amp;gt;\dot z = 0&amp;lt;/math&amp;gt;,&lt;br /&gt;
which implies that the vehicle speed &amp;lt;math&amp;gt;v = v_\text{e} + x&amp;lt;/math&amp;gt; should be&lt;br /&gt;
equal to the desired reference speed &amp;lt;math&amp;gt;v_\text{r}&amp;lt;/math&amp;gt;.  Our controller will be of&lt;br /&gt;
the form&lt;br /&gt;
&amp;lt;center&amp;gt;&amp;lt;math&amp;gt;&lt;br /&gt;
  \begin{aligned}&lt;br /&gt;
    \frac{dz}{dt} &amp;amp;= y - v_\text{r}, \\&lt;br /&gt;
    w &amp;amp;= -k_\text{p} x - k_\text{i} z + k_\text{f} v_\text{r},&lt;br /&gt;
  \end{aligned}&lt;br /&gt;
&amp;lt;/math&amp;gt;&amp;lt;/center&amp;gt;&lt;br /&gt;
and the gains &amp;lt;math&amp;gt;k_\text{p}&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;k_\text{i}&amp;lt;/math&amp;gt;, and &amp;lt;math&amp;gt;k_\text{f}&amp;lt;/math&amp;gt; will be chosen to&lt;br /&gt;
stabilize the system and provide the correct input for the reference speed.&lt;br /&gt;
&lt;br /&gt;
Assume that we wish to design the closed loop system to have the&lt;br /&gt;
characteristic polynomial&lt;br /&gt;
&amp;lt;center&amp;gt;&amp;lt;math&amp;gt;&lt;br /&gt;
  \lambda(s) = s^2 + a_1 s + a_2.&lt;br /&gt;
&amp;lt;/math&amp;gt;&amp;lt;/center&amp;gt;&lt;br /&gt;
Setting the disturbance &amp;lt;math&amp;gt;\theta = 0&amp;lt;/math&amp;gt;, &lt;br /&gt;
the characteristic polynomial of the closed loop system is given by&lt;br /&gt;
&amp;lt;center&amp;gt;&amp;lt;math&amp;gt;&lt;br /&gt;
  \det\bigl(sI - (A - BK)\bigr) = s^2 + (b k_\text{p} + a) s + b k_\text{i},&lt;br /&gt;
&amp;lt;/math&amp;gt;&amp;lt;/center&amp;gt;&lt;br /&gt;
and hence we set&lt;br /&gt;
&amp;lt;center&amp;gt;&amp;lt;math&amp;gt;&lt;br /&gt;
  k_\text{p} = \frac{a_1 - a}{b}, \qquad k_\text{i} = \frac{a_2}{b}, \qquad &lt;br /&gt;
  k_\text{f} = {-1}/\bigl(C (A-BK)^{-1} B\bigr) = \frac{a_1}{b}.&lt;br /&gt;
&amp;lt;/math&amp;gt;&amp;lt;/center&amp;gt;&lt;br /&gt;
[[Image:cruise-statefbk.png|right|border|400px|Velocity and throttle for a car with cruise control based on state feedback (dashed) and state feedback with integral action (solid). The controller with integral action is able to adjust the throttle to compensate for the effect of the hill and maintain the speed at the reference value of vr = 20 m/s. The controller gains are kp = 0.5 and ki = 0.1.]]&lt;br /&gt;
The resulting controller stabilizes the system and hence brings&lt;br /&gt;
&amp;lt;math&amp;gt;\dot z = y - v_\text{r}&amp;lt;/math&amp;gt; to zero, resulting in perfect tracking.&lt;br /&gt;
Notice that even if we have a small error in the values of the&lt;br /&gt;
parameters defining the system, as long as the closed loop eigenvalues&lt;br /&gt;
are still stable, then the tracking error will approach zero.  Thus&lt;br /&gt;
the exact calibration required in&lt;br /&gt;
our previous approach (using &amp;lt;math&amp;gt;k_\text{f}&amp;lt;/math&amp;gt;) is not needed here.&lt;br /&gt;
Indeed, we can even choose &amp;lt;math&amp;gt;k_\text{f} = 0&amp;lt;/math&amp;gt; and let the feedback&lt;br /&gt;
controller do all of the work.  However, &amp;lt;math&amp;gt;k_\text{f}&amp;lt;/math&amp;gt; does influence&lt;br /&gt;
the transient response to reference signals and setting it properly&lt;br /&gt;
will generally give a more favorable response.&lt;br /&gt;
&lt;br /&gt;
Integral feedback can also be used to compensate for constant&lt;br /&gt;
disturbances.  The figure to the right shows the results of&lt;br /&gt;
a simulation in which the car encounters a hill with angle&lt;br /&gt;
&amp;lt;math&amp;gt;\theta = 4&amp;lt;/math&amp;gt; degrees at &amp;lt;math&amp;gt;t = &amp;lt;/math&amp;gt; 5 seconds.  The&lt;br /&gt;
steady-state values of the throttle for a state feedback controller&lt;br /&gt;
and a controller with integral action are very close, but the&lt;br /&gt;
corresponding values of the car velocity are quite different.  The&lt;br /&gt;
reason for this is that the zero frequency gain from throttle to&lt;br /&gt;
velocity is &amp;lt;math&amp;gt;-b/a=130&amp;lt;/math&amp;gt; is high.&lt;br /&gt;
The stability of the system is not affected by this external&lt;br /&gt;
disturbance, and so we once again see that the car's velocity converges&lt;br /&gt;
to the reference speed.  This ability to handle constant disturbances&lt;br /&gt;
is a general property of controllers with integral feedback.&lt;br /&gt;
&lt;br /&gt;
=== Python code ===&lt;br /&gt;
&lt;br /&gt;
{{Code block|State feedback controller}}&lt;br /&gt;
{{Code start}}&lt;br /&gt;
def sf_update(t, z, u, params={}):&lt;br /&gt;
    y, r = u[1], u[2]&lt;br /&gt;
    return y - r&lt;br /&gt;
&lt;br /&gt;
def sf_output(t, z, u, params={}):&lt;br /&gt;
    # Get the controller parameters that we need&lt;br /&gt;
    K = params.get('K', 0)&lt;br /&gt;
    ki = params.get('ki', 0)&lt;br /&gt;
    kf = params.get('kf', 0)&lt;br /&gt;
    xd = params.get('xd', 0)&lt;br /&gt;
    yd = params.get('yd', 0)&lt;br /&gt;
    ud = params.get('ud', 0)&lt;br /&gt;
&lt;br /&gt;
    # Get the system state and reference input&lt;br /&gt;
    x, y, r = u[0], u[1], u[2]&lt;br /&gt;
&lt;br /&gt;
    return ud - K * (x - xd) - ki * z + kf * (r - yd)&lt;br /&gt;
&lt;br /&gt;
# Create the input/output system for the controller&lt;br /&gt;
control_sf = ct.NonlinearIOSystem(&lt;br /&gt;
    sf_update, sf_output, name='control',&lt;br /&gt;
    inputs=('x', 'y', 'r'),&lt;br /&gt;
    outputs=('u'),&lt;br /&gt;
    states=('z'))&lt;br /&gt;
&lt;br /&gt;
# Create the closed loop system for the state space controller&lt;br /&gt;
cruise_sf = ct.InterconnectedSystem(&lt;br /&gt;
    (vehicle, control_sf), name='cruise',&lt;br /&gt;
    connections=(&lt;br /&gt;
        ('vehicle.u', 'control.u'),&lt;br /&gt;
        ('control.x', 'vehicle.v'),&lt;br /&gt;
        ('control.y', 'vehicle.v')),&lt;br /&gt;
    inplist=('control.r', 'vehicle.gear', 'vehicle.theta'),&lt;br /&gt;
    outlist=('control.u', 'vehicle.v'), outputs=['u', 'v'])&lt;br /&gt;
&lt;br /&gt;
# Construct the gain matrices for the system&lt;br /&gt;
A, B, C = vehlin.A, vehlin.B[0, 0], vehlin.C&lt;br /&gt;
K = 0.5&lt;br /&gt;
kf = -1 / (C * np.linalg.inv(A - B * K) * B)&lt;br /&gt;
&lt;br /&gt;
# Compute the steady state velocity and throttle setting&lt;br /&gt;
xd = Xeq[0]&lt;br /&gt;
ud = Ueq[0]&lt;br /&gt;
yd = vref[-1]&lt;br /&gt;
{{Code end}}&lt;br /&gt;
&lt;br /&gt;
{{Code block|Simulation (plot)}}&lt;br /&gt;
{{Code start}}&lt;br /&gt;
# Response of the system with no integral feedback term&lt;br /&gt;
t, y_sfb = ct.input_output_response(&lt;br /&gt;
    cruise_sf, T, [vref, gear, theta_hill], [Xeq[0], 0],&lt;br /&gt;
    params={'K':K, 'ki':0.0, 'kf':kf, 'xd':xd, 'ud':ud, 'yd':yd})&lt;br /&gt;
subplots = cruise_plot(cruise_sf, t, y_sfb, t_hill=5, linetype='b--')&lt;br /&gt;
&lt;br /&gt;
# Response of the system with state feedback + integral action&lt;br /&gt;
t, y_sfb_int = ct.input_output_response(&lt;br /&gt;
    cruise_sf, T, [vref, gear, theta_hill], [Xeq[0], 0],&lt;br /&gt;
    params={'K':K, 'ki':0.1, 'kf':kf, 'xd':xd, 'ud':ud, 'yd':yd})&lt;br /&gt;
cruise_plot(cruise_sf, t, y_sfb_int, t_hill=5, linetype='b-', subplots=subplots)&lt;br /&gt;
&lt;br /&gt;
# Add title and legend&lt;br /&gt;
plt.suptitle('Cruise control with state feedback, integral action')&lt;br /&gt;
p_line = mpl.lines.Line2D([], [], color='blue', linestyle='--', label='State feedback')&lt;br /&gt;
pi_line = mpl.lines.Line2D([], [], color='blue', linestyle='-', label='w/ integral action')&lt;br /&gt;
plt.legend(handles=[p_line, pi_line], frameon=False, loc='lower right')&lt;br /&gt;
plt.savefig(&amp;quot;cruise-statefbk.png&amp;quot;, bbox_inches='tight')&lt;br /&gt;
{{Code end}}&lt;br /&gt;
&lt;br /&gt;
== PID Control ==&lt;br /&gt;
&lt;br /&gt;
The dynamics of the system are given by a first-order system with the transfer function&lt;br /&gt;
&amp;lt;center&amp;gt;&amp;lt;math&amp;gt;&lt;br /&gt;
  P(s)=\frac{b}{s+a}.&lt;br /&gt;
&amp;lt;/math&amp;gt;&amp;lt;/center&amp;gt;&lt;br /&gt;
With a PI controller the closed loop system has the characteristic polynomial&lt;br /&gt;
&amp;lt;center&amp;gt;&amp;lt;math&amp;gt;&lt;br /&gt;
  s (s + a) + b k_\text{p} s + b k_\text{i} = s^2 + (a + b k_\text{p}) s + b k_\text{i}.&lt;br /&gt;
&amp;lt;/math&amp;gt;&amp;lt;/center&amp;gt;&lt;br /&gt;
The closed loop poles can thus be assigned arbitrary values by proper &lt;br /&gt;
choice of the controller gains &amp;lt;math&amp;gt;k_\text{p}&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;k_\text{i}&amp;lt;/math&amp;gt;.&lt;br /&gt;
Requiring that the closed loop system have the characteristic polynomial&lt;br /&gt;
&amp;lt;center&amp;gt;&amp;lt;math&amp;gt;&lt;br /&gt;
  p(s) = s^2+a_1s+a_2,&lt;br /&gt;
&amp;lt;/math&amp;gt;&amp;lt;/center&amp;gt;&lt;br /&gt;
we find that the controller parameters are&lt;br /&gt;
&amp;lt;center&amp;gt;&amp;lt;math&amp;gt;&lt;br /&gt;
  k_\text{p}=\frac{a_1-a}{b},\qquad k_\text{i}=\frac{a_2}{b}.&lt;br /&gt;
&amp;lt;/math&amp;gt;&amp;lt;/center&amp;gt;&lt;br /&gt;
[[Image:cruise-pid-sweep-zeta.png|right|border|400px|Cruise control using PI feedback.  The step responses for the error and input illustrate the effect of parameters zeta_c and omega_c on the response of a car with cruise control.  The slope of the road changes linearly from 0 degrees to 4 degrees between t = 5 and 6 seconds.  Responses for omega_c = 0.5 and zeta_c = 0.5, 1, and 2.  Choosing&lt;br /&gt;
zeta_c &amp;gt;= 1 gives no overshoot in the velocity v.]]&lt;br /&gt;
If we require a response of the closed loop system that is slower than&lt;br /&gt;
that of the open loop system, a reasonable choice is &amp;lt;math&amp;gt;a_1 = a + \alpha&amp;lt;/math&amp;gt;&lt;br /&gt;
and &amp;lt;math&amp;gt;a_2 = \alpha a&amp;lt;/math&amp;gt;, where &amp;lt;math&amp;gt;\alpha &amp;lt; a&amp;lt;/math&amp;gt; determines the closed loop&lt;br /&gt;
response.  If a response faster than that of the open loop system is&lt;br /&gt;
required, a possible choice is &amp;lt;math&amp;gt;a_1 = 2 \zeta_\text{c} \omega_\text{c}&amp;lt;/math&amp;gt; and&lt;br /&gt;
&amp;lt;math&amp;gt;a_2 = \omega_\text{c}^2&amp;lt;/math&amp;gt;,&lt;br /&gt;
where &amp;lt;math&amp;gt;\omega_\text{c}&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;\zeta_\text{c}&amp;lt;/math&amp;gt; are the undamped natural&lt;br /&gt;
frequency and damping ratio of the dominant mode.&lt;br /&gt;
&lt;br /&gt;
To design a PI controller we choose &amp;lt;math&amp;gt;\zeta_\text{c} = 1&amp;lt;/math&amp;gt; to obtain a response without overshoot. The choice of &amp;lt;math&amp;gt;\omega_\text{c}&amp;lt;/math&amp;gt; is a compromise between response speed and control actions: a large value gives a fast response, but it requires fast control action. The largest velocity error decreases with increasing &amp;lt;math&amp;gt;\omega_\text{c}&amp;lt;/math&amp;gt;, but the control signal also changes more rapidly. In the simple model it was assumed that the force responds instantaneously to throttle commands. For rapid changes there may be additional dynamics that have to be accounted for.  There are also physical limits to the rate of change of the force, which also restricts the admissible value of &amp;lt;math&amp;gt;\omega_\text{c}&amp;lt;/math&amp;gt;. A reasonable choice of &amp;lt;math&amp;gt;\omega_\text{c}&amp;lt;/math&amp;gt; is in the range 0.5--1.0.&lt;br /&gt;
&lt;br /&gt;
=== Python code ===&lt;br /&gt;
&lt;br /&gt;
{{Code block|Get transfer function parameters}}&lt;br /&gt;
{{Code start}}&lt;br /&gt;
# Get the transfer function from throttle input + hill to vehicle speed&lt;br /&gt;
P = ct.ss2tf(vehlin[0, 0])&lt;br /&gt;
&lt;br /&gt;
# Construction a controller that cancels the pole&lt;br /&gt;
a = -P.pole()[0]&lt;br /&gt;
b = np.real(P(0)) * a&lt;br /&gt;
{{Code end}}&lt;br /&gt;
&lt;br /&gt;
{{Code block|Fix &amp;lt;math&amp;gt;\omega_\text{c}&amp;lt;/math&amp;gt; and vary &amp;lt;math&amp;gt;\zeta_\text{c} (plot)&amp;lt;/math&amp;gt;}}&lt;br /&gt;
{{Code start}}&lt;br /&gt;
# Fix \omega_0 and vary \zeta&lt;br /&gt;
w0 = 0.5&lt;br /&gt;
subplots = [None, None]&lt;br /&gt;
for zeta in [0.5, 1, 2]:&lt;br /&gt;
    # Create the controller transfer function (as an I/O system)&lt;br /&gt;
    kp = (2*zeta*w0 - a)/b&lt;br /&gt;
    ki = w0**2 / b&lt;br /&gt;
    control_tf = ct.tf2io(&lt;br /&gt;
        ct.TransferFunction([kp, ki], [1, 0.01*ki/kp]),&lt;br /&gt;
        name='control', inputs='u', outputs='y')&lt;br /&gt;
    &lt;br /&gt;
    # Construct the closed loop system by interconnecting process and controller&lt;br /&gt;
    cruise_tf = ct.InterconnectedSystem(&lt;br /&gt;
    (vehicle, control_tf), name='cruise',&lt;br /&gt;
    connections = [('control.u', '-vehicle.v'), ('vehicle.u', 'control.y')],&lt;br /&gt;
    inplist = ('control.u', 'vehicle.gear', 'vehicle.theta'), &lt;br /&gt;
        inputs = ('vref', 'gear', 'theta'),&lt;br /&gt;
    outlist = ('vehicle.v', 'vehicle.u'), outputs = ('v', 'u'))&lt;br /&gt;
&lt;br /&gt;
    # Plot the velocity response&lt;br /&gt;
    X0, U0 = ct.find_eqpt(&lt;br /&gt;
        cruise_tf, [vref[0], 0], [vref[0], gear[0], theta_hill[0]], &lt;br /&gt;
        iu=[1, 2], y0=[vref[0], 0], iy=[0])&lt;br /&gt;
&lt;br /&gt;
    t, y = ct.input_output_response(cruise_tf, T, [vref, gear, theta_hill], X0)&lt;br /&gt;
    subplots = cruise_plot(cruise_tf, t, y, t_hill=5, subplots=subplots)&lt;br /&gt;
&lt;br /&gt;
plt.suptitle('PID controller with varying zeta')&lt;br /&gt;
plt.savefig(&amp;quot;cruise-pid-sweep-zeta.png&amp;quot;, bbox_inches='tight')&lt;br /&gt;
{{Code end}}&lt;br /&gt;
&lt;br /&gt;
{{Code block|Fix vary &amp;lt;math&amp;gt;\zeta_\text{c}&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;\omega_\text{c} (plot)&amp;lt;/math&amp;gt;}}&lt;br /&gt;
{{Code start}}&lt;br /&gt;
zeta = 1&lt;br /&gt;
subplots = [None, None]&lt;br /&gt;
for w0 in [0.2, 0.5, 1]:&lt;br /&gt;
    # Create the controller transfer function (as an I/O system)&lt;br /&gt;
    kp = (2*zeta*w0 - a)/b&lt;br /&gt;
    ki = w0**2 / b&lt;br /&gt;
    control_tf = ct.tf2io(&lt;br /&gt;
        ct.TransferFunction([kp, ki], [1, 0.01*ki/kp]),&lt;br /&gt;
        name='control', inputs='u', outputs='y')&lt;br /&gt;
    &lt;br /&gt;
    # Construct the closed loop system by interconnecting process and controller&lt;br /&gt;
    cruise_tf = ct.InterconnectedSystem(&lt;br /&gt;
        (vehicle, control_tf), name='cruise',&lt;br /&gt;
        connections = [('control.u', '-vehicle.v'), ('vehicle.u', 'control.y')],&lt;br /&gt;
        inplist = ('control.u', 'vehicle.gear', 'vehicle.theta'), &lt;br /&gt;
        inputs = ('vref', 'gear', 'theta'),&lt;br /&gt;
        outlist = ('vehicle.v', 'vehicle.u'), outputs = ('v', 'u'))&lt;br /&gt;
&lt;br /&gt;
    # Plot the velocity response&lt;br /&gt;
    X0, U0 = ct.find_eqpt(&lt;br /&gt;
        cruise_tf, [vref[0], 0], [vref[0], gear[0], theta_hill[0]], &lt;br /&gt;
        iu=[1, 2], y0=[vref[0], 0], iy=[0])&lt;br /&gt;
&lt;br /&gt;
    t, y = ct.input_output_response(cruise_tf, T, [vref, gear, theta_hill], X0)&lt;br /&gt;
    subplots = cruise_plot(cruise_tf, t, y, t_hill=5, subplots=subplots)&lt;br /&gt;
&lt;br /&gt;
plt.suptitle('PID controller with varying omega')&lt;br /&gt;
plt.savefig(&amp;quot;cruise-pid-sweep-omega.png&amp;quot;, bbox_inches='tight')&lt;br /&gt;
{{Code end}}&lt;br /&gt;
&lt;br /&gt;
=== Anti-windup compensation ===&lt;br /&gt;
&lt;br /&gt;
The windup effect is illustrated in the left figure below, which shows what happens when a car encounters a hill that is so steep (6 degrees) that the throttle saturates when the cruise controller attempts to maintain speed. When encountering the slope at time &amp;lt;math&amp;gt;t=5&amp;lt;/math&amp;gt;, the velocity decreases and the throttle increases to generate more torque. However, the torque required is so large that the throttle saturates. The error decreases slowly because the torque generated by the engine is just a little larger than the torque required to compensate for gravity. The error is large and the integral continues to build up until the error reaches zero at time 25, but the controller output is still  larger than the saturation limit and the actuator remains saturated. The integral term starts to decrease and the velocity settles to the desired value at time &amp;lt;math&amp;gt;t = 40&amp;lt;/math&amp;gt;. Also notice the large overshoot.&lt;br /&gt;
&lt;br /&gt;
The right figure below shows what happens when a controller with anti-windup is applied to the system. Because of the feedback from the actuator model, the output of the integrator is quickly reset to a value such that the controller output is at the saturation limit.  The behavior is drastically different from that on the left and the large overshoot is avoided. The tracking gain used in the simulation is &amp;lt;math&amp;gt;k_\text{aw} = 2&amp;lt;/math&amp;gt; which is an order of magnitude larger than the integral gain &amp;lt;math&amp;gt;k_\text{i} = 0.2&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
{| align=center&lt;br /&gt;
|-&lt;br /&gt;
| width=45% |&lt;br /&gt;
[[Image:cruise-windup.png|border|400px|Simulation of PI cruise control with windup. The figure shows the speed v and the throttle u for a car that encounters a slope that is so steep that the throttle saturates. The controller output is a dashed line. The controller parameters are kp = 0.5 and ki = 0.1.]]&lt;br /&gt;
| width=10% |&lt;br /&gt;
| width=45% |&lt;br /&gt;
[[Image:cruise-antiwindup.png|border|400px|Simulation of PI cruise control with anti-windup. The figure shows the speed v and the throttle u for a car that encounters a slope that is so steep that the throttle saturates. The controller output is a dashed line. The controller parameters are kp = 0.5, ki = 0.1. and kaw =2. The anti-windup compensator eliminates the overshoot by preventing the error from building up in the integral term of the controller.]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Python code ===&lt;br /&gt;
&lt;br /&gt;
{{code block|Redefine input with longer duration}}&lt;br /&gt;
{{Code start}}&lt;br /&gt;
T = np.linspace(0, 50, 101)&lt;br /&gt;
vref = 20 * np.ones(T.shape)&lt;br /&gt;
theta_hill = [&lt;br /&gt;
    0 if t &amp;lt;= 5 else&lt;br /&gt;
    6./180. * pi * (t-5) if t &amp;lt;= 6 else&lt;br /&gt;
    6./180. * pi for t in T]&lt;br /&gt;
&lt;br /&gt;
# Compute the equilibrium throttle setting for the desired speed&lt;br /&gt;
X0, U0 = ct.find_eqpt(&lt;br /&gt;
    cruise_nonlin, [vref[0], 0], [vref[0], gear[0], theta0[0]],&lt;br /&gt;
    y0=[0, vref[0]], iu=[1, 2], iy=[1])&lt;br /&gt;
{{Code end}}&lt;br /&gt;
&lt;br /&gt;
{{Code block|Effects of windup (plot)}}&lt;br /&gt;
{{Code start}}&lt;br /&gt;
t, y = ct.input_output_response(&lt;br /&gt;
    cruise_nonlin, T, [vref, gear, theta_hill], X0,&lt;br /&gt;
    params={'kaw':0})&lt;br /&gt;
cruise_plot(cruise_nonlin, t, y, antiwindup=True);&lt;br /&gt;
&lt;br /&gt;
plt.suptitle('Cruise control with integrator windup')&lt;br /&gt;
plt.savefig(&amp;quot;cruise-windup.png&amp;quot;, bbox_inches='tight')&lt;br /&gt;
{{Code end}}&lt;br /&gt;
&lt;br /&gt;
{{Code block|Anti-windup compensation (plot)}}&lt;br /&gt;
{{Code start}}&lt;br /&gt;
t, y = ct.input_output_response(&lt;br /&gt;
    cruise_nonlin, T, [vref, gear, theta_hill], X0,&lt;br /&gt;
    params={'kaw':2})&lt;br /&gt;
cruise_plot(cruise_nonlin, t, y, antiwindup=True);&lt;br /&gt;
&lt;br /&gt;
plt.suptitle('Cruise control with anti-windup')&lt;br /&gt;
plt.savefig(&amp;quot;cruise-antiwindup.png&amp;quot;, bbox_inches='tight')&lt;br /&gt;
{{Code end}}&lt;br /&gt;
&lt;br /&gt;
== Further Reading ==&lt;br /&gt;
* How Stuff Works: [http://auto.howstuffworks.com/cruise-control.htm cruise control]&lt;br /&gt;
* Wikipedia: [[Wikipedia:Cruise control|cruise control]]&lt;br /&gt;
&lt;br /&gt;
[[Category:Running examples]]&lt;/div&gt;</summary>
		<author><name>Murray</name></author>
	</entry>
	<entry>
		<id>http://fbswiki.org/wiki/index.php?title=Frequency_Domain_Design&amp;diff=1328</id>
		<title>Frequency Domain Design</title>
		<link rel="alternate" type="text/html" href="http://fbswiki.org/wiki/index.php?title=Frequency_Domain_Design&amp;diff=1328"/>
		<updated>2024-11-24T16:37:12Z</updated>

		<summary type="html">&lt;p&gt;Murray: Undo revision 1324 by Murray (talk)&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Chapter&lt;br /&gt;
|Chapter number=12&lt;br /&gt;
|Short name=loopsyn&lt;br /&gt;
|Previous chapter=PID Control&lt;br /&gt;
|Next chapter=Robust Performance&lt;br /&gt;
|First edition URL=https://www.cds.caltech.edu/~murray/amwiki/index.php?title=Frequency_Domain_Design#Frequently_Asked_Questions&lt;br /&gt;
|Chapter summary=In this chapter we continue to explore the use of frequency domain techniques with a focus on the design of feedback systems. We begin with a more thorough description of the performance specifications for control systems and then introduce the concept of “loop shaping” as a mechanism for designing controllers in the frequency domain. Additional techniques discussed in this chapter include feedforward compensation, the root locus method, and nested controller design.&lt;br /&gt;
|Chapter contents=# Sensitivity Functions&lt;br /&gt;
# Performance Specifications&lt;br /&gt;
#* Response to Reference Signals&lt;br /&gt;
#* Response to Load Disturbances and Measurement Noise&lt;br /&gt;
#* Measuring Specifications&lt;br /&gt;
# Feedback Design via Loop Shaping&lt;br /&gt;
#* Design Considerations&lt;br /&gt;
#* Lead and Lag Compensation&lt;br /&gt;
# Feedforward Design&lt;br /&gt;
#* Combining Feedforward and Feedback&lt;br /&gt;
#* Difficulties with Feedforward&lt;br /&gt;
#* Approximate Inverses&lt;br /&gt;
# The Root Locus Method&lt;br /&gt;
# Design Example&lt;br /&gt;
# Further Reading&lt;br /&gt;
:: Exercises&lt;br /&gt;
}}&lt;/div&gt;</summary>
		<author><name>Murray</name></author>
	</entry>
	<entry>
		<id>http://fbswiki.org/wiki/index.php?title=PID_Control&amp;diff=1327</id>
		<title>PID Control</title>
		<link rel="alternate" type="text/html" href="http://fbswiki.org/wiki/index.php?title=PID_Control&amp;diff=1327"/>
		<updated>2024-11-24T16:36:59Z</updated>

		<summary type="html">&lt;p&gt;Murray: Undo revision 1323 by Murray (talk)&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Chapter&lt;br /&gt;
|Chapter number=11&lt;br /&gt;
|Short name=pid&lt;br /&gt;
|Previous chapter=Frequency Domain Analysis&lt;br /&gt;
|Next chapter=Frequency Domain Design&lt;br /&gt;
|First edition URL=https://www.cds.caltech.edu/~murray/amwiki/index.php?title=PID_Control#Frequently_Asked_Questions&lt;br /&gt;
|Chapter summary=Proportional-integral-derivative (PID) control is by far the most common way of using feedback in engineering systems. In this chapter we present the basic properties of PID control and the methods for choosing the parameters of the controllers. We also analyze the effects of actuator saturation, an important feature of many feedback systems, and describe methods for compensating for it. Finally, we discuss the implementation of PID controllers as an example of how to implement feedback control systems using analog or digital computation.&lt;br /&gt;
|Chapter contents=# Basic Control Functions&lt;br /&gt;
# Simple Controllers for Complex Systems&lt;br /&gt;
# PID Tuning&lt;br /&gt;
#* Ziegler--Nichols' Tuning&lt;br /&gt;
#* Tuning Based on the FOTD Model&lt;br /&gt;
#* Relay Feedback&lt;br /&gt;
# Integral Windup&lt;br /&gt;
#* Avoiding Windup&lt;br /&gt;
#* Manual Control and Tracking&lt;br /&gt;
#* Anti-Windup for General Controllers&lt;br /&gt;
# Implementation&lt;br /&gt;
#* Filtering the Derivative&lt;br /&gt;
#* Setpoint Weighting&lt;br /&gt;
#* Implementation Based on Operational Amplifiers&lt;br /&gt;
#* Computer Implementation&lt;br /&gt;
# Further Reading&lt;br /&gt;
:: Exercises&lt;br /&gt;
}}&lt;/div&gt;</summary>
		<author><name>Murray</name></author>
	</entry>
	<entry>
		<id>http://fbswiki.org/wiki/index.php?title=Frequency_Domain_Analysis&amp;diff=1326</id>
		<title>Frequency Domain Analysis</title>
		<link rel="alternate" type="text/html" href="http://fbswiki.org/wiki/index.php?title=Frequency_Domain_Analysis&amp;diff=1326"/>
		<updated>2024-11-24T16:36:49Z</updated>

		<summary type="html">&lt;p&gt;Murray: Undo revision 1322 by Murray (talk)&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Chapter&lt;br /&gt;
|Chapter number=10&lt;br /&gt;
|Short name=loopanal&lt;br /&gt;
|Previous chapter=Transfer Functions&lt;br /&gt;
|Next chapter=PID Control&lt;br /&gt;
|First edition URL=https://www.cds.caltech.edu/~murray/amwiki/index.php?title=Frequency_Domain_Analysis#Frequently_Asked_Questions&lt;br /&gt;
|Chapter summary=In this chapter we study how the stability and robustness of closed loop systems can be determined by investigating how sinusoidal signals of different frequencies propagate around the feedback loop. This technique allows us to reason about the closed loop behavior of a system through the frequency domain properties of the open loop transfer function. The Nyquist stability theorem is a key result that provides a way to analyze stability and introduce measures of degrees of stability.&lt;br /&gt;
|Chapter contents=# The Loop Transfer Function&lt;br /&gt;
# The Nyquist Criterion&lt;br /&gt;
#* The Nyquist Plot&lt;br /&gt;
#* The General Nyquist Criterion&lt;br /&gt;
#* Conditional Stability&lt;br /&gt;
# Stability Margins&lt;br /&gt;
# Bode's Relations and Minimum Phase Systems&lt;br /&gt;
# Generalized Notions of Gain and Phase&lt;br /&gt;
#* System Gain and Passivity&lt;br /&gt;
#* Extensions of the Nyquist Criterion&lt;br /&gt;
#* Describing Functions&lt;br /&gt;
# Further Reading&lt;br /&gt;
:: Exercises&lt;br /&gt;
}}&lt;/div&gt;</summary>
		<author><name>Murray</name></author>
	</entry>
	<entry>
		<id>http://fbswiki.org/wiki/index.php?title=Transfer_Functions&amp;diff=1325</id>
		<title>Transfer Functions</title>
		<link rel="alternate" type="text/html" href="http://fbswiki.org/wiki/index.php?title=Transfer_Functions&amp;diff=1325"/>
		<updated>2024-11-24T16:36:39Z</updated>

		<summary type="html">&lt;p&gt;Murray: Undo revision 1321 by Murray (talk)&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Chapter&lt;br /&gt;
|Chapter number=9&lt;br /&gt;
|Short name=xferfcns&lt;br /&gt;
|Previous chapter=Output Feedback&lt;br /&gt;
|Next chapter=Frequency Domain Analysis&lt;br /&gt;
|First edition URL=https://www.cds.caltech.edu/~murray/amwiki/index.php?title=Transfer_Functions#Frequently_Asked_Questions&lt;br /&gt;
|Chapter summary=This chapter introduces the concept of the transfer function, which is a com- pact description of the input/output relation for a linear time-invariant system. We show how to obtain transfer functions analytically and experimentally. Combining transfer functions with block diagrams gives a powerful algebraic method to analyze linear systems with many blocks. The transfer function allows new interpretations of system dynamics. We also introduce the Bode plot, a powerful graphical rep- resentation of the transfer function that was introduced by Bode to analyze and design feedback amplifiers.&lt;br /&gt;
|Chapter contents=# The Loop Transfer Function&lt;br /&gt;
# The Nyquist Criterion&lt;br /&gt;
#* The Nyquist Plot&lt;br /&gt;
#* The General Nyquist Criterion&lt;br /&gt;
#* Conditional Stability&lt;br /&gt;
# Stability Margins&lt;br /&gt;
# Bode's Relations and Minimum Phase Systems&lt;br /&gt;
# Generalized Notions of Gain and Phase&lt;br /&gt;
#* System Gain and Passivity&lt;br /&gt;
#* Extensions of the Nyquist Criterion&lt;br /&gt;
#* Describing Functions&lt;br /&gt;
# Further Reading&lt;br /&gt;
:: Exercises&lt;br /&gt;
}}&lt;/div&gt;</summary>
		<author><name>Murray</name></author>
	</entry>
	<entry>
		<id>http://fbswiki.org/wiki/index.php?title=Frequency_Domain_Design&amp;diff=1324</id>
		<title>Frequency Domain Design</title>
		<link rel="alternate" type="text/html" href="http://fbswiki.org/wiki/index.php?title=Frequency_Domain_Design&amp;diff=1324"/>
		<updated>2024-11-24T16:31:05Z</updated>

		<summary type="html">&lt;p&gt;Murray: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Chapter&lt;br /&gt;
|Chapter number=12&lt;br /&gt;
|Short name=loopsyn&lt;br /&gt;
|Previous chapter=PID Control&lt;br /&gt;
|Next chapter=Robust Performance&lt;br /&gt;
|First edition URL=https://www.cds.caltech.edu/~murray/amwiki/index.php?title=Frequency_Domain_Design#Frequently_Asked_Questions&lt;br /&gt;
|Chapter summary=In this chapter we continue to explore the use of frequency domain techniques with a focus on the design of feedback systems. We begin with a more thorough description of the performance specifications for control systems and then introduce the concept of “loop shaping” as a mechanism for designing controllers in the frequency domain. Additional techniques discussed in this chapter include feedforward compensation, the root locus method, and nested controller design.&lt;br /&gt;
|Chapter contents=# Sensitivity Functions&lt;br /&gt;
# Performance Specifications&lt;br /&gt;
# Feedback Design via Loop Shaping&lt;br /&gt;
# Feedforward Design&lt;br /&gt;
# The Root Locus Method&lt;br /&gt;
# Design Example&lt;br /&gt;
# Further Reading&lt;br /&gt;
:: Exercises&lt;br /&gt;
}}&lt;/div&gt;</summary>
		<author><name>Murray</name></author>
	</entry>
</feed>