Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rethink inverter control methodology #68

Open
longzheng opened this issue Dec 13, 2024 · 5 comments
Open

Rethink inverter control methodology #68

longzheng opened this issue Dec 13, 2024 · 5 comments

Comments

@longzheng
Copy link
Owner

longzheng commented Dec 13, 2024

Just making some notes about my thoughts and welcome any feedback/input others may have.

Currently the application applies export/generation limits by actively controlling the inverter active power output.

The desired inverter active power output (as a % to nameplate capacity) is calculated by

// assume site 
// positive = import
// negative = export

targetSolar = -(site) - exportLimit
targetActivePowerOutput = targetSolar / totalNameplateCapacity

The actual values are derived by a time-weighted average of the last 5 seconds (default) of samples from controlled inverters and meter.

The active power output is then applied to all controlled inverters, which would effectively reduce the maximum power output to the target solar value.

The pros of this approach is that it makes it possible to control multiple inverters with different capacities with ease, as it effectively pools all inverters as one resource. It also allows controller inverters which either do not have a meter attached or does not support load following export limits (or a mix of inverters with meters and without).

The cons of this approach is that it requires the software to "take over" any native load following export limit logic in the inverters which is usually more responsive

This approach is also going to be more complex as I need to handle battery control. The intention is that the software would manually control the charge and discharge rate of batteries to take advantage of market pricing.

One challenge is how hybrid inverters handles setting "target inverter output". Does it restrict just the solar power output or does it also affect the battery power output?

Alternatively I'm thinking of allowing some inverters (with meter attached and has export limit capabililty) to be controlled by setting export limits, and other inverters (without meter) to be controlled by active power output.

@CpuID
Copy link
Contributor

CpuID commented Dec 21, 2024

One challenge is how hybrid inverters handles setting "target inverter output". Does it restrict just the solar power output or does it also affect the battery power output?

Some brainstorm notes when it comes to Fronius:

  • Based on our private conversation where you mentioned:

I think it would limit any DC<>AC conversion so would affect both export and import from the inverter

because there are 2 different sets of registers, I guess we'd need to determine do WMaxLimPct and InWRte / OutWRte play nice together or do they fight with eachother... concur? or potentially what takes precedence in different situations?


I can see in the SunSpec you're relying on a few registers currently, pretty sure these are the ones defined in https://github.com/longzheng/open-dynamic-export/blob/main/src/inverter/sunspec/index.ts#L273-L286

From Gen24_Primo_Symo_Inverter_Register_Map_Float_storage_ROW.xlsx:

<style type="text/css"></style>

Start End Size R/W Functioncodes Name Description Type Units Scale Factor Rangeof values
40243 40243 1 RW 0x030x060x10 WMaxLimPct Set power output to specified level. uint16 % WMax WMaxLimPct_SF 0% - 100%
40244 40244 1 RW 0x030x060x10 WMaxLimPct_WinTms Time window for power limit change. uint16 Secs   0 - 300
40245 40245 1 RW 0x030x060x10 WMaxLimPct_RvrtTms Timeout period for power limit. uint16 Secs   0 - 28800
40246 40246 1 RW 0x030x060x10 WMaxLimPct_RmpTms Ramp time for moving from current setpoint to new setpoint. uint16 Secs   0 - 65534
40247 40247 1 RW 0x030x060x10 WMaxLim_Ena Enumerated valued. Throttle enable/disable control. enum16     0: Disabled1: Enabled

There's also these registers, which according to a Home Assistant forum post are used to control battery charge/discharge:

Discharge: OutWRte (40366)
Charge: InWRte (40367)
Charge Control: StorCtl_Mod (40359)

If using HA this varies though? TBC, see the post for details, may be irrelevant here...

Effectively you will need to use the following addresses in HA:
Discharge: OutWRte becomes 40365
Charge: InWRte becomes 40366
Charge Control: StorCtl_Mod becomes 40358

The important part:

Which registers do I need to set when I want to (dis)charge with xxx watts ?

