Skip to content

Commit

Permalink
added simple PID test
Browse files Browse the repository at this point in the history
  • Loading branch information
aktur committed Dec 29, 2016
1 parent 7591527 commit 318e962
Show file tree
Hide file tree
Showing 4 changed files with 526 additions and 0 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Expand Up @@ -16,3 +16,5 @@ bin/
*~ *~
manager/.factorypath manager/.factorypath
manager/src/ manager/src/

out/
75 changes: 75 additions & 0 deletions agent/src/test/groovy/org/openremote/test/rules/PIDTest.groovy
@@ -0,0 +1,75 @@
package org.openremote.test.rules

import org.kie.api.KieServices
import org.kie.api.io.Resource
import org.openremote.agent.AgentService
import org.openremote.agent.rules.RulesProvider
import org.openremote.agent.sensor.CustomSensorState
import org.openremote.test.ContainerTrait
import spock.lang.Specification

import java.util.stream.Stream

class PIDTest extends Specification implements ContainerTrait {

def "PID controller basic test"() {

given: "a deployment with commands and sensors"
def deploymentXml = getClass().getResourceAsStream(
"/org/openremote/test/rules/pid/agent.xml"
)

and: "some rules"
def rulesProvider = new RulesProvider() {
@Override
Stream<Resource> getResources(KieServices kieServices) {
Stream.of(
kieServices.getResources().newClassPathResource(
"org/openremote/test/rules/pid/PID.drl"
)
)
}
}

and: "the started server"
def testCommandBuilder = new TestCommandBuilder();
def agentService = new AgentService(
deploymentXml,
testCommandBuilder,
rulesProvider
)
def serverPort = findEphemeralPort()
def container = startContainer(defaultConfig(serverPort), [agentService])

when: "we increase Sp"
// can we run PID.sp.inc.ON command instead?
def customStateEvent = new CustomSensorState(358537, "PID.sp.inc", "ON");
agentService.getContext().update(customStateEvent);

and: "we wait"
Thread.sleep(1000);

then: "set point should be 1.5"
agentService.getContext().queryValue(358531) == "1.5" // can we check sensors by name?
agentService.getContext().queryValue(358532) == "1.5000" // PID output

when: "we decrease Sp"
customStateEvent = new CustomSensorState(358525, "PID.sp.dec", "ON");
agentService.getContext().update(customStateEvent);

and: "we wait"
Thread.sleep(1000);

then: "set point should be 1.0"
agentService.getContext().queryValue(358531) == "1.0" // set point
agentService.getContext().queryValue(358532) == "1.0000" // PID output

// how can I test if there is no overshot?
// how can I test if there are no oscillations?

// Problem: sometimes it passes, sometimes fails...

cleanup: "the server should be stopped"
stopContainer(container)
}
}
199 changes: 199 additions & 0 deletions agent/src/test/resources/org/openremote/test/rules/pid/PID.drl
@@ -0,0 +1,199 @@
package org.openremote.agent.test.rules;

import java.util.concurrent.*;
import java.util.Date;
import java.text.SimpleDateFormat;
import java.io.*;
import org.openremote.agent.sensor.*;

global org.openremote.agent.command.Commands commands;
global org.openremote.agent.rules.RulePersistence persistence;
global org.openremote.agent.rules.RuleUtil util;
global com.fasterxml.jackson.databind.ObjectMapper JSON;
global java.util.logging.Logger LOG;

declare SensorState
@role(event)
end

// PID controller start

rule "-PID: init"
then
commands.execute("GV.PID.Kp",persistence.readData("GV.PID.Kp", "0.6"));
commands.execute("GV.PID.Ki",persistence.readData("GV.PID.Ki", "0.3"));
commands.execute("GV.PID.Kd",persistence.readData("GV.PID.Kd", "0.0"));
commands.execute("GV.PID.Db",persistence.readData("GV.PID.Db", "0.0"));
commands.execute("GV.PID.Sp",persistence.readData("GV.PID.Sp", "1.0"));
end

// PID UI

rule "-PID: increase Kp"
timer(int:300ms)
when
SensorState(sensorName=="GV.PID.Kp", $v: value)
SensorState($s:sensorName=="PID.kp.inc", value=="ON")
then
commands.execute("GV.PID.Kp", String.format("%.1f", Double.parseDouble($v.toString())+0.1) );
commands.execute($s, "off");
end

rule "-PID: decrease Kp"
timer(int:300ms)
when
SensorState(sensorName=="GV.PID.Kp", $v: value)
SensorState($s:sensorName=="PID.kp.dec", value=="ON")
then
commands.execute("GV.PID.Kp", String.format("%.1f", Double.parseDouble($v.toString())-0.1) );
commands.execute($s, "off");
end

rule "-PID: increase Ki"
timer(int:300ms)
when
SensorState(sensorName=="GV.PID.Ki", $v: value)
SensorState($s:sensorName=="PID.ki.inc", value=="ON")
then
commands.execute("GV.PID.Ki", String.format("%.1f", Double.parseDouble($v.toString())+0.1) );
commands.execute($s, "off");
end

