Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

create pid subsystem problem? #25

Closed
hzgzh opened this issue May 18, 2020 · 10 comments
Closed

create pid subsystem problem? #25

hzgzh opened this issue May 18, 2020 · 10 comments

Comments

@hzgzh
Copy link

hzgzh commented May 18, 2020

I want to create a pid subsystem like this:

kp =1.0;ki=0.0;kd=0.0
adder=Adder((+,+,+))
inp = Gain(gain=1.0)
gain = Gain(gain=kp)
integral = Integrator(;ki=ki)
differentiator = Differentiator(;kd=kd)
connect!(inp.output,gain.input)
connect!(inp.output,integral.input)
connect!(inp.output,differentiator.input)
connect!(gain.output,adder.input[1])
connect!(integral.output,adder.input[2])
connect!(differentiator.output,adder.input[3])
pid=SubSystem([inp,gain,integral,differentiator,adder],inp.input,adder.output)
MethodError: no method matching Differentiator(::Int64; kd=0.0)
Closest candidates are:
  Differentiator(::Any; callbacks, name) at C:\Users\Administrator\.julia\packages\Jusdl\T9iGV\src\components\systems\staticsystems\staticsystems.jl:273 got unsupported keyword argument "kd"
  Differentiator() at C:\Users\Administrator\.julia\packages\Jusdl\T9iGV\src\components\systems\staticsystems\staticsystems.jl:273 got unsupported keyword argument "kd"

Stacktrace:
 [1] kwerr(::NamedTuple{(:kd,),Tuple{Float64}}, ::Type{T} where T, ::Int64) at .\error.jl:157
 [2] top-level scope at In[6]:6

how to fix it,does the subsystem can't support the component like integrator,diffentiator component?

@hzgzh
Copy link
Author

hzgzh commented May 18, 2020

modify differentiator = Differentiator(;kd=kd) to differentiator = Differentiator(),it is ok,can't modify kd of Differentiator

@zekeriyasari
Copy link
Owner

zekeriyasari commented May 18, 2020

Hi @hzgzh

the subsystem can't support the component like integrator,diffentiator component?

Of course, SubSystem supports components like Differentialtor, Integrator, etc.

As you pointed out, the problem is not related to SubSystem, but is related to Differentiator. Before you open this issue, the signature of Differentiator was Differentiator(kd; callbacks, name) which implies you should have constructed the Differentiator by Differentiator(kd). But, I changed the signature to Differentiator(;kd , callbacks, name). This signature is more convenient if we consider the signature of the Integrator

Now the code block you gave above works as you want. But do not forget to check out the master branch.

]add Jusdl#master

@hzgzh
Copy link
Author

hzgzh commented May 19, 2020

Hi@zekeriyasari
I write some script about pid,but can't terminate,can you check it?

using Jusdl
using UUIDsmacro def(name, code)
    quote
        macro ($(esc(name)))()
            esc($(Meta.quot(code)))
        end
    end
end
@def generic_component_fields begin 
    trigger::TR
    handshake::HS
    callbacks::CB
    name::Symbol
    id::UUID
end

@def generic_source_fields begin
    outputfunc::OF
    output::OB
    @generic_component_fields 
end

@def generic_system_fields begin
    input::IB
    output::OB
    @generic_component_fields
end

@def generic_sink_fields begin
    input::IB
    databuf::DB
    timebuf::TB
    plugin::PL
    @generic_component_fields
end

@def generic_static_system_fields begin
    outputfunc::OF 
    @generic_system_fields
end

@def generic_dynamic_system_fields begin 
    statefunc::SF 
    outputfunc::OF 
    state::ST 
    t::T
    integrator::IN
    @generic_system_fields 
