|
ProtoCore v0.0.1
Deterministic, zero-heap network stack for embedded targets
|
Layer: L7 Application · Build flags: PC_ENABLE_CONTROL
services/control is a zero-heap, FPU-accelerated PID controller. Tuning a PID by hand on hardware is slow and risky, so this example shows the offline workflow: the device runs the loop and records it, you pull the run down as a CSV, and tools/pid_tune.py identifies the plant, simulates candidate gains, and hands you better Kp/Ki/Kd — no trial-and-error on the motor.
The control loop runs at a fixed rate and records each step:
So it runs on any ESP32 with no extra hardware, plant_read()/plant_write() here are a simulated first-order process. To tune a real loop, replace those two functions with your sensor read and actuator write — nothing else changes.
The run is served as a self-describing CSV at GET /log.csv: a # pid ... metadata line (the gains + limits + rate the run used, so the tuner needs no flags), then the t_s,setpoint,measurement,output,dt_s columns.
pid_tune.py reproduces control.cpp's PID exactly (derivative-on-measurement, conditional anti-windup, clamping), so the simulation matches what the device will actually do. It prints the identified plant fit and a suggested gain set, e.g.:
Edit KP / KI / KD at the top of the sketch to the suggestion, re-flash, then GET /reset to record and re-check a fresh run. Other modes: --sweep (compare 0.5x / 1x / 2x the gains), or --kp N --ki N --kd N to simulate one set. The tuner needs numpy (+ matplotlib for the plot, scipy for --autotune): python -m pip install numpy matplotlib scipy.
Tip. The best identification comes from a run with real excitation - the square-wave setpoint here steps the plant so the fit is well-conditioned. A loop that just holds one setpoint gives the tuner little to learn from.
Flash, open Serial @ 115200 for the IP, then run the tuning loop above. GET / shows the current gains + live state and the exact commands.