controller.py, simulator.py and
safety.py from the AEGIS repo, in your browser.
The two halves of the aiming loop: a PID controller driving the pan/tilt gimbal onto a moving target, and the safety gate that decides whether firing is permitted: track all, fire only on the allowlist. Both run the actual turret code, live via Pyodide.
SafetyGate.evaluate(): track all, fire only on the allowlist.Because the plant integrates, the turret holds its angle with zero command, so it reaches a fixed target with no offset even at Ki = 0. Integral mostly rejects bias, not error.
Why Ki=0 still settlesPush Ki up on the step or sine: the loop overshoots and oscillates. The integral keeps winding while the error is already being corrected, so it overshoots before unwinding.
The wind-upPlain PID always trails a moving target: it needs an error to act. Tick feedforward and the crosshair snaps onto the target; add lead and it sits ahead by the dart's flight-time.
The α-β velocity estimateArm, an allowlist, a hard denylist, a lock and a proximity interlock must all pass. The denylist overrides everything: a recognised person can never be a target, even if mis-added.
Every layer