end
kp =1.0;ki=0.1;kd=0.0
adder=Adder((+,+,+))
inp = Gain(gain=1.0)
gain = Gain(gain=kp)
integral = Integrator(;ki=ki)
differentiator = Differentiator()
connect!(inp.output,gain.input)
connect!(inp.output,integral.input)
connect!(inp.output,differentiator.input)
connect!(gain.output,adder.input[1])
connect!(integral.output,adder.input[2])
connect!(differentiator.output,adder.input[3])
pid=SubSystem([inp,gain,integral,differentiator,adder],inp.input,adder.output)
struct Limit{OF, IB, OB, TR, HS, CB, ULT,DLT} <: AbstractStaticSystem
    @generic_static_system_fields
    up::ULT
    dn::DLT
    function Limit(callbacks=nothing,name=Symbol();up=1,dn=-1)
        input = Inport() 
        output = Outport()
        trigger = Inpin() 
        handshake = Outpin{Bool}()
        function outputfunc(uu, tt)
            if uu>up
               return up
            elseif uu<dn
               return dn
            end
            return uu
        end
        new{typeof(outputfunc), typeof(input), typeof(output), typeof(trigger), typeof(handshake), typeof(callbacks), typeof(up), typeof(dn)}(outputfunc, input, output, trigger, handshake, callbacks, name, uuid4(), up, dn)
    end
end
model=Model(clock=Clock(0,0.01,10))
step=StepGenerator(;amplitude=1,delay=0,offset=0)
add=Adder((+,-))
in=Integrator(ki=5)
lm=Limit(;up=1,dn=-1)
writer=Writer()
addnode!(model,step,label=:step)
addnode!(model,add,label=:add)
addnode!(model,lm,label=:lm)
addnode!(model,in,label=:integrator)
addnode!(model,pid,label=:pid)
addnode!(model,writer,label=:writer)
addnode!(model, Memory(0.01, t0=0.0, dt=0.01, initial=zeros(1)), label=:mem)
addbranch!(model,:step=>:add,1=>1)
addbranch!(model,:pid=>:lm)
addbranch!(model,:lm=>:integrator)
addbranch!(model,:integrator=>:mem)
addbranch!(model,:mem=>:add,1=>2)
addbranch!(model,:integrator=>:writer)
sim=simulate!(model)

@hzgzh
Copy link
Author

hzgzh commented May 19, 2020

I forget link add to pid,I will try again

@zekeriyasari
Copy link
Owner

I tried to simulate the following control system given in this tutorial page.

Here is the full script.

# This is an example for PID control. 

using Jusdl 
using Plots 

# PID parameters 
kp, ki, kd = 350, 300, 50

function sfunc(dx, x, u, t)
    dx[1] = x[2] 
    dx[2] = -20 * x[1] - 10 * x[2] + u[1](t) 
end
ofunc(x, u, t) = x[1, :]
plant = ODESystem(sfunc, ofunc, zeros(2), 0., Inport(), Outport())

# Construct the model 
model = Model(clock=Clock(0., 0.01, 5.))
addnode!(model, StepGenerator() , label=:gen)
addnode!(model, Adder((+,-)) , label=:adder1)
addnode!(model, Gain(gain=kp) , label=:gain)
addnode!(model, Integrator(ki=ki) , label=:int)
addnode!(model, Differentiator(kd=kd), label=:dif)
addnode!(model, Adder((+,+,+)) , label=:adder2)
addnode!(model, plant, label=:plant)
addnode!(model, Writer(Inport(2)), label=:writer)

addbranch!(model, :gen => :adder1, 1 => 1)
addbranch!(model, :adder1 => :gain, 1 => 1)
addbranch!(model, :adder1 => :int, 1 => 1)
addbranch!(model, :adder1 => :dif, 1 => 1)
addbranch!(model, :gain => :adder2, 1 => 1)
addbranch!(model, :int => :adder2, 1 => 2)
addbranch!(model, :dif => :adder2, 1 => 3)
addbranch!(model, :adder2 => :plant, 1 => 1)
addbranch!(model, :plant => :adder1, 1 => 2)
addbranch!(model, :gen => :writer, 1 => 1)
addbranch!(model, :plant => :writer, 1 => 2)

