From ffc304158d0016089fcc7698bcd4a01f2f45c1d1 Mon Sep 17 00:00:00 2001 From: gonmolina Date: Mon, 20 Mar 2017 22:22:11 -0300 Subject: [PATCH 01/20] add unsupervised calculation on gains for root locus plot. --- control/rlocus.py | 116 +++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 104 insertions(+), 12 deletions(-) diff --git a/control/rlocus.py b/control/rlocus.py index e272a833d..07281f49a 100644 --- a/control/rlocus.py +++ b/control/rlocus.py @@ -56,6 +56,7 @@ __all__ = ['root_locus', 'rlocus'] + # Main function: compute a root locus diagram def root_locus(sys, kvect=None, xlim=None, ylim=None, plotstr='-', Plot=True, PrintGain=True): @@ -93,11 +94,10 @@ def root_locus(sys, kvect=None, xlim=None, ylim=None, plotstr='-', Plot=True, (nump, denp) = _systopoly1d(sys) if kvect is None: - kvect = _default_gains(sys) - - # Compute out the loci - mymat = _RLFindRoots(sys, kvect) - mymat = _RLSortRoots(sys, mymat) + kvect, mymat, xlim, ylim = _default_gains(nump, denp, xlim, ylim) + else: + mymat = _RLFindRoots(nump, denp, kvect) + mymat = _RLSortRoots(mymat) # Create the plot if (Plot): @@ -130,11 +130,104 @@ def root_locus(sys, kvect=None, xlim=None, ylim=None, plotstr='-', Plot=True, return mymat, kvect -def _default_gains(sys): - # TODO: update with a smart calculation of the gains using sys poles/zeros - return np.logspace(-3, 3) -# Utility function to extract numerator and denominator polynomials +def _default_gains(num, den, xlim, ylim): + """Unsupervised gains calculation for root locus plot""" + k_break, real_break = _break_points(num, den) + kmax = _k_max(num, den, real_break, k_break) + kvect = np.hstack((np.linspace(0, kmax, 50), np.real(k_break))) + kvect.sort() + mymat = _RLFindRoots(num, den, kvect) + mymat = _RLSortRoots(mymat) + open_loop_poles = den.roots + open_loop_zeros = num.roots + + if (open_loop_zeros.size != 0) & (open_loop_zeros.size < open_loop_poles.size): + open_loop_zeros_xl = np.append(open_loop_zeros, + np.ones(open_loop_poles.size - open_loop_zeros.size) * open_loop_zeros[-1]) + mymat_xl = np.append(mymat, open_loop_zeros_xl) + else: + mymat_xl = mymat + + if xlim is None: + x_tolerance = 0.05 * (np.max(np.max(np.real(mymat_xl))) - np.min(np.min(np.real(mymat_xl)))) + xlim = _ax_lim(mymat_xl) + else: + x_tolerance = 0.05 * (xlim[1] - xlim[0]) + if ylim is None: + y_tolerance = 0.05 * (np.max(np.max(np.imag(mymat_xl))) - np.min(np.min(np.imag(mymat_xl)))) + ylim = _ax_lim(mymat_xl * 1j) + else: + y_tolerance = 0.05 * (ylim[1] - ylim[0]) + + tolerance = np.max([x_tolerance, y_tolerance]) + distance_points = np.abs(np.diff(mymat, axis=0)) + indexes_too_far = np.where(distance_points > tolerance) + while (indexes_too_far[0].size > 0) & (kvect.size < 5000): + for index in indexes_too_far[0]: + new_gains = np.linspace(kvect[index], kvect[index+1], 5) + new_points = _RLFindRoots(num, den, new_gains[1:4]) + kvect = np.insert(kvect, index+1, new_gains[1:4]) + mymat = np.insert(mymat, index+1, new_points, axis=0) + mymat = _RLSortRoots(mymat) + distance_points = np.abs(np.diff(mymat, axis=0)) + indexes_too_far = np.where(distance_points > tolerance) + new_gains = np.hstack((np.logspace(np.log10(kvect[-1]), np.log10(kvect[-1]*200), 10))) + new_points = _RLFindRoots(num, den, new_gains[1:10]) + kvect = np.append(kvect, new_gains[1:10]) + mymat = np.concatenate((mymat, new_points), axis=0) + mymat = _RLSortRoots(mymat) + return kvect, mymat, xlim, ylim + + +def _break_points(num, den): + """Extract break points over real axis and the gains give these location""" + # type: (np.poly1d, np.poly1d) -> (np.array, np.array) + dnum = num.deriv(m=1) + dden = den.deriv(m=1) + polynom = den * dnum - num * dden + real_break_pts = polynom.r + real_break_pts = real_break_pts[num(real_break_pts) != 0] # don't care about infinite break points + k_break = -den(real_break_pts) / num(real_break_pts) + idx = k_break >= 0 # only positives gains + k_break = k_break[idx] + real_break_pts = real_break_pts[idx] + return k_break, real_break_pts + + +def _ax_lim(mymat): + """Utility to get the axis limits""" + axmin = np.min(np.min(np.real(mymat))) + axmax = np.max(np.max(np.real(mymat))) + if axmax != axmin: + deltax = (axmax - axmin) * 0.02 + else: + deltax = np.max([1., axmax / 2]) + axlim = [axmin - deltax, axmax + deltax] + return axlim + + +def _k_max(num, den, real_break_points, k_break_points): + """" Calculation the maximum gain for the root locus shown in tne figure""" + asymp_number = den.order - num.order + singular_points = np.concatenate((num.roots, den.roots), axis=0) + important_points = np.concatenate((singular_points, real_break_points), axis=0) + + if asymp_number > 0: + asymp_center = (np.sum(den.roots) - np.sum(num.roots))/asymp_number + distance_max = 2 * np.max(np.abs(important_points - asymp_center)) + asymp_angles = (2 * np.arange(0, asymp_number)-1) * np.pi / asymp_number + farthest_points = asymp_center + distance_max * np.exp(asymp_angles * 1j) # farthest points over asymptotes + kmax_asymp = -den(farthest_points) / num(farthest_points) + else: + farthest_points = 2 * np.max(np.abs(important_points)) + kmax_asymp = -den(farthest_points) / num(farthest_points) + if kmax_asymp == 0: + kmax_asymp = -den(1) / num(1) + kmax = np.max(np.concatenate((np.real(kmax_asymp), k_break_points), axis=0)) + return kmax + + def _systopoly1d(sys): """Extract numerator and denominator polynomails for a system""" # Allow inputs from the signal processing toolbox @@ -163,11 +256,10 @@ def _systopoly1d(sys): return (nump, denp) -def _RLFindRoots(sys, kvect): +def _RLFindRoots(nump, denp, kvect): """Find the roots for the root locus.""" # Convert numerator and denominator to polynomials if they aren't - (nump, denp) = _systopoly1d(sys) roots = [] for k in kvect: @@ -179,7 +271,7 @@ def _RLFindRoots(sys, kvect): return mymat -def _RLSortRoots(sys, mymat): +def _RLSortRoots(mymat): """Sort the roots from sys._RLFindRoots, so that the root locus doesn't show weird pseudo-branches as roots jump from one branch to another.""" From 2aada8b1e0aaa73211fcccbfb79d02ccc5fbdbd0 Mon Sep 17 00:00:00 2001 From: gonmolina Date: Mon, 20 Mar 2017 23:31:41 -0300 Subject: [PATCH 02/20] fix bug concatenating zero dimensional array --- control/rlocus.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/control/rlocus.py b/control/rlocus.py index 07281f49a..108980568 100644 --- a/control/rlocus.py +++ b/control/rlocus.py @@ -172,7 +172,7 @@ def _default_gains(num, den, xlim, ylim): mymat = _RLSortRoots(mymat) distance_points = np.abs(np.diff(mymat, axis=0)) indexes_too_far = np.where(distance_points > tolerance) - new_gains = np.hstack((np.logspace(np.log10(kvect[-1]), np.log10(kvect[-1]*200), 10))) + new_gains = np.hstack((np.linspace(kvect[-1], kvect[-1]*200, 10))) new_points = _RLFindRoots(num, den, new_gains[1:10]) kvect = np.append(kvect, new_gains[1:10]) mymat = np.concatenate((mymat, new_points), axis=0) @@ -192,6 +192,9 @@ def _break_points(num, den): idx = k_break >= 0 # only positives gains k_break = k_break[idx] real_break_pts = real_break_pts[idx] + if len(k_break) == 0: + k_break = [0] + real_break_pts = den.roots return k_break, real_break_pts @@ -219,6 +222,8 @@ def _k_max(num, den, real_break_points, k_break_points): asymp_angles = (2 * np.arange(0, asymp_number)-1) * np.pi / asymp_number farthest_points = asymp_center + distance_max * np.exp(asymp_angles * 1j) # farthest points over asymptotes kmax_asymp = -den(farthest_points) / num(farthest_points) + if kmax_asymp == 0: + kmax_asymp = [den.coeffs[0]/num.coeffs[0]*3] else: farthest_points = 2 * np.max(np.abs(important_points)) kmax_asymp = -den(farthest_points) / num(farthest_points) From 71304344ffe724840e2af200d891fc9bc1d96b43 Mon Sep 17 00:00:00 2001 From: gonmolina Date: Mon, 20 Mar 2017 23:55:21 -0300 Subject: [PATCH 03/20] fix bug concatenating zero dimensional when test in matlab_test.py --- control/rlocus.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/control/rlocus.py b/control/rlocus.py index 108980568..d244d947e 100644 --- a/control/rlocus.py +++ b/control/rlocus.py @@ -222,13 +222,11 @@ def _k_max(num, den, real_break_points, k_break_points): asymp_angles = (2 * np.arange(0, asymp_number)-1) * np.pi / asymp_number farthest_points = asymp_center + distance_max * np.exp(asymp_angles * 1j) # farthest points over asymptotes kmax_asymp = -den(farthest_points) / num(farthest_points) - if kmax_asymp == 0: - kmax_asymp = [den.coeffs[0]/num.coeffs[0]*3] else: farthest_points = 2 * np.max(np.abs(important_points)) - kmax_asymp = -den(farthest_points) / num(farthest_points) - if kmax_asymp == 0: - kmax_asymp = -den(1) / num(1) + kmax_asymp = [-den(farthest_points) / num(farthest_points)] + if kmax_asymp == 0: + kmax_asymp = [den.coeffs[0] / num.coeffs[0] * 3] kmax = np.max(np.concatenate((np.real(kmax_asymp), k_break_points), axis=0)) return kmax From 8b829ccfcc051483c277c899af39e748a362a260 Mon Sep 17 00:00:00 2001 From: gonmolina Date: Tue, 21 Mar 2017 00:13:08 -0300 Subject: [PATCH 04/20] fix bug with array==0! problem test in matlab_test.py --- control/rlocus.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/control/rlocus.py b/control/rlocus.py index d244d947e..b4653740d 100644 --- a/control/rlocus.py +++ b/control/rlocus.py @@ -225,7 +225,7 @@ def _k_max(num, den, real_break_points, k_break_points): else: farthest_points = 2 * np.max(np.abs(important_points)) kmax_asymp = [-den(farthest_points) / num(farthest_points)] - if kmax_asymp == 0: + if np.max(kmax_asymp) == 0: kmax_asymp = [den.coeffs[0] / num.coeffs[0] * 3] kmax = np.max(np.concatenate((np.real(kmax_asymp), k_break_points), axis=0)) return kmax From d4cbc958a123e864307a60d9a88bf1ea2d5ee4ee Mon Sep 17 00:00:00 2001 From: gonmolina Date: Wed, 22 Mar 2017 22:14:22 -0300 Subject: [PATCH 05/20] fix bug with axis limits with no asymptotes fix bug plotting zeros when only zeros in zero exist --- control/rlocus.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/control/rlocus.py b/control/rlocus.py index b4653740d..ee28497d3 100644 --- a/control/rlocus.py +++ b/control/rlocus.py @@ -113,7 +113,7 @@ def root_locus(sys, kvect=None, xlim=None, ylim=None, plotstr='-', Plot=True, # plot open loop zeros zeros = array(nump.r) - if zeros.any(): + if zeros.size > 0: ax.plot(real(zeros), imag(zeros), 'o') # Now plot the loci @@ -148,6 +148,9 @@ def _default_gains(num, den, xlim, ylim): mymat_xl = np.append(mymat, open_loop_zeros_xl) else: mymat_xl = mymat + singular_points = np.concatenate((num.roots, den.roots), axis=0) + important_points = np.concatenate((singular_points, real_break), axis=0) + mymat_xl = np.append(mymat_xl, important_points) if xlim is None: x_tolerance = 0.05 * (np.max(np.max(np.real(mymat_xl))) - np.min(np.min(np.real(mymat_xl)))) @@ -223,11 +226,11 @@ def _k_max(num, den, real_break_points, k_break_points): farthest_points = asymp_center + distance_max * np.exp(asymp_angles * 1j) # farthest points over asymptotes kmax_asymp = -den(farthest_points) / num(farthest_points) else: - farthest_points = 2 * np.max(np.abs(important_points)) - kmax_asymp = [-den(farthest_points) / num(farthest_points)] - if np.max(kmax_asymp) == 0: kmax_asymp = [den.coeffs[0] / num.coeffs[0] * 3] - kmax = np.max(np.concatenate((np.real(kmax_asymp), k_break_points), axis=0)) + if np.max(kmax_asymp) == 0: + kmax = np.max(np.concatenate((np.real(kmax_asymp), k_break_points), axis=0)) + else: + kmax = kmax_asymp return kmax From 6d55d573d3618ed3ba25030beaf042bb1bc41b68 Mon Sep 17 00:00:00 2001 From: gonmolina Date: Wed, 22 Mar 2017 22:55:30 -0300 Subject: [PATCH 06/20] fix bug calculating kmax --- control/rlocus.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/control/rlocus.py b/control/rlocus.py index ee28497d3..93c1a9b78 100644 --- a/control/rlocus.py +++ b/control/rlocus.py @@ -227,10 +227,8 @@ def _k_max(num, den, real_break_points, k_break_points): kmax_asymp = -den(farthest_points) / num(farthest_points) else: kmax_asymp = [den.coeffs[0] / num.coeffs[0] * 3] - if np.max(kmax_asymp) == 0: - kmax = np.max(np.concatenate((np.real(kmax_asymp), k_break_points), axis=0)) - else: - kmax = kmax_asymp + + kmax = np.max(np.concatenate((np.real(kmax_asymp), k_break_points), axis=0)) return kmax From 01e33ea6ef9e606a36909e36129fe9fa85e6fdcc Mon Sep 17 00:00:00 2001 From: gonmolina Date: Mon, 20 Mar 2017 22:22:11 -0300 Subject: [PATCH 07/20] add unsupervised calculation on gains for root locus plot. --- control/rlocus.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/control/rlocus.py b/control/rlocus.py index 93c1a9b78..b4653740d 100644 --- a/control/rlocus.py +++ b/control/rlocus.py @@ -113,7 +113,7 @@ def root_locus(sys, kvect=None, xlim=None, ylim=None, plotstr='-', Plot=True, # plot open loop zeros zeros = array(nump.r) - if zeros.size > 0: + if zeros.any(): ax.plot(real(zeros), imag(zeros), 'o') # Now plot the loci @@ -148,9 +148,6 @@ def _default_gains(num, den, xlim, ylim): mymat_xl = np.append(mymat, open_loop_zeros_xl) else: mymat_xl = mymat - singular_points = np.concatenate((num.roots, den.roots), axis=0) - important_points = np.concatenate((singular_points, real_break), axis=0) - mymat_xl = np.append(mymat_xl, important_points) if xlim is None: x_tolerance = 0.05 * (np.max(np.max(np.real(mymat_xl))) - np.min(np.min(np.real(mymat_xl)))) @@ -226,8 +223,10 @@ def _k_max(num, den, real_break_points, k_break_points): farthest_points = asymp_center + distance_max * np.exp(asymp_angles * 1j) # farthest points over asymptotes kmax_asymp = -den(farthest_points) / num(farthest_points) else: + farthest_points = 2 * np.max(np.abs(important_points)) + kmax_asymp = [-den(farthest_points) / num(farthest_points)] + if np.max(kmax_asymp) == 0: kmax_asymp = [den.coeffs[0] / num.coeffs[0] * 3] - kmax = np.max(np.concatenate((np.real(kmax_asymp), k_break_points), axis=0)) return kmax From 95d69080af42a541ae98b2142d1a99ec377397d0 Mon Sep 17 00:00:00 2001 From: gonmolina Date: Wed, 22 Mar 2017 22:14:22 -0300 Subject: [PATCH 08/20] fix bug with axis limits with no asymptotes fix bug plotting zeros when only zeros in zero exist --- control/rlocus.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/control/rlocus.py b/control/rlocus.py index b4653740d..93c1a9b78 100644 --- a/control/rlocus.py +++ b/control/rlocus.py @@ -113,7 +113,7 @@ def root_locus(sys, kvect=None, xlim=None, ylim=None, plotstr='-', Plot=True, # plot open loop zeros zeros = array(nump.r) - if zeros.any(): + if zeros.size > 0: ax.plot(real(zeros), imag(zeros), 'o') # Now plot the loci @@ -148,6 +148,9 @@ def _default_gains(num, den, xlim, ylim): mymat_xl = np.append(mymat, open_loop_zeros_xl) else: mymat_xl = mymat + singular_points = np.concatenate((num.roots, den.roots), axis=0) + important_points = np.concatenate((singular_points, real_break), axis=0) + mymat_xl = np.append(mymat_xl, important_points) if xlim is None: x_tolerance = 0.05 * (np.max(np.max(np.real(mymat_xl))) - np.min(np.min(np.real(mymat_xl)))) @@ -223,10 +226,8 @@ def _k_max(num, den, real_break_points, k_break_points): farthest_points = asymp_center + distance_max * np.exp(asymp_angles * 1j) # farthest points over asymptotes kmax_asymp = -den(farthest_points) / num(farthest_points) else: - farthest_points = 2 * np.max(np.abs(important_points)) - kmax_asymp = [-den(farthest_points) / num(farthest_points)] - if np.max(kmax_asymp) == 0: kmax_asymp = [den.coeffs[0] / num.coeffs[0] * 3] + kmax = np.max(np.concatenate((np.real(kmax_asymp), k_break_points), axis=0)) return kmax From 1b20bd4e97adda170d9b2d4148b206287a81588d Mon Sep 17 00:00:00 2001 From: gonmolina Date: Fri, 31 Mar 2017 14:27:38 -0300 Subject: [PATCH 09/20] add sgrid to rlocus plot, to the last version of unsupervised gains. --- control/rlocus.py | 77 ++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 72 insertions(+), 5 deletions(-) diff --git a/control/rlocus.py b/control/rlocus.py index 93c1a9b78..1e14db1b8 100644 --- a/control/rlocus.py +++ b/control/rlocus.py @@ -59,7 +59,7 @@ # Main function: compute a root locus diagram def root_locus(sys, kvect=None, xlim=None, ylim=None, plotstr='-', Plot=True, - PrintGain=True): + PrintGain=True, grid = False): """Root locus plot Calculate the root locus by finding the roots of 1+k*TF(s) @@ -99,9 +99,17 @@ def root_locus(sys, kvect=None, xlim=None, ylim=None, plotstr='-', Plot=True, mymat = _RLFindRoots(nump, denp, kvect) mymat = _RLSortRoots(mymat) - # Create the plot - if (Plot): - f = pylab.figure() + # Create the Plot + if Plot: + figure_number = pylab.get_fignums() + figure_title = [pylab.figure(numb).canvas.get_window_title() for numb in figure_number] + new_figure_name = "Root Locus" + rloc_num = 1 + while new_figure_name in figure_title: + new_figure_name = "Root Locus " + str(rloc_num) + rloc_num += 1 + f = pylab.figure(new_figure_name) + if PrintGain: f.canvas.mpl_connect( 'button_release_event', partial(_RLFeedbackClicks, sys=sys)) @@ -127,6 +135,8 @@ def root_locus(sys, kvect=None, xlim=None, ylim=None, plotstr='-', Plot=True, ax.set_ylim(ylim) ax.set_xlabel('Real') ax.set_ylabel('Imaginary') + if grid: + sgrid_func(f) return mymat, kvect @@ -305,6 +315,63 @@ def _RLFeedbackClicks(event, sys): K = -1./sys.horner(s) if abs(K.real) > 1e-8 and abs(K.imag/K.real) < 0.04: print("Clicked at %10.4g%+10.4gj gain %10.4g damp %10.4g" % - (s.real, s.imag, K.real, -1*s.real/abs(s))) + (s.real, s.imag, K.real, -1 * s.real / abs(s))) + + +def sgrid_func(fig): + ax = fig.gca() + ylocator = ax.get_yaxis().get_major_locator() + xlocator = ax.get_yaxis().get_major_locator() + + long_xaxis = xlocator()[-1] - xlocator()[0] + long_yaxis = ylocator()[-1] - ylocator()[0] + + angules = np.arange(-90, 80, 15)*np.pi/180 + + # radial lines + y_over_x = np.tan(angules[1::])*ylocator()[-1]/xlocator()[-1] + + ylim = ax.get_ylim() + ytext_pos_lim = ylim[1]-(ylim[1]-ylim[0])*0.03 + + xlim = ax.get_xlim() + xtext_pos_lim = xlim[0]+(xlim[1]-xlim[0])*0.0 + + index = 0 + zeta = np.sin(np.pi/2-angules[1::]) + + for yp in y_over_x: + ax.plot([0, xlocator()[0]], [0, yp*xlocator()[0]], color='gray', + linestyle='dashed', linewidth=0.5) + an = "%.2f" % zeta[index] + if yp > 0: + xtext_pos = -1/yp * ylim[1] + ytext_pos = -yp * xtext_pos_lim + if np.abs(xtext_pos) > np.abs(xtext_pos_lim): + xtext_pos = xtext_pos_lim + else: + ytext_pos = ytext_pos_lim + ax.annotate(an, textcoords='data', xy=[xtext_pos, ytext_pos], fontsize=8) + elif yp < 0: + xtext_pos = -1/yp * ylim[1] + ytext_pos = yp * xtext_pos_lim + if np.abs(xtext_pos) > np.abs(xtext_pos_lim): + xtext_pos = xtext_pos_lim + ytext_pos = - ytext_pos + else: + ytext_pos = ylim[0] + xtext_pos = -xtext_pos + ax.annotate(an, textcoords='data', xy=[xtext_pos, ytext_pos], fontsize=8) + index += 1 + + angules = np.linspace(-90, 90, 20)*np.pi/180 + for xt in xlocator(): + if xt < 0: + yp = np.sin(angules)*np.abs(xt) + xp = -np.cos(angules)*np.abs(xt) + ax.plot(xp, yp, color='gray', + linestyle='dashed', linewidth=0.5) + an = "%.2f" % -xt + ax.annotate(an, textcoords='data', xy=[xt, 0], fontsize=8) rlocus = root_locus From 5e47d1f128f1d2a573218e47c170d52ae02c00cd Mon Sep 17 00:00:00 2001 From: gonmolina Date: Fri, 31 Mar 2017 14:48:08 -0300 Subject: [PATCH 10/20] add origin to rlocus plot and x=0 line (zeta =0 grid line). --- control/rlocus.py | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/control/rlocus.py b/control/rlocus.py index 1e14db1b8..f76050aac 100644 --- a/control/rlocus.py +++ b/control/rlocus.py @@ -160,6 +160,7 @@ def _default_gains(num, den, xlim, ylim): mymat_xl = mymat singular_points = np.concatenate((num.roots, den.roots), axis=0) important_points = np.concatenate((singular_points, real_break), axis=0) + important_points = np.concatenate((singular_points, np.zeros(2)), axis=0) mymat_xl = np.append(mymat_xl, important_points) if xlim is None: @@ -323,20 +324,14 @@ def sgrid_func(fig): ylocator = ax.get_yaxis().get_major_locator() xlocator = ax.get_yaxis().get_major_locator() - long_xaxis = xlocator()[-1] - xlocator()[0] - long_yaxis = ylocator()[-1] - ylocator()[0] - angules = np.arange(-90, 80, 15)*np.pi/180 - # radial lines + # zeta-constant lines y_over_x = np.tan(angules[1::])*ylocator()[-1]/xlocator()[-1] - ylim = ax.get_ylim() ytext_pos_lim = ylim[1]-(ylim[1]-ylim[0])*0.03 - xlim = ax.get_xlim() xtext_pos_lim = xlim[0]+(xlim[1]-xlim[0])*0.0 - index = 0 zeta = np.sin(np.pi/2-angules[1::]) @@ -363,6 +358,7 @@ def sgrid_func(fig): xtext_pos = -xtext_pos ax.annotate(an, textcoords='data', xy=[xtext_pos, ytext_pos], fontsize=8) index += 1 + ax.plot([0, 0], [ylim[0], ylim[1]], color='gray', linestyle='dashed', linewidth=0.5) angules = np.linspace(-90, 90, 20)*np.pi/180 for xt in xlocator(): From ebd39047108c35712cfc67fe819f3186e85c256c Mon Sep 17 00:00:00 2001 From: gonmolina Date: Fri, 31 Mar 2017 14:52:33 -0300 Subject: [PATCH 11/20] add grid arguments in docstring. --- control/rlocus.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/control/rlocus.py b/control/rlocus.py index f76050aac..1edb63011 100644 --- a/control/rlocus.py +++ b/control/rlocus.py @@ -59,7 +59,7 @@ # Main function: compute a root locus diagram def root_locus(sys, kvect=None, xlim=None, ylim=None, plotstr='-', Plot=True, - PrintGain=True, grid = False): + PrintGain=True, grid=False): """Root locus plot Calculate the root locus by finding the roots of 1+k*TF(s) @@ -77,10 +77,12 @@ def root_locus(sys, kvect=None, xlim=None, ylim=None, plotstr='-', Plot=True, ylim : tuple or list, optional control of y-axis range Plot : boolean, optional (default = True) - If True, plot magnitude and phase + If True, plot root locus diagram. PrintGain: boolean (default = True) If True, report mouse clicks when close to the root-locus branches, calculate gain, damping and print + grid: boolean (default = False) + If True plot s-plane grid. Returns ------- From de87bb0ff1b8bace2efc78efd45d19c81bd5affc Mon Sep 17 00:00:00 2001 From: gonmolina Date: Sun, 2 Apr 2017 12:22:01 -0300 Subject: [PATCH 12/20] reorder code that calculate defaults damp coefficents constant lines and wn constant lines. grids ok in most of the examples. --- control/rlocus.py | 69 +++++++++++++++++++++++++++++------------------ 1 file changed, 43 insertions(+), 26 deletions(-) diff --git a/control/rlocus.py b/control/rlocus.py index 1edb63011..9e446ad50 100644 --- a/control/rlocus.py +++ b/control/rlocus.py @@ -139,7 +139,6 @@ def root_locus(sys, kvect=None, xlim=None, ylim=None, plotstr='-', Plot=True, ax.set_ylabel('Imaginary') if grid: sgrid_func(f) - return mymat, kvect @@ -162,7 +161,7 @@ def _default_gains(num, den, xlim, ylim): mymat_xl = mymat singular_points = np.concatenate((num.roots, den.roots), axis=0) important_points = np.concatenate((singular_points, real_break), axis=0) - important_points = np.concatenate((singular_points, np.zeros(2)), axis=0) + important_points = np.concatenate((important_points, np.zeros(2)), axis=0) mymat_xl = np.append(mymat_xl, important_points) if xlim is None: @@ -267,6 +266,7 @@ def _systopoly1d(sys): # Check to see if num, den are already polynomials; otherwise convert if (not isinstance(nump, poly1d)): nump = poly1d(nump) + if (not isinstance(denp, poly1d)): denp = poly1d(denp) @@ -321,55 +321,72 @@ def _RLFeedbackClicks(event, sys): (s.real, s.imag, K.real, -1 * s.real / abs(s))) -def sgrid_func(fig): +def sgrid_func(fig, zeta=None, wn=None): ax = fig.gca() ylocator = ax.get_yaxis().get_major_locator() - xlocator = ax.get_yaxis().get_major_locator() + xlocator = ax.get_xaxis().get_major_locator() - angules = np.arange(-90, 80, 15)*np.pi/180 + if zeta is None: + zeta = _default_zetas(xlocator(), ylocator()) + + angules = [] + for z in zeta: + if (z >= 1e-4) & (z < 1): + angules.append(np.pi/2 + np.arcsin(z)) + else: + zeta.remove(z) + y_over_x = np.tan(angules) # zeta-constant lines - y_over_x = np.tan(angules[1::])*ylocator()[-1]/xlocator()[-1] ylim = ax.get_ylim() ytext_pos_lim = ylim[1]-(ylim[1]-ylim[0])*0.03 xlim = ax.get_xlim() xtext_pos_lim = xlim[0]+(xlim[1]-xlim[0])*0.0 index = 0 - zeta = np.sin(np.pi/2-angules[1::]) for yp in y_over_x: ax.plot([0, xlocator()[0]], [0, yp*xlocator()[0]], color='gray', linestyle='dashed', linewidth=0.5) + ax.plot([0, xlocator()[0]], [0, -yp * xlocator()[0]], color='gray', + linestyle='dashed', linewidth=0.5) an = "%.2f" % zeta[index] - if yp > 0: - xtext_pos = -1/yp * ylim[1] - ytext_pos = -yp * xtext_pos_lim - if np.abs(xtext_pos) > np.abs(xtext_pos_lim): - xtext_pos = xtext_pos_lim - else: - ytext_pos = ytext_pos_lim - ax.annotate(an, textcoords='data', xy=[xtext_pos, ytext_pos], fontsize=8) - elif yp < 0: - xtext_pos = -1/yp * ylim[1] + if yp < 0: + xtext_pos = 1/yp * ylim[1] ytext_pos = yp * xtext_pos_lim if np.abs(xtext_pos) > np.abs(xtext_pos_lim): xtext_pos = xtext_pos_lim - ytext_pos = - ytext_pos else: - ytext_pos = ylim[0] - xtext_pos = -xtext_pos + ytext_pos = ytext_pos_lim ax.annotate(an, textcoords='data', xy=[xtext_pos, ytext_pos], fontsize=8) index += 1 ax.plot([0, 0], [ylim[0], ylim[1]], color='gray', linestyle='dashed', linewidth=0.5) angules = np.linspace(-90, 90, 20)*np.pi/180 - for xt in xlocator(): - if xt < 0: - yp = np.sin(angules)*np.abs(xt) - xp = -np.cos(angules)*np.abs(xt) + if wn is None: + wn = _default_wn(xlocator(), ylocator()) + + for om in wn: + if om < 0: + yp = np.sin(angules)*np.abs(om) + xp = -np.cos(angules)*np.abs(om) ax.plot(xp, yp, color='gray', linestyle='dashed', linewidth=0.5) - an = "%.2f" % -xt - ax.annotate(an, textcoords='data', xy=[xt, 0], fontsize=8) + an = "%.2f" % -om + ax.annotate(an, textcoords='data', xy=[om, 0], fontsize=8) + + +def _default_zetas(xloc, yloc): + """Return default list of dumps coefficients""" + # TODO: smart selection on zetas to draw in root locus plot + angules = np.arange(0, 80, 15) * np.pi / 180 + zeta = np.sin(np.pi/2 - angules[1::]) + return zeta.tolist() + + +def _default_wn(xloc, yloc): + """Return default wn for root locus plot""" + # TODO: better selection of wn (up to maximum ylim with same separation in xloc) + wn = xloc + return wn rlocus = root_locus From 5438746ba1b7ce69bf9b9f26d727c5fe79c7b893 Mon Sep 17 00:00:00 2001 From: gonmolina Date: Sun, 2 Apr 2017 15:31:21 -0300 Subject: [PATCH 13/20] formatting code to make easier sgrid() function in future. --- control/rlocus.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/control/rlocus.py b/control/rlocus.py index 9e446ad50..248991f87 100644 --- a/control/rlocus.py +++ b/control/rlocus.py @@ -138,7 +138,7 @@ def root_locus(sys, kvect=None, xlim=None, ylim=None, plotstr='-', Plot=True, ax.set_xlabel('Real') ax.set_ylabel('Imaginary') if grid: - sgrid_func(f) + _sgrid_func() return mymat, kvect @@ -321,7 +321,9 @@ def _RLFeedbackClicks(event, sys): (s.real, s.imag, K.real, -1 * s.real / abs(s))) -def sgrid_func(fig, zeta=None, wn=None): +def _sgrid_func(fig=None, zeta=None, wn=None): + if fig is None: + fig = pylab.gcf() ax = fig.gca() ylocator = ax.get_yaxis().get_major_locator() xlocator = ax.get_xaxis().get_major_locator() From 1e356b8515436a61dd443b1468bcd702a07d4437 Mon Sep 17 00:00:00 2001 From: gonmolina Date: Thu, 8 Jun 2017 18:11:54 -0300 Subject: [PATCH 14/20] trying that algorithm work with 0 degrees root locus with equal number of poles and zeros --- control/rlocus.py | 30 ++++++++++++++++++++++++------ 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/control/rlocus.py b/control/rlocus.py index 248991f87..36dde3ad1 100644 --- a/control/rlocus.py +++ b/control/rlocus.py @@ -127,6 +127,8 @@ def root_locus(sys, kvect=None, xlim=None, ylim=None, plotstr='-', Plot=True, ax.plot(real(zeros), imag(zeros), 'o') # Now plot the loci + infinity_roots=np.where(mymat.T == np.inf) + print(infinity_roots) for col in mymat.T: ax.plot(real(col), imag(col), plotstr) @@ -163,12 +165,17 @@ def _default_gains(num, den, xlim, ylim): important_points = np.concatenate((singular_points, real_break), axis=0) important_points = np.concatenate((important_points, np.zeros(2)), axis=0) mymat_xl = np.append(mymat_xl, important_points) + false_gain = den.coeffs[0] / num.coeffs[0] - if xlim is None: + if xlim is None and false_gain > 0: x_tolerance = 0.05 * (np.max(np.max(np.real(mymat_xl))) - np.min(np.min(np.real(mymat_xl)))) xlim = _ax_lim(mymat_xl) + elif xlim is None and false_gain < 0: + xlim = _ax_lim(important_points) + x_tolerance = 0.05 * (np.max(np.max(np.real(mymat_xl))) - np.min(np.min(np.real(mymat_xl)))) else: x_tolerance = 0.05 * (xlim[1] - xlim[0]) + if ylim is None: y_tolerance = 0.05 * (np.max(np.max(np.imag(mymat_xl))) - np.min(np.min(np.imag(mymat_xl)))) ylim = _ax_lim(mymat_xl * 1j) @@ -178,6 +185,7 @@ def _default_gains(num, den, xlim, ylim): tolerance = np.max([x_tolerance, y_tolerance]) distance_points = np.abs(np.diff(mymat, axis=0)) indexes_too_far = np.where(distance_points > tolerance) + while (indexes_too_far[0].size > 0) & (kvect.size < 5000): for index in indexes_too_far[0]: new_gains = np.linspace(kvect[index], kvect[index+1], 5) @@ -185,8 +193,10 @@ def _default_gains(num, den, xlim, ylim): kvect = np.insert(kvect, index+1, new_gains[1:4]) mymat = np.insert(mymat, index+1, new_points, axis=0) mymat = _RLSortRoots(mymat) - distance_points = np.abs(np.diff(mymat, axis=0)) - indexes_too_far = np.where(distance_points > tolerance) + distance_points = np.abs(np.diff(mymat, axis=0))>tolerance + points_in_figure = np.logical_and(mymat[1:]>xlim[0], mymat[1:] 0: asymp_center = (np.sum(den.roots) - np.sum(num.roots))/asymp_number distance_max = 2 * np.max(np.abs(important_points - asymp_center)) asymp_angles = (2 * np.arange(0, asymp_number)-1) * np.pi / asymp_number - farthest_points = asymp_center + distance_max * np.exp(asymp_angles * 1j) # farthest points over asymptotes + if false_gain > 0: + farthest_points = asymp_center + distance_max * np.exp(asymp_angles * 1j) # farthest points over asymptotes + else: + asymp_angles = asymp_angles + np.pi + farthest_points = asymp_center + distance_max * np.exp(asymp_angles * 1j) # farthest points over asymptotes kmax_asymp = -den(farthest_points) / num(farthest_points) else: - kmax_asymp = [den.coeffs[0] / num.coeffs[0] * 3] + kmax_asymp = np.abs([den.coeffs[0] / num.coeffs[0] * 3]) kmax = np.max(np.concatenate((np.real(kmax_asymp), k_break_points), axis=0)) return kmax @@ -277,13 +292,16 @@ def _RLFindRoots(nump, denp, kvect): """Find the roots for the root locus.""" # Convert numerator and denominator to polynomials if they aren't - roots = [] for k in kvect: curpoly = denp + k * nump curroots = curpoly.r + if len(curroots) < denp.order: + curroots = np.insert(curroots, len(curroots), np.inf) # if i have less poles than open loop is becuase i have one in infinity + curroots.sort() roots.append(curroots) + mymat = row_stack(roots) return mymat From 6c813ce107cc121ee96e9cafa9c280c8e5b9cc38 Mon Sep 17 00:00:00 2001 From: gonmolina Date: Tue, 13 Jun 2017 18:41:09 -0300 Subject: [PATCH 15/20] adding some support for 0 degrees root locus. better selection of sgrid zetas and omega --- control/rlocus.py | 78 +++++++++++++++++++++++++++++------------------ 1 file changed, 49 insertions(+), 29 deletions(-) diff --git a/control/rlocus.py b/control/rlocus.py index 36dde3ad1..376c40a47 100644 --- a/control/rlocus.py +++ b/control/rlocus.py @@ -127,8 +127,6 @@ def root_locus(sys, kvect=None, xlim=None, ylim=None, plotstr='-', Plot=True, ax.plot(real(zeros), imag(zeros), 'o') # Now plot the loci - infinity_roots=np.where(mymat.T == np.inf) - print(infinity_roots) for col in mymat.T: ax.plot(real(col), imag(col), plotstr) @@ -166,18 +164,25 @@ def _default_gains(num, den, xlim, ylim): important_points = np.concatenate((important_points, np.zeros(2)), axis=0) mymat_xl = np.append(mymat_xl, important_points) false_gain = den.coeffs[0] / num.coeffs[0] + if false_gain < 0 and not den.order > num.order: + raise ValueError("Not implemented support for 0 degrees root " + "locus with equal order of numerator and denominator.") if xlim is None and false_gain > 0: - x_tolerance = 0.05 * (np.max(np.max(np.real(mymat_xl))) - np.min(np.min(np.real(mymat_xl)))) + x_tolerance = 0.03 * (np.max(np.real(mymat_xl)) - np.min(np.real(mymat_xl))) xlim = _ax_lim(mymat_xl) elif xlim is None and false_gain < 0: - xlim = _ax_lim(important_points) - x_tolerance = 0.05 * (np.max(np.max(np.real(mymat_xl))) - np.min(np.min(np.real(mymat_xl)))) + axmin = np.min(np.real(important_points))-(np.max(np.real(important_points))-np.min(np.real(important_points))) + axmin = np.min(np.array([axmin, np.min(np.real(mymat_xl))])) + axmax = np.max(np.real(important_points))+np.max(np.real(important_points))-np.min(np.real(important_points)) + axmax = np.max(np.array([axmax, np.max(np.real(mymat_xl))])) + xlim = [axmin, axmax] + x_tolerance = 0.05 * (axmax - axmin) else: x_tolerance = 0.05 * (xlim[1] - xlim[0]) if ylim is None: - y_tolerance = 0.05 * (np.max(np.max(np.imag(mymat_xl))) - np.min(np.min(np.imag(mymat_xl)))) + y_tolerance = 0.05 * (np.max(np.imag(mymat_xl)) - np.min(np.imag(mymat_xl))) ylim = _ax_lim(mymat_xl * 1j) else: y_tolerance = 0.05 * (ylim[1] - ylim[0]) @@ -192,10 +197,10 @@ def _default_gains(num, den, xlim, ylim): new_points = _RLFindRoots(num, den, new_gains[1:4]) kvect = np.insert(kvect, index+1, new_gains[1:4]) mymat = np.insert(mymat, index+1, new_points, axis=0) + mymat = _RLSortRoots(mymat) - distance_points = np.abs(np.diff(mymat, axis=0))>tolerance - points_in_figure = np.logical_and(mymat[1:]>xlim[0], mymat[1:] tolerance # distance between points + indexes_too_far = np.where(distance_points) new_gains = np.hstack((np.linspace(kvect[-1], kvect[-1]*200, 10))) new_points = _RLFindRoots(num, den, new_gains[1:10]) @@ -225,8 +230,8 @@ def _break_points(num, den): def _ax_lim(mymat): """Utility to get the axis limits""" - axmin = np.min(np.min(np.real(mymat))) - axmax = np.max(np.max(np.real(mymat))) + axmin = np.min(np.real(mymat)) + axmax = np.max(np.real(mymat)) if axmax != axmin: deltax = (axmax - axmin) * 0.02 else: @@ -244,18 +249,18 @@ def _k_max(num, den, real_break_points, k_break_points): if asymp_number > 0: asymp_center = (np.sum(den.roots) - np.sum(num.roots))/asymp_number - distance_max = 2 * np.max(np.abs(important_points - asymp_center)) + distance_max = 4 * np.max(np.abs(important_points - asymp_center)) asymp_angles = (2 * np.arange(0, asymp_number)-1) * np.pi / asymp_number if false_gain > 0: farthest_points = asymp_center + distance_max * np.exp(asymp_angles * 1j) # farthest points over asymptotes else: asymp_angles = asymp_angles + np.pi farthest_points = asymp_center + distance_max * np.exp(asymp_angles * 1j) # farthest points over asymptotes - kmax_asymp = -den(farthest_points) / num(farthest_points) + kmax_asymp = np.real(np.abs(den(farthest_points) / num(farthest_points))) else: - kmax_asymp = np.abs([den.coeffs[0] / num.coeffs[0] * 3]) + kmax_asymp = np.abs([np.abs(den.coeffs[0]) / np.abs(num.coeffs[0]) * 3]) - kmax = np.max(np.concatenate((np.real(kmax_asymp), k_break_points), axis=0)) + kmax = np.max(np.concatenate((np.real(kmax_asymp), np.real(k_break_points)), axis=0)) return kmax @@ -297,7 +302,8 @@ def _RLFindRoots(nump, denp, kvect): curpoly = denp + k * nump curroots = curpoly.r if len(curroots) < denp.order: - curroots = np.insert(curroots, len(curroots), np.inf) # if i have less poles than open loop is becuase i have one in infinity + curroots = np.insert(curroots, len(curroots), np.inf) # if i have less poles than open loop is because i + # have one in infinity curroots.sort() roots.append(curroots) @@ -346,22 +352,24 @@ def _sgrid_func(fig=None, zeta=None, wn=None): ylocator = ax.get_yaxis().get_major_locator() xlocator = ax.get_xaxis().get_major_locator() + ylim = ax.get_ylim() + ytext_pos_lim = ylim[1] - (ylim[1] - ylim[0]) * 0.03 + xlim = ax.get_xlim() + xtext_pos_lim = xlim[0] + (xlim[1] - xlim[0]) * 0.0 + if zeta is None: - zeta = _default_zetas(xlocator(), ylocator()) + zeta = _default_zetas(xlim, ylim) angules = [] for z in zeta: - if (z >= 1e-4) & (z < 1): + if (z >= 1e-4) & (z <= 1): angules.append(np.pi/2 + np.arcsin(z)) else: zeta.remove(z) y_over_x = np.tan(angules) # zeta-constant lines - ylim = ax.get_ylim() - ytext_pos_lim = ylim[1]-(ylim[1]-ylim[0])*0.03 - xlim = ax.get_xlim() - xtext_pos_lim = xlim[0]+(xlim[1]-xlim[0])*0.0 + index = 0 for yp in y_over_x: @@ -383,7 +391,7 @@ def _sgrid_func(fig=None, zeta=None, wn=None): angules = np.linspace(-90, 90, 20)*np.pi/180 if wn is None: - wn = _default_wn(xlocator(), ylocator()) + wn = _default_wn(xlocator(), ylim) for om in wn: if om < 0: @@ -395,18 +403,30 @@ def _sgrid_func(fig=None, zeta=None, wn=None): ax.annotate(an, textcoords='data', xy=[om, 0], fontsize=8) -def _default_zetas(xloc, yloc): +def _default_zetas(xlim, ylim): """Return default list of dumps coefficients""" - # TODO: smart selection on zetas to draw in root locus plot - angules = np.arange(0, 80, 15) * np.pi / 180 - zeta = np.sin(np.pi/2 - angules[1::]) + sep1 = -xlim[0]/4 + ang1 = [np.arctan((sep1*i)/ylim[1]) for i in np.arange(1,4,1)] + sep2 = ylim[1] / 3 + ang2 = [np.arctan(-xlim[0]/(ylim[1]-sep2*i)) for i in np.arange(1,3,1)] + + angules = np.concatenate((ang1, ang2)) + angules = np.insert(angules, len(angules), np.pi/2) + zeta = np.sin(angules) return zeta.tolist() -def _default_wn(xloc, yloc): +def _default_wn(xloc, ylim): """Return default wn for root locus plot""" - # TODO: better selection of wn (up to maximum ylim with same separation in xloc) + wn = xloc + sep = xloc[1]-xloc[0] + while np.abs(wn[0]) < ylim[1]: + wn = np.insert(wn, 0, wn[0]-sep) + + while len(wn)>7: + wn = wn[0:-1:2] + return wn rlocus = root_locus From 6e4d14f105c75dbbb0a4c1d121c4f82bb5492d5e Mon Sep 17 00:00:00 2001 From: gonmolina Date: Tue, 13 Jun 2017 19:02:16 -0300 Subject: [PATCH 16/20] add book reference of root locus calculation. fix a minor bug. --- control/rlocus.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/control/rlocus.py b/control/rlocus.py index 376c40a47..19941262c 100644 --- a/control/rlocus.py +++ b/control/rlocus.py @@ -143,7 +143,11 @@ def root_locus(sys, kvect=None, xlim=None, ylim=None, plotstr='-', Plot=True, def _default_gains(num, den, xlim, ylim): - """Unsupervised gains calculation for root locus plot""" + """Unsupervised gains calculation for root locus plot. + + References: + Ogata, K. (2002). Modern control engineering (4th ed.). Upper Saddle River, NJ : New Delhi: Prentice Hall..""" + k_break, real_break = _break_points(num, den) kmax = _k_max(num, den, real_break, k_break) kvect = np.hstack((np.linspace(0, kmax, 50), np.real(k_break))) @@ -169,7 +173,7 @@ def _default_gains(num, den, xlim, ylim): "locus with equal order of numerator and denominator.") if xlim is None and false_gain > 0: - x_tolerance = 0.03 * (np.max(np.real(mymat_xl)) - np.min(np.real(mymat_xl))) + x_tolerance = 0.05 * (np.max(np.real(mymat_xl)) - np.min(np.real(mymat_xl))) xlim = _ax_lim(mymat_xl) elif xlim is None and false_gain < 0: axmin = np.min(np.real(important_points))-(np.max(np.real(important_points))-np.min(np.real(important_points))) From a680c6a234e9a022b64b15ab1053b3c7a419bb6b Mon Sep 17 00:00:00 2001 From: gonmolina Date: Wed, 14 Jun 2017 10:07:30 -0300 Subject: [PATCH 17/20] fix minor bug when fartest point is zero. --- control/rlocus.py | 1 + 1 file changed, 1 insertion(+) diff --git a/control/rlocus.py b/control/rlocus.py index 19941262c..99783e39f 100644 --- a/control/rlocus.py +++ b/control/rlocus.py @@ -265,6 +265,7 @@ def _k_max(num, den, real_break_points, k_break_points): kmax_asymp = np.abs([np.abs(den.coeffs[0]) / np.abs(num.coeffs[0]) * 3]) kmax = np.max(np.concatenate((np.real(kmax_asymp), np.real(k_break_points)), axis=0)) + kmax = np.max([kmax, np.abs(false_gain)]) return kmax From 81b73a6b6e949e7666692821c45d834c8169958d Mon Sep 17 00:00:00 2001 From: gonmolina Date: Sat, 17 Jun 2017 15:15:09 -0300 Subject: [PATCH 18/20] fix minor bugs when that solves problems in paricular cases --- control/rlocus.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/control/rlocus.py b/control/rlocus.py index 99783e39f..57d1ee920 100644 --- a/control/rlocus.py +++ b/control/rlocus.py @@ -191,6 +191,7 @@ def _default_gains(num, den, xlim, ylim): else: y_tolerance = 0.05 * (ylim[1] - ylim[0]) + tolerance = np.max([x_tolerance, y_tolerance]) distance_points = np.abs(np.diff(mymat, axis=0)) indexes_too_far = np.where(distance_points > tolerance) @@ -206,7 +207,7 @@ def _default_gains(num, den, xlim, ylim): distance_points = np.abs(np.diff(mymat, axis=0)) > tolerance # distance between points indexes_too_far = np.where(distance_points) - new_gains = np.hstack((np.linspace(kvect[-1], kvect[-1]*200, 10))) + new_gains = np.hstack((np.linspace(kvect[-1], kvect[-1]*1e10, 10))) new_points = _RLFindRoots(num, den, new_gains[1:10]) kvect = np.append(kvect, new_gains[1:10]) mymat = np.concatenate((mymat, new_points), axis=0) From 62ed2aac9aaa0423e0aa5d781ec6cfb050a6c775 Mon Sep 17 00:00:00 2001 From: gonmolina Date: Fri, 30 Jun 2017 18:46:44 -0300 Subject: [PATCH 19/20] Fixed requested issues. --- control/rlocus.py | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/control/rlocus.py b/control/rlocus.py index 57d1ee920..15f39e5ba 100644 --- a/control/rlocus.py +++ b/control/rlocus.py @@ -157,7 +157,7 @@ def _default_gains(num, den, xlim, ylim): open_loop_poles = den.roots open_loop_zeros = num.roots - if (open_loop_zeros.size != 0) & (open_loop_zeros.size < open_loop_poles.size): + if (open_loop_zeros.size != 0) and (open_loop_zeros.size < open_loop_poles.size): open_loop_zeros_xl = np.append(open_loop_zeros, np.ones(open_loop_poles.size - open_loop_zeros.size) * open_loop_zeros[-1]) mymat_xl = np.append(mymat, open_loop_zeros_xl) @@ -191,12 +191,11 @@ def _default_gains(num, den, xlim, ylim): else: y_tolerance = 0.05 * (ylim[1] - ylim[0]) - tolerance = np.max([x_tolerance, y_tolerance]) distance_points = np.abs(np.diff(mymat, axis=0)) indexes_too_far = np.where(distance_points > tolerance) - while (indexes_too_far[0].size > 0) & (kvect.size < 5000): + while (indexes_too_far[0].size > 0) and (kvect.size < 5000): for index in indexes_too_far[0]: new_gains = np.linspace(kvect[index], kvect[index+1], 5) new_points = _RLFindRoots(num, den, new_gains[1:4]) @@ -246,7 +245,7 @@ def _ax_lim(mymat): def _k_max(num, den, real_break_points, k_break_points): - """" Calculation the maximum gain for the root locus shown in tne figure""" + """" Calculate the maximum gain for the root locus shown in the figure""" asymp_number = den.order - num.order singular_points = np.concatenate((num.roots, den.roots), axis=0) important_points = np.concatenate((singular_points, real_break_points), axis=0) @@ -308,8 +307,8 @@ def _RLFindRoots(nump, denp, kvect): curpoly = denp + k * nump curroots = curpoly.r if len(curroots) < denp.order: - curroots = np.insert(curroots, len(curroots), np.inf) # if i have less poles than open loop is because i - # have one in infinity + # if I have fewer poles than open loop, it is because i have one at infinity + curroots = np.insert(curroots, len(curroots), np.inf) curroots.sort() roots.append(curroots) @@ -355,7 +354,6 @@ def _sgrid_func(fig=None, zeta=None, wn=None): if fig is None: fig = pylab.gcf() ax = fig.gca() - ylocator = ax.get_yaxis().get_major_locator() xlocator = ax.get_xaxis().get_major_locator() ylim = ax.get_ylim() @@ -368,7 +366,7 @@ def _sgrid_func(fig=None, zeta=None, wn=None): angules = [] for z in zeta: - if (z >= 1e-4) & (z <= 1): + if (z >= 1e-4) and (z <= 1): angules.append(np.pi/2 + np.arcsin(z)) else: zeta.remove(z) @@ -430,7 +428,7 @@ def _default_wn(xloc, ylim): while np.abs(wn[0]) < ylim[1]: wn = np.insert(wn, 0, wn[0]-sep) - while len(wn)>7: + while len(wn) > 7: wn = wn[0:-1:2] return wn From 4eedde4bf8844a28f9a583d83fee4b3a450d5f1c Mon Sep 17 00:00:00 2001 From: gonmolina Date: Sat, 1 Jul 2017 00:09:48 -0300 Subject: [PATCH 20/20] fix build problem (numerical problem) --- control/rlocus.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/control/rlocus.py b/control/rlocus.py index 15f39e5ba..e22a31c25 100644 --- a/control/rlocus.py +++ b/control/rlocus.py @@ -206,9 +206,9 @@ def _default_gains(num, den, xlim, ylim): distance_points = np.abs(np.diff(mymat, axis=0)) > tolerance # distance between points indexes_too_far = np.where(distance_points) - new_gains = np.hstack((np.linspace(kvect[-1], kvect[-1]*1e10, 10))) - new_points = _RLFindRoots(num, den, new_gains[1:10]) - kvect = np.append(kvect, new_gains[1:10]) + new_gains = kvect[-1] * np.hstack((np.logspace(0, 3, 4))) + new_points = _RLFindRoots(num, den, new_gains[1:4]) + kvect = np.append(kvect, new_gains[1:4]) mymat = np.concatenate((mymat, new_points), axis=0) mymat = _RLSortRoots(mymat) return kvect, mymat, xlim, ylim @@ -265,7 +265,8 @@ def _k_max(num, den, real_break_points, k_break_points): kmax_asymp = np.abs([np.abs(den.coeffs[0]) / np.abs(num.coeffs[0]) * 3]) kmax = np.max(np.concatenate((np.real(kmax_asymp), np.real(k_break_points)), axis=0)) - kmax = np.max([kmax, np.abs(false_gain)]) + if np.abs(false_gain) > kmax: + kmax = np.abs(false_gain) return kmax @@ -410,9 +411,9 @@ def _sgrid_func(fig=None, zeta=None, wn=None): def _default_zetas(xlim, ylim): """Return default list of dumps coefficients""" sep1 = -xlim[0]/4 - ang1 = [np.arctan((sep1*i)/ylim[1]) for i in np.arange(1,4,1)] + ang1 = [np.arctan((sep1*i)/ylim[1]) for i in np.arange(1, 4, 1)] sep2 = ylim[1] / 3 - ang2 = [np.arctan(-xlim[0]/(ylim[1]-sep2*i)) for i in np.arange(1,3,1)] + ang2 = [np.arctan(-xlim[0]/(ylim[1]-sep2*i)) for i in np.arange(1, 3, 1)] angules = np.concatenate((ang1, ang2)) angules = np.insert(angules, len(angules), np.pi/2) pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy