Figure 7.7: Simulation results for the controlled predator–prey system

From FBSwiki
Jump to navigation Jump to search
Chapter State Feedback
Figure number 7
Figure title Simulation results for the controlled predator–prey system
GitHub URL https://github.com/murrayrm/fbs2e-python/blob/main/figure-7.7-predprey place.py
Requires python-control, predprey.py

Figure-7.7-predprey place-time.png   Figure-7.7-predprey place-pp.png

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.

# predprey_place.py - stabilization via state space feedback
# RMM, 18 Jan 2026

import matplotlib.pyplot as plt
import numpy as np
import control as ct
import fbs                      # FBS plotting customizations

# System definition
from predprey import predprey

# Find the equilibrium point and linearize
xe, ue = ct.find_eqpt(predprey, [20, 30], 0)
sys = predprey.linearize(xe, ue)

# Eigenvalue placement
K = ct.place(sys.A, sys.B, [-0.1, -0.2])
kf = -1 / (sys.C[1] @ np.linalg.inv(sys.A - sys.B @ K) @ sys.B)

ctrl = ct.nlsys(
    None, lambda t, x, u, params: -K @ (u[0:2] - xe) + kf * (u[2] - xe[1]),
    inputs=['H', 'L', 'r'], outputs=['u'],
)
clsys = ct.interconnect(
    [predprey, ctrl], inputs=['r'], outputs=['H', 'L', 'u'],
    name='predprey_clsys'
)

# Compute initial condition response
T = np.linspace(0, 100, 1000)
response = ct.input_output_response(clsys, T, 30, [20, 15])

# Plot results
fbs.figure('mlh')               # Create figure using FBS defaults
plt.plot(response.time, response.outputs[0], 'b-', label="Hare")
plt.plot(response.time, response.outputs[1], 'r--', label="Lynx")

plt.xlabel("Time [years]")
plt.ylabel("Population")
plt.legend(frameon=False)

plt.tight_layout()
fbs.savefig('figure-7.7-predprey_place-time.png')

# Generate a phase portrait
fbs.figure('mlh')               # Create figure using FBS defaults

# Create a function for the phase portrait that includes the reference input
ppfcn = lambda t, x: clsys.dynamics(t, x, [30], {})

ct.phase_plane_plot(
    ppfcn, [1, 100, 1, 100], 30, plot_separatrices=False)
# Turn off vector field lines since phase_plan_plot has arrows
# ct.phaseplot.vectorfield(ppfcn, [0, 100, 0, 100], gridspec=[20, 20])

plt.xlabel("Hares")
plt.ylabel("Lynxes")
plt.suptitle("")

plt.tight_layout()
fbs.savefig('figure-7.7-predprey_place-pp.png')