# Simulate the model 
simulate!(model)

# Plot results 
t, x = read(getnode(model, :writer).component)
plot(t, x[:, 1], label=:r)
plot!(t, x[:, 2], label=:y)

pidcontrol

NOTE: that there is not SubSystem in the model construction given above. The pid controller is constructed explicitly. The reason is as follows: the control system has an algebraic loop and this loop must be broken automatically during the simulation. If the algebraic loop has SubSystem, then loop-breaking cannot be succeeded because of the structure of SubSystems. So to simulate the system given above, DO NOT place a subsystem in an algebraic loop. Instead, construct the model explicitly.

@zekeriyasari
Copy link
Owner

zekeriyasari commented May 19, 2020

If your want to implement pid controller as a SubSystem, you must break the algebraic loop by inserting a Memory component. This is given below.

using Jusdl 
using Plots 

# Time settings 
ti, dt, tf = 0., 0.01, 5.

# Construct PID 
kp, ki, kd = 350, 300, 50
gain = Gain(gain=kp)
int = Integrator(ki=ki)
dif = Differentiator(kd=kd)
adderpid = Adder((+, +, +))
connect!(gain.output, adderpid.input[1])
connect!(int.output, adderpid.input[2])
connect!(dif.output, adderpid.input[3])
pid = SubSystem([gain, int, dif, adderpid], [gain.input[1], dif.input[1], int.input[1]], adderpid.output)

# Contruct plant
function sfunc(dx, x, u, t)
    dx[1] = x[2] 
    dx[2] = -20 * x[1] - 10 * x[2] + u[1](t) 
end
ofunc(x, u, t) = x[1, :]
plant = ODESystem(sfunc, ofunc, zeros(2), 0., Inport(), Outport())

# Construct remaining components of the control system.
adder = Adder((+, -))
gen = StepGenerator() 
writer = Writer(Inport(2))
mem = Memory(dt)

# Construct the model 
model = Model(clock=Clock(ti, dt, tf))
addnode!(model, gen, label=:gen)
addnode!(model, adder, label=:adder)
addnode!(model, pid, label=:pid)
addnode!(model, plant, label=:plant)
addnode!(model, mem, label=:mem)
addnode!(model, writer, label=:writer)

addbranch!(model, :gen => :adder, 1 => 1)
addbranch!(model, :adder => :pid, 1 => 1)
addbranch!(model, :adder => :pid, 1 => 2)
addbranch!(model, :adder => :pid, 1 => 3)
addbranch!(model, :pid => :plant, 1 => 1)
addbranch!(model, :plant => :mem, 1 => 1)
addbranch!(model, :mem => :adder, 1 => 2)
addbranch!(model, :gen => :writer, 1 => 1)
addbranch!(model, :plant => :writer, 1 => 2)


# Simulate the model 
simulate!(model)

# Plot results 
t, x = read(getnode(model, :writer).component)
plot(t, x[:, 1], label=:r)
plot!(t, x[:, 2], label=:y)

pidcontrol_with_memory

@zekeriyasari
Copy link
Owner

You may take a look at here if you would like to,

@hzgzh
Copy link
Author

hzgzh commented May 21, 2020

hi @zekeriyasari

using Jusdl 
using Plots 

# Time settings 
ti, dt, tf = 0., 0.01, 5.
th=1; ul = 1.0; dl=-1.0
# Construct PID 
kp, ki, kd = 10, 0, 0
gain = Gain(gain=kp)
int = Integrator(ki=ki)
dif = Differentiator(kd=kd)
adderpid = Adder((+, +, +))
connect!(gain.output, adderpid.input[1])
connect!(int.output, adderpid.input[2])
connect!(dif.output, adderpid.input[3])
pid = SubSystem([gain, int, dif, adderpid], [gain.input[1], dif.input[1], int.input[1]], adderpid.output)

