2
0
mirror of https://github.com/fhem/fhem-mirror.git synced 2025-03-10 09:16:53 +00:00

93_PWMR.pm : ILookBackCnt, buffer holding previouse temperatures used for PID I-Part calculation

git-svn-id: https://svn.fhem.de/fhem/trunk@10770 2b470e98-0d58-463d-a4d8-8e2adae1ed80
This commit is contained in:
jamesgo 2016-02-08 12:23:57 +00:00
parent fd53adcf1b
commit 0b9f7dcd5b

View File

@ -28,6 +28,7 @@
# 26.01.16 GA add implementation of PID regulation
# 27.01.16 GA add attribute desiredTempFrom to take desiredTemp from another object
# 04.02.16 GA add DLookBackCnt, buffer holding previouse temperatures used for PID D-Part calculation
# 08.02.16 GA add ILookBackCnt, buffer holding previouse temperatures used for PID I-Part calculation
# module for PWM (Pulse Width Modulation) calculation
@ -36,7 +37,7 @@
# reference to the PWM object is via IODev
# PWMR object defines:
# IODev: reference to PWM
# factor (also used in PID calculation):
# factor (also used in Pulse calculation):
# temperatur difference * factor * cycletime (from PWM) defines on/off periods (pulse)
# sensor delivering the temperature (temperature is read from reading using a regexp)
# actor to switch on/off the heating devices (may be a structure if more than on actor..)
@ -463,7 +464,7 @@ PWMR_Define($$)
my $name = $hash->{NAME};
return "syntax: define <name> PWMR <IODev> <factor[,offset]> <tsensor[:reading[:t_regexp]]> <actor>[:<a_regexp_on>] [<window|dummy>[,<window>][:<w_regexp>]] [<usePID 0|1>:<PFactor>:<IFactor>:<DFactor>:<DLookBackCnt>]"
return "syntax: define <name> PWMR <IODev> <factor[,offset]> <tsensor[:reading[:t_regexp]]> <actor>[:<a_regexp_on>] [<window|dummy>[,<window>][:<w_regexp>]] [<usePID 0|1>:<PFactor>:<IFactor>[,<ILookBackCnt>]:<DFactor>[,<DLookBackCnt>]]"
if(int(@a) < 6 || int(@a) > 9);
my $iodevname = $a[2];
@ -542,17 +543,25 @@ PWMR_Define($$)
##########
# check pid definition
my ($usePID, $PFactor, $IFactor, $DFactor, $DLookBackCnt) = split (":", $pid, 5);
my ($usePID, $PFactor, $IFactorTemp, $DFactorTemp) = split (":", $pid, 5);
$IFactorTemp = "0,1" unless (defined ($IFactorTemp));
$DFactorTemp = "0,1"unless (defined ($DFactorTemp));
my ($IFactor, $ILookBackCnt) = split (",", $IFactorTemp, 2);
my ($DFactor, $DLookBackCnt) = split (",", $DFactorTemp, 2);
$hash->{c_PID_useit} = !defined($usePID) ? -1 : $usePID;
$hash->{c_PID_PFactor} = !defined($PFactor) ? 0 : $PFactor;
$hash->{c_PID_IFactor} = !defined($IFactor) ? 0 : $IFactor;
$hash->{c_PID_DFactor} = !defined($DFactor) ? 0 : $DFactor;
$hash->{c_PID_ILookBackCnt} = !defined($ILookBackCnt) ? 3 : $ILookBackCnt;
$hash->{c_PID_DLookBackCnt} = !defined($DLookBackCnt) ? 1 : $DLookBackCnt;
$hash->{h_deltaTemp} = 0 unless defined ($hash->{h_deltaTemp});
$hash->{h_deltaTemp_prev} = 0 unless defined ($hash->{h_deltaTemp_prev});
$hash->{h_pid_integrator} = 0;
$hash->{h_deltaTemp_D} = 0 unless defined ($hash->{h_deltaTemp_D});
#$hash->{h_pid_integrator} = 0;
if ($pid eq "") {
@ -562,46 +571,68 @@ PWMR_Define($$)
delete ($hash->{READINGS}{PID_PWMPulse}) if (defined($hash->{READINGS}{PID_PWMPulse}));
delete ($hash->{READINGS}{PID_PWMOnTime}) if (defined($hash->{READINGS}{PID_PWMOnTime}));
delete ($hash->{helper}{PID_previousTemps}) if (defined (($hash->{helper}{PID_previousTemps})));
delete ($hash->{helper}{PID_D_previousTemps}) if (defined (($hash->{helper}{PID_D_previousTemps})));
#delete ($hash->{h_deltaTemp}) if (defined($hash->{h_deltaTemp}));
#delete ($hash->{h_pid_integrator}) if (defined($hash->{h_pid_integrator}));
} else {
### I-Factor
# initialize if not yet done
$hash->{helper}{PID_previousTemps} = [] unless defined (($hash->{helper}{PID_previousTemps}));
$hash->{helper}{PID_I_previousTemps} = [] unless defined (($hash->{helper}{PID_I_previousTemps}));
# shorter reference to array
my $TBuffer = $hash->{helper}{PID_previousTemps};
my $cnt = ( @{$TBuffer} ); # or scalar @{$TBuffer}
my $IBuffer = $hash->{helper}{PID_I_previousTemps};
my $Icnt = ( @{$IBuffer} ); # or scalar @{$IBuffer}
# reference
Log3 ($hash, 3, "org reference is $hash->{helper}{PID_previousTemps} short is $TBuffer, cnt is ". scalar @{$TBuffer}." (starting from 0)");
Log3 ($hash, 3, "content of TBuffer is @{$TBuffer}");
#Log3 ($hash, 3, "org reference IBuffer is $hash->{helper}{PID_I_previousTemps} short is $IBuffer, cnt is ". scalar @{$IBuffer}." (starting from 0)");
Log3 ($hash, 4, "content of IBuffer is @{$IBuffer}");
# cut Buffer if it is too large
while (scalar @{$IBuffer} > $hash->{c_PID_DLookBackCnt}) {
my $v = shift @{$IBuffer};
# Log3 ($hash, 3, "shift $v from IBuffer");
}
# Log3 ($hash, 3, "IBuffer contains ".scalar @{$IBuffer}." elements");
### D-Factor
# initialize if not yet done
$hash->{helper}{PID_D_previousTemps} = [] unless defined (($hash->{helper}{PID_D_previousTemps}));
# shorter reference to array
my $DBuffer = $hash->{helper}{PID_D_previousTemps};
my $Dcnt = ( @{$DBuffer} ); # or scalar @{$DBuffer}
# reference
#Log3 ($hash, 3, "org reference DBuffer is $hash->{helper}{PID_D_previousTemps} short is $DBuffer, cnt is ". scalar @{$DBuffer}." (starting from 0)");
Log3 ($hash, 4, "content of DBuffer is @{$DBuffer}");
# for my $i ( 0 .. $cnt -1 ) {
# Log3 ($hash, 3, "value $i $TBuffer->[$i]");
# Log3 ($hash, 3, "value $i $DBuffer->[$i]");
# }
#
# push @{$TBuffer}, $TBuffer->[1] + $TBuffer->[0];
# push @{$DBuffer}, $DBuffer->[1] + $DBuffer->[0];
#
# for my $i ( 0 .. @{$TBuffer} -1 ) {
# Log3 ($hash, 3, "value after push $i $TBuffer->[$i]");
# for my $i ( 0 .. @{$DBuffer} -1 ) {
# Log3 ($hash, 3, "value after push $i $DBuffer->[$i]");
# }
#
# shift @{$TBuffer};
# shift @{$DBuffer};
#
# for my $i ( 0 .. @{$TBuffer} -1 ) {
# Log3 ($hash, 3, "value after shift $i $TBuffer->[$i]");
# for my $i ( 0 .. @{$DBuffer} -1 ) {
# Log3 ($hash, 3, "value after shift $i $DBuffer->[$i]");
# }
# cut Buffer if it is too large
while (scalar @{$TBuffer} > $hash->{c_PID_DLookBackCnt}) {
my $v = shift @{$TBuffer};
# Log3 ($hash, 3, "shift $v from TBuffer");
while (scalar @{$DBuffer} > $hash->{c_PID_DLookBackCnt}) {
my $v = shift @{$DBuffer};
# Log3 ($hash, 3, "shift $v from DBuffer");
}
# Log3 ($hash, 3, "TBuffer contains ".scalar @{$TBuffer}." elements");
# Log3 ($hash, 3, "DBuffer contains ".scalar @{$DBuffer}." elements");
}
@ -879,25 +910,49 @@ PWMR_ReadRoom(@)
$PWMOnTime = "00:00";
}
# PID calculation
my $TBuffer = $room->{helper}{PID_previousTemps};
push @{$TBuffer}, $temperaturV;
### PID calculation
my $DBuffer = $room->{helper}{PID_D_previousTemps};
push @{$DBuffer}, $temperaturV;
my $IBuffer = $room->{helper}{PID_I_previousTemps};
push @{$IBuffer}, $temperaturV;
# cut I-Buffer if it is too large
while (scalar @{$IBuffer} > $room->{c_PID_ILookBackCnt}) {
my $v = shift @{$IBuffer};
#Log3 ($room, 3, "shift $v from IBuffer");
}
#Log3 ($room, 3, "IBuffer contains ".scalar @{$IBuffer}." elements");
# cut D-Buffer if it is too large
while (scalar @{$DBuffer} > $room->{c_PID_DLookBackCnt}) {
my $v = shift @{$DBuffer};
#Log3 ($room, 3, "shift $v from DBuffer");
}
#Log3 ($room, 3, "DBuffer contains ".scalar @{$DBuffer}." elements");
$room->{h_PID_I_previousTemps} = join (" ", @{$IBuffer});
$room->{h_PID_D_previousTemps} = join (" ", @{$DBuffer});
my $deltaTempPID = $desiredTemp - $temperaturV;
$room->{h_deltaTemp} = sprintf ("%.1f", -1 * $deltaTempPID);
$room->{h_deltaTemp_prev} = sprintf ("%.1f", -1 * ($desiredTemp - $TBuffer->[0]));
# cut Buffer if it is too large
while (scalar @{$TBuffer} > $room->{c_PID_DLookBackCnt}) {
my $v = shift @{$TBuffer};
#Log3 ($room, 3, "shift $v from TBuffer");
}
#Log3 ($room, 3, "TBuffer contains ".scalar @{$TBuffer}." elements");
$room->{h_PID_previousTemps} = join (" ", @{$TBuffer});
$room->{h_deltaTemp} = sprintf ("%.1f", -1 * $deltaTempPID);
$room->{h_deltaTemp_D} = sprintf ("%.1f", -1 * ($desiredTemp - $DBuffer->[0]));
my $ISum = 0;
foreach my $t (@{$IBuffer}) {
$ISum += ($desiredTemp - $t);
}
#$ISum = $ISum / scalar @{$IBuffer};
$ISum = $ISum;
#my $deltaTempPID = $desiredTemp - $temperaturV;
#my $IVal = $room->{c_PID_IFactor} * $deltaTempPID + $room->{h_pid_integrator};
my $PVal = $room->{c_PID_PFactor} * $deltaTemp;
my $IVal = $room->{c_PID_IFactor} * $deltaTempPID + $room->{h_pid_integrator};
my $DVal = $room->{c_PID_DFactor} * ($room->{h_deltaTemp_prev} - $room->{h_deltaTemp});
my $IVal = $room->{c_PID_IFactor} * $ISum;
my $DVal = $room->{c_PID_DFactor} * ($room->{h_deltaTemp_D} - $room->{h_deltaTemp});
$PVal = minNum (1, sprintf ("%.2f", $PVal));
$IVal = minNum (1, sprintf ("%.2f", $IVal));
@ -905,7 +960,7 @@ PWMR_ReadRoom(@)
$IVal = maxNum (-1, $IVal);
$room->{h_pid_integrator} = $IVal;
#$room->{h_pid_integrator} = $IVal;
my $newpulsePID = ($PVal + $IVal + $DVal);
$newpulsePID = minNum ($MaxPulse, sprintf ("%.2f", $newpulsePID));
@ -1277,7 +1332,7 @@ PWMR_Boost(@)
<b>Define</b>
<ul>
<code>define &lt;name&gt; PWMR &lt;IODev&gt; &lt;factor[,offset]&gt; &lt;tsensor[:reading:[t_regexp]]&gt; &lt;actor&gt;[:&lt;a_regexp_on&gt;] [&lt;window|dummy&gt;[,&lt;window&gt;[:&lt;w_regexp&gt;]] [&lt;usePID 0|1&gt;:&lt;PFactor&gt;:&lt;IFactor&gt;:&lt;DFactor&gt;:&lt;DLookBackCnt&gt;]<br></code>
<code>define &lt;name&gt; PWMR &lt;IODev&gt; &lt;factor[,offset]&gt; &lt;tsensor[:reading:[t_regexp]]&gt; &lt;actor&gt;[:&lt;a_regexp_on&gt;] [&lt;window|dummy&gt;[,&lt;window&gt;[:&lt;w_regexp&gt;]] [&lt;usePID 0|1&gt;:&lt;PFactor&gt;:&lt;IFactor&gt;[,&lt;ILookBackCnt&gt;]:&lt;DFactor&gt;,[&lt;DLookBackCnt&gt;]<br></code>
<br>
Define a calculation object with the following parameters:<br>
@ -1310,14 +1365,15 @@ PWMR_Boost(@)
<i>w_regexp</i> defines a regular expression to be applied to the reading. Default is '.*Open.*'.<br>
</li>
<li>&lt;usePID 0|1&gt;:&lt;PFactor&gt;:&lt;IFactor&gt;:&lt;DFactor&gt;:&lt;DLookBackCnt&gt;<br>
<li>&lt;usePID 0|1&gt;:&lt;PFactor&gt;:&lt;IFactor&gt;[,&lt;ILookBackCnt&gt;]:&lt;DFactor&gt;[,&lt;DLookBackCnt&gt;]<br>
<i>usePID 0|1</i>: 0 .. calculate Pulse based on PID but do not use it. 1 .. calculate Pulse based on PID and use it.<br>
<i>PFactor</i>: Konstant for P.<br>
<i>IFactor</i>: Konstant for I.<br>
<i>DFactor</i>: Konstant for D.<br>
<i>ILookBackCnt</i>: Buffer size to store previous temperatures. For I calculation all values will be used. Default is 3.<br>
<i>DLookBackCnt</i>: Buffer size to store previous temperatures. For D calculation actual and oldest temperature will be used. Default is 1.<br>
Internals c_PID_PFactor, c_PID_IFactor, c_PID_DFactor and c_PID_useit will reflect the above configuration values.<br>
Internals h_deltaTemp h_deltaTemp_prev, h_pid_integrator will store the values needed for calculation of the next PID value.<br>
Internals h_deltaTemp h_deltaTemp_D store the values needed for calculation of the next PID value.<br>
Readings PID_DVal, PID_IVal, PID_PVal, PID_PWMOnTime and PID_PWMPulse will reflect the actual calculated PID values and Pulse.<br>
</li>