@@ -57,7 +57,8 @@ class Window(QDialog):
5757 def __init__ (self , parent = None ):
5858 super (Window , self ).__init__ (parent )
5959
60- self ._plot_ref = None
60+ self .model_ref = None
61+ self .input_ref = None
6162 self .closed_loop_ref = None
6263 self .closed_loop_ax = None
6364 self .bode_plot_ref = None
@@ -124,6 +125,17 @@ def __init__(self, parent=None):
124125 layout_tf = self .createTfLayout ()
125126 left_menu .addLayout (layout_tf )
126127
128+
129+ trim_group = QFormLayout ()
130+ self .line_edit_trim = QDoubleSpinBox ()
131+ self .trim_airspeed = 20.0
132+ self .line_edit_trim .setValue (self .trim_airspeed )
133+ self .line_edit_trim .setRange (0.0 , 100.0 )
134+ self .line_edit_trim .textChanged .connect (self .onTrimChanged )
135+ self .line_edit_trim .setEnabled (False )
136+ trim_group .addRow (QLabel ("Trim airspeed" ), self .line_edit_trim )
137+ left_menu .addLayout (trim_group )
138+
127139 offset_group = QFormLayout ()
128140 self .line_edit_offset = QDoubleSpinBox ()
129141 self .line_edit_offset .setValue (0.0 )
@@ -151,7 +163,8 @@ def __init__(self, parent=None):
151163 self .setLayout (layout_v )
152164
153165 def reset (self ):
154- self ._plot_ref = None
166+ self .model_ref = None
167+ self .input_ref = None
155168 self .closed_loop_ref = None
156169 self .bode_plot_ref = None
157170 self .state_plot_refs = []
@@ -206,6 +219,16 @@ def updateCoeffTable(self):
206219 def onModelChanged (self ):
207220 self .btn_update_model .setEnabled (True )
208221
222+ def onTrimChanged (self ):
223+ try :
224+ self .trim_airspeed = float (self .line_edit_trim .text ())
225+ except ValueError :
226+ self .trim_airspeed = 0
227+ self .line_edit_trim .setValue (self .trim_airspeed )
228+
229+ self .btn_run_sys_id .setEnabled (True )
230+ self .plotInputOutput ()
231+
209232 def onOffsetChanged (self ):
210233 self .plotInputOutput ()
211234
@@ -543,17 +566,13 @@ def updateClosedLoop(self):
543566 kd = self .kd
544567 kff = self .kff
545568
546- airspeed = 40.0
547- airspeed_trim = 38.0
548- airspeed_scale = airspeed_trim / airspeed
549569 delays = ctrl .TransferFunction ([1 ], np .append ([1 ], np .zeros (self .sys_id_delays )), dt , inputs = 'r' , outputs = 'rd' )
550570 plant = ctrl .TransferFunction (num , den , dt , inputs = 'u' , outputs = 'plant_out' )
551571 sampler = ctrl .TransferFunction ([1 ], [1 , 0 ], dt , inputs = 'plant_out' , outputs = 'y' )
552572 sum_feedback = ctrl .summing_junction (inputs = ['rd' , '-y' ], output = 'e' )
553573
554574 # Default is standard PID
555575 feedforward = ctrl .TransferFunction ([kff ], [1 ], dt , inputs = 'rd' , outputs = 'ff_out' )
556- ff_scale = ctrl .TransferFunction ([airspeed_scale ], [1 ], inputs = 'ff_out' , outputs = 'ff_out_scaled' )
557576 i_control = ctrl .TransferFunction ([ki * dt , ki * dt ], [2 , - 2 ], dt , inputs = 'e' , outputs = 'i_out' ) # Integrator discretized using bilinear transform: s = 2(z-1)/(dt(z+1))
558577
559578 # Derivative with 1st order LPF (discretized using Euler method: s = (z-1)/dt)
@@ -565,8 +584,7 @@ def updateClosedLoop(self):
565584
566585 id_control = ctrl .summing_junction (inputs = ['e' , 'i_out' , 'd_out' ], output = 'id_out' )
567586 p_control = ctrl .TransferFunction ([kc ], [1 ], dt , inputs = 'id_out' , outputs = 'pid_out' )
568- pid_scale = ctrl .TransferFunction ([airspeed_scale ** 2 ], [1 ], inputs = 'pid_out' , outputs = 'pid_out_scaled' )
569- sum_control = ctrl .summing_junction (inputs = ['pid_out_scaled' , 'ff_out_scaled' ], output = 'u' )
587+ sum_control = ctrl .summing_junction (inputs = ['pid_out' , 'ff_out' ], output = 'u' )
570588
571589 remove_zero = False
572590 no_derivative_kick = True
@@ -579,7 +597,7 @@ def updateClosedLoop(self):
579597 # Derivative on feedback only to remove the "derivative kick"
580598 d_control = ctrl .TransferFunction (- derivative_num , derivative_den , dt , inputs = 'y' , outputs = 'd_out' )
581599
582- closed_loop = ctrl .interconnect ([delays , sampler , sum_feedback , feedforward , sum_control , p_control , i_control , d_control , id_control , pid_scale , ff_scale , plant ], inputs = 'r' , outputs = 'y' )
600+ closed_loop = ctrl .interconnect ([delays , sampler , sum_feedback , feedforward , sum_control , p_control , i_control , d_control , id_control , plant ], inputs = 'r' , outputs = 'y' )
583601
584602 t_out ,y_out = ctrl .step_response (closed_loop , T = np .arange (0 ,1 ,dt ))
585603 self .plotClosedLoop (t_out , y_out )
@@ -624,28 +642,36 @@ def plotBode(self, w, mag, w_cl, mag_cl):
624642 self .canvas .draw ()
625643
626644 def plotInputOutput (self , redraw = False ):
627- if self ._plot_ref is None or redraw :
645+ if len (self .true_airspeed ) == len (self .input ):
646+ scale = np .array (self .true_airspeed ) / self .trim_airspeed
647+ self .u = self .input * scale ** 2
648+ self .line_edit_trim .setEnabled (True )
649+
650+ if self .model_ref is None or redraw :
628651 # First time we have no plot reference, so do a normal plot.
629652 # .plot returns a list of line <reference>s, as we're
630653 # only getting one we can take the first element.
631654 self .figure .clear ()
632655 ax = self .figure .add_subplot (3 ,3 ,(1 ,3 ))
633- ax .plot (self .t , self .u , self .t , self .y )
656+ input_ref = ax .plot (self .t , self .u )
657+ self .input_ref = input_ref [0 ]
658+ ax .plot (self .t , self .y )
634659 plot_refs = ax .plot (0 , 0 )
635- self ._plot_ref = plot_refs [0 ]
660+ self .model_ref = plot_refs [0 ]
636661 ax .set_title ("Logged data" )
637662 ax .set_xlabel ("Time (s)" )
638663 ax .set_ylabel ("Amplitude" )
639664 ax .legend (["Input" , "Output" , "Model" ])
640665 else :
641666 # We have a reference, we can use it to update the data for that line.
642- self ._plot_ref .set_xdata (self .t_est )
667+ self .model_ref .set_xdata (self .t_est )
643668 try :
644669 offset = float (self .line_edit_offset .text ())
645670 except ValueError :
646671 offset = 0
647672
648- self ._plot_ref .set_ydata (self .y_est + offset )
673+ self .model_ref .set_ydata (self .y_est + offset )
674+ self .input_ref .set_ydata (self .u )
649675
650676 self .canvas .draw ()
651677
@@ -660,8 +686,10 @@ def loadLog(self):
660686 if select .exec_ ():
661687 self .reset ()
662688 self .t = select .t - select .t [0 ]
663- self .u = select .u
689+ self .input = select .u
690+ self .u = self .input
664691 self .y = select .y
692+ self .true_airspeed = select .v
665693 self .refreshInputOutputData ()
666694 self .runIdentification ()
667695 self .computeController ()
@@ -674,10 +702,14 @@ def refreshInputOutputData(self):
674702 self .plotInputOutput (redraw = True )
675703
676704 def resampleData (self , dt ):
677- self .dt = dt
678- self .t = np .arange (0 , self .t [- 1 ]+ self .dt , self .dt )
679- self .u = resample (self .u , len (self .t ))
680- self .y = resample (self .y , len (self .t ))
705+ self .dt = dt
706+ self .t = np .arange (0 , self .t [- 1 ]+ self .dt , self .dt )
707+ self .u = resample (self .u , len (self .t ))
708+ self .y = resample (self .y , len (self .t ))
709+ self .input = resample (self .input , len (self .t ))
710+
711+ if len (self .true_airspeed ) > 0 :
712+ self .true_airspeed = resample (self .true_airspeed , len (self .t ))
681713
682714class DoubleSlider (QSlider ):
683715
0 commit comments