summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRaptor Engineering Development Team <support@raptorengineering.com>2018-04-29 07:31:49 -0500
committerRaptor Engineering Development Team <support@raptorengineering.com>2019-04-19 22:07:46 +0000
commit1d0acc187668066d83f365b8be9463e9ad315a7b (patch)
tree244536f6131b22e2b66e422bb172b6e3423fb35d
parenta964223bba361d62e14621137b353daaf3dacdc8 (diff)
downloadphosphor-fan-presence-1d0acc187668066d83f365b8be9463e9ad315a7b.tar.gz
phosphor-fan-presence-1d0acc187668066d83f365b8be9463e9ad315a7b.zip
Add PID control option to fan controller
-rw-r--r--control/actions.hpp42
-rw-r--r--control/main.cpp2
-rw-r--r--control/zone.cpp86
-rw-r--r--control/zone.hpp50
4 files changed, 178 insertions, 2 deletions
diff --git a/control/actions.hpp b/control/actions.hpp
index bb98b7c..22449e0 100644
--- a/control/actions.hpp
+++ b/control/actions.hpp
@@ -1,3 +1,6 @@
+// PID controls (c) 2018 Raptor Engineering, LLC
+// Licensed for use with Raptor Computing Systems machines only
+
#pragma once
#include <algorithm>
@@ -184,7 +187,7 @@ auto set_net_increase_speed(T&& state, T&& factor, uint64_t speedDelta)
std::get<propPos>(entry.second));
// TODO openbmc/phosphor-fan-presence#7 - Support possible
// state types for comparison
- if (value >= state)
+ if ((value >= state) && (factor != 0))
{
// Increase by at least a single delta(factor)
// to attempt bringing under 'state'
@@ -241,7 +244,7 @@ auto set_net_decrease_speed(T&& state, T&& factor, uint64_t speedDelta)
std::get<propPos>(entry.second));
// TODO openbmc/phosphor-fan-presence#7 - Support possible
// state types for comparison
- if (value < state)
+ if ((value < state) && (factor != 0))
{
if (netDelta == 0)
{
@@ -347,6 +350,41 @@ auto use_alternate_events_on_state(T&& state,
};
}
+template <typename T>
+auto run_pid_control(T&& state, T&& integrator_timestep, T&& kp, T&& ki, T&& kd)
+{
+ return [integrator_timestep = std::forward<T>(integrator_timestep),
+ kp = std::forward<T>(kp),
+ ki = std::forward<T>(ki),
+ kd = std::forward<T>(kd),
+ state = std::forward<T>(state)](auto& zone, auto& group)
+ {
+ auto error_accum = 0;
+ auto error_items = 0;
+ for (auto& entry : group)
+ {
+ try
+ {
+ T value = zone.template getPropertyValue<T>(
+ entry.first,
+ std::get<intfPos>(entry.second),
+ std::get<propPos>(entry.second));
+ error_accum += (value - state);
+ error_items++;
+ }
+ catch (const std::out_of_range& oore)
+ {
+ // Property value not found
+ }
+ }
+
+ if (error_items > 0)
+ {
+ zone.runPidLoop((error_accum / error_items), integrator_timestep, kp, ki, kd);
+ }
+ };
+}
+
} // namespace action
} // namespace control
} // namespace fan
diff --git a/control/main.cpp b/control/main.cpp
index 10492b8..2f3ceb4 100644
--- a/control/main.cpp
+++ b/control/main.cpp
@@ -76,8 +76,10 @@ int main(int argc, char* argv[])
return 0;
}
+ printf("Setup complete. Starting event loop...\n");
return event.loop();
}
+
//Log the useful metadata on these exceptions and let the app
//return 1 so it is restarted without a core dump.
catch (phosphor::fan::util::DBusServiceError& e)
diff --git a/control/zone.cpp b/control/zone.cpp
index 468a605..ce187b1 100644
--- a/control/zone.cpp
+++ b/control/zone.cpp
@@ -13,6 +13,9 @@
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
+ *
+ * PID controls (c) 2018 Raptor Engineering, LLC
+ * Licensed for use with Raptor Computing Systems machines only
*/
#include <chrono>
#include <functional>
@@ -61,6 +64,7 @@ Zone::Zone(Mode mode,
_decInterval(std::get<decIntervalPos>(def)),
_incTimer(event, std::bind(&Zone::incTimerExpired, this)),
_decTimer(event, std::bind(&Zone::decTimerExpired, this)),
+ _pidTimer(event, std::bind(&Zone::pidTimerExpired, this)),
_eventLoop(event)
{
auto& fanDefs = std::get<fanListPos>(def);
@@ -97,6 +101,8 @@ Zone::Zone(Mode mode,
}
// Start timer for fan speed decreases
_decTimer.restart(_decInterval);
+ // Start timer for PID loops
+ _pidTimer.restart(std::chrono::seconds(1));
}
}
@@ -122,6 +128,9 @@ void Zone::setFullSpeed()
fan->setSpeed(_targetSpeed);
}
}
+
+ _pidIntegrator = 0;
+ _pidPrevError = 0;
}
void Zone::disableRotors()
@@ -130,6 +139,9 @@ void Zone::disableRotors()
{
fan->disableRotor();
}
+
+ _pidIntegrator = 0;
+ _pidPrevError = 0;
}
void Zone::setActiveAllow(const Group* group, bool isActiveAllow)
@@ -147,6 +159,9 @@ void Zone::setActiveAllow(const Group* group, bool isActiveAllow)
_active.end(),
actPred);
}
+
+ _pidIntegrator = 0;
+ _pidPrevError = 0;
}
void Zone::removeService(const Group* group,
@@ -289,6 +304,16 @@ void Zone::requestSpeedDecrease(uint64_t targetDelta)
}
}
+void Zone::runPidLoop(int64_t error, uint64_t integrator_timestep, int64_t kp, int64_t ki, int64_t kd)
+{
+ _pidLastError = error;
+ _pidLastIntegratorTimestep = integrator_timestep;
+ _pidLastKp = kp;
+ _pidLastKi = ki;
+ _pidLastKd = kd;
+ _pidActive = true;
+}
+
void Zone::decTimerExpired()
{
// Check all entries are set to allow a decrease
@@ -326,6 +351,67 @@ void Zone::decTimerExpired()
// Decrease timer is restarted since its repeating
}
+void Zone::pidTimerExpired()
+{
+ if (!_pidActive)
+ {
+ return;
+ }
+
+ /* If error is positive on startup, set fans to full by forcing integrator to maximum value */
+ if (!_pidActivePrev) {
+ if (_pidLastError > 0) {
+ _pidIntegrator = ((_ceilingSpeed - _floorSpeed) * 100000) / _pidLastKi;
+ }
+ }
+
+ auto pidIntegrator_orig = _pidIntegrator;
+ _pidIntegrator += _pidLastError * _pidLastIntegratorTimestep;
+ int64_t derivative = (_pidLastError - _pidPrevError) / _pidLastIntegratorTimestep;
+ int64_t pid_output = ((_pidLastError * _pidLastKp) + (_pidIntegrator * _pidLastKi) + (derivative * _pidLastKd)) / 100000;
+ _pidPrevError = _pidLastError;
+
+ uint64_t requestTarget = _floorSpeed;
+ if (pid_output > 0)
+ {
+ requestTarget = _floorSpeed + pid_output;
+ }
+ else {
+ _pidIntegrator = pidIntegrator_orig;
+ }
+ // Target speed can not go above a defined ceiling speed
+ if (requestTarget > _ceilingSpeed)
+ {
+ requestTarget = _ceilingSpeed;
+ _pidIntegrator = pidIntegrator_orig;
+ }
+ // Target speed can not go below a defined floor speed
+ if (requestTarget < _floorSpeed)
+ {
+ requestTarget = _floorSpeed;
+ _pidIntegrator = pidIntegrator_orig;
+ }
+
+ /* Cap integrator wind-up */
+ if (_pidLastKi != 0) {
+ if (_pidIntegrator > (int64_t)(((_ceilingSpeed - _floorSpeed) * 100000) / _pidLastKi))
+ {
+ _pidIntegrator = ((_ceilingSpeed - _floorSpeed) * 100000) / _pidLastKi;
+ }
+ if (_pidIntegrator < ((int64_t)((_ceilingSpeed - _floorSpeed) * -100000) / _pidLastKi))
+ {
+ _pidIntegrator = ((_ceilingSpeed - _floorSpeed) * -100000) / _pidLastKi;
+ }
+ }
+ else {
+ _pidIntegrator = 0;
+ }
+
+ setRequestSpeedBase(requestTarget);
+
+ _pidActivePrev = true;
+}
+
void Zone::initEvent(const SetSpeedEvent& event)
{
sdbusplus::message::message nullMsg{nullptr};
diff --git a/control/zone.hpp b/control/zone.hpp
index ec0d9bb..1739930 100644
--- a/control/zone.hpp
+++ b/control/zone.hpp
@@ -1,3 +1,6 @@
+// PID controls (c) 2018 Raptor Engineering, LLC
+// Licensed for use with Raptor Computing Systems machines only
+
#pragma once
#include <algorithm>
#include <cassert>
@@ -335,6 +338,18 @@ class Zone : public ThermalObject
void requestSpeedDecrease(uint64_t targetDelta);
/**
+ * @brief Run PID algorithm based on provided error, Kp, Ki, and Kd
+ * values.
+ *
+ * @param[in] error - Current error value
+ * @param[in] integrator_timestep - Integrator time step
+ * @param[in] kp - PID proportional constant
+ * @param[in] ki - PID integral constant
+ * @param[in] kd - PID derivative constant
+ */
+ void runPidLoop(int64_t error, uint64_t integrator_timestep, int64_t kp, int64_t ki, int64_t kd);
+
+ /**
* @brief Callback function for the increase timer that delays
* processing of requested speed increases while fans are increasing
*/
@@ -347,6 +362,11 @@ class Zone : public ThermalObject
void decTimerExpired();
/**
+ * @brief Callback function for the PID timer
+ */
+ void pidTimerExpired();
+
+ /**
* @brief Get the event loop used with this zone's timers
*
* @return - The event loop for timers
@@ -638,6 +658,31 @@ class Zone : public ThermalObject
uint64_t _requestSpeedBase = 0;
/**
+ * PID active
+ */
+ bool _pidActive = false;
+ bool _pidActivePrev = false;
+
+ /**
+ * PID integrator
+ */
+ int64_t _pidIntegrator = 0;
+
+ /**
+ * PID values
+ */
+ int64_t _pidLastError = 0;
+ uint64_t _pidLastIntegratorTimestep = 0;
+ int64_t _pidLastKp = 0;
+ int64_t _pidLastKi = 0;
+ int64_t _pidLastKd = 0;
+
+ /**
+ * PID prior state tracker
+ */
+ int64_t _pidPrevError = 0;
+
+ /**
* Speed increase delay in seconds
*/
std::chrono::seconds _incDelay;
@@ -658,6 +703,11 @@ class Zone : public ThermalObject
Timer _decTimer;
/**
+ * The PID timer object
+ */
+ Timer _pidTimer;
+
+ /**
* Event loop used on set speed event timers
*/
sdeventplus::Event _eventLoop;
OpenPOWER on IntegriCloud