Skip to content

Commit 0e44596

Browse files
committed
Merge branch 'develop'
2 parents 4c126d9 + c5508cc commit 0e44596

9 files changed

Lines changed: 1490 additions & 23 deletions

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
*.$*

CHANGE_LOG.md

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# Changelog
2+
All notable changes to this project/module will be documented in this file.
3+
4+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
5+
and this project/module adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6+
7+
---
8+
## V1.0.0 - 27.08.2025
9+
10+
### Added
11+
- PID controller written to be used as instance
12+
- Dual init option: dynamically and statically
13+
- Fully configurable in runtime
14+
- Support for **derivative on measurement** (no setpoint kick).
15+
- Optional **low-pass filter (LPF)** for derivative term with configurable cutoff frequency.
16+
- **Feed-forward input** included in the control law.
17+
- `pid_set_d_lpf_fc()` API to adjust derivative LPF cutoff at runtime.
18+
- New API functions for min/max setters: `pid_set_min()`, `pid_set_max()`.
19+
- Telemetry accessors for P/I/D parts: `pid_get_p_part()`, `pid_get_i_part()`, `pid_get_d_part()`.
20+
21+
---

LICENSE

Lines changed: 0 additions & 21 deletions
This file was deleted.

README.md

Lines changed: 113 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,113 @@
1-
# pid_controller
2-
PID controller implementation in C for general use
1+
# **PID Controller**
2+
Tiny, fast, and portable **PID controller in C** for embedded systems.
3+
Designed for deterministic, low-overhead control loops with optional feed-forward, filtered derivative, and robust anti-windup.
4+
5+
## ✨ Features
6+
7+
- **P + I + D (on measurement)** → no setpoint kick; derivative operates on the measured value.
8+
- **First-order LPF on D** → configurable cutoff frequency, state reset to avoid startup spikes.
9+
- **Single saturation point** → only the final output is clamped, ensuring predictable behavior.
10+
- **Back-calculation anti-windup** → integral state unwinds quickly when saturated.
11+
- **Feed-forward input** for model-based enhancements.
12+
- **Precomputed gains**`Ki*Ts` and `Kd/Ts` calculated once at init for minimal runtime math.
13+
- **Instance-based API** → multiple controllers supported, either dynamically or statically allocated.
14+
- **MIT Licensed**.
15+
16+
Top level diagram:
17+
18+
![](doc/pid_controller_V1_0_0_diagram.png)
19+
20+
21+
## **Dependencies**
22+
23+
### **1. Utils Module**
24+
[Utils](https://github.com/GeneralEmbeddedCLibraries/utils) module must take following path:
25+
```
26+
"root/common/utils/src/utils.h"
27+
```
28+
29+
### **2. Filter Module**
30+
[Filter](https://github.com/GeneralEmbeddedCLibraries/filter) module must take following path:
31+
```
32+
"root/middleware/filter/src/filter.h"
33+
```
34+
35+
## **General Embedded C Libraries Ecosystem**
36+
In order to be part of *General Embedded C Libraries Ecosystem* this module must be placed in following path:
37+
```
38+
root/middleware/pid_controller/"module_space"
39+
```
40+
41+
## **General API**
42+
43+
| API Functions | Description | Prototype |
44+
| --- | ----------- | ----- |
45+
| **pid_init** | Initialization of PID controller | pid_status_t pid_init(p_pid_t * p_inst, const pid_cfg_t * const p_cfg) |
46+
| **pid_init_static** | Static initialization of PID controller | pid_status_t pid_init_static(p_pid_t pid_inst, const pid_cfg_t * const p_cfg) |
47+
| **pid_is_init** | Get initialization status | bool pid_is_init(p_pid_t pid_inst) |
48+
| **pid_hndl** | Main handler of PID controller | float32_t pid_hndl(p_pid_t pid_inst, const pid_in_t * const p_in) |
49+
| **pid_set_cfg** | Set controller settings | pid_status_t pid_set_cfg(p_pid_t pid_inst, const pid_cfg_t * const p_cfg) |
50+
| **pid_get_cfg** | Get controller settings | const pid_cfg_t * pid_get_cfg(p_pid_t pid_inst) |
51+
| **pid_reset** | Reset PID controller | pid_status_t pid_reset(p_pid_t pid_inst) |
52+
| **pid_set_kp** | Set PID controller Kp | pid_status_t pid_set_kp(p_pid_t pid_inst, const float32_t kp) |
53+
| **pid_get_kp** | Get PID controller Kp | float32_t pid_get_kp(p_pid_t pid_inst) |
54+
| **pid_set_ki** | Set PID controller Ki | pid_status_t pid_set_ki(p_pid_t pid_inst, const float32_t ki) |
55+
| **pid_get_ki** | Get PID controller Ki | float32_t pid_get_ki(p_pid_t pid_inst) |
56+
| **pid_set_kd** | Set PID controller Kd | pid_status_t pid_set_kd(p_pid_t pid_inst, const float32_t kd) |
57+
| **pid_get_kd** | Get PID controller Kd | float32_t pid_get_kd(p_pid_t pid_inst) |
58+
| **pid_set_min** | Set PID controller minimum output | pid_status_t pid_set_min(p_pid_t pid_inst, const float32_t min) |
59+
| **pid_get_min** | Get PID controller minimum output | float32_t pid_get_min(p_pid_t pid_inst) |
60+
| **pid_set_max** | Set PID controller maximum output | pid_status_t pid_set_max(p_pid_t pid_inst, const float32_t max) |
61+
| **pid_get_max** | Get PID controller maximum output | float32_t pid_get_max(p_pid_t pid_inst) |
62+
| **pid_set_d_lpf_fc** | Set PID controller D part LPF fc | pid_status_t pid_set_d_lpf_fc(p_pid_t pid_inst, const float32_t fc) |
63+
| **pid_get_d_lpf_fc** | Get PID controller D part LPF fc | float32_t pid_get_d_lpf_fc(p_pid_t pid_inst) |
64+
| **pid_get_out** | Get PID controller output | float32_t pid_get_out(p_pid_t pid_inst) |
65+
| **pid_get_err** | Get PID controller error | float32_t pid_get_err(p_pid_t pid_inst) |
66+
| **pid_get_p_part** | Get PID controller P part | float32_t pid_get_p_part(p_pid_t pid_inst) |
67+
| **pid_get_i_part** | Get PID controller I part | float32_t pid_get_i_part(p_pid_t pid_inst) |
68+
| **pid_get_d_part** | Get PID controller D part | float32_t pid_get_d_part(p_pid_t pid_inst) |
69+
70+
71+
## **Usage**
72+
73+
```C
74+
#include "pid.h"
75+
76+
// Controller instance (dynamic or static)
77+
static p_pid_t pid = NULL;
78+
79+
// Controller configuration (1 kHz loop)
80+
static const pid_cfg_t cfg = {
81+
.kp = 1.0f,
82+
.ki = 10.0f,
83+
.kd = 0.0f, // set >0 to enable D
84+
.ts = 1e-3f, // 1 kHz
85+
.min = 0.0f,
86+
.max = 1.0f,
87+
.d_lpf_fc = 0.0f // set >0 to enable derivative LPF
88+
};
89+
90+
int app_init(void)
91+
{
92+
if (ePID_OK != pid_init(&pid, &cfg)) {
93+
// handle error
94+
return -1;
95+
}
96+
return 0;
97+
}
98+
99+
void control_loop_1kHz(void)
100+
{
101+
pid_in_t in = {
102+
.ref = /* setpoint */,
103+
.act = /* measurement */,
104+
.ff = /* feed-forward (optional) */
105+
};
106+
107+
// Compute control output
108+
const float32_t u = pid_hndl(pid, &in);
109+
110+
// Apply output u to actuator...
111+
}
112+
```
113+

doc/pid_controller.drawio

Lines changed: 426 additions & 0 deletions
Large diffs are not rendered by default.
61.6 KB
Loading
64.1 KB
Loading

0 commit comments

Comments
 (0)