Skip to content

Commit 4ec3219

Browse files
committed
Merge pull request #41 from JuliaControl/kalman
Kalman
2 parents 88b75d8 + 0cedddb commit 4ec3219

File tree

10 files changed

+97
-23
lines changed

10 files changed

+97
-23
lines changed

src/ControlSystems.jl

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ export LTISystem,
1515
dlyap,
1616
lqr,
1717
dlqr,
18+
kalman,
19+
dkalman,
1820
covar,
1921
norm,
2022
gram,
@@ -54,7 +56,10 @@ export LTISystem,
5456
evalfr,
5557
bode,
5658
nyquist,
57-
sigma
59+
sigma,
60+
# utilities
61+
numpoly,
62+
denpoly
5863

5964
import Plots
6065
import Base: +, -, *, /, (./), (==), (.+), (.-), (.*), (!=), isapprox, call, convert

src/synthesis.jl

Lines changed: 52 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,13 @@ function lqr(A, B, Q, R)
1717
return K
1818
end
1919

20+
@doc """`kalman(A, C, R1, R2)` kalman(sys, R1, R2)`
21+
22+
Calculate the optimal Kalman gain
23+
24+
""" ->
25+
kalman(A, C, R1,R2) = lqr(A',C',R1,R2)'
26+
2027
function lqr(sys::StateSpace, Q, R)
2128
if iscontinuous(sys)
2229
return lqr(sys.A, sys.B, Q, R)
@@ -25,6 +32,14 @@ function lqr(sys::StateSpace, Q, R)
2532
end
2633
end
2734

