Real Time Step Response and Bode Plot Updating #1247
|
Hello community, I would like to make a GUI that allows you to make changes to the zero and pole locations and have that reflected seamlessly on a live graph (step response and bod plot). Is this possible with the control library? If this is possible, can you please recommend any libraries that are best suited for this as well as any links that I may reference for additional information. Thank you. |
Replies: 4 comments 4 replies
|
Typically, the way this is done is with a root locus plot - the set of all pole locations as a single gain is varied from zero to infinity. Check out sisotool, which can do this if you are in interactive mode in Matplotlib. If you want more precision in what gain you choose when you click on the root locus plot, you can zoom in first. Another option is the place command, but it is not interactive. I am not sure how much that function would benefit from an interactive presentation, but maybe? If this answers your question , please mark as resolved, or, ask a follow up question. |
|
Yes, the root locus provides the gain for all potential root locations - but does not provide visual performance metrics like the Bode and step response (the former in frequency, the latter in time). I recall seeing a video where the user was moving the pole/zero locations via either a slider or typing in the values (it has actually been a few months so I don't recall the exact video but I am sure this was done with Matlab). As the values were being modified, the Bode plot waveform was changing in real time. Thus, you can quickly determine if you're moving the zero/poles in the right direction or magnitude (or both). First thing that popped in my mind: That there is a whole lotta' awesomeness! If I can create something that closely resembles this, it can make stabilizing a system rather easy (beats having to iteratively run consecutive tests). Do you have any links related to Python/control/sisotool examples where this was done (demo, etc.)? |
|
Oh, ok. Yes, the sisotool is pretty cool. It shows the results dynamically when changing the gain of the plant (this is analogous to only adding/modifying the value "P" or gain of the plant) . What I have in mind is say you have a plant. Now I am adding a compensator to stabilize the system. The compensator has gain, zeros and poles. I have the freedom to change any of these values. I don't have the freedom to change the plant as this is fixed. I want to see the impact in real time, much in the same way that the sisotool has when clicking the different points (gains) of the root locus, on the overall system response (including Bode and step). Are there any examples using Python (demo videos) where you move the compensator zeros/poles and it interactively changes the plots? I am not familiar with Marimo NB or Jupyter NB. Is this like having to learn a whole other course? |
|
you can do this with ordinary matplotlib sliders. keep the plant fixed and rebuild the compensator in the slider callback: C = K * (s + z) / (s + p)
L = C * G
T = ct.feedback(L, 1)then update the existing plot lines using the example below has sliders for all three parameters. run it as a script with an interactive matplotlib backend. """Run with Python using an interactive Matplotlib backend (e.g. TkAgg/QtAgg)."""
import control as ct
import matplotlib.pyplot as plt
from matplotlib.widgets import Slider
import numpy as np
s = ct.tf('s')
G = 1 / (s * (s + 1)) # Fixed plant.
w = np.logspace(-2, 2, 400)
t = np.linspace(0, 30, 600)
fig, axes = plt.subplots(3, 1, figsize=(8, 8))
fig.subplots_adjust(bottom=0.24, hspace=0.65)
mag_line, = axes[0].semilogx(w, np.zeros_like(w))
phase_line, = axes[1].semilogx(w, np.zeros_like(w))
step_line, = axes[2].plot(t, np.zeros_like(t))
axes[0].set(title='Loop transfer L = C G', ylabel='Magnitude [dB]')
axes[1].set(xlabel='Frequency [rad/s]', ylabel='Phase [deg]')
axes[2].set(xlabel='Time [s]', ylabel='Step response')
for ax in axes:
ax.grid(True, alpha=0.3)
sliders = [
Slider(fig.add_axes([0.2, 0.14, 0.65, 0.025]), 'K', 0.1, 10, valinit=1),
Slider(fig.add_axes([0.2, 0.09, 0.65, 0.025]), 'z (zero = -z)', 0.1, 10, valinit=1),
Slider(fig.add_axes([0.2, 0.04, 0.65, 0.025]), 'p (pole = -p)', 0.1, 10, valinit=3),
]
def update(_=None):
K, z, p = [slider.val for slider in sliders]
C = K * (s + z) / (s + p)
L = C * G
T = ct.feedback(L, 1) # Negative unity feedback.
mag, phase, _ = ct.frequency_response(L, w)
mag_line.set_ydata(20 * np.log10(mag))
phase_line.set_ydata(np.rad2deg(np.unwrap(phase)))
stable = np.all(np.real(ct.poles(T)) < 0)
if stable:
tout, yout = ct.step_response(T, t)
step_line.set_data(tout, yout)
axes[2].set_title('Closed-loop step: T = L / (1 + L)')
else:
step_line.set_data([], [])
axes[2].set_title('Closed loop unstable or marginal; step hidden')
for ax in axes:
ax.relim()
ax.autoscale_view(scalex=False)
fig.canvas.draw_idle()
for slider in sliders:
slider.on_changed(update)
update()
if __name__ == '__main__':
plt.show() |
you can do this with ordinary matplotlib sliders. keep the plant fixed and rebuild the compensator in the slider callback:
then update the existing plot lines using
ct.frequency_response(L, w)andct.step_response(T, t). no notebook needed.the example below has sliders for all three parameters.
zandpare positive here, so the actual zero and pole are at-zand-p. the Bode plots showL; the step plot showsT. it hides the step response if the closed loop becomes unstable.run it as a script with an interactive matplotlib backend.