rule "-PID: decrease Ki"
timer(int:300ms)
when
SensorState(sensorName=="GV.PID.Ki", $v: value)
SensorState($s:sensorName=="PID.ki.dec", value=="ON")
then
commands.execute("GV.PID.Ki", String.format("%.1f", Double.parseDouble($v.toString())-0.1) );
commands.execute($s, "off");
end

rule "-PID: increase Kd"
timer(int:300ms)
when
SensorState(sensorName=="GV.PID.Kd", $v: value)
SensorState($s:sensorName=="PID.kd.inc", value=="ON")
then
commands.execute("GV.PID.Kd", String.format("%.1f", Double.parseDouble($v.toString())+0.1) );
commands.execute($s, "off");
end

rule "-PID: decrease Kd"
timer(int:300ms)
when
SensorState(sensorName=="GV.PID.Kd", $v: value)
SensorState($s:sensorName=="PID.kd.dec", value=="ON")
then
commands.execute("GV.PID.Kd", String.format("%.1f", Double.parseDouble($v.toString())-0.1) );
commands.execute($s, "off");
end

rule "-PID: increase Db"
timer(int:300ms)
when
SensorState(sensorName=="GV.PID.Db", $v: value)
SensorState($s:sensorName=="PID.db.inc", value=="ON")
then
commands.execute("GV.PID.Db", String.format("%.1f", Double.parseDouble($v.toString())+0.1) );
commands.execute($s, "off");
end

rule "-PID: decrease Db"
timer(int:300ms)
when
SensorState(sensorName=="GV.PID.Db", $v: value)
SensorState($s:sensorName=="PID.db.dec", value=="ON")
then
commands.execute("GV.PID.Db", String.format("%.1f", Double.parseDouble($v.toString())-0.1) );
commands.execute($s, "off");
end

rule "PID: increase Sp"
timer(int:300ms)
when
SensorState(sensorName=="GV.PID.Sp", $v: value)
SensorState($s:sensorName=="PID.sp.inc", value=="ON")
then
commands.execute("GV.PID.Sp", String.format("%.1f", Double.parseDouble($v.toString())+0.5) );
commands.execute($s, "off");
end

rule "-PID: decrease Sp"
timer(int:300ms)
when
SensorState(sensorName=="GV.PID.Sp", $v: value)
SensorState($s:sensorName=="PID.sp.dec", value=="ON")
then
commands.execute("GV.PID.Sp", String.format("%.1f", Double.parseDouble($v.toString())-0.5) );
commands.execute($s, "off");
end

rule "-PID: reset inc/dec"
salience -10
timer(int: 1s)
when
SensorState($s: sensorName matches "^PID......c$", value!="off")
then
commands.execute($s, "off");
end

// PID algorithm

declare PIDVars
// Input==Output;
output: double
iterm: double
prevInput: double
outMin: double
outMax: double
active: Boolean
end

rule "-PID: algo init"
then
PIDVars pv = new PIDVars();
pv.setActive(true);
pv.setOutMax(100);
pv.setOutMin(-100);
insert(pv);
end

rule "-PID: compute"
timer(int: 0 10) // 10 times faster than the implemented one
when
$pv: PIDVars(active==true)
SensorState(sensorName=="GV.PID.Kp", $kp: value)
SensorState(sensorName=="GV.PID.Ki", $ki: value)
SensorState(sensorName=="GV.PID.Kd", $kd: value)
SensorState(sensorName=="GV.PID.Sp", $sp: value)
SensorState(sensorName=="GV.PID.Db", $db: value)
then
double error = Double.parseDouble($sp.toString() ) - $pv.getOutput();
double kp = Double.parseDouble($kp.toString());
double ki = Double.parseDouble($ki.toString()); // * sample time in sec
double kd = Double.parseDouble($kd.toString()); // / sample time in sec
double sp = Double.parseDouble($sp.toString());
double db = Double.parseDouble($db.toString());

double iterm = $pv.getIterm() + (ki * error);
if(iterm > $pv.getOutMax() ) iterm = $pv.getOutMax();
else if(iterm < $pv.getOutMin() ) iterm = $pv.getOutMin();
$pv.setIterm( iterm );

//double dInput = (Input - lastInput);
double dInput = ($pv.getOutput() - $pv.getPrevInput() );
$pv.setPrevInput($pv.getOutput());

// Compute PID Output
double output = kp * error + $pv.getIterm()- kd * dInput;
if(output > $pv.getOutMax()) output = $pv.getOutMax();
else if(output < $pv.getOutMin()) output = $pv.getOutMin();
$pv.setOutput(output);

commands.execute("PID.Se", String.format("%.4f", error) );
commands.execute("PID.Output", String.format("%.4f", output) );
LOG.fine($pv.toString() + " error: "+String.format("%f", Double.parseDouble($sp.toString() ) - $pv.getOutput()));
end

// PID end

0 comments on commit 318e962

Please sign in to comment.