StorCtl_Mod set to 3 (binary 11) to allow charge + discharge (this should be the default!)
OutWRte set to XXXX (where XXXX is the numeric value of your desired battery output in Watt, e.g. 200 will result in a discharge of 200W and 4500 in a maximum output of 4,5kW - note: depending on the size of your batteries, different scaling factors might apply

The register specs for these from Gen24_Primo_Symo_Inverter_Register_Map_Float_storage_ROW.xlsx:

<style type="text/css"></style>

Start End Size R/W Functioncodes Name Description Type Units Scale Factor Rangeof values
40354 40354 1 R 0x03 ID Well-known value. Uniquely identifies this as a sunspec model storage (124) uint16     124
40355 40355 1 R 0x03 L Length of sunspec model storage (124) uint16 Registers   24
40356 40356 1 R 0x03 WChaMax Setpoint for maximum charge.Additional Fronius description:Reference Value for maximum Charge and Discharge.Multiply this value by InWRte to define maximum charging and OutWRte to define maximum discharging.Every rate between this two limits is allowed.The inverter is not fully capable of transferring power as reported by this reference value.Note that InWRte and OutWRte can be negative to define ranges for charging and discharging only uint16 W WChaMax_SF  
40357 40357 1 R 0x03 WChaGra Setpoint for maximum charging rate. Default is MaxChaRte. uint16 % WChaMax/sec WChaDisChaGra_SF 100%
40358 40358 1 R 0x03 WDisChaGra Setpoint for maximum discharge rate. Default is MaxDisChaRte. uint16 % WChaMax/sec WChaDisChaGra_SF 100%
40359 40359 1 RW 0x030x060x10 StorCtl_Mod Activate hold/discharge/charge storage control mode. Bitfield value.Additional Fronius description:Active hold/discharge/charge storage control mode.Set the charge field to enable charging and the discharge field to enable discharging. bitfield16     bit 0: CHARGEbit 1: DiSCHARGE
40360 40360 1 R 0x03 VAChaMax Setpoint for maximum charging VA. uint16 VA VAChaMax_SF not supported
40361 40361 1 RW 0x030x060x10 MinRsvPct Setpoint for minimum reserve for storage as a percentage of the nominal maximum storage. uint16 % WChaMax MinRsvPct_SF  
40362 40362 1 R 0x03 ChaState Currently available energy as a percent of the capacity rating. uint16 % AhrRtg ChaState_SF  
40363 40363 1 R 0x03 StorAval State of charge (ChaState) minus storage reserve (MinRsvPct) times capacity rating (AhrRtg). uint16 AH StorAval_SF not supported
40364 40364 1 R 0x03 InBatV Internal battery voltage. uint16 V InBatV_SF not supported
40365 40365 1 R 0x03 ChaSt Charge status of storage device. Enumerated value. enum16     1: OFF2: EMPTY3: DISCHAGING4: CHARGING5: FULL6: HOLDING7: TESTINGThe status TESTING is used during battery calibration or service charge.
40366 40366 1 RW 0x030x060x10 OutWRte Percent of max discharge rate.Additional Fronius description:Defines maximum Discharge rate. If not used than the default is 100 and WChaMax defines max. Discharge rate.See WChaMax for details int16 % WDisChaMax InOutWRte_SF valid range -100.00% - +100.00%Please note that this register has a scale factor in Register InOutWRte_SF, so for InOutWRte_SF = -2 the valid range in raw values is from -10000 to 10000.Please be aware that setting an invalid power window will result in a modbus exception 3 (ILLEGAL DATA VALUE).Invalid power windows are all windows where condition:((StorCtl_Mod == 3) AND ((-1) * InWRtg > OutWRtg))evaluates to true.This can happen for example if two negative values are written into InWRtg and OutWRtg and both limits are activated by StorCtl_Mod = 3.
40367 40367 1 RW 0x030x060x10 InWRte Percent of max charging rate.Additional Fronius description:Defines maximum Charge rate. If not used than the default is 100 and WChaMax defines max. Charge rate.See WChaMax for details int16 % WChaMax InOutWRte_SF valid range -100.00% - +100.00%Please note that this register has a scale factor in Register InOutWRte_SF, so for InOutWRte_SF = -2 the valid range in raw values is from -10000 to 10000.Please be aware that setting an invalid power window will result in a modbus exception 3 (ILLEGAL DATA VALUE).Invalid power windows are all windows where condition:((StorCtl_Mod == 3) AND ((-1) * InWRtg > OutWRtg))evaluates to true.This can happen for example if two negative values are written into InWRtg and OutWRtg and both limits are activated by StorCtl_Mod = 3.

@CpuID
Copy link
Contributor

CpuID commented Dec 21, 2024

Another consideration: if the max inverter output and battery charge/discharge can be managed independently at all - does that open up the need for a new limiter? Or maybe adjusting an existing limiter? Where you specify "desired max battery SoC %"?

@longzheng
Copy link
Owner Author

longzheng commented Dec 22, 2024

Another consideration: if the max inverter output and battery charge/discharge can be managed independently at all - does that open up the need for a new limiter? Or maybe adjusting an existing limiter? Where you specify "desired max battery SoC %"?

I've been a bit under the weather but just wanted to make a quick comment on this (I'll respond to the other points you raised later), yes I think it would be necessary to have something like "desired battery SOC %" as well as "desired import" and "desired export" to take advantage of market pricing.

The term "limiters" is kind of misleading in this case, so I'm thinking maybe renaming it to "setpoints" which is more consistent with proper engineering terminology, and have something like

  • connection (boolean: connection to grid)
  • energize (boolean: inverter shut off)
  • exportLimitWatts (number: maximum site export)
  • importLimitWatts (number: maximum site import)
  • generationLimitWatts (number: maximum site generation)
  • loadLimitWatts (number: maximum site load)
  • importTargetWatts (number: ideal site import, cannot exceed import limit)
  • exportTargetWatts (number: ideal site export, cannot exceed export limit)
  • batterySocTarget (number: ideal battery storage %)

@CpuID
Copy link
Contributor

CpuID commented Dec 23, 2024

The term "limiters" is kind of misleading in this case, so I'm thinking maybe renaming it to "setpoints"

I had a similar thought - once you start doing more than "limiting" a better name makes sense :)

@CpuID
Copy link
Contributor

CpuID commented Dec 25, 2024

Had another consideration to add here for calculations: what if multiple inverters are in use? Since right now, if multiple inverters are configured, it will split the output power equally so the export kW remains under the limits provided by the grid operator... (?)

If one inverter has a battery behind it and another does not (common), does that involve some math like "split the desired output, but then compensate for the battery on the inverter that has a battery"? 🤷‍♂️

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants