Net Zero Strategy
The Net Zero strategy minimises grid import and export by continuously adjusting the battery setpoint to match site generation and consumption. It uses a PID controller as its feedback loop, with each control cycle reading live energy-flow metrics, computing a new setpoint, and dispatching a SetBatterySetpointCommand.
How It Works
Each time the strategy fires it runs through this pipeline:
Net Grid Power
The error signal fed into the PID controller is the net grid power - the power that the site is currently drawing from (or exporting to) the grid, after accounting for any charging power already supplied by the battery:
netGridPower = batteryPower < 0
? gridPower − |batteryPower| // battery is charging, subtract from grid
: gridPower // battery is discharging or idleA positive value means the site is net-importing; the strategy will command the battery to discharge to compensate. A negative value means the site is net-exporting; the strategy will command the battery to charge.
SoC Guard
Before dispatching any setpoint the strategy checks that the battery SoC is within its configured MinStateOfCharge/MaxStateOfCharge range. If the SoC is outside these limits the cycle is skipped entirely - no command is sent and the PID state is left unchanged. See Common Behaviour for the base class pattern.
Deadband
If |netGridPower| is less than ControlParameters.Deadband (default 500 W) no command is sent. Instead the PID integral is decayed by PidTuning.IntegralDecayFactor (default 0.5) to prevent integral windup when the site is already close to balance. The integral is then re-seeded via SetState before the next cycle.
Output Pipeline
After the raw PID output is produced it passes through three transforms in order:
| Step | Description | Parameter |
|---|---|---|
| Clamp | Constrains output to [−MaxChargePower, MaxDischargePower] so the command never exceeds the hardware limit. | BatteryConfiguration.MaxChargePower / MaxDischargePower |
| Quantize | Rounds to the nearest multiple of DeviceStep using round-half-away-from-zero, matching device resolution. | ControlParameters.DeviceStep (default 100 W) |
| Rate limit | Caps the change between cycles to prevent large step changes. Uses the last known setpoint from the distributed cache as the reference. Cache entries expire after 5 minutes. | ControlParameters.MaxRateOfChange (default 500 W) |
Setpoint Mode
The final numeric setpoint is mapped to a BatterySetpointMode:
| Condition | Mode |
|---|---|
|setpoint| < 1 W | Idle |
setpoint < 0 | Charging (value sent as positive magnitude) |
setpoint > 0 | Discharging |
Configuration
PID and control parameters are stored per battery asset in the platform database and loaded by GetBatteryConfiguration on each control cycle.
PID Tuning (PidTuning)
| Parameter | Default | Description |
|---|---|---|
ProportionalGain | 0.8 | Kp - scales correction proportional to current error |
IntegralGain | 0.02 | Ki - eliminates steady-state error over time |
DerivativeGain | 2.5 | Kd - dampens correction based on rate of change |
IntegralDecayFactor | 0.5 | Multiplier applied to the integral when within deadband (0 = reset, 1 = no decay) |
Control Parameters (ControlParameters)
| Parameter | Default | Description |
|---|---|---|
Deadband | 500 W | Minimum grid imbalance before a setpoint is sent |
MaxRateOfChange | 500 W | Maximum setpoint change per control cycle |
DeviceStep | 100 W | Setpoints are quantized to multiples of this value |
PID Controller
The PidController class in Voltimax.Edge.Gateway.Common implements the standard discrete PID formula:
Where the integral and derivative are approximated using the wall-clock time delta (dt) between control cycles. On the first call dt defaults to 10 seconds as a stable fallback. Backwards-in-time timestamps are rejected with an ArgumentException.
Gain Hot-Reload
The strategy holds a single PidController instance for the lifetime of a gateway run. If the PID gains are changed via the API (e.g. by updating PidTuning in the platform), the controller is recreated on the next cycle and state is reset. Gains that are unchanged reuse the existing controller, preserving the accumulated integral.
Behaviour Tests
Gateway mode gating is covered by GatewayModeBehaviorTests - see Strategies overview.
The underlying PidController is tested exhaustively in PidControllerTests (Voltimax.Edge.Gateway.Tests), including:
- First-call default
dtsemantics - Integral accumulation and decay (
DecayIntegral) - State injection via
SetState - Backwards-time and NaN rejection
- Real-world convergence scenario with default gains
Kp=0.8,Ki=0.02,Kd=2.5 - Deadband simulation (build integral → decay → verify halving)
No direct unit tests for NetZeroStrategy
NetZeroStrategy itself does not yet have dedicated unit or integration tests. The PID arithmetic is covered by PidControllerTests and the mode-gate behaviour is covered by GatewayModeBehaviorTests.