@@ -165,9 +165,10 @@ def root_locus(sys, kvect=None, xlim=None, ylim=None, plotstr='b' if int(matplot
165165 ax .axvline (0. , linestyle = ':' , color = 'k' )
166166 return mymat , kvect
167167
168- def _default_gains (num , den , xlim , ylim , point_tolerance = 5e-2 ,zoom_xlim = None ,zoom_ylim = None ):
169- """Unsupervised gains calculation for root locus plot.
170-
168+
169+ def _default_gains (num , den , xlim , ylim ,zoom_xlim = None ,zoom_ylim = None ):
170+ """Unsupervised gains calculation for root locus plot.
171+
171172 References:
172173 Ogata, K. (2002). Modern control engineering (4th ed.). Upper Saddle River, NJ : New Delhi: Prentice Hall.."""
173174
@@ -197,126 +198,92 @@ def _default_gains(num, den, xlim, ylim, point_tolerance=5e-2,zoom_xlim=None,zoo
197198 "locus with equal order of numerator and denominator." )
198199
199200 if xlim is None and false_gain > 0 :
200- x_tolerance = point_tolerance * (np .max (np .real (mymat_xl )) - np .min (np .real (mymat_xl )))
201+ x_tolerance = 0.05 * (np .max (np .real (mymat_xl )) - np .min (np .real (mymat_xl )))
201202 xlim = _ax_lim (mymat_xl )
202203 elif xlim is None and false_gain < 0 :
203- axmin = np .min (np .real (important_points ))- (np .max (np .real (important_points ))- np .min (np .real (important_points )))
204+ axmin = np .min (np .real (important_points )) - (
205+ np .max (np .real (important_points )) - np .min (np .real (important_points )))
204206 axmin = np .min (np .array ([axmin , np .min (np .real (mymat_xl ))]))
205- axmax = np .max (np .real (important_points ))+ np .max (np .real (important_points ))- np .min (np .real (important_points ))
207+ axmax = np .max (np .real (important_points )) + np .max (np .real (important_points )) - np .min (
208+ np .real (important_points ))
206209 axmax = np .max (np .array ([axmax , np .max (np .real (mymat_xl ))]))
207210 xlim = [axmin , axmax ]
208- x_tolerance = 5e-2 * (axmax - axmin )
211+ x_tolerance = 0.05 * (axmax - axmin )
209212 else :
210- x_tolerance = 5e-2 * (xlim [1 ] - xlim [0 ])
213+ x_tolerance = 0.05 * (xlim [1 ] - xlim [0 ])
211214
212215 if ylim is None :
213- y_tolerance = 5e-2 * (np .max (np .imag (mymat_xl )) - np .min (np .imag (mymat_xl )))
216+ y_tolerance = 0.05 * (np .max (np .imag (mymat_xl )) - np .min (np .imag (mymat_xl )))
214217 ylim = _ax_lim (mymat_xl * 1j )
215218 else :
216- y_tolerance = 5e-2 * (ylim [1 ] - ylim [0 ])
217- print (len (mymat ))
218- tolerance = np .max ([x_tolerance , y_tolerance ])
219- # print('normal smoothing start')
220- # mymat,kvect =_smooth_rootlocus(num, den, tolerance,mymat,kvect, xlim=None, ylim=None)
221- # print('normal smoothing end')
222- mymat = _RLSortRoots (mymat )
223- print (len (mymat ))
219+ y_tolerance = 0.05 * (ylim [1 ] - ylim [0 ])
220+
221+ tolerance = np .min ([x_tolerance , y_tolerance ])
222+ indexes_too_far = _indexes_filt (mymat ,tolerance ,zoom_xlim ,zoom_ylim )
223+
224+ while (len (indexes_too_far ) > 0 ) and (kvect .size < 5000 ):
225+ for counter ,index in enumerate (indexes_too_far ):
226+ index = index + counter * 3
227+ new_gains = np .linspace (kvect [index ], kvect [index + 1 ], 5 )
228+ new_points = _RLFindRoots (num , den , new_gains [1 :4 ])
229+ kvect = np .insert (kvect , index + 1 , new_gains [1 :4 ])
230+ mymat = np .insert (mymat , index + 1 , new_points , axis = 0 )
224231
225- # If a zoom on the plot is used insert points on this interval and use a smaller tolerance
226- if zoom_xlim != None and zoom_ylim != None :
227- print ('zoom smoothing start' )
228- y_tolerance = 5e-2 * (zoom_ylim [1 ] - zoom_ylim [0 ])
229- x_tolerance = 5e-2 * (zoom_xlim [1 ] - zoom_xlim [0 ])
230- tolerance = np .max ([x_tolerance , y_tolerance ])
231- tolerance = np .max ([x_tolerance , y_tolerance ])
232- #print(mymat)
233- mymat ,kvect = _smooth_rootlocus (num ,den ,tolerance ,mymat ,kvect ,zoom_xlim ,zoom_ylim )
234232 mymat = _RLSortRoots (mymat )
235- #print(mymat)
236- print ('zoom smoothing end' )
237- print (len (mymat ))
238- kvect = np .sort (kvect )
233+ indexes_too_far = _indexes_filt (mymat ,tolerance ,zoom_xlim ,zoom_ylim )
239234
240- mymat = _RLFindRoots (num , den , kvect )
235+ new_gains = kvect [- 1 ] * np .hstack ((np .logspace (0 , 3 , 4 )))
236+ new_points = _RLFindRoots (num , den , new_gains [1 :4 ])
237+ kvect = np .append (kvect , new_gains [1 :4 ])
238+ mymat = np .concatenate ((mymat , new_points ), axis = 0 )
239+ mymat = _RLSortRoots (mymat )
241240 return kvect , mymat , xlim , ylim
242241
243-
244- def _smooth_rootlocus (num ,den ,tolerance ,mymat ,kvect , xlim ,ylim ):
245- """Smooth the rootlocus plot by inserting points at locations where the distance between
246- two points exceeds a certain tolerance."""
242+ def _indexes_filt (mymat ,tolerance ,zoom_xlim = None ,zoom_ylim = None ):
243+ """Calculate the distance between points and return the indexes.
244+ Filter the indexes so only the resolution of points within the xlim and ylim is improved when zoom is used"""
247245
248246 distance_points = np .abs (np .diff (mymat , axis = 0 ))
249- indexes_too_far = np .where (distance_points > tolerance )
250- indexes_too_far_filtered = _indexes_filter (indexes_too_far ,mymat ,xlim ,ylim )
251-
252- print ('length of indexes too_far_filtered' )
253- print (len (indexes_too_far_filtered ))
254- print (indexes_too_far_filtered )
255-
256- if len (indexes_too_far_filtered ) > 0 :
257- print ('points are added' )
258- # if indexes_too_far_filtered[0] != 0:
259- # point_before_start = indexes_too_far_filtered[0] -1
260- # indexes_too_far_filtered.insert(0,point_before_start)
261- #
262- # if indexes_too_far_filtered[-1] != len(mymat):
263- # point_at_end = indexes_too_far_filtered[-1]+1
264- # indexes_too_far_filtered.append(point_at_end)
265-
266- print ('before while loop' )
267- print (len (indexes_too_far_filtered ))
268- print (indexes_too_far_filtered )
269- while (len (indexes_too_far_filtered ) > 0 ) and (kvect .size < 5000 ):
270- counter = 0
271- for list_index , index in enumerate (indexes_too_far_filtered ):
272- index += counter * 3
273- new_gains = np .linspace (kvect [index ], kvect [index + 1 ], 5 )
274- new_points = _RLFindRoots (num , den , new_gains [1 :4 ])
275-
276- print ('inside while loop' )
277- print (index )
278- print (kvect [index ],kvect [index + 1 ])
279- print (new_gains [1 :4 ])
280-
281- kvect = np .insert (kvect , index + 1 , new_gains [1 :4 ])
282-
283- mymat = np .insert (mymat , index + 1 , new_points , axis = 0 )
284- counter += 1
285-
286-
287-
288- mymat = _RLSortRoots (mymat )
289- distance_points = np .abs (np .diff (mymat , axis = 0 )) > tolerance
290- indexes_too_far = np .where (distance_points )
291- indexes_too_far_filtered = _indexes_filter (indexes_too_far , mymat , xlim , ylim )
292- print ('new indexes too far' )
293- print (indexes_too_far_filtered )
294- print ('K SORTED?' )
295- print (all (kvect [i ] <= kvect [i + 1 ] for i in range (len (kvect )- 1 )))
296-
297- #print(kvect)
298-
299- new_gains = kvect [- 1 ] * np .hstack ((np .logspace (0 , 3 , 4 )))
300- new_points = _RLFindRoots (num , den , new_gains [1 :4 ])
301- kvect = np .append (kvect , new_gains [1 :4 ])
302- mymat = np .concatenate ((mymat , new_points ), axis = 0 )
303-
304-
305- return mymat ,kvect
306-
307- def _indexes_filter (indexes_too_far ,mymat ,xlim ,ylim ):
308- """Filter the indexes so only the resolution of points within the xlim and ylim is improved"""
309- if xlim == None and ylim == None :
310- indexes_too_far_filtered = list (np .unique (indexes_too_far [0 ]))
311- else :
247+ indexes_too_far = list (np .unique (np .where (distance_points > tolerance )[0 ]))
248+
249+ if zoom_xlim != None and zoom_ylim != None :
250+ x_tolerance_zoom = 0.05 * (zoom_xlim [1 ] - zoom_xlim [0 ])
251+ y_tolerance_zoom = 0.05 * (zoom_ylim [1 ] - zoom_ylim [0 ])
252+ tolerance_zoom = np .min ([x_tolerance_zoom , y_tolerance_zoom ])
253+ distance_points_zoom_ = np .abs (np .diff (mymat , axis = 0 ))
254+ indexes_too_far_zoom = list (np .unique (np .where (distance_points_zoom_ > tolerance_zoom )[0 ]))
312255 indexes_too_far_filtered = []
313- for index in np .unique (indexes_too_far [0 ]):
256+
257+ for index in indexes_too_far_zoom :
314258 for point in mymat [index ]:
315- if (xlim [0 ] <= point .real <= xlim [1 ]) and (ylim [0 ] <= point .imag <= ylim [1 ]):
259+ if (zoom_xlim [0 ] <= point .real <= zoom_xlim [1 ]) and (zoom_ylim [0 ] <= point .imag <= zoom_ylim [1 ]):
316260 indexes_too_far_filtered .append (index )
317261 break
318262
319- return indexes_too_far_filtered
263+ # Check if the zoom box is not overshot and insert points where neccessary
264+ if len (indexes_too_far_filtered ) == 0 and len (mymat ) < 300 :
265+ limits = [zoom_xlim [0 ],zoom_xlim [1 ],zoom_ylim [0 ],zoom_ylim [1 ]]
266+ for index ,limit in enumerate (limits ):
267+ if index <= 1 :
268+ asign = np .sign (real (mymat )- limit )
269+ else :
270+ asign = np .sign (imag (mymat ) - limit )
271+ signchange = ((np .roll (asign , 1 , axis = 0 ) - asign ) != 0 ).astype (int )
272+ signchange [0 ] = np .zeros ((len (mymat [0 ])))
273+ if len (np .where (signchange == 1 )) > 0 :
274+ indexes_too_far_filtered .append (np .where (signchange == 1 )[0 ][0 ])
275+
276+ if len (indexes_too_far_filtered ) > 0 :
277+ if indexes_too_far_filtered [0 ] != 0 :
278+ indexes_too_far_filtered .insert (0 ,indexes_too_far_filtered [0 ]- 1 )
279+ if not indexes_too_far_filtered [- 1 ] + 1 >= len (mymat )- 2 :
280+ indexes_too_far_filtered .append (indexes_too_far_filtered [- 1 ]+ 1 )
281+
282+ indexes_too_far .extend (indexes_too_far_filtered )
283+
284+ indexes_too_far = list (np .unique (indexes_too_far ))
285+ indexes_too_far .sort ()
286+ return indexes_too_far
320287
321288def _break_points (num , den ):
322289 """Extract break points over real axis and the gains give these location"""
@@ -405,7 +372,6 @@ def _systopoly1d(sys):
405372
406373def _RLFindRoots (nump , denp , kvect ):
407374 """Find the roots for the root locus."""
408-
409375 # Convert numerator and denominator to polynomials if they aren't
410376 roots = []
411377 for k in kvect :
@@ -421,7 +387,6 @@ def _RLFindRoots(nump, denp, kvect):
421387 mymat = row_stack (roots )
422388 return mymat
423389
424-
425390def _RLSortRoots (mymat ):
426391 """Sort the roots from sys._RLFindRoots, so that the root
427392 locus doesn't show weird pseudo-branches as roots jump from
@@ -446,20 +411,17 @@ def _RLSortRoots(mymat):
446411
447412def _RLClickDispatcher (event ,sys ,fig ,ax_rlocus ,plotstr ,sisotool = False ,bode_plot_params = None ,tvect = None ):
448413 """Rootlocus plot click dispatcher"""
449-
450414 # If zoom is used on the rootlocus plot smooth and update it
451415 if plt .get_current_fig_manager ().toolbar .mode == 'zoom rect' and event .inaxes == ax_rlocus .axes :
452416
453417 (nump , denp ) = _systopoly1d (sys )
454- xlim = ax_rlocus .get_xlim ()
455- ylim = ax_rlocus . get_ylim ()
418+ xlim , ylim = ax_rlocus .get_xlim (), ax_rlocus . get_ylim ()
419+
456420 kvect ,mymat , xlim ,ylim = _default_gains (nump , denp ,xlim = None ,ylim = None , zoom_xlim = xlim ,zoom_ylim = ylim )
457421 _removeLine ('rootlocus' , ax_rlocus )
458422
459- for index ,col in enumerate (mymat .T ):
460- ax_rlocus .plot (real (col ), imag (col ), plotstr ,label = index )
461-
462- fig .canvas .draw ()
423+ for i ,col in enumerate (mymat .T ):
424+ ax_rlocus .plot (real (col ), imag (col ), plotstr ,label = 'rootlocus' )
463425
464426 # if a point is clicked on the rootlocus plot visually emphasize it
465427 else :
0 commit comments