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>2018-04-29 07:31:49 -0500
commit0d3dbf0d342b91a648bf9fd16db8cc8a69551b39 (patch)
treeada1bbb55e19ff1360e847b1d1d1b6958782a929
parentdd0c9c33050ce10a34331de369da800cf8592ea2 (diff)
downloadphosphor-fan-presence-0d3dbf0d342b91a648bf9fd16db8cc8a69551b39.tar.gz
phosphor-fan-presence-0d3dbf0d342b91a648bf9fd16db8cc8a69551b39.zip
Add PID control option to fan controller
-rw-r--r--control/actions.hpp42
-rw-r--r--control/main.cpp1
-rw-r--r--control/zone.cpp92
-rw-r--r--control/zone.hpp50
4 files changed, 183 insertions, 2 deletions
diff --git a/control/actions.hpp b/control/actions.hpp
index c7356ab..653364d 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>
@@ -183,7 +186,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'
@@ -240,7 +243,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)
{
@@ -274,6 +277,41 @@ auto set_net_decrease_speed(T&& state, T&& factor, uint64_t speedDelta)
};
}
+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 dd49c93..9d12781 100644
--- a/control/main.cpp
+++ b/control/main.cpp
@@ -85,6 +85,7 @@ int main(int argc, char* argv[])
}
else
{
+ printf("Setup complete. Starting event loop...\n");
r = sd_event_loop(eventPtr.get());
if (r < 0)
{
diff --git a/control/zone.cpp b/control/zone.cpp
index 8e665f0..4ce62ea 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 <phosphor-logging/log.hpp>
@@ -49,6 +52,7 @@ Zone::Zone(Mode mode,
_decInterval(std::get<decIntervalPos>(def)),
_incTimer(events, [this](){ this->incTimerExpired(); }),
_decTimer(events, [this](){ this->decTimerExpired(); }),
+ _pidTimer(events, [this](){ this->pidTimerExpired(); }),
_sdEvents(events)
{
auto& fanDefs = std::get<fanListPos>(def);
@@ -77,6 +81,12 @@ Zone::Zone(Mode mode,
_decTimer.start(_decInterval,
util::Timer::TimerType::repeating);
}
+ // Start timer for PID loops
+ if (!_pidTimer.running())
+ {
+ _pidTimer.start(std::chrono::seconds(1),
+ util::Timer::TimerType::repeating);
+ }
}
}
@@ -102,6 +112,9 @@ void Zone::setFullSpeed()
fan->setSpeed(_targetSpeed);
}
}
+
+ _pidIntegrator = 0;
+ _pidPrevError = 0;
}
void Zone::disableRotors()
@@ -110,6 +123,9 @@ void Zone::disableRotors()
{
fan->disableRotor();
}
+
+ _pidIntegrator = 0;
+ _pidPrevError = 0;
}
void Zone::setActiveAllow(const Group* group, bool isActiveAllow)
@@ -127,6 +143,9 @@ void Zone::setActiveAllow(const Group* group, bool isActiveAllow)
_active.end(),
actPred);
}
+
+ _pidIntegrator = 0;
+ _pidPrevError = 0;
}
void Zone::removeService(const Group* group,
@@ -275,6 +294,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
@@ -312,6 +341,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};
@@ -319,6 +409,8 @@ void Zone::initEvent(const SetSpeedEvent& event)
for (auto& sig : std::get<signalsPos>(event))
{
// Initialize the event signal using handler
+ // FIXME
+ // This next line is very slow, delaying fan control startup significantly
std::get<sigHandlerPos>(sig)(_bus, nullMsg, *this);
// Setup signal matches of the property for event
std::unique_ptr<EventData> eventData =
diff --git a/control/zone.hpp b/control/zone.hpp
index 2185afa..fad2ae6 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 <chrono>
#include <vector>
@@ -328,6 +331,18 @@ class Zone
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
*/
@@ -340,6 +355,11 @@ class Zone
void decTimerExpired();
/**
+ * @brief Callback function for the PID timer
+ */
+ void pidTimerExpired();
+
+ /**
* @brief Get the event pointer used with this zone's timers
*
* @return - The Dbus event pointer for timers
@@ -502,6 +522,31 @@ class Zone
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;
@@ -522,6 +567,11 @@ class Zone
phosphor::fan::util::Timer _decTimer;
/**
+ * The PID timer object
+ */
+ phosphor::fan::util::Timer _pidTimer;
+
+ /**
* Dbus event used on set speed event timers
*/
phosphor::fan::event::EventPtr& _sdEvents;
OpenPOWER on IntegriCloud