2 * Copyright (c) 2010-2023 Contributors to the openHAB project
4 * See the NOTICE file(s) distributed with this work for additional
7 * This program and the accompanying materials are made available under the
8 * terms of the Eclipse Public License 2.0 which is available at
9 * http://www.eclipse.org/legal/epl-2.0
11 * SPDX-License-Identifier: EPL-2.0
13 package org.openhab.automation.pidcontroller.internal.handler;
15 import org.eclipse.jdt.annotation.NonNullByDefault;
16 import org.openhab.automation.pidcontroller.internal.LowpassFilter;
19 * The {@link PIDController} provides the necessary methods for retrieving part(s) of the PID calculations
20 * and it provides the method for the overall PID calculations. It also resets the PID controller
22 * @author George Erhan - Initial contribution
23 * @author Hilbrand Bouwkamp - Adapted for new rule engine
24 * @author Fabian Wolter - Add T1 to D part, add debugging ability for PID values
28 private double integralResult;
29 private double derivativeResult;
30 private double previousError;
35 private double derivativeTimeConstantSec;
36 private double iMinResult;
37 private double iMaxResult;
39 public PIDController(double kpAdjuster, double kiAdjuster, double kdAdjuster, double derivativeTimeConstantSec,
40 double iMinValue, double iMaxValue, double previousIntegralPart, double previousDerivativePart,
41 double previousError) {
45 this.derivativeTimeConstantSec = derivativeTimeConstantSec;
46 this.iMinResult = Double.NaN;
47 this.iMaxResult = Double.NaN;
49 // prepare min/max, restore previous state for the integral result accumulator
50 if (Double.isFinite(kiAdjuster) && Math.abs(kiAdjuster) > 0.0) {
51 if (Double.isFinite(iMinValue)) {
52 this.iMinResult = iMinValue / kiAdjuster;
54 if (Double.isFinite(iMaxValue)) {
55 this.iMaxResult = iMaxValue / kiAdjuster;
57 if (Double.isFinite(previousIntegralPart)) {
58 this.integralResult = previousIntegralPart / kiAdjuster;
62 // restore previous state for the derivative result accumulator
63 if (Double.isFinite(kdAdjuster) && Math.abs(kdAdjuster) > 0.0) {
64 if (Double.isFinite(previousDerivativePart)) {
65 this.derivativeResult = previousDerivativePart / kdAdjuster;
69 // restore previous state for the previous error variable
70 if (Double.isFinite(previousError)) {
71 this.previousError = previousError;
75 public PIDOutputDTO calculate(double input, double setpoint, long lastInvocationMs, int loopTimeMs) {
76 final double lastInvocationSec = lastInvocationMs / 1000d;
77 final double error = setpoint - input;
79 // derivative T1 calculation
80 final double timeQuotient = lastInvocationSec / derivativeTimeConstantSec;
81 if (derivativeTimeConstantSec != 0) {
82 derivativeResult = LowpassFilter.calculate(derivativeResult, error - previousError, timeQuotient);
83 previousError = error;
86 // integral calculation
87 integralResult += error * lastInvocationMs / loopTimeMs;
88 if (Double.isFinite(iMinResult)) {
89 integralResult = Math.max(integralResult, iMinResult);
91 if (Double.isFinite(iMaxResult)) {
92 integralResult = Math.min(integralResult, iMaxResult);
96 final double proportionalPart = kp * error;
98 double integralPart = ki * integralResult;
100 final double derivativePart = kd * derivativeResult;
102 final double output = proportionalPart + integralPart + derivativePart;
104 return new PIDOutputDTO(output, proportionalPart, integralPart, derivativePart, error);
107 public void setIntegralResult(double integralResult) {
108 this.integralResult = integralResult;
111 public void setDerivativeResult(double derivativeResult) {
112 this.derivativeResult = derivativeResult;