我有以下scipy.lti对象,它基本上是一个表示LTI系统拉普拉斯变换的对象:
G_s = lti([1], [1, 2])
Run Code Online (Sandbox Code Playgroud)
如何将这样的传递函数与另一个函数相乘,例如:
H_s = lti([2], [1, 2])
#I_s = G_s * H_s <---- How to multiply this properly?
Run Code Online (Sandbox Code Playgroud)
我想我能做到
I_s = lti(np.polymul([1], [2]), np.polymul([1, 2], [1, 2]))
Run Code Online (Sandbox Code Playgroud)
但是,如果我想这样做:
#I_s = H_s / (1 + H_s) <---- Does not work since H_s is an lti object
Run Code Online (Sandbox Code Playgroud)
用scipy有一个简单的方法吗?
有趣的是,Scipy 似乎不提供该功能。另一种方法是将 LTI 系统转换为 Sympy 有理函数。Sympy 允许您轻松扩展和取消多项式:
from IPython.display import display
from scipy import signal
import sympy as sy
sy.init_printing() # LaTeX like pretty printing for IPython
def lti_to_sympy(lsys, symplify=True):
""" Convert Scipy's LTI instance to Sympy expression """
s = sy.Symbol('s')
G = sy.Poly(lsys.num, s) / sy.Poly(lsys.den, s)
return sy.simplify(G) if symplify else G
def sympy_to_lti(xpr, s=sy.Symbol('s')):
""" Convert Sympy transfer function polynomial to Scipy LTI """
num, den = sy.simplify(xpr).as_numer_denom() # expressions
p_num_den = sy.poly(num, s), sy.poly(den, s) # polynomials
c_num_den = [sy.expand(p).all_coeffs() for p in p_num_den] # coefficients
l_num, l_den = [sy.lambdify((), c)() for c in c_num_den] # convert to floats
return signal.lti(l_num, l_den)
pG, pH, pGH, pIGH = sy.symbols("G, H, GH, IGH") # only needed for displaying
# Sample systems:
lti_G = signal.lti([1], [1, 2])
lti_H = signal.lti([2], [1, 0, 3])
# convert to Sympy:
Gs, Hs = lti_to_sympy(lti_G), lti_to_sympy(lti_H)
print("Converted LTI expressions:")
display(sy.Eq(pG, Gs))
display(sy.Eq(pH, Hs))
print("Multiplying Systems:")
GHs = sy.simplify(Gs*Hs).expand() # make sure polynomials are canceled and expanded
display(sy.Eq(pGH, GHs))
print("Closing the loop:")
IGHs = sy.simplify(GHs / (1+GHs)).expand()
display(sy.Eq(pIGH, IGHs))
print("Back to LTI:")
lti_IGH = sympy_to_lti(IGHs)
print(lti_IGH)
Run Code Online (Sandbox Code Playgroud)
输出是:
根据您对“简单”的定义,您应该考虑从 派生您自己的类lti,对您的传递函数实施必要的代数运算。这可能是最优雅的方法。
这是我对这个主题的看法:
from __future__ import division
from scipy.signal.ltisys import TransferFunction as TransFun
from numpy import polymul,polyadd
class ltimul(TransFun):
def __neg__(self):
return ltimul(-self.num,self.den)
def __floordiv__(self,other):
# can't make sense of integer division right now
return NotImplemented
def __mul__(self,other):
if type(other) in [int, float]:
return ltimul(self.num*other,self.den)
elif type(other) in [TransFun, ltimul]:
numer = polymul(self.num,other.num)
denom = polymul(self.den,other.den)
return ltimul(numer,denom)
def __truediv__(self,other):
if type(other) in [int, float]:
return ltimul(self.num,self.den*other)
if type(other) in [TransFun, ltimul]:
numer = polymul(self.num,other.den)
denom = polymul(self.den,other.num)
return ltimul(numer,denom)
def __rtruediv__(self,other):
if type(other) in [int, float]:
return ltimul(other*self.den,self.num)
if type(other) in [TransFun, ltimul]:
numer = polymul(self.den,other.num)
denom = polymul(self.num,other.den)
return ltimul(numer,denom)
def __add__(self,other):
if type(other) in [int, float]:
return ltimul(polyadd(self.num,self.den*other),self.den)
if type(other) in [TransFun, type(self)]:
numer = polyadd(polymul(self.num,other.den),polymul(self.den,other.num))
denom = polymul(self.den,other.den)
return ltimul(numer,denom)
def __sub__(self,other):
if type(other) in [int, float]:
return ltimul(polyadd(self.num,-self.den*other),self.den)
if type(other) in [TransFun, type(self)]:
numer = polyadd(polymul(self.num,other.den),-polymul(self.den,other.num))
denom = polymul(self.den,other.den)
return ltimul(numer,denom)
def __rsub__(self,other):
if type(other) in [int, float]:
return ltimul(polyadd(-self.num,self.den*other),self.den)
if type(other) in [TransFun, type(self)]:
numer = polyadd(polymul(other.num,self.den),-polymul(other.den,self.num))
denom = polymul(self.den,other.den)
return ltimul(numer,denom)
# sheer laziness: symmetric behaviour for commutative operators
__rmul__ = __mul__
__radd__ = __add__
Run Code Online (Sandbox Code Playgroud)
这定义了ltimul类,即lti加法、乘法、除法、减法和否定;二进制的也定义为整数和浮点数作为伙伴。
我以 Dietrich 的示例对其进行了测试:
G_s = ltimul([1], [1, 2])
H_s = ltimul([2], [1, 0, 3])
print(G_s*H_s)
print(G_s*H_s/(1+G_s*H_s))
Run Code Online (Sandbox Code Playgroud)
虽然GH很好地等于
ltimul(
array([ 2.]),
array([ 1., 2., 3., 6.])
)
Run Code Online (Sandbox Code Playgroud)
GH/(1+GH) 的最终结果不太漂亮:
ltimul(
array([ 2., 4., 6., 12.]),
array([ 1., 4., 10., 26., 37., 42., 48.])
)
Run Code Online (Sandbox Code Playgroud)
由于我对传递函数不是很熟悉,因此由于缺少一些简化,我不确定这与基于 sympy 的解决方案得到相同结果的可能性有多大。我发现它的lti行为已经出乎意料地很可疑:lti([1,2],[1,2])即使我怀疑这个函数是常数 1,也不会简化它的参数。所以我宁愿不猜测这个最终结果的正确性。
无论如何,主要信息是继承本身,因此上述实现中可能存在的错误希望只会带来轻微的不便。我对类定义也很不熟悉,所以我可能没有遵循上面的最佳实践。
在@ochurlaud 指出后,我最终重写了上面的内容,我的原文仅适用于 Python 2。原因是该/操作是由Python 2 中的__div__/实现的__rdiv__(并且是模棱两可的“经典除法”)。然而,在 Python 3 中,/(真除法)和//(底除法)之间存在区别,它们分别调用__truediv__和__floordiv__(以及它们的“正确”对应物)。的(除非其他操作实现它)。__future__进口先在上面的代码行触发甚至在Python 2中适当的Python 3的行为,所以在两个Python版本上述作品。由于地板(整数)除法对我们的类没有多大意义,我们明确表示它不能做任何事情//
人们还可以很容易地定义各自的__iadd__,__idiv__对等的就地操作+=,/=等等,分别。
| 归档时间: |
|
| 查看次数: |
3725 次 |
| 最近记录: |