> For the complete documentation index, see [llms.txt](https://renewables.docs.helinplatform.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://renewables.docs.helinplatform.com/sgm-product-docs/concepts/closed-loop-control.md).

# Closed Loop Control

PID-based feedback control that automatically maintains a target setpoint against changing site conditions.

Closed Loop Control is the SGM feature that turns a "set this asset to X" command into a continuous control problem. Instead of writing the value once and walking away, the platform reads a meter on every cycle, compares the actual reading to the target, and adjusts the asset's output to close the gap.

This is what makes SGM useful for grid-connection compliance, CBC contracts, and any scenario where the site's behaviour is influenced by uncontrolled loads or production you can't predict in advance.

{% hint style="info" %}
Closed Loop Control is configured by Helin during commissioning. The PID gains, time step, and fallback behaviour are tuned per site to match the assets and the response time of the meters they feed off. Contact support if you need to enable, disable, or retune it.
{% endhint %}

## Why closed loop?

Without feedback, a "produce 50 kW" command sets the inverter to 50 kW and that's it. If the site's local consumption changes, or another uncontrolled PV string starts producing, your actual grid feed-in is no longer 50 kW, it's whatever the inverter is producing minus whatever the site is consuming.

Closed Loop turns the same command into "**keep grid feed-in at 50 kW**". The platform monitors the grid meter, watches the actual feed-in, and continuously nudges the inverter setpoint up or down to hit that target. If consumption drops by 10 kW, the inverter scales back by the same amount. If a second PV string kicks in, the controlled inverter scales back to compensate.

```mermaid
flowchart LR
    A[Your system<br/>sends target setpoint] --> B[SGM<br/>closed loop controller]
    M[Grid meter<br/>actual reading] --> B
    B --> C{Reading vs target}
    C -->|At target| D[Hold output]
    C -->|Above target| E[Reduce inverter<br/>setpoint]
    C -->|Below target| F[Increase inverter<br/>setpoint]
    D & E & F --> G[Asset]
    G --> M

    style A fill:#dbeafe,stroke:#3b82f6,color:#000
    style M fill:#f3e8ff,stroke:#a855f7,color:#000
    style G fill:#dcfce7,stroke:#22c55e,color:#000
```

## How the controller works

A standard PID (Proportional-Integral-Derivative) controller drives the loop. On every time step, it calculates the **error**, the difference between the meter reading and the target setpoint, and uses that error to compute an adjustment.

* **Proportional** term reacts to the immediate error. Bigger gap, bigger adjustment.
* **Integral** term accumulates persistent error over time. If the proportional term keeps the system close to but not on target, the integral term nudges it the rest of the way.
* **Derivative** term smooths the response when the error is changing fast.
* **Anti-windup** prevents the integral term from ballooning during prolonged offsets (e.g., when the asset is already at its `max_value` and physically can't comply).
* **Rate limiting** caps how fast the setpoint can change, so the controller can't issue abrupt swings that destabilise the inverter or the grid.

All four terms are tuned during commissioning. The defaults work for most sites; aggressive tuning is reserved for sites with very fast meters or unusually responsive assets.

## Freshness and fallback

Closed Loop Control trusts its meter readings. If the meter goes stale, the controller can't make safe decisions.

* **Reading age threshold:** 10 seconds. Any reading older than this is considered stale.
* **On stale readings:** the controller stops adjusting and reverts the asset to a configured fallback setpoint until fresh readings resume.
* **On total meter loss:** the gateway escalates to the [watchdog](/sgm-product-docs/reference-docs/scheduling-setpoints.md#the-watchdog), which reverts every affected asset to its `default_value`.

This is the same defence-in-depth philosophy the rest of the platform uses: every layer assumes the layer above might fail and has its own safe fallback.

## Interaction with other setpoint sources

Closed Loop Control sits **above** scheduled setpoints and the cyclic writer in the priority hierarchy, but **below** direct API requests:

| Source             | What happens when closed loop is active                                         |
| ------------------ | ------------------------------------------------------------------------------- |
| Direct API request | Overrides the loop's target. The new value becomes the new target.              |
| Scheduled setpoint | Becomes the target when its time slot is active.                                |
| Watchdog           | Activates if no input arrives within `curtailment_time_limit`, the loop pauses. |
| Cyclic writer      | Bypassed; the closed loop is the writer.                                        |

So when a [scheduled setpoint](/sgm-product-docs/reference-docs/scheduling-setpoints.md) for an asset under closed loop says "70 kW from 12:00 to 14:00", the loop interprets that as "**hold the meter at 70 kW** for that window", not "set the inverter to 70 kW".

## When closed loop is the right answer

Use closed loop when **the thing you care about isn't the asset's output, it's a downstream measurement**.

* **CBC contract enforcement**, you care about the grid meter, not the inverter output. Local consumption changes, the inverter output should change to compensate.
* **SDE feed-in cap**, same shape. Cap is on grid export, not on PV production.
* **Limited-grid sites with mixed assets**, the loop coordinates the asset's actual contribution to the grid total.
* **Sites with uncontrolled PV**, a controlled inverter can absorb the variation from an uncontrolled one if both feed the same meter.

Use a [direct setpoint](/sgm-product-docs/reference-docs/scheduling-setpoints.md#direct-setpoints) without closed loop when **what you write is what you want**: a hard inverter cap, a curtailment instruction from a grid operator, or a manual operator override.

## Relevant pages

<table data-view="cards"><thead><tr><th></th><th></th></tr></thead><tbody><tr><td><p><i class="fa-clock">:clock:</i></p><p><a href="/sgm-product-docs/reference-docs/scheduling-setpoints.md"><strong>Scheduling setpoints</strong></a></p></td><td>How direct, scheduled, and watchdog setpoints feed into the closed loop.</td></tr><tr><td><p><i class="fa-rotate">:rotate:</i></p><p><a href="/sgm-product-docs/concepts/cyclic-writer.md"><strong>Cyclic Writer</strong></a></p></td><td>The simpler "rewrite-on-a-timer" alternative when feedback isn't needed.</td></tr><tr><td><p><i class="fa-network-wired">:network-wired:</i></p><p><a href="/sgm-product-docs/reference-docs/site-control.md"><strong>Site Control</strong></a></p></td><td>The orchestration layer that closed loop runs inside.</td></tr></tbody></table>


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://renewables.docs.helinplatform.com/sgm-product-docs/concepts/closed-loop-control.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
