libxr
Embedded C++ framework for bare-metal, RTOS and Linux
Loading...
Searching...
No Matches
pid.hpp
1#pragma once
2
3#include <algorithm>
4#include <cmath>
5#include <utility>
6
7#include "cycle_value.hpp"
8
9namespace LibXR
10{
11using DefaultScalar = LIBXR_DEFAULT_SCALAR;
12
13constexpr DefaultScalar PID_SIGMA = 1e-6f;
14
25template <typename Scalar = DefaultScalar>
26class PID
27{
28 public:
33 struct Param
34 {
35 Scalar k = 1.0;
36 Scalar p = 0.0;
37 Scalar i = 0.0;
38 Scalar d = 0.0;
39 Scalar i_limit = 0.0;
40 Scalar out_limit = 0.0;
41 bool cycle = false;
42 };
43
51 template <typename P>
52 PID(P&& p) : param_(std::forward<P>(p))
53 {
54 Reset();
55 }
56
73 Scalar Calculate(Scalar sp, Scalar fb, Scalar dt)
74 {
75 if (!std::isfinite(sp) || !std::isfinite(fb) || !std::isfinite(dt) || dt <= Scalar(0))
76 {
77 return last_out_;
78 }
79
80 // Compute error
81 const Scalar ERR = param_.cycle ? (CycleValue<Scalar>(sp) - fb) : (sp - fb);
82 const Scalar E_K = ERR * param_.k;
83
84 // Derivative from feedback change (scaled by k)
85 Scalar fb_dot = (fb - last_fb_) / dt;
86 if (!std::isfinite(fb_dot))
87 {
88 fb_dot = Scalar(0);
89 }
90
91 Scalar fb_d_k = fb_dot * param_.k;
92 if (!std::isfinite(fb_d_k))
93 {
94 fb_d_k = Scalar(0);
95 }
96
97 // Compute PD
98 const Scalar OUTPUT_PD = (E_K * param_.p) - (fb_d_k * param_.d);
99
100 // -------------------------------
101 // Integrator update: anti-windup + allow unwind
102 // Rule: i_limit == 0 disables I (force i_ = 0)
103 // -------------------------------
104 if (param_.i > PID_SIGMA && param_.i_limit > PID_SIGMA)
105 {
106 Scalar i_candidate = i_ + E_K * dt;
107
108 if (std::isfinite(i_candidate))
109 {
110 // Clamp integrator state
111 i_candidate = std::clamp(i_candidate, -param_.i_limit, param_.i_limit);
112
113 bool accept = true;
114
115 // Output-limit-aware gating (windup prevention + unwind)
116 if (param_.out_limit > PID_SIGMA)
117 {
118 const Scalar OUT_BEFORE = OUTPUT_PD + (i_ * param_.i) + feed_forward_;
119 const Scalar OUT_AFTER = OUTPUT_PD + (i_candidate * param_.i) + feed_forward_;
120
121 if (std::isfinite(OUT_BEFORE) && std::isfinite(OUT_AFTER))
122 {
123 const bool BEFORE_SAT = (std::abs(OUT_BEFORE) > param_.out_limit);
124 const bool AFTER_SAT = (std::abs(OUT_AFTER) > param_.out_limit);
125
126 if (AFTER_SAT)
127 {
128 // If saturated (or would be), only allow integral update if it reduces
129 // saturation magnitude
130 // - If not saturated before but would saturate after: reject (prevent
131 // windup)
132 // - If already saturated: allow only if |out_after| < |out_before| (unwind)
133 accept = BEFORE_SAT && (std::abs(OUT_AFTER) < std::abs(OUT_BEFORE));
134 }
135 }
136 else
137 {
138 accept = false;
139 }
140 }
141
142 if (accept)
143 {
144 i_ = i_candidate;
145 }
146 }
147 }
148 else
149 {
150 // Disable I
151 i_ = Scalar(0);
152 }
153
154 const Scalar I_OUT = i_ * param_.i;
155
156 // Apply output limits
157 Scalar output = OUTPUT_PD + I_OUT + feed_forward_;
158 if (std::isfinite(output) && param_.out_limit > PID_SIGMA)
159 {
160 output = std::clamp(output, -param_.out_limit, param_.out_limit);
161 }
162
163 // Store states
164 last_err_ = ERR;
165 last_fb_ = fb; // store raw feedback
166 last_out_ = output;
167 last_der_ = fb_d_k; // store scaled derivative: k * d(fb)/dt
168 return output;
169 }
170
188 Scalar Calculate(Scalar sp, Scalar fb, Scalar fb_dot, Scalar dt)
189 {
190 if (!std::isfinite(sp) || !std::isfinite(fb) || !std::isfinite(fb_dot) ||
191 !std::isfinite(dt) || dt <= Scalar(0))
192 {
193 return last_out_;
194 }
195
196 // Compute error
197 const Scalar ERR = param_.cycle ? (CycleValue<Scalar>(sp) - fb) : (sp - fb);
198 const Scalar E_K = ERR * param_.k;
199
200 // Use externally provided derivative (scaled by k)
201 Scalar fb_d_k = fb_dot * param_.k;
202 if (!std::isfinite(fb_d_k))
203 {
204 fb_d_k = Scalar(0);
205 }
206
207 // Compute PD
208 const Scalar OUTPUT_PD = (E_K * param_.p) - (fb_d_k * param_.d);
209
210 // -------------------------------
211 // Integrator update: anti-windup + allow unwind
212 // Rule: i_limit == 0 disables I (force i_ = 0)
213 // -------------------------------
214 if (param_.i > PID_SIGMA && param_.i_limit > PID_SIGMA)
215 {
216 Scalar i_candidate = i_ + E_K * dt;
217
218 if (std::isfinite(i_candidate))
219 {
220 // Clamp integrator state
221 i_candidate = std::clamp(i_candidate, -param_.i_limit, param_.i_limit);
222
223 bool accept = true;
224
225 if (param_.out_limit > PID_SIGMA)
226 {
227 const Scalar OUT_BEFORE = OUTPUT_PD + (i_ * param_.i) + feed_forward_;
228 const Scalar OUT_AFTER = OUTPUT_PD + (i_candidate * param_.i) + feed_forward_;
229
230 if (std::isfinite(OUT_BEFORE) && std::isfinite(OUT_AFTER))
231 {
232 const bool BEFORE_SAT = (std::abs(OUT_BEFORE) > param_.out_limit);
233 const bool AFTER_SAT = (std::abs(OUT_AFTER) > param_.out_limit);
234
235 if (AFTER_SAT)
236 {
237 accept = BEFORE_SAT && (std::abs(OUT_AFTER) < std::abs(OUT_BEFORE));
238 }
239 }
240 else
241 {
242 accept = false;
243 }
244 }
245
246 if (accept)
247 {
248 i_ = i_candidate;
249 }
250 }
251 }
252 else
253 {
254 // Disable I
255 i_ = Scalar(0);
256 }
257
258 const Scalar I_OUT = i_ * param_.i;
259
260 // Apply output limits
261 Scalar output = OUTPUT_PD + I_OUT + feed_forward_;
262 if (std::isfinite(output) && param_.out_limit > PID_SIGMA)
263 {
264 output = std::clamp(output, -param_.out_limit, param_.out_limit);
265 }
266
267 // Store states
268 last_err_ = ERR;
269 last_fb_ = fb; // store raw feedback
270 last_out_ = output;
271 last_der_ = fb_d_k; // store scaled derivative: k * d(fb)/dt
272 return output;
273 }
274
276 void SetK(Scalar k) { param_.k = k; }
278 void SetP(Scalar p) { param_.p = p; }
280 void SetI(Scalar i) { param_.i = i; }
282 void SetD(Scalar d) { param_.d = d; }
284 void SetILimit(Scalar limit) { param_.i_limit = limit; }
286 void SetOutLimit(Scalar limit) { param_.out_limit = limit; }
287
289 Scalar K() const { return param_.k; }
291 Scalar P() const { return param_.p; }
293 Scalar I() const { return param_.i; }
295 Scalar D() const { return param_.d; }
297 Scalar ILimit() const { return param_.i_limit; }
299 Scalar OutLimit() const { return param_.out_limit; }
301 Scalar LastError() const { return last_err_; }
303 Scalar LastFeedback() const { return last_fb_; }
305 Scalar LastOutput() const { return last_out_; }
307 Scalar LastDerivative() const { return last_der_; }
308
313 void Reset()
314 {
315 i_ = 0;
316 last_err_ = 0;
317 last_fb_ = 0;
318 last_out_ = 0;
319 last_der_ = 0;
320 }
321
327 void SetIntegralError(Scalar err) { i_ = err; }
328
334 Scalar GetIntegralError() const { return i_; }
335
341 void SetFeedForward(Scalar feed_forward) { feed_forward_ = feed_forward; }
342
348 Scalar GetFeedForward() const { return feed_forward_; }
349
350 private:
351 Param param_;
352 Scalar i_ = 0;
353 Scalar last_err_ = 0;
354 Scalar last_fb_ = 0;
355 Scalar last_der_ = 0;
356 Scalar last_out_ = 0;
357 Scalar feed_forward_ = 0;
358};
359
360} // namespace LibXR
角度循环处理类,用于处理周期性角度计算。 A cyclic angle handling class for periodic angle calculations.
通用 PID 控制器类。 Generic PID controller.
Definition pid.hpp:27
Scalar D() const
获取 D 项系数 / Get derivative gain
Definition pid.hpp:295
void SetOutLimit(Scalar limit)
设置输出限幅 / Set output limit
Definition pid.hpp:286
Scalar ILimit() const
获取积分限幅 / Get integral limit
Definition pid.hpp:297
Scalar LastFeedback() const
获取上一次反馈值(未缩放) / Get last feedback (raw)
Definition pid.hpp:303
Scalar GetFeedForward() const
获取前馈项 / Get feedforward
Definition pid.hpp:348
Scalar P() const
获取 P 项系数 / Get proportional gain
Definition pid.hpp:291
Scalar LastDerivative() const
获取上一次导数(k * d(fb)/dt 或 k * fb_dot) / Get last derivative (scaled by k)
Definition pid.hpp:307
Scalar LastOutput() const
获取上一次输出 / Get last output
Definition pid.hpp:305
void SetD(Scalar d)
设置 D 项系数 / Set derivative gain
Definition pid.hpp:282
void Reset()
重置控制器状态。 Reset all internal states.
Definition pid.hpp:313
void SetP(Scalar p)
设置 P 项系数 / Set proportional gain
Definition pid.hpp:278
Scalar Calculate(Scalar sp, Scalar fb, Scalar dt)
使用反馈值计算 PID 输出。 Compute output from feedback only.
Definition pid.hpp:73
void SetILimit(Scalar limit)
设置积分限幅 / Set integral limit
Definition pid.hpp:284
Scalar I() const
获取 I 项系数 / Get integral gain
Definition pid.hpp:293
Scalar OutLimit() const
获取输出限幅 / Get output limit
Definition pid.hpp:299
Scalar Calculate(Scalar sp, Scalar fb, Scalar fb_dot, Scalar dt)
使用外部导数计算 PID 输出。 Compute output using external feedback derivative.
Definition pid.hpp:188
void SetIntegralError(Scalar err)
设置累计误差 / Set integral error
Definition pid.hpp:327
Scalar GetIntegralError() const
获取累计误差 / Get integral error
Definition pid.hpp:334
void SetI(Scalar i)
设置 I 项系数 / Set integral gain
Definition pid.hpp:280
void SetK(Scalar k)
设置全局比例系数 / Set global proportional gain
Definition pid.hpp:276
Scalar LastError() const
获取上一次误差 / Get last error
Definition pid.hpp:301
Scalar K() const
获取全局比例系数 / Get global proportional gain
Definition pid.hpp:289
PID(P &&p)
构造 PID 控制器。 Construct a PID controller.
Definition pid.hpp:52
void SetFeedForward(Scalar feed_forward)
设置前馈项 / Set feedforward
Definition pid.hpp:341
LibXR 命名空间 / LibXR namespace.
Definition ch32_can.hpp:14
PID 参数结构体。 Structure holding PID parameters.
Definition pid.hpp:34
Scalar i_limit
积分限幅 / Integral limit
Definition pid.hpp:39
Scalar out_limit
输出限幅 / Output limit
Definition pid.hpp:40
Scalar p
比例项 / Proportional gain
Definition pid.hpp:36
bool cycle
是否处理周期误差 / Whether input is cyclic
Definition pid.hpp:41
Scalar k
全局比例因子 / Global gain
Definition pid.hpp:35
Scalar i
积分项 / Integral gain
Definition pid.hpp:37
Scalar d
微分项 / Derivative gain
Definition pid.hpp:38