84 lines
3.8 KiB
Markdown
84 lines
3.8 KiB
Markdown
|
|
---
|
|||
|
|
title: Flow-based mode
|
|||
|
|
mode: flowbased
|
|||
|
|
tier: 2
|
|||
|
|
status: placeholder
|
|||
|
|
updated: 2026-04-22
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
# Flow-based mode — *Tier 2 template*
|
|||
|
|
|
|||
|
|
> **Status — not yet implemented.** The `flowbased` entry is a placeholder in `_controlLogic`. This page reserves the shape and documents the intended design so all Tier-2 modes share the same layout.
|
|||
|
|
|
|||
|
|
## At a glance
|
|||
|
|
|
|||
|
|
| Item | Value |
|
|||
|
|
|---|---|
|
|||
|
|
| Tier | 2 — parameterised transfer function |
|
|||
|
|
| Signal driving demand | measured outflow (actual pumps) |
|
|||
|
|
| Secondary inputs | integrator + derivative state (for PID) |
|
|||
|
|
| Output | demand 0–100 % via PID correction |
|
|||
|
|
| Thresholds adjusted at runtime? | No (but the demand can move independently of level) |
|
|||
|
|
| Use when | The station has a flow sensor on the outlet and you want to hold a target outflow rate regardless of basin level |
|
|||
|
|
|
|||
|
|
## Diagram
|
|||
|
|
|
|||
|
|
**Primary plot.** Demand vs *outflow-error* (not level!) is the meaningful transfer function for flow-based control. The curve is a classic PID surface — proportional slope times error, plus integral + derivative terms.
|
|||
|
|
|
|||
|
|
**Secondary plot.** Level still enters as gates (STOP below `minLevel`, don't overfill above `maxLevel`) — same thresholds as levelbased, but the mode doesn't *use* level to pick demand.
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
Placeholder image — replace with:
|
|||
|
|
diagrams/modes/flowbased.drawio.svg (demand vs outflow-error, showing Kp slope)
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## Inputs
|
|||
|
|
|
|||
|
|
| Signal | Where from | Role |
|
|||
|
|
|---|---|---|
|
|||
|
|
| measured outflow | sum of `flow.measured.*` at outflow positions | error = (flowSetpoint − measuredOutflow) |
|
|||
|
|
| `config.control.flowBased.flowSetpoint` | editor, static | target outflow in m³/h |
|
|||
|
|
| `config.control.flowBased.flowDeadband` | editor, static | zone around setpoint where PID output holds |
|
|||
|
|
| `config.control.flowBased.pid.{kp, ki, kd, ...}` | editor / schema | PID gains + rate limits |
|
|||
|
|
| current level | fallback → threshold gates | only used for `minLevel`/`maxLevel` bounds |
|
|||
|
|
|
|||
|
|
## Threshold policy
|
|||
|
|
|
|||
|
|
The **control** thresholds (`minLevel`, `startLevel`, `maxLevel`) are still enforced but for different reasons than levelbased:
|
|||
|
|
|
|||
|
|
| Threshold | Role in flowbased |
|
|||
|
|
|---|---|
|
|||
|
|
| `minLevel` | If level drops below, force demand=0 regardless of PID output (prevents pump undercut) |
|
|||
|
|
| `startLevel` | unused — demand is driven by error, not level |
|
|||
|
|
| `maxLevel` | If level climbs above, force demand=100 regardless of PID output (prevents spill) |
|
|||
|
|
|
|||
|
|
## Demand formula
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
error = flowSetpoint − measuredOutflow
|
|||
|
|
|
|||
|
|
if level < minLevel:
|
|||
|
|
demand = 0 # pump-undercut guard
|
|||
|
|
elif level > maxLevel:
|
|||
|
|
demand = 100 # anti-spill guard
|
|||
|
|
else:
|
|||
|
|
# normal PID branch
|
|||
|
|
P = Kp × error
|
|||
|
|
I += Ki × error × dt # with anti-windup clamp
|
|||
|
|
D = Kd × d(error)/dt # with low-pass filter
|
|||
|
|
demand = clamp(P + I + D, 0, 100) # with rate limits Δup/Δdown
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## Edge cases
|
|||
|
|
|
|||
|
|
- **Cold start, no prior outflow measurement.** PID state starts at 0; first error is `flowSetpoint`. Integral term will build up — rate-limit the demand ramp to avoid over-shoot.
|
|||
|
|
- **Sensor dropout on the outflow meter.** Fall back to predicted outflow (sum of pump curve predictions). Log a warning — PID on predicted-only is unreliable.
|
|||
|
|
- **Setpoint step change.** PID with derivative filter + rate limits handles this gracefully; without filter, the D-kick would saturate output.
|
|||
|
|
- **Safety layer interaction.** Same as levelbased — `dryRunLevel` and `overflowLevel` override the PID output. See [functional description § Safety](../functional-description.md#safety-controller).
|
|||
|
|
|
|||
|
|
## Related
|
|||
|
|
|
|||
|
|
- [Functional description](../functional-description.md) — basin model + shared safety layer
|
|||
|
|
- [modes/README.md](README.md) — mode index + page template
|
|||
|
|
- [modes/levelbased.md](levelbased.md) — Tier 1 reference implementation
|