32#ifndef PROTOCORE_CONTROL_H
33#define PROTOCORE_CONTROL_H
37#if PROTOCORE_ENABLE_CONTROL
45#define CONTROL_UNBOUNDED 1e30f
50#define CONTROL_LOG_HEADER "t_s,setpoint,measurement,output,dt_s"
60#define PID_LOG_MAGIC "DPID"
61#define PID_LOG_VERSION 1
62#define PID_LOG_HEADER_LEN 36
63#define PID_LOG_RECORD_LEN 16
64#define PID_LOG_STATUS_SATURATED 0x1u
94static inline float control_clamp(
float v,
float lo,
float hi)
96 return v < lo ? lo : (v > hi ? hi : v);
100static inline float control_deadband(
float v,
float band)
114static inline float control_slew(
float target,
float current,
float max_step)
116 float d = target - current;
119 return current + max_step;
123 return current - max_step;
129static inline float control_lpf(
float prev,
float sample,
float alpha)
131 return prev + alpha * (sample - prev);
137static inline float pid_step_(Pid *p,
float setpoint,
float measurement,
float dt,
float inv_dt)
139 float error = setpoint - measurement;
146 deriv = -(measurement - p->prev_meas) * inv_dt;
147 p->d_filt = (p->d_alpha > 0.0f) ? p->d_filt + p->d_alpha * (deriv - p->d_filt) : deriv;
149 p->prev_meas = measurement;
153 float integ_next = control_clamp(p->integ + p->ki * error * dt, p->integ_min, p->integ_max);
156 float unclamped = p->kp * error + integ_next + p->kd * p->d_filt + p->kff * setpoint;
157 float out = control_clamp(unclamped, p->out_min, p->out_max);
162 proto_bool worsen_high = (unclamped > p->out_max) && (error > 0.0f);
163 proto_bool worsen_low = (unclamped < p->out_min) && (error < 0.0f);
164 if (!worsen_high && !worsen_low)
166 p->integ = integ_next;
179static inline float pid_update(Pid *p,
float setpoint,
float measurement,
float dt)
181 if (!p || dt <= 0.0f)
185 return pid_step_(p, setpoint, measurement, dt, 1.0f / dt);
193static inline float pid_update_fixed(Pid *p,
float setpoint,
float measurement)
195 if (!p || p->dt <= 0.0f)
199 return pid_step_(p, setpoint, measurement, p->dt, p->inv_dt);
217} ControlPidSetOutputLimitsArgs;
225} ControlPidSetIntegralLimitsArgs;
232} ControlPidSetDerivativeFilterArgs;
239} ControlPidSetFeedforwardArgs;
246} ControlPidSetRateArgs;
252} ControlPidResetArgs;
258 const float *setpoint;
259 const float *measurement;
263} ControlPidUpdateNArgs;
272} ControlPidLogHeaderArgs;
283} ControlPidLogRecordArgs;
327 ControlPidInitArgs pid_init_args;
328 ControlPidSetOutputLimitsArgs pid_set_output_limits_args;
329 ControlPidSetIntegralLimitsArgs pid_set_integral_limits_args;
330 ControlPidSetDerivativeFilterArgs pid_set_derivative_filter_args;
331 ControlPidSetFeedforwardArgs pid_set_feedforward_args;
332 ControlPidSetRateArgs pid_set_rate_args;
333 ControlPidResetArgs pid_reset_args;
334 ControlPidUpdateNArgs pid_update_n_args;
335 ControlPidLogHeaderArgs pid_log_header_args;
336 ControlPidLogRecordArgs pid_log_record_args;
342extern ControlVars ControlV;
347 void (*
const pid_init)(uint8_t *work);
348 void (*
const pid_set_output_limits)(uint8_t *work);
349 void (*
const pid_set_integral_limits)(uint8_t *work);
350 void (*
const pid_set_derivative_filter)(uint8_t *work);
351 void (*
const pid_set_feedforward)(uint8_t *work);
352 void (*
const pid_set_rate)(uint8_t *work);
353 void (*
const pid_reset)(uint8_t *work);
354 void (*
const pid_update_n)(uint8_t *work);
355 void (*
const pid_log_header)(uint8_t *work);
356 void (*
const pid_log_record)(uint8_t *work);
361void protocore_control_pid_init(uint8_t *work);
362void protocore_control_pid_set_output_limits(uint8_t *work);
363void protocore_control_pid_set_integral_limits(uint8_t *work);
364void protocore_control_pid_set_derivative_filter(uint8_t *work);
365void protocore_control_pid_set_feedforward(uint8_t *work);
366void protocore_control_pid_set_rate(uint8_t *work);
367void protocore_control_pid_reset(uint8_t *work);
368void protocore_control_pid_update_n(uint8_t *work);
369void protocore_control_pid_log_header(uint8_t *work);
370void protocore_control_pid_log_record(uint8_t *work);
376static const ControlNs Control __attribute__((unused)) = {
377 .pid_init = protocore_control_pid_init,
378 .pid_set_output_limits = protocore_control_pid_set_output_limits,
379 .pid_set_integral_limits = protocore_control_pid_set_integral_limits,
380 .pid_set_derivative_filter = protocore_control_pid_set_derivative_filter,
381 .pid_set_feedforward = protocore_control_pid_set_feedforward,
382 .pid_set_rate = protocore_control_pid_set_rate,
383 .pid_reset = protocore_control_pid_reset,
384 .pid_update_n = protocore_control_pid_update_n,
385 .pid_log_header = protocore_control_pid_log_header,
386 .pid_log_record = protocore_control_pid_log_record,
#define PROTOCORE_BEGIN_DECLS
Give a header's declarations C linkage, so their symbol names carry no parameter types.
_Bool proto_bool
The truth value.
#define PROTO_TRUE
the true value, spelled so a caller never writes a bare 1
#define PROTOCORE_END_DECLS