<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>http://fbswiki.org/wiki/index.php?action=history&amp;feed=atom&amp;title=Figure_5.17%3A_Stabilized_inverted_pendulum</id>
	<title>Figure 5.17: Stabilized inverted pendulum - Revision history</title>
	<link rel="self" type="application/atom+xml" href="http://fbswiki.org/wiki/index.php?action=history&amp;feed=atom&amp;title=Figure_5.17%3A_Stabilized_inverted_pendulum"/>
	<link rel="alternate" type="text/html" href="http://fbswiki.org/wiki/index.php?title=Figure_5.17:_Stabilized_inverted_pendulum&amp;action=history"/>
	<updated>2026-04-24T21:52:24Z</updated>
	<subtitle>Revision history for this page on the wiki</subtitle>
	<generator>MediaWiki 1.35.1</generator>
	<entry>
		<id>http://fbswiki.org/wiki/index.php?title=Figure_5.17:_Stabilized_inverted_pendulum&amp;diff=1295&amp;oldid=prev</id>
		<title>Murray: Created page with &quot;{{Figure |Chapter=Dynamic Behavior |Figure number=5.17 |Sort key=517 |Figure title=Stabilized inverted pendulum |GitHub URL=https://github.com/murrayrm/fbs2e-python/blob/main/...&quot;</title>
		<link rel="alternate" type="text/html" href="http://fbswiki.org/wiki/index.php?title=Figure_5.17:_Stabilized_inverted_pendulum&amp;diff=1295&amp;oldid=prev"/>
		<updated>2024-11-18T18:05:59Z</updated>

		<summary type="html">&lt;p&gt;Created page with &amp;quot;{{Figure |Chapter=Dynamic Behavior |Figure number=5.17 |Sort key=517 |Figure title=Stabilized inverted pendulum |GitHub URL=https://github.com/murrayrm/fbs2e-python/blob/main/...&amp;quot;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;New page&lt;/b&gt;&lt;/p&gt;&lt;div&gt;{{Figure&lt;br /&gt;
|Chapter=Dynamic Behavior&lt;br /&gt;
|Figure number=5.17&lt;br /&gt;
|Sort key=517&lt;br /&gt;
|Figure title=Stabilized inverted pendulum&lt;br /&gt;
|GitHub URL=https://github.com/murrayrm/fbs2e-python/blob/main/example-5.14-invpend_stabilized.py&lt;br /&gt;
}}&lt;br /&gt;
[[Image:figure-5.17-balpend_phaseplot.png]]&lt;br /&gt;
&lt;br /&gt;
'''Figure 5.17''': Stabilized inverted pendulum. A control law applies a force u at the bottom of the pendulum to stabilize the inverted position (a) [not shown]. The phase portrait (b) shows that the equilibrium point corresponding to the vertical position is stabilized. The shaded region indicates the set of initial conditions that converge to the origin. The ellipse corresponds to a level set of a Lyapunov function &amp;lt;math&amp;gt;V(x)&amp;lt;/math&amp;gt; for which &amp;lt;math&amp;gt;V(x) &amp;gt; 0&amp;lt;/math&amp;gt; and &amp;lt;math&amp;gt;\dot V(x) &amp;lt; 0&amp;lt;/math&amp;gt; for all points inside the ellipse. This can be used as an estimate of the region of attraction of the equilibrium point. The actual dynamics of the system evolve on a manifold (c) [not shown].&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&lt;br /&gt;
# example-5.14-invpend_stabilized.py - stabilized inverted pendulum&lt;br /&gt;
# RMM, 17 Nov 2024&lt;br /&gt;
&lt;br /&gt;
# Figure 5.17: Stabilized inverted pendulum. A control law applies a&lt;br /&gt;
# force u at the bottom of the pendulum to stabilize the inverted&lt;br /&gt;
# position (a). The phase portrait (b) shows that the equilibrium&lt;br /&gt;
# point corresponding to the vertical position is stabilized. The&lt;br /&gt;
# shaded region indicates the set of initial conditions that converge&lt;br /&gt;
# to the origin. The ellipse corresponds to a level set of a Lyapunov&lt;br /&gt;
# function V (x) for which V (x) &amp;gt; 0 and V ̇ (x) &amp;lt; 0 for all points&lt;br /&gt;
# inside the ellipse. This can be used as an estimate of the region of&lt;br /&gt;
# attraction of the equilibrium point. The actual dynamics of the&lt;br /&gt;
# system evolve on a manifold (c).&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, sin, cos, 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;
# Stablized (and normalized) inverted pendulum dynamics&lt;br /&gt;
def balpend_update(t, x, v, params):&lt;br /&gt;
    a = params.get('a', 2)&lt;br /&gt;
    u = -2 * a * sin(x[0]) - x[1] * cos(x[0])&lt;br /&gt;
    return [x[1], sin(x[0]) + u * cos(x[0])]&lt;br /&gt;
balpend = ct.nlsys(&lt;br /&gt;
    balpend_update, states=2, inputs=0, name='inverted pendulum')&lt;br /&gt;
&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, 4)&lt;br /&gt;
ax = fig.add_subplot(gs[0, 1:3])&lt;br /&gt;
&lt;br /&gt;
# Generate the phase plot (easy part)&lt;br /&gt;
cplt = ct.phase_plane_plot(&lt;br /&gt;
    balpend, [-2*pi, 2*pi, -4, 4], 4.5, gridspec=[10, 10],&lt;br /&gt;
    plot_separatrices={'timedata': np.linspace(0, 20, 500), 'arrows': 1},&lt;br /&gt;
    ax=ax)&lt;br /&gt;
&lt;br /&gt;
#&lt;br /&gt;
# Next we need to shade the region of attraction containing the origin.  We&lt;br /&gt;
# do this by extracting out the bounding separatrices and then filling in&lt;br /&gt;
# the region between them, using some messy NumPy/Matplotlib commands...&lt;br /&gt;
#&lt;br /&gt;
&lt;br /&gt;
# Get the separatrices on either side of the origin&lt;br /&gt;
for line in cplt.lines[0]:&lt;br /&gt;
    xdata, ydata = line.get_data()&lt;br /&gt;
    if line.get_linestyle() != '--' or abs(ydata[-1]) &amp;gt; 0.1:&lt;br /&gt;
        continue                # Not the line we are looking for&lt;br /&gt;
&lt;br /&gt;
    # Extract out the separatrices for the middle region&lt;br /&gt;
    if xdata[-1] &amp;lt; 0 and xdata[-1] &amp;gt; -2 and ydata[-1] &amp;gt; 0:&lt;br /&gt;
        ul = (xdata[::-1], ydata[::-1])         # Sort along increasing y&lt;br /&gt;
    elif xdata[-1] &amp;lt; 0 and xdata[-1] &amp;gt; -2 and ydata[-1] &amp;lt; 0:&lt;br /&gt;
        ll = (xdata, ydata)&lt;br /&gt;
    elif xdata[-1] &amp;gt; 0 and xdata[-1] &amp;lt; 2 and ydata[-1] &amp;gt; 0:&lt;br /&gt;
        ur = (xdata[::-1], ydata[::-1])         # Sort along increasing y&lt;br /&gt;
    elif xdata[-1] &amp;gt; 0 and xdata[-1] &amp;lt; 2 and ydata[-1] &amp;lt; 0:&lt;br /&gt;
        lr = (xdata, ydata)&lt;br /&gt;
&lt;br /&gt;
# Shade the region between the separatrices (including the middle)&lt;br /&gt;
ax.fill_betweenx(ul[1], ul[0], np.interp(ul[1], ur[1], ur[0]), color='0.95')&lt;br /&gt;
ax.fill_betweenx(lr[1], np.interp(lr[1], ll[1], ll[0]), lr[0], color='0.95')&lt;br /&gt;
ax.fill_betweenx(&lt;br /&gt;
    [ul[1][0], ll[1][-1]], [ul[0][0], ll[0][-1]], [ur[0][0], lr[0][-1]],&lt;br /&gt;
    color='0.95')&lt;br /&gt;
&lt;br /&gt;
# Add the Lypaunov level set&lt;br /&gt;
A = balpend.linearize(0, 0).A   # Linearized dynamics matrix&lt;br /&gt;
P = ct.lyap(A.T, np.eye(2))     # Solve Lyapunov equation for P&lt;br /&gt;
&lt;br /&gt;
# Figure out the states along the level set&lt;br /&gt;
rho = 1.2                       # value of the level set&lt;br /&gt;
xval, yval = [], []&lt;br /&gt;
for theta in np.linspace(0, 2*pi, 100):&lt;br /&gt;
    # Find the length of state vector at this angle that gives V(x) = 1&lt;br /&gt;
    r = 1 / sqrt(np.array([cos(theta), sin(theta)]).T @ P @ &lt;br /&gt;
                 np.array([cos(theta), sin(theta)]))&lt;br /&gt;
    xval.append(rho * r * cos(theta))&lt;br /&gt;
    yval.append(rho * r * sin(theta))&lt;br /&gt;
ax.plot(xval, yval, 'r-', linewidth=2)&lt;br /&gt;
&lt;br /&gt;
# Label the plot&lt;br /&gt;
ax.set_xticks([-2*pi, -pi, 0, pi, 2*pi])&lt;br /&gt;
ax.set_xticklabels([r'$-2\pi$', r'$-pi$', r'$0$', r'$\pi$', r'$2\pi$'])&lt;br /&gt;
ax.set_xlabel('$x_1$')&lt;br /&gt;
ax.set_ylabel('$x_2$', rotation=0)&lt;br /&gt;
ax.set_title(&amp;quot;(b) Phase portrait&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
plt.savefig('figure-5.17-balpend_phaseplot.png', bbox_inches='tight')&lt;br /&gt;
&amp;lt;/nowiki&amp;gt;&lt;/div&gt;</summary>
		<author><name>Murray</name></author>
	</entry>
</feed>