libstp
Robotics library for Botball
Loading...
Searching...
No Matches
control_elements.hpp
Go to the documentation of this file.
1#pragma once
2
3#include <string>
4#include <vector>
5
7
8// Faithful ports of the individual control elements
9// (Control/shared/ControlElement{P,I,D,PT1,PT2,PD1,DeadTime}.ts). Discrete-time
10// difference equations match the TypeScript Calculate() implementations
11// exactly so the simulation produces bit-identical numerical results.
12
14{
15
17class ControlElementP final : public ControlElement
18{
19public:
20 explicit ControlElementP(double k) : k_(k) {}
21
22 [[nodiscard]] std::string getType() const override { return "P"; }
23 [[nodiscard]] double getLowestTimeConstant() const override { return -1; }
24 void initialize(double /*dt*/) override {}
25 double calculate(double u, double /*dt*/) override { return k_ * u; }
26 [[nodiscard]] int optimizeGetNumParameters() const override { return 1; }
27 void optimizeSetParameters(const std::vector<double>& params, int startIdx) override
28 {
29 k_ = params[static_cast<std::size_t>(startIdx + 0)];
30 }
31
32private:
33 double k_;
34};
35
37class ControlElementI final : public ControlElement
38{
39public:
40 explicit ControlElementI(double t);
41
42 [[nodiscard]] std::string getType() const override { return "I"; }
43 [[nodiscard]] double getLowestTimeConstant() const override { return t_; }
44 void initialize(double /*dt*/) override { u_sum_ = 0; }
45 double calculate(double u, double dt) override;
46 [[nodiscard]] int optimizeGetNumParameters() const override { return 1; }
47 void optimizeSetParameters(const std::vector<double>& params, int startIdx) override;
48
49private:
50 double t_;
51 double u_sum_{0};
52};
53
55class ControlElementD final : public ControlElement
56{
57public:
58 explicit ControlElementD(double k) : k_(k) {}
59
60 [[nodiscard]] std::string getType() const override { return "D"; }
61 [[nodiscard]] double getLowestTimeConstant() const override { return -1; }
62 void initialize(double /*dt*/) override { u_n1_ = 0; }
63 double calculate(double u, double dt) override;
64 [[nodiscard]] int optimizeGetNumParameters() const override { return 1; }
65 void optimizeSetParameters(const std::vector<double>& params, int startIdx) override
66 {
67 k_ = params[static_cast<std::size_t>(startIdx + 0)];
68 }
69
70private:
71 double k_;
72 double u_n1_{0};
73};
74
77{
78public:
79 ControlElementPT1(double k, double t);
80
81 [[nodiscard]] std::string getType() const override { return "PT1"; }
82 [[nodiscard]] double getLowestTimeConstant() const override { return t_; }
83 void initialize(double /*dt*/) override { y_n1_ = 0; }
84 double calculate(double u, double dt) override;
85 [[nodiscard]] int optimizeGetNumParameters() const override { return 2; }
86 void optimizeSetParameters(const std::vector<double>& params, int startIdx) override;
87
88private:
89 double k_;
90 double t_;
91 double y_n1_{0};
92};
93
96{
97public:
98 ControlElementPT2(double k, double t, double d);
99
100 [[nodiscard]] std::string getType() const override { return "PT2"; }
101 [[nodiscard]] double getLowestTimeConstant() const override { return t_; }
102 void initialize(double /*dt*/) override { y_n1_ = 0; y_n2_ = 0; }
103 double calculate(double u, double dt) override;
104 [[nodiscard]] int optimizeGetNumParameters() const override { return 3; }
105 void optimizeSetParameters(const std::vector<double>& params, int startIdx) override;
106
107private:
108 double k_;
109 double t_;
110 double d_;
111 double y_n1_{0};
112 double y_n2_{0};
113};
114
117{
118public:
119 ControlElementPD1(double k, double t);
120
121 [[nodiscard]] std::string getType() const override { return "PD1"; }
122 [[nodiscard]] double getLowestTimeConstant() const override { return t_; }
123 void initialize(double /*dt*/) override { u_n1_ = 0; }
124 double calculate(double u, double dt) override;
125 [[nodiscard]] int optimizeGetNumParameters() const override { return 2; }
126 void optimizeSetParameters(const std::vector<double>& params, int startIdx) override;
127
128private:
129 double k_;
130 double t_;
131 double u_n1_{0};
132};
133
146{
147public:
148 // out_max / i_max default to "unbounded" (huge) so the element is a pure
149 // parallel PID unless saturation is requested. When tuning the firmware
150 // velocity loop, set both to MOTOR_MAX_DUTYCYCLE so the simulated loop
151 // saturates exactly like the STM32 pid.c — otherwise an unbounded sim
152 // output produces fictional overshoot and DE picks uselessly small gains.
153 ControlElementPID(double kp, double ki, double kd,
154 double out_max = 1e30, double i_max = 1e30)
155 : kp_(kp), ki_(ki), kd_(kd), out_max_(out_max), i_max_(i_max)
156 {
157 }
158
159 [[nodiscard]] std::string getType() const override { return "PID"; }
160 [[nodiscard]] double getLowestTimeConstant() const override { return -1; }
161 void initialize(double /*dt*/) override
162 {
163 iErr_ = 0;
164 prevErr_ = 0;
165 }
166 double calculate(double e, double dt) override
167 {
168 double d = dt;
169 if (d <= 0.0)
170 d = 1e-9;
171 iErr_ += e * d;
172 const double dErr = (e - prevErr_) / d;
173 prevErr_ = e;
174
175 // Integral anti-windup: clamp the I-term and back-calculate iErr,
176 // matching stm32 pid.c.
177 double iTerm = ki_ * iErr_;
178 if (ki_ > 0.0)
179 {
180 if (iTerm > i_max_)
181 {
182 iTerm = i_max_;
183 iErr_ = i_max_ / ki_;
184 }
185 else if (iTerm < -i_max_)
186 {
187 iTerm = -i_max_;
188 iErr_ = -i_max_ / ki_;
189 }
190 }
191
192 // Output saturation, matching stm32 pid.c clamp to +/-MOTOR_MAX_DUTYCYCLE.
193 double cmd = kp_ * e + iTerm + kd_ * dErr;
194 if (cmd > out_max_)
195 cmd = out_max_;
196 else if (cmd < -out_max_)
197 cmd = -out_max_;
198 return cmd;
199 }
200 [[nodiscard]] int optimizeGetNumParameters() const override { return 3; }
201 void optimizeSetParameters(const std::vector<double>& params, int startIdx) override
202 {
203 kp_ = params[static_cast<std::size_t>(startIdx + 0)];
204 ki_ = params[static_cast<std::size_t>(startIdx + 1)];
205 kd_ = params[static_cast<std::size_t>(startIdx + 2)];
206 }
207
208private:
209 double kp_;
210 double ki_;
211 double kd_;
212 double out_max_;
213 double i_max_;
214 double iErr_{0};
215 double prevErr_{0};
216};
217
220{
221public:
222 explicit ControlElementDeadTime(double t);
223
224 [[nodiscard]] std::string getType() const override { return "DeadTime"; }
225 [[nodiscard]] double getLowestTimeConstant() const override { return t_; }
226 void initialize(double dt) override;
227 double calculate(double u, double dt) override;
228 [[nodiscard]] int optimizeGetNumParameters() const override { return 1; }
229 void optimizeSetParameters(const std::vector<double>& params, int startIdx) override;
230
231private:
232 double t_;
233 double y_{0};
234 std::vector<double> u_arr_;
235 std::size_t read_idx_{0};
236 std::size_t write_idx_{0};
237 double tt_{0};
238};
239
240} // namespace libstp::autotune::control
std::string getType() const override
Short type tag, e.g. "P", "PT1", "ControlLoop".
void initialize(double) override
Reset internal state for a simulation run at sample time dt.
double getLowestTimeConstant() const override
Lowest time constant of the element, or -1 if it has none.
double calculate(double u, double dt) override
Advance one sample: feed input u, return output.
void optimizeSetParameters(const std::vector< double > &params, int startIdx) override
Load parameters from params starting at startIdx.
int optimizeGetNumParameters() const override
Number of tunable parameters this element consumes.
double calculate(double u, double dt) override
Advance one sample: feed input u, return output.
std::string getType() const override
Short type tag, e.g. "P", "PT1", "ControlLoop".
double getLowestTimeConstant() const override
Lowest time constant of the element, or -1 if it has none.
void optimizeSetParameters(const std::vector< double > &params, int startIdx) override
Load parameters from params starting at startIdx.
int optimizeGetNumParameters() const override
Number of tunable parameters this element consumes.
void initialize(double dt) override
Reset internal state for a simulation run at sample time dt.
int optimizeGetNumParameters() const override
Number of tunable parameters this element consumes.
double getLowestTimeConstant() const override
Lowest time constant of the element, or -1 if it has none.
std::string getType() const override
Short type tag, e.g. "P", "PT1", "ControlLoop".
void optimizeSetParameters(const std::vector< double > &params, int startIdx) override
Load parameters from params starting at startIdx.
double calculate(double u, double dt) override
Advance one sample: feed input u, return output.
void initialize(double) override
Reset internal state for a simulation run at sample time dt.
double calculate(double u, double dt) override
Advance one sample: feed input u, return output.
void optimizeSetParameters(const std::vector< double > &params, int startIdx) override
Load parameters from params starting at startIdx.
double getLowestTimeConstant() const override
Lowest time constant of the element, or -1 if it has none.
std::string getType() const override
Short type tag, e.g. "P", "PT1", "ControlLoop".
int optimizeGetNumParameters() const override
Number of tunable parameters this element consumes.
void initialize(double) override
Reset internal state for a simulation run at sample time dt.
std::string getType() const override
Short type tag, e.g. "P", "PT1", "ControlLoop".
void optimizeSetParameters(const std::vector< double > &params, int startIdx) override
Load parameters from params starting at startIdx.
void initialize(double) override
Reset internal state for a simulation run at sample time dt.
double calculate(double e, double dt) override
Advance one sample: feed input u, return output.
double getLowestTimeConstant() const override
Lowest time constant of the element, or -1 if it has none.
ControlElementPID(double kp, double ki, double kd, double out_max=1e30, double i_max=1e30)
int optimizeGetNumParameters() const override
Number of tunable parameters this element consumes.
double getLowestTimeConstant() const override
Lowest time constant of the element, or -1 if it has none.
double calculate(double u, double dt) override
Advance one sample: feed input u, return output.
void optimizeSetParameters(const std::vector< double > &params, int startIdx) override
Load parameters from params starting at startIdx.
void initialize(double) override
Reset internal state for a simulation run at sample time dt.
std::string getType() const override
Short type tag, e.g. "P", "PT1", "ControlLoop".
int optimizeGetNumParameters() const override
Number of tunable parameters this element consumes.
void optimizeSetParameters(const std::vector< double > &params, int startIdx) override
Load parameters from params starting at startIdx.
ControlElementPT2(double k, double t, double d)
double getLowestTimeConstant() const override
Lowest time constant of the element, or -1 if it has none.
double calculate(double u, double dt) override
Advance one sample: feed input u, return output.
std::string getType() const override
Short type tag, e.g. "P", "PT1", "ControlLoop".
void initialize(double) override
Reset internal state for a simulation run at sample time dt.
int optimizeGetNumParameters() const override
Number of tunable parameters this element consumes.
int optimizeGetNumParameters() const override
Number of tunable parameters this element consumes.
void optimizeSetParameters(const std::vector< double > &params, int startIdx) override
Load parameters from params starting at startIdx.
double calculate(double u, double) override
Advance one sample: feed input u, return output.
void initialize(double) override
Reset internal state for a simulation run at sample time dt.
std::string getType() const override
Short type tag, e.g. "P", "PT1", "ControlLoop".
double getLowestTimeConstant() const override
Lowest time constant of the element, or -1 if it has none.
Abstract base for every control element (P, I, D, PT1, ...).