Add stepwise controller
This adds the ability to use stepwise curves alongside
pid control. This creates a base controller class that
pidcontroller and stepwise controller inherit from.
Note: Hysteresis to come in follow-on patch
Tested-by:
Created a stepwise controller and noticed that when it
crossed a threshold that it contributed to the pwm setting.
Change-Id: I6cf842f80eaccafc905d620970afe91e2092d568
Signed-off-by: James Feist <james.feist@linux.intel.com>
diff --git a/pid/builder.cpp b/pid/builder.cpp
index 8ffa77c..e98ba22 100644
--- a/pid/builder.cpp
+++ b/pid/builder.cpp
@@ -17,7 +17,9 @@
#include "pid/builder.hpp"
#include "conf.hpp"
+#include "pid/controller.hpp"
#include "pid/fancontroller.hpp"
+#include "pid/stepwisecontroller.hpp"
#include "pid/thermalcontroller.hpp"
#include <iostream>
@@ -89,7 +91,7 @@
}
auto pid = FanController::CreateFanPid(zone.get(), name, inputs,
- info->info);
+ info->pidInfo);
zone->addFanPID(std::move(pid));
}
else if (info->type == "temp" || info->type == "margin")
@@ -101,9 +103,21 @@
}
auto pid = ThermalController::CreateThermalPid(
- zone.get(), name, inputs, info->setpoint, info->info);
+ zone.get(), name, inputs, info->setpoint, info->pidInfo);
+
zone->addThermalPID(std::move(pid));
}
+ else if (info->type == "stepwise")
+ {
+ for (auto& i : info->inputs)
+ {
+ inputs.push_back(i);
+ zone->addThermalInput(i);
+ }
+ auto stepwise = StepwiseController::CreateStepwiseController(
+ zone.get(), name, inputs, info->stepwiseInfo);
+ zone->addThermalPID(std::move(stepwise));
+ }
std::cerr << "inputs: ";
for (auto& i : inputs)
diff --git a/pid/builderconfig.cpp b/pid/builderconfig.cpp
index 0106057..c72ef9e 100644
--- a/pid/builderconfig.cpp
+++ b/pid/builderconfig.cpp
@@ -102,20 +102,22 @@
/* set-point is only required to be set for thermal. */
/* TODO(venture): Verify this works optionally here. */
info.setpoint = pid.lookup("set-point");
- info.info.ts = pid.lookup("pid.sampleperiod");
- info.info.p_c = pid.lookup("pid.p_coefficient");
- info.info.i_c = pid.lookup("pid.i_coefficient");
- info.info.ff_off = pid.lookup("pid.ff_off_coefficient");
- info.info.ff_gain = pid.lookup("pid.ff_gain_coefficient");
- info.info.i_lim.min = pid.lookup("pid.i_limit.min");
- info.info.i_lim.max = pid.lookup("pid.i_limit.max");
- info.info.out_lim.min = pid.lookup("pid.out_limit.min");
- info.info.out_lim.max = pid.lookup("pid.out_limit.max");
- info.info.slew_neg = pid.lookup("pid.slew_neg");
- info.info.slew_pos = pid.lookup("pid.slew_pos");
+ info.pidInfo.ts = pid.lookup("pid.sampleperiod");
+ info.pidInfo.p_c = pid.lookup("pid.p_coefficient");
+ info.pidInfo.i_c = pid.lookup("pid.i_coefficient");
+ info.pidInfo.ff_off = pid.lookup("pid.ff_off_coefficient");
+ info.pidInfo.ff_gain = pid.lookup("pid.ff_gain_coefficient");
+ info.pidInfo.i_lim.min = pid.lookup("pid.i_limit.min");
+ info.pidInfo.i_lim.max = pid.lookup("pid.i_limit.max");
+ info.pidInfo.out_lim.min = pid.lookup("pid.out_limit.min");
+ info.pidInfo.out_lim.max = pid.lookup("pid.out_limit.max");
+ info.pidInfo.slew_neg = pid.lookup("pid.slew_neg");
+ info.pidInfo.slew_pos = pid.lookup("pid.slew_pos");
- std::cerr << "out_lim.min: " << info.info.out_lim.min << "\n";
- std::cerr << "out_lim.max: " << info.info.out_lim.max << "\n";
+ std::cerr << "out_lim.min: " << info.pidInfo.out_lim.min
+ << "\n";
+ std::cerr << "out_lim.max: " << info.pidInfo.out_lim.max
+ << "\n";
const Setting& inputs = pid["inputs"];
int icount = inputs.getLength();
diff --git a/pid/controller.hpp b/pid/controller.hpp
index 57ee43f..f52c412 100644
--- a/pid/controller.hpp
+++ b/pid/controller.hpp
@@ -3,57 +3,23 @@
#include "ec/pid.hpp"
#include "fan.hpp"
-#include <memory>
-#include <vector>
-
-class ZoneInterface;
+#include <string>
/*
- * Base class for PID controllers. Each PID that implements this needs to
- * provide an input_proc, setpt_proc, and output_proc.
+ * Base class for controllers. Each controller that implements this needs to
+ * provide an input_proc, process, and output_proc.
*/
-class PIDController
-{
- public:
- PIDController(const std::string& id, ZoneInterface* owner) :
- _owner(owner), _setpoint(0), _id(id)
- {
- }
+class ZoneInterface;
- virtual ~PIDController()
- {
- }
+struct Controller
+{
+ virtual ~Controller() = default;
virtual float input_proc(void) = 0;
- virtual float setpt_proc(void) = 0;
+
virtual void output_proc(float value) = 0;
- void pid_process(void);
+ virtual void process(void) = 0;
- std::string get_id(void)
- {
- return _id;
- }
- float get_setpoint(void)
- {
- return _setpoint;
- }
- void set_setpoint(float setpoint)
- {
- _setpoint = setpoint;
- }
-
- ec::pid_info_t* get_pid_info(void)
- {
- return &_pid_info;
- }
-
- protected:
- ZoneInterface* _owner;
-
- private:
- // parameters
- ec::pid_info_t _pid_info;
- float _setpoint;
- std::string _id;
+ virtual std::string get_id(void) = 0;
};
diff --git a/pid/ec/stepwise.cpp b/pid/ec/stepwise.cpp
new file mode 100644
index 0000000..4246fb3
--- /dev/null
+++ b/pid/ec/stepwise.cpp
@@ -0,0 +1,45 @@
+/*
+// Copyright (c) 2018 Intel Corporation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// 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.
+*/
+
+#include "stepwise.hpp"
+
+#include <cstddef>
+#include <limits>
+
+namespace ec
+{
+float stepwise(const ec::StepwiseInfo& info, float input)
+{
+ float value = info.output[0]; // if we are below the lowest
+ // point, we set the lowest value
+
+ for (size_t ii = 1; ii < ec::maxStepwisePoints; ii++)
+ {
+
+ if (info.reading[ii] == std::numeric_limits<float>::quiet_NaN())
+ {
+ break;
+ }
+ if (info.reading[ii] > input)
+ {
+ break;
+ }
+ value = info.output[ii];
+ }
+
+ return value;
+}
+} // namespace ec
\ No newline at end of file
diff --git a/pid/ec/stepwise.hpp b/pid/ec/stepwise.hpp
new file mode 100644
index 0000000..ed07b44
--- /dev/null
+++ b/pid/ec/stepwise.hpp
@@ -0,0 +1,36 @@
+
+/*
+// Copyright (c) 2018 Intel Corporation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// 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.
+*/
+
+#pragma once
+
+#include <cstddef>
+#include <vector>
+
+namespace ec
+{
+constexpr size_t maxStepwisePoints = 20;
+
+struct StepwiseInfo
+{
+ float ts; // sample time in seconds
+ float reading[maxStepwisePoints];
+ float output[maxStepwisePoints];
+};
+
+float stepwise(const ec::StepwiseInfo& info, float value);
+
+} // namespace ec
\ No newline at end of file
diff --git a/pid/fancontroller.hpp b/pid/fancontroller.hpp
index aaccd4b..521d638 100644
--- a/pid/fancontroller.hpp
+++ b/pid/fancontroller.hpp
@@ -1,8 +1,8 @@
#pragma once
-#include "controller.hpp"
#include "ec/pid.hpp"
#include "fan.hpp"
+#include "pidcontroller.hpp"
#include <memory>
#include <string>
diff --git a/pid/controller.cpp b/pid/pidcontroller.cpp
similarity index 94%
rename from pid/controller.cpp
rename to pid/pidcontroller.cpp
index 138268b..524b0ef 100644
--- a/pid/controller.cpp
+++ b/pid/pidcontroller.cpp
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#include "controller.hpp"
+#include "pidcontroller.hpp"
#include "ec/pid.hpp"
@@ -26,7 +26,7 @@
#include <thread>
#include <vector>
-void PIDController::pid_process(void)
+void PIDController::process(void)
{
float input;
float setpt;
diff --git a/pid/pidcontroller.hpp b/pid/pidcontroller.hpp
new file mode 100644
index 0000000..18a448e
--- /dev/null
+++ b/pid/pidcontroller.hpp
@@ -0,0 +1,60 @@
+#pragma once
+
+#include "controller.hpp"
+#include "ec/pid.hpp"
+#include "fan.hpp"
+
+#include <memory>
+#include <vector>
+
+class ZoneInterface;
+
+/*
+ * Base class for PID controllers. Each PID that implements this needs to
+ * provide an input_proc, setpt_proc, and output_proc.
+ */
+class PIDController : public Controller
+{
+ public:
+ PIDController(const std::string& id, ZoneInterface* owner) :
+ Controller(), _owner(owner), _setpoint(0), _id(id)
+ {
+ }
+
+ virtual ~PIDController()
+ {
+ }
+
+ virtual float input_proc(void) = 0;
+ virtual float setpt_proc(void) = 0;
+ virtual void output_proc(float value) = 0;
+
+ void process(void);
+
+ std::string get_id(void)
+ {
+ return _id;
+ }
+ float get_setpoint(void)
+ {
+ return _setpoint;
+ }
+ void set_setpoint(float setpoint)
+ {
+ _setpoint = setpoint;
+ }
+
+ ec::pid_info_t* get_pid_info(void)
+ {
+ return &_pid_info;
+ }
+
+ protected:
+ ZoneInterface* _owner;
+
+ private:
+ // parameters
+ ec::pid_info_t _pid_info;
+ float _setpoint;
+ std::string _id;
+};
diff --git a/pid/pidthread.cpp b/pid/pidthread.cpp
index 490b70e..4dfc22a 100644
--- a/pid/pidthread.cpp
+++ b/pid/pidthread.cpp
@@ -16,7 +16,7 @@
#include "pidthread.hpp"
-#include "pid/controller.hpp"
+#include "pid/pidcontroller.hpp"
#include "sensors/sensor.hpp"
#include <chrono>
diff --git a/pid/stepwisecontroller.cpp b/pid/stepwisecontroller.cpp
new file mode 100644
index 0000000..cea21e5
--- /dev/null
+++ b/pid/stepwisecontroller.cpp
@@ -0,0 +1,75 @@
+/*
+// Copyright (c) 2018 Intel Corporation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// 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.
+*/
+
+#include "stepwisecontroller.hpp"
+
+#include "ec/stepwise.hpp"
+#include "util.hpp"
+#include "zone.hpp"
+
+#include <algorithm>
+#include <chrono>
+#include <iostream>
+#include <map>
+#include <memory>
+#include <thread>
+#include <vector>
+
+void StepwiseController::process(void)
+{
+ // Get input value
+ float input = input_proc();
+
+ // Calculate new output
+ float output = ec::stepwise(get_stepwise_info(), input);
+
+ // Output new value
+ output_proc(output);
+
+ return;
+}
+
+std::unique_ptr<Controller> StepwiseController::CreateStepwiseController(
+ ZoneInterface* owner, const std::string& id,
+ const std::vector<std::string>& inputs, const ec::StepwiseInfo& initial)
+{
+ // StepwiseController currently only supports precisely one input.
+ if (inputs.size() != 1)
+ {
+ return nullptr;
+ }
+
+ auto thermal = std::make_unique<StepwiseController>(id, inputs, owner);
+
+ ec::StepwiseInfo& info = thermal->get_stepwise_info();
+
+ info = initial;
+
+ return thermal;
+}
+
+float StepwiseController::input_proc(void)
+{
+ double value = _owner->getCachedValue(_inputs.at(0));
+ return static_cast<float>(value);
+}
+
+void StepwiseController::output_proc(float value)
+{
+ _owner->addRPMSetPoint(value);
+
+ return;
+}
diff --git a/pid/stepwisecontroller.hpp b/pid/stepwisecontroller.hpp
new file mode 100644
index 0000000..c3af1a1
--- /dev/null
+++ b/pid/stepwisecontroller.hpp
@@ -0,0 +1,52 @@
+#pragma once
+
+#include "controller.hpp"
+#include "ec/stepwise.hpp"
+#include "fan.hpp"
+
+#include <memory>
+#include <vector>
+
+class ZoneInterface;
+
+class StepwiseController : public Controller
+{
+ public:
+ static std::unique_ptr<Controller>
+ CreateStepwiseController(ZoneInterface* owner, const std::string& id,
+ const std::vector<std::string>& inputs,
+ const ec::StepwiseInfo& initial);
+
+ StepwiseController(const std::string& id,
+ const std::vector<std::string>& inputs,
+ ZoneInterface* owner) :
+ Controller(),
+ _owner(owner), _id(id), _inputs(inputs)
+ {
+ }
+
+ float input_proc(void) override;
+
+ void output_proc(float value) override;
+
+ void process(void) override;
+
+ std::string get_id(void)
+ {
+ return _id;
+ }
+
+ ec::StepwiseInfo& get_stepwise_info(void)
+ {
+ return _stepwise_info;
+ }
+
+ protected:
+ ZoneInterface* _owner;
+
+ private:
+ // parameters
+ ec::StepwiseInfo _stepwise_info;
+ std::string _id;
+ std::vector<std::string> _inputs;
+};
diff --git a/pid/thermalcontroller.hpp b/pid/thermalcontroller.hpp
index ed94acb..85c3a2b 100644
--- a/pid/thermalcontroller.hpp
+++ b/pid/thermalcontroller.hpp
@@ -1,7 +1,7 @@
#pragma once
-#include "controller.hpp"
#include "ec/pid.hpp"
+#include "pidcontroller.hpp"
#include <memory>
#include <string>
diff --git a/pid/zone.cpp b/pid/zone.cpp
index 9e948f0..92a332e 100644
--- a/pid/zone.cpp
+++ b/pid/zone.cpp
@@ -21,6 +21,7 @@
#include "pid/controller.hpp"
#include "pid/ec/pid.hpp"
#include "pid/fancontroller.hpp"
+#include "pid/stepwisecontroller.hpp"
#include "pid/thermalcontroller.hpp"
#include <algorithm>
@@ -80,12 +81,12 @@
return _minThermalRpmSetPt;
}
-void PIDZone::addFanPID(std::unique_ptr<PIDController> pid)
+void PIDZone::addFanPID(std::unique_ptr<Controller> pid)
{
_fans.push_back(std::move(pid));
}
-void PIDZone::addThermalPID(std::unique_ptr<PIDController> pid)
+void PIDZone::addThermalPID(std::unique_ptr<Controller> pid)
{
_thermals.push_back(std::move(pid));
}
@@ -305,7 +306,7 @@
{
for (auto& p : _fans)
{
- p->pid_process();
+ p->process();
}
}
@@ -313,7 +314,7 @@
{
for (auto& p : _thermals)
{
- p->pid_process();
+ p->process();
}
}
diff --git a/pid/zone.hpp b/pid/zone.hpp
index 99a8f14..6d5f6cc 100644
--- a/pid/zone.hpp
+++ b/pid/zone.hpp
@@ -2,6 +2,7 @@
#include "conf.hpp"
#include "controller.hpp"
+#include "pidcontroller.hpp"
#include "sensors/manager.hpp"
#include "sensors/sensor.hpp"
#include "xyz/openbmc_project/Control/Mode/server.hpp"
@@ -76,8 +77,8 @@
void process_fans(void);
void process_thermals(void);
- void addFanPID(std::unique_ptr<PIDController> pid);
- void addThermalPID(std::unique_ptr<PIDController> pid);
+ void addFanPID(std::unique_ptr<Controller> pid);
+ void addThermalPID(std::unique_ptr<Controller> pid);
double getCachedValue(const std::string& name) override;
void addFanInput(std::string fan);
void addThermalInput(std::string therm);
@@ -111,6 +112,6 @@
std::map<std::string, double> _cachedValuesByName;
const SensorManager& _mgr;
- std::vector<std::unique_ptr<PIDController>> _fans;
- std::vector<std::unique_ptr<PIDController>> _thermals;
+ std::vector<std::unique_ptr<Controller>> _fans;
+ std::vector<std::unique_ptr<Controller>> _thermals;
};