forked from openhab/openhab-addons
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[pidcontroller] Initial Contribution (openhab#9512)
* [pidcontroller] Initial Contribution * Incorporate review feedback No.1 Signed-off-by: Fabian Wolter <github@fabian-wolter.de> Signed-off-by: Joseph Hagberg <joseph@zoidberg.se>
- Loading branch information
Showing
21 changed files
with
1,227 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
This content is produced and maintained by the openHAB project. | ||
|
||
* Project home: https://www.openhab.org | ||
|
||
== Declared Project Licenses | ||
|
||
This program and the accompanying materials are made available under the terms | ||
of the Eclipse Public License 2.0 which is available at | ||
https://www.eclipse.org/legal/epl-2.0/. | ||
|
||
== Source Code | ||
|
||
/~https://github.com/openhab/openhab-addons |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,137 @@ | ||
# PID Controller Automation | ||
|
||
This automation implements a [PID](https://en.wikipedia.org/wiki/PID_controller)-T1 controller for openHAB. | ||
|
||
A PID controller can be used for closed-loop controls. For example: | ||
|
||
- Heating: A sensor measures the room temperature. | ||
The PID controller calculates the heater's valve opening, so that the room temperature is kept at the setpoint. | ||
- Lighting: A light sensor measures the room's illuminance. | ||
The PID controller controls the dimmer of the room's lighting, so that the illuminance in the room is kept at a constant level. | ||
- PV zero export: A meter measures the power at the grid point of the building. | ||
The PID controller calculates the amount of power the battery storage system needs to feed-in or charge the battery, so that the building's grid power consumption is around zero, | ||
i.e. PV generation, battery storage output power and the building's power consumption are at balance. | ||
|
||
## Modules | ||
|
||
The PID controller can be used in openHAB's [rule engine](https://www.openhab.org/docs/configuration/rules-dsl.html). This automation provides a trigger and an action module. | ||
|
||
### Trigger | ||
|
||
This module triggers whenever the `input` or the `setpoint` changes or the `loopTime` expires. | ||
Every trigger calculates the P, the I and the D part and sums them up to form the `output` value. | ||
This is then transferred to the action module. | ||
|
||
| Name | Type | Description | Required | | ||
|--------------------|---------|----------------------------------------------------------------------------------------------------------------------------------------------------|----------| | ||
| `input` | Item | Name of the input [Item](https://www.openhab.org/docs/configuration/items.html) (e.g. temperature sensor value) | Y | | ||
| `setpoint` | Item | Name of the setpoint Item (e.g. desired room temperature) | Y | | ||
| `kp` | Decimal | P: [Proportional Gain](#proportional-p-gain-parameter) Parameter | Y | | ||
| `ki` | Decimal | I: [Integral Gain](#integral-i-gain-parameter) Parameter | Y | | ||
| `kd` | Decimal | D: [Derivative Gain](#derivative-d-gain-parameter) Parameter | Y | | ||
| `kdTimeConstant` | Decimal | D-T1: [Derivative Gain Time Constant](#derivative-time-constant-d-t1-parameter) in sec. | Y | | ||
| `outputLowerLimit` | Decimal | The output of the PID controller will be max this value | Y | | ||
| `outputUpperLimit` | Decimal | The output of the PID controller will be min this value | Y | | ||
| `loopTime` | Decimal | The interval the output value will be updated in milliseconds. Note: the output will also be updated when the input value or the setpoint changes. | Y | | ||
|
||
The purpose of the limit parameters are to keep the output value and the integral value in a reasonable range, if the regulation cannot meet its setpoint. | ||
E.g. the window is open and the heater doesn't manage to heat up the room. | ||
|
||
The `loopTime` should be max a tenth of the system response. | ||
E.g. the heating needs 10 min to heat up the room, the loop time should be max 1 min. | ||
Lower values won't harm, but need more calculation resources. | ||
|
||
### Action | ||
|
||
This module writes the PID controller's output value into the `output` Item and provides debugging abilities. | ||
|
||
| Name | Type | Description | Required | | ||
|--------------|------|----------------------------------------------------------------------|----------| | ||
| `output` | Item | Name of the output Item (e.g. the valve actuator 0-100%) | Y | | ||
| `pInspector` | Item | Name of the debug Item for the current P part | N | | ||
| `iInspector` | Item | Name of the debug Item for the current I part | N | | ||
| `dInspector` | Item | Name of the debug Item for the current D part | N | | ||
| `eInspector` | Item | Name of the debug Item for the current regulation difference (error) | N | | ||
|
||
You can view the internal P, I and D parts of the controller with the inspector Items. | ||
These values are useful when tuning the controller. | ||
They are updated everytime the output is updated. | ||
|
||
## Proportional (P) Gain Parameter | ||
|
||
Parameter: `kp` | ||
|
||
A value of 0 disables the P part. | ||
|
||
A value of 1 sets the output to the current setpoint deviation (error). | ||
E.g. the setpoint is 25°C and the measured value is 20°C, the output will be set to 5. | ||
If the output is the opening of a valve in %, you might want to set this parameter to higher values (`kp=10` would result in 50%). | ||
|
||
## Integral (I) Gain Parameter | ||
|
||
Parameter: `ki` | ||
|
||
The purpose of this parameter is to let the output drift towards the setpoint. | ||
The bigger this parameter, the faster the drifting. | ||
|
||
A value of 0 disables the I part. | ||
|
||
A value of 1 adds the current setpoint deviation (error) to the output each second. | ||
E.g. the setpoint is 25°C and the measured value is 20°C, the output will be set to 5 after 1 sec. | ||
After 2 sec the output will be 10. | ||
If the output is the opening of a valve in %, you might want to set this parameter to a lower value (`ki=0.1` would result in 30% after 60 sec: 5\*0.1\*60=30). | ||
|
||
## Derivative (D) Gain Parameter | ||
|
||
Parameter: `kd` | ||
|
||
The purpose of this parameter is to react to sudden changes (e.g. an opened window) and also to damp the regulation. | ||
This makes the regulation more resilient against oscillations, i.e. bigger `kp` and `ki` values can be set. | ||
|
||
A value of 0 disables the D part. | ||
|
||
A value of 1 sets the output to the difference between the last setpoint deviation (error) and the current. | ||
E.g. the setpoint is 25°C and the measured value is 20°C (error=5°C). | ||
When the temperature drops to 10°C due to an opened window (error=15°C), the output is set to 15°C - 5°C = 10. | ||
|
||
## Derivative Time Constant (D-T1) Parameter | ||
|
||
Parameter: `kdTimeConstant` | ||
|
||
The purpose of this parameter is to slow down the impact of the D part. | ||
|
||
This parameter behaves like a [low-pass](https://en.wikipedia.org/wiki/Low-pass_filter) filter. | ||
The D part will become 63% of its actual value after `kdTimeConstant` seconds and 99% after 5 times `kdTimeConstant`. E.g. `kdTimeConstant` is set to 10s, the D part will become 99% after 50s. | ||
|
||
Higher values lead to a longer lasting impact of the D part (stretching) after a change in the setpoint deviation (error). | ||
The "stretching" also results in a lower amplitude, i.e. if you increase this value, you might want to also increase `kd` to keep the height of the D part at the same level. | ||
|
||
## Tuning | ||
|
||
Tuning the `Kp`, `Ki` and `Kd` parameters can be done by applying science. | ||
It can also be done by heuristic methods like the [Ziegler–Nichols method](https://en.wikipedia.org/wiki/Ziegler%E2%80%93Nichols_method). | ||
But it can also be done by trial and error. | ||
This results in quite reasonable working systems in most cases. | ||
So, this will be described in the following. | ||
|
||
To be able to proceed with this method, you need to visualize the input and the output value of the PID controller over time. | ||
It's also good to visualize the individual P, I and D parts (these are forming the output value) via the inspector Items. | ||
The visualization can be done by the analyze function in Main UI or by adding a persistence and use Grafana for example. | ||
|
||
After you added a [Rule](https://www.openhab.org/docs/configuration/rules-dsl.html) with above trigger and action module and configured those, proceed with the following steps: | ||
|
||
> *Notice:* A good starting point for the derivative time constant `kdTimeConstant` is the response time of the control loop. | ||
E.g. the time it takes from opening the heater valve and seeing an effect of the measured temperature. | ||
|
||
1. Set `kp`, `ki` and `kd` to 0 | ||
2. Increase `kp` until the system starts to oscillate (continuous over- and undershoot) | ||
3. Decrease `kp` a bit, that the system doesn't oscillate anymore | ||
4. Repeat the two steps for the `ki` parameter (keep `kp` set) | ||
5. Repeat the two steps for the `kd` parameter (keep `kp` and `ki` set) | ||
6. As the D part acts as a damper, you should now be able to increase `kp` and `ki` further without resulting in oscillations | ||
|
||
After each modification of above parameters, test the system response by introducing a setpoint deviation (error). | ||
This can be done either by changing the setpoint (e.g. 20°C -> 25°C) or by forcing the measured value to change (e.g. by opening a window). | ||
|
||
This process can take some time with slow responding control loops like heating systems. | ||
You will get faster results with constant lighting or PV zero export applications. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
<?xml version="1.0" encoding="UTF-8" standalone="no"?> | ||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | ||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> | ||
|
||
<modelVersion>4.0.0</modelVersion> | ||
|
||
<parent> | ||
<groupId>org.openhab.addons.bundles</groupId> | ||
<artifactId>org.openhab.addons.reactor.bundles</artifactId> | ||
<version>3.1.0-SNAPSHOT</version> | ||
</parent> | ||
|
||
<artifactId>org.openhab.automation.pidcontroller</artifactId> | ||
|
||
<name>openHAB Add-ons :: Bundles :: Automation :: PID Controller</name> | ||
|
||
</project> |
9 changes: 9 additions & 0 deletions
9
bundles/org.openhab.automation.pidcontroller/src/main/feature/feature.xml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
<?xml version="1.0" encoding="UTF-8"?> | ||
<features name="org.openhab.automation.pidcontroller-${project.version}" xmlns="http://karaf.apache.org/xmlns/features/v1.4.0"> | ||
<repository>mvn:org.openhab.core.features.karaf/org.openhab.core.features.karaf.openhab-core/${ohc.version}/xml/features</repository> | ||
|
||
<feature name="openhab-automation-pidcontroller" description="PID Controller" version="${project.version}"> | ||
<feature>openhab-runtime-base</feature> | ||
<bundle start-level="80">mvn:org.openhab.addons.bundles/org.openhab.automation.pidcontroller/${project.version}</bundle> | ||
</feature> | ||
</features> |
44 changes: 44 additions & 0 deletions
44
...controller/src/main/java/org/openhab/automation/pidcontroller/internal/LowpassFilter.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
/** | ||
* Copyright (c) 2010-2020 Contributors to the openHAB project | ||
* | ||
* See the NOTICE file(s) distributed with this work for additional | ||
* information. | ||
* | ||
* This program and the accompanying materials are made available under the | ||
* terms of the Eclipse Public License 2.0 which is available at | ||
* http://www.eclipse.org/legal/epl-2.0 | ||
* | ||
* SPDX-License-Identifier: EPL-2.0 | ||
*/ | ||
package org.openhab.automation.pidcontroller.internal; | ||
|
||
import org.eclipse.jdt.annotation.NonNullByDefault; | ||
|
||
/** | ||
* Realizes an first-order FIR low pass filter. To keep code complexity low, it is implemented as moving average (all | ||
* FIR coefficients are set to normalized ones). | ||
* | ||
* The exponential decaying function is used for the calculation (see https://en.wikipedia.org/wiki/Time_constant). That | ||
* means the output value is approx. 63% of the input value after one time constant and approx. 99% after 5 time | ||
* constants. | ||
* | ||
* @author Fabian Wolter - Initial contribution | ||
* | ||
*/ | ||
@NonNullByDefault | ||
public class LowpassFilter { | ||
/** | ||
* Executes one low pass filter step. | ||
* | ||
* @param lastOutput the current filter value (result of the last invocation) | ||
* @param newValue the just sampled value | ||
* @param timeQuotient quotient of the current time and the time constant | ||
* @return the new filter value | ||
*/ | ||
public static double calculate(double lastOutput, double newValue, double timeQuotient) { | ||
double output = lastOutput * Math.exp(-timeQuotient); | ||
output += newValue * (1 - Math.exp(-timeQuotient)); | ||
|
||
return output; | ||
} | ||
} |
41 changes: 41 additions & 0 deletions
41
...r/src/main/java/org/openhab/automation/pidcontroller/internal/PIDControllerConstants.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
/** | ||
* Copyright (c) 2010-2020 Contributors to the openHAB project | ||
* | ||
* See the NOTICE file(s) distributed with this work for additional | ||
* information. | ||
* | ||
* This program and the accompanying materials are made available under the | ||
* terms of the Eclipse Public License 2.0 which is available at | ||
* http://www.eclipse.org/legal/epl-2.0 | ||
* | ||
* SPDX-License-Identifier: EPL-2.0 | ||
*/ | ||
package org.openhab.automation.pidcontroller.internal; | ||
|
||
import org.eclipse.jdt.annotation.NonNullByDefault; | ||
|
||
/** | ||
* | ||
* Constants for PID controller. | ||
* | ||
* @author Fabian Wolter - Initial contribution | ||
* | ||
*/ | ||
@NonNullByDefault | ||
public class PIDControllerConstants { | ||
public static final String AUTOMATION_NAME = "pidcontroller"; | ||
public static final String CONFIG_INPUT_ITEM = "input"; | ||
public static final String CONFIG_SETPOINT_ITEM = "setpoint"; | ||
public static final String CONFIG_OUTPUT_LOWER_LIMIT = "outputLowerLimit"; | ||
public static final String CONFIG_OUTPUT_UPPER_LIMIT = "outputUpperLimit"; | ||
public static final String CONFIG_LOOP_TIME = "loopTime"; | ||
public static final String CONFIG_KP_GAIN = "kp"; | ||
public static final String CONFIG_KI_GAIN = "ki"; | ||
public static final String CONFIG_KD_GAIN = "kd"; | ||
public static final String CONFIG_KD_TIMECONSTANT = "kdTimeConstant"; | ||
public static final String P_INSPECTOR = "pInspector"; | ||
public static final String I_INSPECTOR = "iInspector"; | ||
public static final String D_INSPECTOR = "dInspector"; | ||
public static final String E_INSPECTOR = "eInspector"; | ||
public static final String OUTPUT = "output"; | ||
} |
31 changes: 31 additions & 0 deletions
31
...dcontroller/src/main/java/org/openhab/automation/pidcontroller/internal/PIDException.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
/** | ||
* Copyright (c) 2010-2020 Contributors to the openHAB project | ||
* | ||
* See the NOTICE file(s) distributed with this work for additional | ||
* information. | ||
* | ||
* This program and the accompanying materials are made available under the | ||
* terms of the Eclipse Public License 2.0 which is available at | ||
* http://www.eclipse.org/legal/epl-2.0 | ||
* | ||
* SPDX-License-Identifier: EPL-2.0 | ||
*/ | ||
package org.openhab.automation.pidcontroller.internal; | ||
|
||
import org.eclipse.jdt.annotation.NonNullByDefault; | ||
|
||
/** | ||
* | ||
* Common Exception for PID controller. | ||
* | ||
* @author Fabian Wolter - Initial contribution | ||
* | ||
*/ | ||
@NonNullByDefault | ||
public class PIDException extends Exception { | ||
private static final long serialVersionUID = -3029834022610530982L; | ||
|
||
public PIDException(String message) { | ||
super(message); | ||
} | ||
} |
72 changes: 72 additions & 0 deletions
72
.../openhab/automation/pidcontroller/internal/factory/PIDControllerModuleHandlerFactory.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
/** | ||
* Copyright (c) 2010-2020 Contributors to the openHAB project | ||
* | ||
* See the NOTICE file(s) distributed with this work for additional | ||
* information. | ||
* | ||
* This program and the accompanying materials are made available under the | ||
* terms of the Eclipse Public License 2.0 which is available at | ||
* http://www.eclipse.org/legal/epl-2.0 | ||
* | ||
* SPDX-License-Identifier: EPL-2.0 | ||
*/ | ||
package org.openhab.automation.pidcontroller.internal.factory; | ||
|
||
import java.util.Collection; | ||
import java.util.Set; | ||
|
||
import org.eclipse.jdt.annotation.NonNullByDefault; | ||
import org.eclipse.jdt.annotation.Nullable; | ||
import org.openhab.automation.pidcontroller.internal.handler.PIDControllerActionHandler; | ||
import org.openhab.automation.pidcontroller.internal.handler.PIDControllerTriggerHandler; | ||
import org.openhab.core.automation.Action; | ||
import org.openhab.core.automation.Module; | ||
import org.openhab.core.automation.Trigger; | ||
import org.openhab.core.automation.handler.BaseModuleHandlerFactory; | ||
import org.openhab.core.automation.handler.ModuleHandler; | ||
import org.openhab.core.automation.handler.ModuleHandlerFactory; | ||
import org.openhab.core.events.EventPublisher; | ||
import org.openhab.core.items.ItemRegistry; | ||
import org.osgi.framework.BundleContext; | ||
import org.osgi.service.component.annotations.Activate; | ||
import org.osgi.service.component.annotations.Component; | ||
import org.osgi.service.component.annotations.Reference; | ||
|
||
/** | ||
* | ||
* @author Hilbrand Bouwkamp - Initial Contribution | ||
*/ | ||
@Component(service = ModuleHandlerFactory.class, configurationPid = "action.pidcontroller") | ||
@NonNullByDefault | ||
public class PIDControllerModuleHandlerFactory extends BaseModuleHandlerFactory { | ||
private static final Collection<String> TYPES = Set.of(PIDControllerTriggerHandler.MODULE_TYPE_ID, | ||
PIDControllerActionHandler.MODULE_TYPE_ID); | ||
private ItemRegistry itemRegistry; | ||
private EventPublisher eventPublisher; | ||
private BundleContext bundleContext; | ||
|
||
@Activate | ||
public PIDControllerModuleHandlerFactory(@Reference ItemRegistry itemRegistry, | ||
@Reference EventPublisher eventPublisher, BundleContext bundleContext) { | ||
this.itemRegistry = itemRegistry; | ||
this.eventPublisher = eventPublisher; | ||
this.bundleContext = bundleContext; | ||
} | ||
|
||
@Override | ||
public Collection<String> getTypes() { | ||
return TYPES; | ||
} | ||
|
||
@Override | ||
protected @Nullable ModuleHandler internalCreate(Module module, String ruleUID) { | ||
switch (module.getTypeUID()) { | ||
case PIDControllerTriggerHandler.MODULE_TYPE_ID: | ||
return new PIDControllerTriggerHandler((Trigger) module, itemRegistry, eventPublisher, bundleContext); | ||
case PIDControllerActionHandler.MODULE_TYPE_ID: | ||
return new PIDControllerActionHandler((Action) module, itemRegistry, eventPublisher); | ||
} | ||
|
||
return null; | ||
} | ||
} |
Oops, something went wrong.