bundles / scipy latest / scipy / signal / _ltisys / place_poles
function
scipy.signal._ltisys:place_poles
source: /scipy/signal/_ltisys.py :2689
Signature
def place_poles ( A , B , poles , method = YT , rtol = 0.001 , maxiter = 30 ) Summary
Compute K such that eigenvalues (A - dot(B, K))=poles.
Extended Summary
K is the gain matrix such as the plant described by the linear system AX+BU will have its closed-loop poles, i.e the eigenvalues A - B*K, as close as possible to those asked for in poles.
SISO, MISO and MIMO systems are supported.
Parameters
A, B: ndarrayState-space representation of linear system
AX + BU.poles: array_likeDesired real poles and/or complex conjugates poles. Complex poles are only supported with
method="YT"(default).method: {'YT', 'KNV0'}, optionalWhich method to choose to find the gain matrix K. One of:
'YT': Yang Tits
'KNV0': Kautsky, Nichols, Van Dooren update method 0
See References and Notes for details on the algorithms.
rtol: float, optionalAfter each iteration the determinant of the eigenvectors of
A - B*Kis compared to its previous value, when the relative error between these two values becomes lower thanrtolthe algorithm stops. Default is 1e-3.maxiter: int, optionalMaximum number of iterations to compute the gain matrix. Default is 30.
Returns
full_state_feedback: Bunch objectfull_state_feedback is composed of:
gain_matrix
gain_matrix
computed_poles
computed_poles
requested_poles
requested_poles
X
X
rtol
rtol
nb_iter
nb_iter
Notes
The Tits and Yang (YT), [2] paper is an update of the original Kautsky et al. (KNV) paper [1]. KNV relies on rank-1 updates to find the transfer matrix X such that X * diag(poles) = (A - B*K)*X, whereas YT uses rank-2 updates. This yields on average more robust solutions (see [2] pp 21-22), furthermore the YT algorithm supports complex poles whereas KNV does not in its original version. Only update method 0 proposed by KNV has been implemented here, hence the name 'KNV0'.
KNV extended to complex poles is used in Matlab's place function, YT is distributed under a non-free licence by Slicot under the name robpole. It is unclear and undocumented how KNV0 has been extended to complex poles (Tits and Yang claim on page 14 of their paper that their method can not be used to extend KNV to complex poles), therefore only YT supports them in this implementation.
As the solution to the problem of pole placement is not unique for MIMO systems, both methods start with a tentative transfer matrix which is altered in various way to increase its determinant. Both methods have been proven to converge to a stable solution, however depending on the way the initial transfer matrix is chosen they will converge to different solutions and therefore there is absolutely no guarantee that using 'KNV0' will yield results similar to Matlab's or any other implementation of these algorithms.
Using the default method 'YT' should be fine in most cases; 'KNV0' is only provided because it is needed by 'YT' in some specific cases. Furthermore 'YT' gives on average more robust results than 'KNV0' when abs(det(X)) is used as a robustness indicator.
[2] is available as a technical report on the following URL: https://hdl.handle.net/1903/5598
Array API Standard Support
place_poles has experimental support for Python Array API Standard compatible backends in addition to NumPy. Please consider testing these features by setting an environment variable SCIPY_ARRAY_API=1 and providing CuPy, PyTorch, JAX, or Dask arrays as array arguments. The following combinations of backend and device (or other capability) are supported.
==================== ==================== ==================== Library CPU GPU ==================== ==================== ==================== NumPy ✅ n/a CuPy n/a ✅ PyTorch ✅ ✅ JAX ✅ ✅ Dask ✅ n/a ==================== ==================== ====================
See
dev-arrayapifor more information.
Examples
A simple example demonstrating real pole placement using both KNV and YT algorithms. This is example number 1 from section 4 of the reference KNV publication ([1]_):import numpy as np from scipy import signal import matplotlib.pyplot as plt✓
A = np.array([[ 1.380, -0.2077, 6.715, -5.676 ], [-0.5814, -4.290, 0, 0.6750 ], [ 1.067, 4.273, -6.654, 5.893 ], [ 0.0480, 4.273, 1.343, -2.104 ]]) B = np.array([[ 0, 5.679 ], [ 1.136, 1.136 ], [ 0, 0, ], [-3.146, 0 ]]) P = np.array([-0.2, -0.5, -5.0566, -8.6659])✓
fsf1 = signal.place_poles(A, B, P, method='KNV0') fsf1.gain_matrix✓
fsf2 = signal.place_poles(A, B, P) # uses YT method fsf2.computed_poles✓
fsf3 = signal.place_poles(A, B, P, rtol=-1, maxiter=100)
✓fsf3.X
✗abs(np.linalg.det(fsf1.X)) < abs(np.linalg.det(fsf2.X)) abs(np.linalg.det(fsf2.X)) < abs(np.linalg.det(fsf3.X))✗
A = np.array([[ 0, 7/3., 0, 0 ], [ 0, 0, 0, 7/9. ], [ 0, 0, 0, 0 ], [ 0, 0, 0, 0 ]]) B = np.array([[ 0, 0 ], [ 0, 0 ], [ 1, 0 ], [ 0, 1 ]]) P = np.array([-3, -1, -2-1j, -2+1j]) / 3. fsf = signal.place_poles(A, B, P, method='YT')✓
t = np.linspace(0, 2*np.pi, 401)
✓plt.plot(np.cos(t), np.sin(t), 'k--') # unit circle plt.plot(fsf.requested_poles.real, fsf.requested_poles.imag, 'wo', label='Desired') plt.plot(fsf.computed_poles.real, fsf.computed_poles.imag, 'bx', label='Placed')✗
plt.grid()
✓plt.axis('image') plt.axis([-1.1, 1.1, -1.1, 1.1]) plt.legend(bbox_to_anchor=(1.05, 1), loc=2, numpoints=1)✗
Aliases
-
scipy.signal.place_poles