# Contruct plant
function sfunc(dx, x, u, t)
    u = u[1](t)
    if u > ul
        dx[1] = 1.0/th*ul
    elseif u < dl
        dx[1] = 1.0/th*dl
    else
        dx[1] = 1.0/th*u
    end
    dx
end
ofunc(x, u, t) = x

plant = ODESystem(sfunc, ofunc, zeros(1), 0., Inport(), Outport())

# Construct remaining components of the control system.
adder = Adder((+, -))
gen = StepGenerator(;amplitude=0.8,delay=1.0,offset=0.1) 
writer = Writer(Inport(2))
mem = Memory(dt)

# Construct the model 
model = Model()
addnode!(model, gen, label=:gen)
addnode!(model, adder, label=:adder)
addnode!(model, pid, label=:pid)
addnode!(model, plant, label=:plant)
addnode!(model, mem, label=:mem)
addnode!(model, writer, label=:writer)

addbranch!(model, :gen => :adder, 1 => 1)
addbranch!(model, :adder => :pid, 1 => 1)
addbranch!(model, :adder => :pid, 1 => 2)
addbranch!(model, :adder => :pid, 1 => 3)
addbranch!(model, :pid => :plant, 1 => 1)
addbranch!(model, :plant => :mem, 1 => 1)
addbranch!(model, :mem => :adder, 1 => 2)
addbranch!(model, :gen => :writer, 1 => 1)
addbranch!(model, :plant => :writer, 1 => 2)


# Simulate the model 
#@time simulate!(model)
@time simulate!(model,(ti,dt,tf)...)

according you example,I change my program,it's success,run time about 0.9s
but when I change tf to 10,run simulate!(model,(ti,dt,tf)...) again,It's can't terminal

@hzgzh
Copy link
Author

hzgzh commented May 21, 2020

another advise is to seperate simulate to two step,first phase to initialize model,second to simulate model,when using some simple component like "gain,integrator",only change component parameter,not initialize model again when simulate model,so reduce the running time when perform parameter estimate

I found this function

function _simulate(sim::Simulation, reportsim::Bool, withbar::Bool, breakpoints::Vector{Int})
    model = sim.model
    @siminfo "Started simulation..."
    sim.state = :running

    @siminfo "Inspecting model..."
    inspect!(model, breakpoints)
    @siminfo "Done."

    @siminfo "Initializing the model..."
    initialize!(model)
    @siminfo "Done..."

    @siminfo "Running the simulation..."
    run!(model, withbar)
    sim.state = :done
    sim.retcode = :success
    @siminfo "Done..."
    
    @siminfo "Terminating the simulation..."
    terminate!(model)
    @siminfo "Done."

    reportsim && report(sim)
    return sim
end

if terminate!(model) is called,second start simulate may be terminate,that is right?

@zekeriyasari
Copy link
Owner

zekeriyasari commented May 22, 2020

according you example,I change my program,it's success,run time about 0.9s
but when I change tf to 10,run simulate!(model,(ti,dt,tf)...) again,It's can't terminal

There is nothing wrong with changing tf. I simulated your example for tf=10 and nothing went wrong.

The problem may be re-simulating the model. During the simulation, data flow through the connections of the model is carried out through Julia Channels. Similarly, Model clock generates pulses based on Channels. After the simulation of a Model, all these channels are closed and all the tasks that are bound to those channels are terminated. So, the model that has been simulated cannot be re-simulated again as it is. To re-simulate the model, the model must be re-constructed.

another advise is to seperate simulate to two step,first phase to initialize model,second to simulate model

All the functions of the _simulate function are exported and can be used on their own. Here is a simple example,

using Jusdl 

# Construct model
model = Model() 
addnode!(model, SinewaveGenerator(), label=:gen)
addnode!(model, Writer(), label=:writer)
addbranch!(model, :gen => :writer)

# Call simulation steps individually.
inspect!(model) 
initialize!(model)
run!(model)
terminate!(model)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants