diff options
author | Raptor Engineering Development Team <support@raptorengineering.com> | 2018-04-29 07:31:49 -0500 |
---|---|---|
committer | Raptor Engineering Development Team <support@raptorengineering.com> | 2019-04-19 22:07:46 +0000 |
commit | 1d0acc187668066d83f365b8be9463e9ad315a7b (patch) | |
tree | 244536f6131b22e2b66e422bb172b6e3423fb35d | |
parent | a964223bba361d62e14621137b353daaf3dacdc8 (diff) | |
download | phosphor-fan-presence-1d0acc187668066d83f365b8be9463e9ad315a7b.tar.gz phosphor-fan-presence-1d0acc187668066d83f365b8be9463e9ad315a7b.zip |
Add PID control option to fan controller
-rw-r--r-- | control/actions.hpp | 42 | ||||
-rw-r--r-- | control/main.cpp | 2 | ||||
-rw-r--r-- | control/zone.cpp | 86 | ||||
-rw-r--r-- | control/zone.hpp | 50 |
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; |