343 lines
9.0 KiB
Tcl
343 lines
9.0 KiB
Tcl
# software pi loop
|
|
# new version (old version has strange parameters)
|
|
|
|
namespace eval piloop {
|
|
}
|
|
|
|
if {![namespace exists trun]} {
|
|
source drivers/trun.tcl
|
|
}
|
|
|
|
proc stdConfig::piloop args {
|
|
variable name
|
|
variable path
|
|
variable node
|
|
|
|
scanargs $args var -invar -outvar -outmin 0 -outmax 1 -prop 1 -int 10 \
|
|
-maxdelta 60 -precision 0.001 -inpfunc 0 -outfunc 0 -title $name \
|
|
-custinp log -custout log -span 0 -deadband 0 \
|
|
-outlinear 1e-6 -inplinear 1e-3 -ramptime 0 -smoothtime 0 -ramptol 0.05
|
|
|
|
# custinp, custout:
|
|
# custom input / output function
|
|
# log: allow choice between linear and logarithmic
|
|
# none: only linear
|
|
# else: a custom function. the last arguments are either
|
|
# <value> forward conversion
|
|
# inv <value> inverted conversion
|
|
|
|
variable hostport none
|
|
controller dumprot
|
|
|
|
if {$span == 0} {
|
|
if {$custout eq "none"} {
|
|
set span [expr $outmax - $outmin]
|
|
}
|
|
}
|
|
|
|
obj loop -drive out
|
|
prop lastinput 0
|
|
prop label setpoint
|
|
prop custinp $custinp
|
|
prop custout $custout
|
|
prop span [expr double($span)]
|
|
|
|
kids "$title control parameters" {
|
|
node mode -int out
|
|
default 0
|
|
prop enum off,on
|
|
prop check piloop::checkmode
|
|
prop write stdSct::completeUpdate
|
|
|
|
node reg upd
|
|
prop geterror "off"
|
|
|
|
node output out
|
|
default $outmin
|
|
prop check piloop::checkoutput
|
|
prop write stdSct::completeUpdate
|
|
|
|
node ramptime par $ramptime
|
|
prop help {time for ramping [sec] deltax = 1 or a factor 10}
|
|
|
|
node ramptol par $ramptol
|
|
prop help {log of max. deviation when ramping}
|
|
|
|
node smoothtime par $smoothtime
|
|
prop help {time for smoothing ramp near setpoint}
|
|
|
|
node invar -text out
|
|
default 0
|
|
prop check piloop::setinvar
|
|
prop write stdSct::completeUpdate
|
|
prop visible false
|
|
|
|
node outvar -text par $outvar
|
|
prop visible false
|
|
|
|
node prop par $prop
|
|
prop help {smaller means more sensitive. a change of 'prop' on input\
|
|
-> a change of 100 % or a factor 10 on output}
|
|
|
|
node int par $int
|
|
prop help "integration time (sec)"
|
|
|
|
node outmin par $outmin
|
|
prop help "output for maximal decrease of input var."
|
|
|
|
node outmax par $outmax
|
|
prop help "output for maximal increase of input var."
|
|
|
|
if {$custinp eq "log"} {
|
|
node inpfunction par -int $inpfunc
|
|
prop label "input function"
|
|
prop enum linear,logarithmic
|
|
|
|
node inplinear par $inplinear
|
|
prop help "function gets linear below about this value"
|
|
}
|
|
|
|
if {$custout eq "log"} {
|
|
node outfunction par -int $outfunc
|
|
prop label "output function"
|
|
prop enum linear,exponential
|
|
|
|
node outlinear par $outlinear
|
|
prop help "function gets linear below about this value"
|
|
}
|
|
|
|
node precision par $precision
|
|
prop label "output precision"
|
|
|
|
node deadband par $deadband
|
|
|
|
node maxdelta par $maxdelta
|
|
}
|
|
stdConfig::tdrive "$name control parameters" self piloop::settarget
|
|
# hset $path/setcmd piloop::settarget
|
|
hset $path/invar $invar
|
|
}
|
|
|
|
proc piloop::checkmode {} {
|
|
if {[sct target] == 0} {
|
|
set outmin [hvali [sct parent]/outmin]
|
|
set effoutput [silent $outmin eval "result [hvali [sct parent]/outvar]"]
|
|
if {$effoutput > $outmin} {
|
|
hset [sct parent]/output $outmin
|
|
}
|
|
updateerror [sct parent]/reg off
|
|
} elseif {[hvali [sct]] == 0} {
|
|
hupdate [sct parent]/reg [hvali [sct parent]]
|
|
set outvar_start [silent "" sct @outvar_start]
|
|
if {$outvar_start ne ""} {
|
|
eval $outvar_start
|
|
}
|
|
}
|
|
}
|
|
|
|
proc piloop::updateinput {value} {
|
|
|
|
set maxdelta [hvali [sct]/maxdelta]
|
|
set now [DoubleTime]
|
|
if {[sct lastinput] == 0} {
|
|
set deltat 1
|
|
} else {
|
|
set deltat [expr $now - [sct lastinput]]
|
|
if {$deltat > $maxdelta} {
|
|
set deltat $maxdelta
|
|
}
|
|
}
|
|
set timestep [silent 1 sct timestep]
|
|
if {$deltat < $timestep * 0.9 && $timestep > 0.01} {
|
|
set timestep [expr $timestep * 0.9]
|
|
} else {
|
|
set timestep $deltat
|
|
}
|
|
sct timestep $timestep
|
|
sct lastinput $now
|
|
if {[hvali [sct]/mode] == 0} {
|
|
return idle
|
|
}
|
|
|
|
set prop [hvali [sct]/prop]
|
|
set int [hvali [sct]/int]
|
|
set outmin [hvali [sct]/outmin]
|
|
set outmax [hvali [sct]/outmax]
|
|
set precision [hvali [sct]/precision]
|
|
set ramptime [hvali [sct]/ramptime]
|
|
set smoothtime [hvali [sct]/smoothtime]
|
|
set reg [hvali [sct]/reg]
|
|
set input $value
|
|
set soll [hvali [sct]/set]
|
|
set deadband [hvali [sct]/deadband]
|
|
if {abs($soll - $input) <= $deadband} {
|
|
return idle
|
|
}
|
|
|
|
set effoutput [eval "result [hvali [sct]/outvar]"]
|
|
set output [hvali [sct]/output]
|
|
set span [sct span]
|
|
if {[sct custout] eq "log"} {
|
|
set outlinear [hvali [sct]/outlinear]
|
|
set outfunction [hvali [sct]/outfunction]
|
|
if {$outfunction == 1} {
|
|
set output [expr log10($output + $outlinear)]
|
|
set effoutput [expr log10($effoutput + $outlinear)]
|
|
if {$span == 0} {
|
|
set span 1.0
|
|
}
|
|
} else {
|
|
}
|
|
} elseif {[sct custout] ne "none"} {
|
|
set output [eval [sct custout] inv $output]
|
|
set effoutput [eval [sct custout] inv $effoutput]
|
|
}
|
|
set lastdif [silent 0 sct lastdif]
|
|
if {$span == 0} {
|
|
set span [expr double($outmax - $outmin)]
|
|
}
|
|
|
|
set otol [silent $precision sct otol]
|
|
if {$output > $effoutput + $otol} {
|
|
#clientput "$otol [expr $output - $effoutput + $otol]"
|
|
set output [expr $effoutput + $otol]
|
|
} elseif {$output < $effoutput - $otol} {
|
|
#clientput "$otol [expr $output - ($effoutput - $otol)]"
|
|
set output [expr $effoutput - $otol]
|
|
}
|
|
|
|
if {[sct custinp] eq "log"} {
|
|
set inplinear [hvali [sct]/inplinear]
|
|
set inpfunction [hvali [sct]/inpfunction]
|
|
if {$inpfunction == 1} {
|
|
set soll [expr log10($soll + $inplinear)]
|
|
set input [expr log10($input + $inplinear)]
|
|
set reg [expr log10($reg + $inplinear)]
|
|
}
|
|
} elseif {[sct custinp] ne "none"} {
|
|
set soll [eval [sct custinp] $soll]
|
|
set input [eval [sct custinp] $input]
|
|
set reg [eval [sct custinp] $reg]
|
|
|
|
}
|
|
|
|
if {$ramptime == 0} {
|
|
set reg $soll
|
|
} else {
|
|
set step [expr $deltat / double($ramptime)]
|
|
set dist [expr abs($reg - $soll)]
|
|
if {$dist * $ramptime < $smoothtime } {
|
|
set step [expr $step * sqrt($dist * $ramptime / $smoothtime)]
|
|
}
|
|
set tol [hvali [sct]/ramptol]
|
|
if {$tol * $ramptime < $timestep} {
|
|
set tol [expr $timestep/double($ramptime)]
|
|
}
|
|
if {$soll > $input} {
|
|
set newreg [expr $reg + $step]
|
|
if {$newreg > $input + $tol} {
|
|
set newreg [expr $input + $tol]
|
|
}
|
|
if {$newreg > $soll} {
|
|
set reg $soll
|
|
} elseif {$newreg > $reg} {
|
|
set reg $newreg
|
|
}
|
|
} else {
|
|
set newreg [expr $reg - $step]
|
|
if {$newreg < $input - $tol} {
|
|
set newreg [expr $input - $tol]
|
|
}
|
|
if {$newreg < $soll} {
|
|
set reg $soll
|
|
} elseif {$newreg < $reg} {
|
|
set reg $newreg
|
|
}
|
|
}
|
|
}
|
|
set dif [expr double($reg - $input)]
|
|
set l [llength $lastdif]
|
|
set difdif [expr ($dif - [lindex $lastdif 0]) / $l]
|
|
lappend lastdif $dif
|
|
sct lastdif [lrange $lastdif end-5 end]
|
|
|
|
set do [expr $span/double($prop) * ($difdif + $deltat * $dif / double($int))]
|
|
set output [expr $output + $do]
|
|
#clientput [format "out %12.5g dd %12.5g dtd %12.5g s/p %12.5g" $output $difdif [expr $deltat * $dif / double($int)] [expr $span/double($prop)]]
|
|
|
|
sct otol [expr 2 * abs($do) + $precision]
|
|
|
|
if {[sct custinp] eq "log"} {
|
|
if {$inpfunction == 1} {
|
|
set reg [expr pow(10, $reg) - $inplinear]
|
|
}
|
|
} elseif {[sct custinp] ne "none"} {
|
|
set reg [eval [sct custinp] inv $reg]
|
|
}
|
|
updateval [sct]/reg $reg
|
|
if {[sct custout] eq "log"} {
|
|
if {$outfunction == 1} {
|
|
set output [expr pow(10, $output) - $outlinear]
|
|
#clientput $output
|
|
}
|
|
} elseif {[sct custout] ne "none"} {
|
|
set output [eval [sct custout] $output]
|
|
}
|
|
if {$outmax > $outmin} {
|
|
if {$output > $outmax} {
|
|
set output $outmax
|
|
} elseif {$output < $outmin} {
|
|
set output $outmin
|
|
}
|
|
} else {
|
|
if {$output > $outmin} {
|
|
set output $outmin
|
|
} elseif {$output < $outmax} {
|
|
set output $outmax
|
|
}
|
|
}
|
|
if {[catch {hset [sct]/output $output} msg]} {
|
|
hsetprop [sct]/reg geterror $msg
|
|
}
|
|
return idle
|
|
}
|
|
|
|
# updatescript on the remote input node
|
|
proc piloop::updateinput1 {value} {
|
|
set loopobj [sct loopobj]
|
|
if {[silent 0 sct geterror] eq "0"} {
|
|
updateval $loopobj $value
|
|
}
|
|
return
|
|
}
|
|
|
|
proc piloop::setinvar {} {
|
|
catch {
|
|
[sct controllerName] killupdatescript [hval [sct]] piloop::updateinput1
|
|
}
|
|
[sct controllerName] updatescript [sct target] piloop::updateinput1
|
|
[sct controllerName] updatescript [sct parent] piloop::updateinput
|
|
hsetprop [sct target] loopobj [sct parent]
|
|
return
|
|
}
|
|
|
|
proc piloop::settarget {target} {
|
|
# clientput "settarget $target == [silent 0 sct @offvalue]"
|
|
if {$target == [silent 0 sct @offvalue]} {
|
|
hset [sct]/mode 0
|
|
} else {
|
|
hset [sct]/mode 1
|
|
}
|
|
sct print "[sct]/set = $target"
|
|
return idle
|
|
}
|
|
|
|
proc piloop::updtarget {} {
|
|
sct print "[sct]/set = [sct target]"
|
|
return idle
|
|
}
|
|
|
|
proc piloop::checkoutput {} {
|
|
eval "[hvali [sct parent]/outvar] [sct target]"
|
|
}
|