35+
function kalman(sys::StateSpace, R1,R2)
36+
if iscontinuous(sys)
37+
return lqr(sys.A', sys.C', R1,R2)'
38+
else
39+
return dlqr(sys.A', sys.C', R1,R2)'
40+
end
41+
end
42+
2843
@doc """`dlqr(A, B, Q, R)`, `dlqr(sys, Q, R)`
2944
3045
Calculate the optimal gain matrix `K` for the state-feedback law `u[k] = K*x[k]` that
@@ -39,6 +54,13 @@ function dlqr(A, B, Q, R)
3954
return K
4055
end
4156

57+
@doc """`dkalman(A, C, R1, R2)` kalman(sys, R1, R2)`
58+
59+
Calculate the optimal Kalman gain for discrete time systems
60+
61+
""" ->
62+
dkalman(A, C, R1,R2) = dlqr(A',C',R1,R2)'
63+
4264
@doc """`place(A, B, p)`, `place(sys::StateSpace, p)`
4365
4466
Calculate gain matrix `K` such that
@@ -83,14 +105,40 @@ feedback(L::TransferFunction) = L/(1+L)
83105
feedback(P::TransferFunction, C::TransferFunction) = feedback(P*C)
84106

85107

108+
"""
109+
`feedback(sys)`
110+
111+
`feedback(sys1,sys2)`
112+
113+
Forms the negative feedback interconnection
114+
```julia
115+
>-+ sys1 +-->
116+
| |
117+
(-)sys2 +
118+
```
119+
If no second system is given, negative identity feedback is assumed
120+
"""
121+
function feedback(sys::StateSpace)
122+
sys.ny != sys.nu && error("Use feedback(sys1::StateSpace,sys2::StateSpace) if sys.ny != sys.nu")
123+
feedback(sys,ss(eye(sys.ny)))
124+
end
125+
126+
function feedback(sys1::StateSpace,sys2::StateSpace)
127+
sum(abs(sys1.D)) != 0 && sum(abs(sys2.D)) != 0 && error("There can not be a direct term (D) in both sys1 and sys2")
128+
A = [sys1.A+sys1.B*(-sys2.D)*sys1.C sys1.B*(-sys2.C); sys2.B*sys1.C sys2.A+sys2.B*sys1.D*(-sys2.C)]
129+
B = [sys1.B; sys2.B*sys1.D]
130+
C = [sys1.C sys1.D*(-sys2.C)]
131+
ss(A,B,C,sys1.D)
132+
end
133+
134+
86135
"""
87136
`feedback2dof(P,R,S,T)` Return `BT/(AR+ST)` where B and A are the numerator and denomenator polynomials of `P` respectively
88137
`feedback2dof(B,A,R,S,T)` Return `BT/(AR+ST)`
89138
"""
90139
function feedback2dof(P::TransferFunction,R,S,T)
91-
if !issiso(P)
92-
error("Feedback not implemented for MIMO systems")
93-
end
94-
tf(conv(poly2vec(numpoly(P)[1]),T),zpconv(poly2vec(denpoly(P)[1]),R,poly2vec(numpoly(P)[1]),S))
140+
!issiso(P) && error("Feedback not implemented for MIMO systems")
141+
tf(conv(poly2vec(numpoly(P)[1]),T),zpconv(poly2vec(denpoly(P)[1]),R,poly2vec(numpoly(P)[1]),S))
95142
end
143+
96144
feedback2dof(B,A,R,S,T) = tf(conv(B,T),zpconv(A,R,B,S))

src/types/polys.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -178,11 +178,11 @@ function ==(p1::Poly, p2::Poly)
178178
end
179179
end
180180

181-
function isapprox(p1::Poly, p2::Poly)
181+
function isapprox(p1::Poly, p2::Poly; kwargs...)
182182
if length(p1) != length(p2)
183183
return false
184184
else
185-
return p1.a[p1.nzfirst:end] p2.a[p2.nzfirst:end]
185+
return isapprox(p1.a[p1.nzfirst:end], p2.a[p2.nzfirst:end]; kwargs...)
186186
end
187187
end
188188

src/types/sisogeneralized.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ SisoRational(expr::Number) = isa(expr,Real) ? SisoRational([expr],[1]) : error("
8888

8989
==(t1::SisoGeneralized, t2::SisoGeneralized) = (t1.expr == t2.expr)
9090

91-
#isapprox(t1::SisoRational, t2::SisoRational) = (t1.num ≈ t2.num && t1.den ≈ t2.den)
91+
isapprox(t1::SisoGeneralized, t2::SisoGeneralized; kwargs...) = error("isapprox not implemented for generalized tf")
9292

9393
+(t1::SisoGeneralized, t2::SisoGeneralized) = SisoGeneralized(:($(t1.expr) + $(t2.expr)))
9494
+(t::SisoGeneralized, n::Real) = SisoGeneralized(:($(t.expr) + $n))

src/types/sisotf.jl

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,9 @@ pole(t::SisoRational) = roots(t.den)
8585

8686
==(t1::SisoRational, t2::SisoRational) = (t1.num == t2.num && t1.den == t2.den)
8787
# We might want to consider alowing scaled num and den as equal
88-
isapprox(t1::SisoRational, t2::SisoRational) = (t1.num t2.num && t1.den t2.den)
88+
function isapprox(t1::SisoRational, t2::SisoRational; rtol::Real=sqrt(eps()), atol::Real=sqrt(eps()))
89+
isapprox(t1.num,t2.num, rtol=rtol, atol=atol) && isapprox(t1.den, t2.den, rtol=rtol, atol=atol)
90+
end
8991

9092
+(t1::SisoRational, t2::SisoRational) = SisoRational(t1.num*t2.den + t2.num*t1.den, t1.den*t2.den)
9193
+(t::SisoRational, n::Real) = SisoRational(t.num + n*t.den, t.den)

src/types/sisozpk.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -150,9 +150,9 @@ Base.length(t::SisoZpk) = max(length(t.z), length(t.p))
150150

151151

152152
==(t1::SisoZpk, t2::SisoZpk) = (t1-t2).k == 0.0
153-
function isapprox(t1::SisoZpk, t2::SisoZpk, res = sqrt(eps()))
153+
function isapprox(t1::SisoZpk, t2::SisoZpk; rtol::Real=sqrt(eps()), atol::Real=sqrt(eps()))
154154
tdiff = t1 - t2
155-
isapprox(tdiff.k, 0, atol=res)
155+
isapprox(tdiff.k, 0, atol=atol, rtol=rtol)
156156
end
157157

158158
function +(t1::SisoZpk, t2::SisoZpk)

src/types/tf2ss.jl

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -104,18 +104,28 @@ function balance_transform{R}(A::Matrix{R}, B::Matrix{R}, C::Matrix{R}, perm::Bo
104104
return T
105105
end
106106

107-
@doc """`sys = ss2tf(s::StateSpace)`, ` sys = ss2tf(A, B, C, Ts = 0, inames = "", onames = "")`
107+
@doc """`sys = ss2tf(s::StateSpace)`, ` sys = ss2tf(A, B, C, Ts = 0; inputnames = "", outputnames = "")`
108108
109109
Convert a `StateSpace` realization to a `TransferFunction`""" ->
110110
function ss2tf(s::StateSpace)
111-
return ss2tf(s.A, s.B, s.C, s.Ts, s.inputnames, s.outputnames)
111+
return ss2tf(s.A, s.B, s.C, s.Ts, inputnames=s.inputnames, outputnames=s.outputnames)
112112
end
113113

114-
function ss2tf(A, B, C, Ts = 0, inames = "", onames = "")
114+
function ss2tf(A, B, C, Ts = 0; inputnames = "", outputnames = "")
115+
nu,ny = size(B,2),size(C,1)
116+
ubernum = Matrix{Vector}(ny,nu)
117+
uberden = Matrix{Vector}(ny,nu)
118+
for i = 1:nu, j=1:ny
119+
ubernum[j,i],uberden[j,i] = sisoss2tf(A, B[:,i], C[j,:])
120+
end
121+
tf(ubernum,uberden, Ts, inputnames=inputnames, outputnames=outputnames)
122+
end
123+
124+
function sisoss2tf(A, B, C)
115125
charpolA = charpoly(A)
116126
numP = charpoly(A-B*C) - charpolA
117127
denP = charpolA
118-
return tf(numP[1:length(numP)], denP[1:length(denP)], Ts, inputnames=inames, outputnames=onames)
128+
return numP[1:length(numP)], denP[1:length(denP)]
119129
end
120130

121131
tf(sys::StateSpace) = ss2tf(sys)
@@ -124,7 +134,7 @@ zpk(sys::StateSpace) = zpk(ss2tf(sys))
124134
function charpoly(A)
125135
λ = eigvals(A);
126136
p = reduce(*,ControlSystems.Poly([1.]), ControlSystems.Poly[ControlSystems.Poly([1, -λᵢ]) for λᵢ in λ]);
127-
if maximum(imag(p[:])) < sqrt(eps())
137+
if maximum(imag(p[:])./(1+abs(real(p[:])))) < sqrt(eps())
128138
for i = 1:length(p)
129139
p[i] = real(p[i])
130140
end

src/types/transferfunction.jl

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ tzero(sys::SisoTf) = error("tzero is not implemented for type $(typeof(sys))")
115115

116116
==(a::SisoTf, b::SisoTf) = ==(promote(a,b)...)
117117
!=(a::SisoTf, b::SisoTf) = !(a==b)
118-
isapprox(a::SisoTf, b::SisoTf) = (promote(a,b)...)
118+
isapprox(a::SisoTf, b::SisoTf; kwargs...) = isapprox(promote(a,b)...; kwargs...)
119119
#####################################################################
120120
## Constructor Functions ##
121121
#####################################################################
@@ -360,12 +360,12 @@ function ==(t1::TransferFunction, t2::TransferFunction)
360360
end
361361

362362
## Approximate ##
363-
function isapprox(t1::TransferFunction, t2::TransferFunction)
363+
function isapprox(t1::TransferFunction, t2::TransferFunction; kwargs...)
364364
t1, t2 = promote(t1, t2)
365365
fieldsApprox = [:Ts, :ny, :nu, :matrix]
366366
fieldsEqual = [:inputnames, :outputnames]
367367
for field in fieldsApprox
368-
if !(getfield(t1, field) getfield(t2, field))
368+
if !(isapprox(getfield(t1, field), getfield(t2, field); kwargs...))
369369
return false
370370
end
371371
end
@@ -377,8 +377,8 @@ function isapprox(t1::TransferFunction, t2::TransferFunction)
377377
return true
378378
end
379379

380-
function isapprox{T<:SisoTf, S<:SisoTf}(t1::Array{T}, t2::Array{S})
381-
reduce(&, [isapprox(t1[i], t2[i]) for i in eachindex(t1)])
380+
function isapprox{T<:SisoTf, S<:SisoTf}(t1::Array{T}, t2::Array{S}; kwargs...)
381+
reduce(&, [isapprox(t1[i], t2[i]; kwargs...) for i in eachindex(t1)])
382382
end
383383

384384
## ADDITION ##

test/test_synthesis.jl

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,23 @@ using ControlSystems
44

55
P = tf(1,[1,1])
66
C = tf([1,1],[1,0])
7+
L = P*C
8+
Lsys = ss(L)
9+
710

811
B = [1]
912
A = [1,1]
1013
R = [1,1]
1114
S = [1]
1215
T = [1]
1316

14-
@test feedback(P,C) == feedback(P*C) == tf([1,2,1,0],[1,3,3,1,0])
17+
@test isapprox(minreal(feedback(P,C),1e-5), minreal(feedback(L),1e-5))
18+
@test isapprox(numpoly(minreal(feedback(L),1e-5))[1].a, numpoly(tf(1,[1,1]))[1].a)# This test is ugly, but numerical stability is poor for minreal
1519
@test feedback2dof(B,A,R,S,T) == tf(B.*T, conv(A,R) + [0;0;conv(B,S)])
1620
@test feedback2dof(P,R,S,T) == tf(B.*T, conv(A,R) + [0;0;conv(B,S)])
21+
@test isapprox(pole(minreal(tf(feedback(Lsys)),1e-5)) , pole(minreal(feedback(L),1e-5)), atol=1e-5)
22+
23+
@test_err feedback(ss(1),ss(1))
24+
@test_err feedback(ss(eye(2), ones(2,2), ones(1,2),0))
25+
1726
end

test/test_zpk.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ z = zpk("z", 0.005)
3232
@test C_022 == zpk(vecarray(Int64, 2, 2, [], [], [], []), vecarray(Int64, 2, 2, [], [], [], []), [4 0; 0 4])
3333
@test D_022 == zpk(vecarray(2, 2, [], [], [], []), vecarray(2, 2, [], [], [], []), [4 0; 0 4], 0.005)
3434
@test C_022 == [zpk(4) 0;0 4]
35-
#We might want to fix this
35+
#TODO We might want to fix this
3636
#@test D_022 == [zpk(4, 0.005) 0;0 4])
3737

3838
#TODO improve polynomial accuracy se these are equal

0 commit comments

Comments
 (0)