@@ -93,7 +93,7 @@ def __init__(self, parent=None):
9393 self .input_ref = None
9494 self .closed_loop_ref = None
9595 self .closed_loop_ax = None
96- self .bode_plot_ref = None
96+ self .bode_plot_ref = []
9797 self .pz_plot_refs = []
9898 self .file_name = None
9999 self .is_system_identified = False
@@ -213,7 +213,7 @@ def reset(self):
213213 self .model_ref = None
214214 self .input_ref = None
215215 self .closed_loop_ref = None
216- self .bode_plot_ref = None
216+ self .bode_plot_ref = []
217217 self .pz_plot_refs = []
218218 self .is_system_identified = False
219219
@@ -538,7 +538,7 @@ def plotPolesZeros(self):
538538 poles = self .Gz .poles ()
539539 zeros = self .Gz .zeros ()
540540 if not self .pz_plot_refs :
541- ax = self .figure .add_subplot (3 , 3 , 6 )
541+ ax = self .figure .add_subplot (3 , 3 , 4 )
542542 plot_ref = ax .plot (poles .real , poles .imag , "rx" , markersize = 10 )
543543 self .pz_plot_refs .append (plot_ref [0 ])
544544 plot_ref = ax .plot (zeros .real , zeros .imag , "ro" , markersize = 10 )
@@ -737,11 +737,27 @@ def updateClosedLoop(self):
737737 y_out += y_d
738738
739739 self .plotClosedLoop (t_out , y_out )
740- w = np .logspace (- 1 , 3 , 40 ).tolist ()
741- (mag_cl , phase_cl , omega_cl ) = ctrl .frequency_response (
742- closed_loop , omega = np .asarray (w )
740+
741+ sum_feedback = ctrl .summing_junction (inputs = ["rd" ], output = "e" )
742+ open_loop = ctrl .interconnect (
743+ [
744+ delays ,
745+ sampler ,
746+ sum_feedback ,
747+ feedforward ,
748+ sum_control ,
749+ p_control ,
750+ i_control ,
751+ d_control ,
752+ id_control ,
753+ out_sign ,
754+ plant ,
755+ ],
756+ inputs = "r" ,
757+ outputs = "y" ,
743758 )
744- self .plotBode (omega_cl , mag_cl )
759+
760+ self .plotBode (open_loop , closed_loop )
745761
746762 def plotClosedLoop (self , t , y ):
747763 if self .closed_loop_ref is None :
@@ -760,21 +776,72 @@ def plotClosedLoop(self, t, y):
760776
761777 self .canvas .draw ()
762778
763- def plotBode (self , w_cl , mag_cl ):
764- if self .bode_plot_ref is None :
765- ax = self .figure .add_subplot (3 , 3 , (8 , 9 ))
766- f = w_cl / (2 * np .pi )
767- plot_ref = ax .semilogx (f , 20 * np .log10 (mag_cl ))
779+ def plotBode (self , open_loop , closed_loop ):
780+
781+ (
782+ gain_margin ,
783+ phase_margin ,
784+ stab_margin ,
785+ phase_crossover ,
786+ gain_crossover ,
787+ stab_margin_w ,
788+ ) = ctrl .stability_margins (open_loop )
789+ stability_margins_text = f"Gain margin: { 20 * np .log10 (gain_margin ):.2f} dB (@{ phase_crossover / (2 * np .pi ):.1f} Hz)\n Phase margin: { phase_margin :.1f} deg (@{ gain_crossover / (2 * np .pi ):.1f} Hz)"
790+
791+ w = np .logspace (- 1 , 3 , 40 ).tolist ()
792+ (mag_ol , phase_ol , omega_ol ) = ctrl .frequency_response (
793+ open_loop , omega = np .asarray (w )
794+ )
795+
796+ (mag_cl , phase_cl , omega_cl ) = ctrl .frequency_response (
797+ closed_loop , omega = np .asarray (w )
798+ )
799+ f = omega_cl / (2 * np .pi )
800+
801+ if not self .bode_plot_ref :
802+ ax = self .figure .add_subplot (3 , 3 , (5 , 6 ))
803+ plot_ref = ax .semilogx (f , 20 * np .log10 (mag_ol ), label = "Open-loop" )
804+ self .bode_plot_ref .append (plot_ref [0 ])
805+ plot_ref = ax .semilogx (f , 20 * np .log10 (mag_cl ), label = "Closed-loop" )
806+ self .bode_plot_ref .append (plot_ref [0 ])
807+ ax .set_ylim (- 20 , 20 )
768808 ax .plot ([f [0 ], f [- 1 ]], [0 , 0 ], "k--" )
769809 ax .plot ([f [0 ], f [- 1 ]], [- 3 , - 3 ], "g--" )
770- self . bode_plot_ref = plot_ref [ 0 ]
810+
771811 ax .set_title ("Bode" )
772- ax .set_xlabel ("Frequency (Hz)" )
773812 ax .set_ylabel ("Magnitude (dB)" )
813+ ax .legend ()
814+
815+ ax = self .figure .add_subplot (3 , 3 , (8 , 9 ))
816+ plot_ref = ax .semilogx (f , phase_ol * 180 / np .pi , label = "Open-loop" )
817+ self .bode_plot_ref .append (plot_ref [0 ])
818+ plot_ref = ax .semilogx (f , phase_cl * 180 / np .pi , label = "Closed-loop" )
819+ self .bode_plot_ref .append (plot_ref [0 ])
820+ ax .set_ylim (- 180 , 180 )
821+
822+ ax .set_xlabel ("Frequency (Hz)" )
823+ ax .set_ylabel ("Phase (deg)" )
824+ self .gain_margin_text_ref = ax .text (
825+ 0.01 ,
826+ 0.9 ,
827+ stability_margins_text ,
828+ verticalalignment = "top" ,
829+ transform = ax .transAxes ,
830+ )
831+
774832 else :
775- f = w_cl / (2 * np .pi )
776- self .bode_plot_ref .set_xdata (f )
777- self .bode_plot_ref .set_ydata (20 * np .log10 (mag_cl ))
833+ self .bode_plot_ref [0 ].set_xdata (f )
834+ mag_ol_db = 20 * np .log10 (mag_ol )
835+ self .bode_plot_ref [0 ].set_ydata (mag_ol_db )
836+ self .bode_plot_ref [1 ].set_xdata (f )
837+ mag_cl_db = 20 * np .log10 (mag_cl )
838+ self .bode_plot_ref [1 ].set_ydata (mag_cl_db )
839+ self .gain_margin_text_ref .set_text (stability_margins_text )
840+
841+ self .bode_plot_ref [2 ].set_xdata (f )
842+ self .bode_plot_ref [2 ].set_ydata (phase_ol * 180 / np .pi )
843+ self .bode_plot_ref [3 ].set_xdata (f )
844+ self .bode_plot_ref [3 ].set_ydata (phase_cl * 180 / np .pi )
778845
779846 self .canvas .draw ()
780847
0 commit comments