# C++ control runtime
Source: https://kaustubhkr.github.io/projects/control-runtime.html

← Flagship work (https://kaustubhkr.github.io/work.html) 04 / Control engineering 2025–2026

# Measured C++ control

Built a C++20 control core with explicit actuator limits and state-consistent controller switching.
System C++ control runtime
My contribution Control-library design & implementation
Access Public implementation

Engineering & evidence Arena-backed controller state, bumpless transfer and timing sweeps over 1–64 channels.
Recorded position response settles within ±2% at 342 ms in a simulated integrator plant. Response plots, percentiles and source CSVs are public.

Recorded measurements

## Control response. Execution cost.

Position tracking, cascade response and controller-update timing from the toolkit’s recorded CSV artifacts.
Step response Latency

### Step response

Image: Position response and applied command across 1000 samples, including the setpoint and 2 percent settling band (https://kaustubhkr.github.io/assets/control-response.svg)
Position-control example · 1 ms model step · 1,000 samples. Recorded response enters and stays in the ±2% band at 342 ms. This is a simulated integrator plant.

Image: Cascade position and velocity response with the applied actuator command (https://kaustubhkr.github.io/assets/control-cascade.svg)
Cascade example · 1 ms model step · 1,000 samples. Position overshoot 0.0904%; ±2% settling at 420 ms.

Position CSV ↓ (https://kaustubhkr.github.io/evidence/pid-position.csv) Cascade CSV ↓ (https://kaustubhkr.github.io/evidence/pid-cascade.csv) Plant & controller configuration ↗ (https://github.com/kaustubhkr/industrial-control-toolkit/blob/main/examples/pid/pid_position_control.cpp)

### Latency

Image: Recorded p50 p95 and p99 nanoseconds per update for 1 4 8 16 32 and 64 channels, showing both recorded sweeps (https://kaustubhkr.github.io/assets/control-latency.svg)
Raw PID + loop + timer measurements from both recorded sweeps. Each record summarises 200,000 timing batches, with 64 updates per batch after 10,000 warm-up updates.

This is an amortised update microbenchmark, not scheduler jitter or end-to-end robot latency. Hardware identity and run flags are absent from the CSV. The two recorded sweeps are shown separately; neither is labelled as a safety-on/off comparison.
Exact recorded percentiles
Sweep | Channels | p50 ns | p95 ns | p99 ns | p99.9 ns |
A | 1 | 51.188 | 52.469 | 65.391 | 76.453 |
A | 4 | 161.922 | 196.578 | 224.828 | 262.828 |
A | 8 | 294.859 | 330.453 | 356.469 | 426.766 |
A | 16 | 578.672 | 638.422 | 698.125 | 823.578 |
A | 32 | 1130.094 | 1267.703 | 1332.922 | 1566.734 |
A | 64 | 2297.484 | 2576.203 | 2643.047 | 3201.656 |
B | 1 | 56.484 | 63.203 | 74.125 | 92.547 |
B | 4 | 173.578 | 191.141 | 210.203 | 251.016 |
B | 8 | 318.266 | 424.547 | 471.344 | 491.781 |
B | 16 | 629.359 | 705.469 | 830.547 | 941.734 |
B | 32 | 1259.109 | 1411.266 | 1486.062 | 1839.609 |
B | 64 | 2508.844 | 2809.562 | 2860.547 | 3436.656 |

Original benchmark CSV ↓ (https://kaustubhkr.github.io/evidence/pid-benchmark.csv) Benchmark method ↗ (https://github.com/kaustubhkr/industrial-control-toolkit/blob/main/benchmarks/runners/bench_pid_vs_baseline.cc)

Technical deep dive Architecture, implementation & tradeoffs
System architecture C++20 / fixed-period control

### The actuator limit feeds back into state.

- 01

#### Compute

u_raw = P + I + D + FF Setpoint weighting, gain scheduling and Tustin-discretised derivative filtering.

- 02

#### Constrain

saturate → rate → jerk Preserve each stage’s effect and the final applied command.

- 03

#### Reconcile

u_applied − u_raw Feed constraint error into anti-windup; align internal state on transfer.

- 04

#### Supervise

health + watchdog Track timing, limiting hits and fallback state with the output.

↖ Applied command → anti-windup correction → next integrator state. The controller’s memory follows what the actuator was allowed to do.
Lifecycle Initialise → start → update → stop / reset
Memory Arena-allocated workspaces reused on the update path
Recording JSONL · MCAP · FlatBuffers · integrity checks

01 / Implementation

## Execution architecture

- Allocated working buffers from a memory arena during initialisation and reused them on the update path.

- Defined controller dimensions, start/stop/reset lifecycle, measurement-validity masks, fixed-tick timing and runtime health.

- Preserved the unconstrained and constrained commands separately so each limiting stage can report its effect.

02 / Implementation

## Control and constraints

- Implemented setpoint weighting, feedforward bias and a filtered derivative with Tustin discretisation.

- Added gain scheduling over configured breakpoints, with interpolation of gains and weights.

- Composed saturation, slew-rate and jerk constraints before anti-windup, plus bumpless state alignment and watchdog/fallback components.

03 / Implementation

## Inspectability

- Included checks for derivative kick, bumpless transfer, saturation, rate and jerk behaviour, scheduling and deterministic execution.

- Built JSONL and MCAP evidence-recorder paths, a FlatBuffers schema, hash/round-trip checks and example traces.

- Kept runtime health attached to the output: deadline misses, clamp magnitudes, limiting hits and anti-windup correction magnitude.

Engineering note

## The integrator must see the command that survives the constraints.

A controller can request far more than an actuator is allowed to deliver. Updating its internal state as though the raw request was executed creates a mismatch. The core preserves both values and feeds their difference into the anti-windup path after saturation, rate and jerk limiting.

Project context The implemented controller family is PID/IMC-PID. Broader controllers described in the repository roadmap are ongoing work.
Source on GitHub ↗ (https://github.com/kaustubhkr/industrial-control-toolkit)

Keep exploring

## One world. Multiple robots.

↗ (https://kaustubhkr.github.io/projects/multi-robot-runtime.html) Multi-robot autonomy runtime

END OF SECTION 9: projects/control-runtime.html
