def dplace(A, B, poles, alpha=1e-6): """Set the poles of (A - BF) to poles. Args: A: numpy.matrix(n x n), The A matrix. B: numpy.matrix(n x m), The B matrix. poles: array(imaginary numbers), The poles to use. Complex conjugates poles must be in pairs. Raises: ValueError: Arguments were the wrong shape or there were too many poles. PolePlacementError: Pole placement failed. Returns: numpy.matrix(m x n), K """ # See http://www.icm.tu-bs.de/NICONET/doc/SB01BD.html for a description of the # fortran code that this is cleaning up the interface to. n = A.shape[0] if A.shape[1] != n: raise ValueError("A must be square") if B.shape[0] != n: raise ValueError("B must have the same number of states as A.") m = B.shape[1] num_poles = len(poles) if num_poles > n: raise ValueError("Trying to place more poles than states.") out = slycot.sb01bd(n=n, m=m, np=num_poles, alpha=alpha, A=A, B=B, w=numpy.array(poles), dico='D') A_z = numpy.matrix(out[0]) num_too_small_eigenvalues = out[2] num_assigned_eigenvalues = out[3] num_uncontrollable_eigenvalues = out[4] K = numpy.matrix(-out[5]) Z = numpy.matrix(out[6]) if num_too_small_eigenvalues != 0: raise PolePlacementError("Number of eigenvalues that are too small " "and are therefore unmodified is %d." % num_too_small_eigenvalues) if num_assigned_eigenvalues != num_poles: raise PolePlacementError("Did not place all the eigenvalues that were " "requested. Only placed %d eigenvalues." % num_assigned_eigenvalues) if num_uncontrollable_eigenvalues != 0: raise PolePlacementError("Found %d uncontrollable eigenvlaues." % num_uncontrollable_eigenvalues) return K
def place(A, B, p): """Place closed loop eigenvalues Parameters ---------- A : 2-d array Dynamics matrix B : 2-d array Input matrix p : 1-d list Desired eigenvalue locations Returns ------- K : 2-d array Gains such that A - B K has given eigenvalues Examples -------- >>> A = [[-1, -1], [0, 1]] >>> B = [[0], [1]] >>> K = place(A, B, [-2, -5]) """ # Make sure that SLICOT is installed try: from slycot import sb01bd except ImportError: raise ControlSlycot("can't find slycot module 'sb01bd'") # Convert the system inputs to NumPy arrays A_mat = np.array(A); B_mat = np.array(B); if (A_mat.shape[0] != A_mat.shape[1] or A_mat.shape[0] != B_mat.shape[0]): raise ControlDimension("matrix dimensions are incorrect") # Compute the system eigenvalues and convert poles to numpy array system_eigs = np.linalg.eig(A_mat)[0] placed_eigs = np.array(p); # SB01BD sets eigenvalues with real part less than alpha # We want to place all poles of the system => set alpha to minimum alpha = min(system_eigs.real); # Call SLICOT routine to place the eigenvalues A_z,w,nfp,nap,nup,F,Z = \ sb01bd(B_mat.shape[0], B_mat.shape[1], len(placed_eigs), alpha, A_mat, B_mat, placed_eigs, 'C'); # Return the gain matrix, with MATLAB gain convention return -F
def place_varg(A, B, Ns, Nu, P_real, P_imag): # import numpy.distutils.system_info as sysinfo # p = sysinfo.get_info('lapack_opt') # print(p) import numpy as np #np.show_config() # import numpy as np # import scipy.linalg as linalg import sys # #import ipdb # # old_path = '/usr/local/lib/python2.7/dist-packages/control-dev-py2.7.egg' # # paths = sys.path # # if old_path in paths: # # sys.path.remove(old_path) slycot_path = '/home/arnold/pythonBox/control_dev/slycot-rabraker/build/lib.linux-x86_64-2.7/' if slycot_path not in sys.path: sys.path.append(slycot_path) # # A = np.array([[0, 1],[100, 0]]) # # B = np.array([[0],[1]]) # # Pdes = np.array([-20 + 10*1j, -20 - 10*1j]) # # ####################################################### from slycot import sb01bd A = np.array(A) A = A.reshape((Ns, Ns), order='F') # A = np.array([[0.9904, -0.0897, 0.0399], # [0.1594, 0.9928, 0.0032], # [0, 0, 1.0000]]) B = np.array(B, order='F') B = B.reshape((Ns, Nu)) Pdes = np.array(P_real) + np.array(P_imag)*1j # # # SB01BD sets eigenvalues with real part less than alpha # # # We want to place all poles of the system => set alpha to minimum #alpha = min(np.linalg.eigvals(A).real)*0 alpha = 0.1 # # # # Call SLICOT routine to place the eigenvalues A_z,w,nfp,nap,nup,K,Z = sb01bd(B.shape[0], B.shape[1], len(Pdes), alpha, A, B, Pdes, 'D') #print 'Placed Poles: %s'%np.linalg.eigvals(A + B.dot(K)) import array return array.array('d', K.flatten())
def place_varga(A, B, p, dtime=False, alpha=None): """Place closed loop eigenvalues K = place_varga(A, B, p, dtime=False, alpha=None) Required Parameters ---------- A : 2D array_like Dynamics matrix B : 2D array_like Input matrix p : 1D array_like Desired eigenvalue locations Optional Parameters --------------- dtime : bool False for continuous time pole placement or True for discrete time. The default is dtime=False. alpha : double scalar If `dtime` is false then place_varga will leave the eigenvalues with real part less than alpha untouched. If `dtime` is true then place_varga will leave eigenvalues with modulus less than alpha untouched. By default (alpha=None), place_varga computes alpha such that all poles will be placed. Returns ------- K : 2D array (or matrix) Gain such that A - B K has eigenvalues given in p. Algorithm --------- This function is a wrapper for the slycot function sb01bd, which implements the pole placement algorithm of Varga [1]. In contrast to the algorithm used by place(), the Varga algorithm can place multiple poles at the same location. The placement, however, may not be as robust. [1] Varga A. "A Schur method for pole assignment." IEEE Trans. Automatic Control, Vol. AC-26, pp. 517-519, 1981. Notes ----- The return type for 2D arrays depends on the default class set for state space operations. See :func:`~control.use_numpy_matrix`. Examples -------- >>> A = [[-1, -1], [0, 1]] >>> B = [[0], [1]] >>> K = place_varga(A, B, [-2, -5]) See Also: -------- place, acker """ # Make sure that SLICOT is installed try: from slycot import sb01bd except ImportError: raise ControlSlycot("can't find slycot module 'sb01bd'") # Convert the system inputs to NumPy arrays A_mat = np.array(A) B_mat = np.array(B) if (A_mat.shape[0] != A_mat.shape[1] or A_mat.shape[0] != B_mat.shape[0]): raise ControlDimension("matrix dimensions are incorrect") # Compute the system eigenvalues and convert poles to numpy array system_eigs = np.linalg.eig(A_mat)[0] placed_eigs = np.atleast_1d(np.squeeze(np.asarray(p))) # Need a character parameter for SB01BD if dtime: DICO = 'D' else: DICO = 'C' if alpha is None: # SB01BD ignores eigenvalues with real part less than alpha # (if DICO='C') or with modulus less than alpha # (if DICO = 'D'). if dtime: # For discrete time, slycot only cares about modulus, so just make # alpha the smallest it can be. alpha = 0.0 else: # Choosing alpha=min_eig is insufficient and can lead to an # error or not having all the eigenvalues placed that we wanted. # Evidently, what python thinks are the eigs is not precisely # the same as what slicot thinks are the eigs. So we need some # numerical breathing room. The following is pretty heuristic, # but does the trick alpha = -2*abs(min(system_eigs.real)) elif dtime and alpha < 0.0: raise ValueError("Discrete time systems require alpha > 0") # Call SLICOT routine to place the eigenvalues A_z, w, nfp, nap, nup, F, Z = \ sb01bd(B_mat.shape[0], B_mat.shape[1], len(placed_eigs), alpha, A_mat, B_mat, placed_eigs, DICO) # Return the gain matrix, with MATLAB gain convention return _ssmatrix(-F)
# System matrices A = np.matrix([[1, -1, 1.], [1, -k / m, -b / m], [1, 1, 1]]) B = np.matrix([[0], [1 / m], [1]]) C = np.matrix([[1., 0, 1.]]) sys = ss(A, B, C, 0) # Python control may be used without slycot, for example for a pole placement. # Eigenvalue placement w = [-3, -2, -1] K = place(A, B, w) print("[python-control (from scipy)] K = ", K) print("[python-control (from scipy)] eigs = ", np.linalg.eig(A - B * K)[0]) # Before using one of its routine, check that slycot is installed. w = np.array([-3, -2, -1]) if slycot_check(): # Import routine sb01bd used for pole placement. from slycot import sb01bd n = 3 # Number of states m = 1 # Number of inputs npp = 3 # Number of placed eigen values alpha = 1 # Maximum threshold for eigen values dico = 'D' # Discrete system _, _, _, _, _, K, _ = sb01bd(n, m, npp, alpha, A, B, w, dico, tol=0.0, ldwork=None) print("[slycot] K = ", K) print("[slycot] eigs = ", np.linalg.eig(A + np.dot(B, K))[0]) else: print("Slycot is not installed.")
def place_varga(A, B, p): """Place closed loop eigenvalues K = place_varga(A, B, p) Parameters ---------- A : 2-d array Dynamics matrix B : 2-d array Input matrix p : 1-d list Desired eigenvalue locations Returns ------- K : 2-d array Gain such that A - B K has eigenvalues given in p. Algorithm --------- This function is a wrapper for the slycot function sb01bd, which implements the pole placement algorithm of Varga [1]. In contrast to the algorithm used by place(), the Varga algorithm can place multiple poles at the same location. The placement, however, may not be as robust. [1] Varga A. "A Schur method for pole assignment." IEEE Trans. Automatic Control, Vol. AC-26, pp. 517-519, 1981. Examples -------- >>> A = [[-1, -1], [0, 1]] >>> B = [[0], [1]] >>> K = place(A, B, [-2, -5]) See Also: -------- place, acker """ # Make sure that SLICOT is installed try: from slycot import sb01bd except ImportError: raise ControlSlycot("can't find slycot module 'sb01bd'") # Convert the system inputs to NumPy arrays A_mat = np.array(A); B_mat = np.array(B); if (A_mat.shape[0] != A_mat.shape[1] or A_mat.shape[0] != B_mat.shape[0]): raise ControlDimension("matrix dimensions are incorrect") # Compute the system eigenvalues and convert poles to numpy array system_eigs = np.linalg.eig(A_mat)[0] placed_eigs = np.array(p); # SB01BD sets eigenvalues with real part less than alpha # We want to place all poles of the system => set alpha to minimum alpha = min(system_eigs.real); # Call SLICOT routine to place the eigenvalues A_z,w,nfp,nap,nup,F,Z = \ sb01bd(B_mat.shape[0], B_mat.shape[1], len(placed_eigs), alpha, A_mat, B_mat, placed_eigs, 'C'); # Return the gain matrix, with MATLAB gain convention return -F
w = [-3, -2, -1] K = place(A, B, w) print("[python-control (from scipy)] K = ", K) print("[python-control (from scipy)] eigs = ", np.linalg.eig(A - B * K)[0]) # Before using one of its routine, check that slycot is installed. w = np.array([-3, -2, -1]) if slycot_check(): # Import routine sb01bd used for pole placement. from slycot import sb01bd n = 3 # Number of states m = 1 # Number of inputs npp = 3 # Number of placed eigen values alpha = 1 # Maximum threshold for eigen values dico = 'D' # Discrete system _, _, _, _, _, K, _ = sb01bd(n, m, npp, alpha, A, B, w, dico, tol=0.0, ldwork=None) print("[slycot] K = ", K) print("[slycot] eigs = ", np.linalg.eig(A + np.dot(B, K))[0]) else: print("Slycot is not installed.")
def place_varga(A, B, p, dtime=False, alpha=None): """Place closed loop eigenvalues K = place_varga(A, B, p, dtime=False, alpha=None) Required Parameters ---------- A : 2-d array Dynamics matrix B : 2-d array Input matrix p : 1-d list Desired eigenvalue locations Optional Parameters --------------- dtime: False for continuous time pole placement or True for discrete time. The default is dtime=False. alpha: double scalar If DICO='C', then place_varga will leave the eigenvalues with real real part less than alpha untouched. If DICO='D', the place_varga will leave eigenvalues with modulus less than alpha untouched. By default (alpha=None), place_varga computes alpha such that all poles will be placed. Returns ------- K : 2-d array Gain such that A - B K has eigenvalues given in p. Algorithm --------- This function is a wrapper for the slycot function sb01bd, which implements the pole placement algorithm of Varga [1]. In contrast to the algorithm used by place(), the Varga algorithm can place multiple poles at the same location. The placement, however, may not be as robust. [1] Varga A. "A Schur method for pole assignment." IEEE Trans. Automatic Control, Vol. AC-26, pp. 517-519, 1981. Examples -------- >>> A = [[-1, -1], [0, 1]] >>> B = [[0], [1]] >>> K = place_varga(A, B, [-2, -5]) See Also: -------- place, acker """ # Make sure that SLICOT is installed try: from slycot import sb01bd except ImportError: raise ControlSlycot("can't find slycot module 'sb01bd'") # Convert the system inputs to NumPy arrays A_mat = np.array(A) B_mat = np.array(B) if (A_mat.shape[0] != A_mat.shape[1] or A_mat.shape[0] != B_mat.shape[0]): raise ControlDimension("matrix dimensions are incorrect") # Compute the system eigenvalues and convert poles to numpy array system_eigs = np.linalg.eig(A_mat)[0] placed_eigs = np.array(p) # Need a character parameter for SB01BD if dtime: DICO = 'D' else: DICO = 'C' if alpha is None: # SB01BD ignores eigenvalues with real part less than alpha # (if DICO='C') or with modulus less than alpha # (if DICO = 'D'). if dtime: # For discrete time, slycot only cares about modulus, so just make # alpha the smallest it can be. alpha = 0.0 else: # Choosing alpha=min_eig is insufficient and can lead to an # error or not having all the eigenvalues placed that we wanted. # Evidently, what python thinks are the eigs is not precisely # the same as what slicot thinks are the eigs. So we need some # numerical breathing room. The following is pretty heuristic, # but does the trick alpha = -2*abs(min(system_eigs.real)) elif dtime and alpha < 0.0: raise ValueError("Need alpha > 0 when DICO='D'") # Call SLICOT routine to place the eigenvalues A_z,w,nfp,nap,nup,F,Z = \ sb01bd(B_mat.shape[0], B_mat.shape[1], len(placed_eigs), alpha, A_mat, B_mat, placed_eigs, DICO) # Return the gain matrix, with MATLAB gain convention return -F