Description
The pole placement function place
fails in some simple situations.
This bug is related to the current implementation of place
, which is a wrapper for slicot sb01bd. If PR 176 is accepted, this bug report will refer to place_varga
instead.
Example of failure:
import numpy as np
import control as pc
A = np.array([[0, 1],[100, 0]])
B = np.array([[0],[1]])
Pdes = np.array([-20 + 10*1j, -20 - 10*1j])
K = pc.place(A, B, Pdes)
This raises the error (with python 2.7)
"ValueError: an attempt was made to place a complex conjugate pair on the location of a real eigenvalue"
The problem is related to how the alpha
parameter is computed when calling sb01bd
, which is calculated as
system_eigs = np.linalg.eig(A_mat)[0]
alpha = min(system_eigs.real);
Clearly, A has eigenvalues [-10, +10]. But np.lingalg.eigvals(A) reports
eigvals = [10.000000000000002, -9.9999999999999982], and so we have set
alpha = -9.9999999999999982
sb01bd
will only move eigenvalues that are greater than alpha
. It seems that sb01bd
must calculate the eigenvalues slightly differently than scipy, so it thinks it has only one eigenvalue to move (namely, the one at +10). This evidently corresponds to error 4.
If we fudge the alpha
parameter, and call the sb01bd wrapper directly, this works as expected
import numpy as np
from slycot import sb01bd
A = np.array([[0, 1],[100, 0]])
B = np.array([[0],[1]])
Pdes = np.array([-20 + 10*1j, -20 - 10*1j])
alpha = min(np.linalg.eigvals(A).real)*2;
A_z,w,nfp,nap,nup,K,Z = sb01bd(B.shape[0], B.shape[1],
len(Pdes), alpha, A, B, Pdes, 'C')
print 'Placed Poles: %s'%np.linalg.eigvals(A + B.dot(K))
# yields on my system:
# Placed Poles: [-20.+10.j -20.-10.j]
place() is not implemented for discrete time
sb01bd
can place poles for discrete time systems. However, the current wrapper assumes a continuous time system, both in how alpha
is calculated (it should be based on abs(eigvals)
for discrete systems) and by passing C
as the dico parameter. This is undocumented in the doc string.