Implemented a better balancing algorith for string balancing

* Added error logging to inverter fb
* Changed HMI actual power feedback from dc to ac power
* Small config adjustments
This commit is contained in:
Matthias Heisig
2025-04-16 12:35:06 +02:00
parent 082c250543
commit 7b810b19d2
5 changed files with 61 additions and 46 deletions

View File

@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<TcPlcObject Version="1.1.0.1" ProductVersion="3.1.4026.11">
<TcPlcObject Version="1.1.0.1" ProductVersion="3.1.4026.12">
<GVL Name="GVL_CONFIG" Id="{0773bf51-0237-454d-a970-cfd896054edb}">
<Declaration><![CDATA[{attribute 'qualified_only'}
VAR_GLOBAL CONSTANT
@@ -187,10 +187,10 @@ VAR_GLOBAL PERSISTENT
// ===========================
// Pump posolyt on power in %
rPumpPosolytOnPower : REAL := 65.0;
rPumpPosolytOnPower : REAL := 70.0;
// Pump negolyt on power in %
rPumpNegolytOnPower : REAL := 65.0;
rPumpNegolytOnPower : REAL := 70.0;
// Pump posolyt discharge segment without inverter power in %
rPumpPosolytDisChrgPower : REAL := 45.0;
@@ -259,7 +259,10 @@ VAR_GLOBAL PERSISTENT
timStringReadyTimeout : TIME := T#3M;
// Timeout for isolation error
timIsoErrorTimeout : TIME := T#20s;
timIsoErrorTimeout : TIME := T#20S;
// Balancing factor
rBalancingFactor : REAL := 20.0;
// Dummy to deactivate functions
{attribute 'analysis' := '-33'}

File diff suppressed because one or more lines are too long

View File

@@ -131,6 +131,7 @@ VAR
// Sum of voltage of all active strings
_rStringsSumVoltage : REAL;
_rDeltaUm : REAL;
_arPowerString : ARRAY[0..(GVL_CONFIG.uiNumberOfStrings-1)] OF REAL;
_ui : UINT := 0;
@@ -607,7 +608,7 @@ GVL_SCADA.xCanChangeControlMode := _xCanChangeMode;
GVL_SCADA.eCurrentControlMode := _eBMSControlMode;
// Calculate current battery dc power
GVL_SCADA.diCurrentBatteryPower := REAL_TO_DINT(_afbStrings[0].stInverterData.rActDCPower + _afbStrings[1].stInverterData.rActDCPower);
GVL_SCADA.diCurrentBatteryPower := REAL_TO_DINT(_afbStrings[0].stInverterData.rActACPower + _afbStrings[1].stInverterData.rActACPower);
// Read power values if commanded
_fbPowerMeterPower(
@@ -927,14 +928,17 @@ _fbPowerMeter24V();]]></ST>
END_CASE
// Calculate string power balancing
IF _rStringsSumVoltage <> 0 THEN
IF _rStringsSumVoltage <> 0 AND (GVL_CONFIG.uiNumberOfStrings <> 0) THEN
FOR _ui := 0 TO (GVL_CONFIG.uiNumberOfStrings-1) DO
// Calculate delta u to middle voltage
_rDeltaUm := (_afbStrings[_ui].rCurrentVoltage * GVL_CONFIG.uiNumberOfStrings - _rStringsSumVoltage) / _rStringsSumVoltage;
// Discharging
IF _rPowerInverter > 0 THEN
_arPowerString[_ui] := _rPowerInverter * (_afbStrings[_ui].rCurrentVoltage / _rStringsSumVoltage);
_arPowerString[_ui] := (_rPowerInverter / GVL_CONFIG.uiNumberOfStrings) * ( 1 + (_rDeltaUm * GVL_CONFIG.rBalancingFactor));
// Charging
ELSIF _rPowerInverter < 0 THEN
_arPowerString[_ui] := _rPowerInverter * (1.0 - (_afbStrings[_ui].rCurrentVoltage / _rStringsSumVoltage));
_arPowerString[_ui] := (_rPowerInverter / GVL_CONFIG.uiNumberOfStrings) * ( 1 - (_rDeltaUm * GVL_CONFIG.rBalancingFactor));
// Nothing
ELSE
_arPowerString[_ui] := 0.0;

View File

@@ -260,6 +260,7 @@ CASE _iState OF
IF NOT _fbReadRegister.bError THEN
_iState := 2;
ELSE
_iErrorInState := _iState;
_iState := 1000;
END_IF
_fbReadRegister(bExecute := FALSE);
@@ -276,7 +277,6 @@ CASE _iState OF
3: // Read current DC values
_iErrorInState := _iState;
_fbReadRegister(
sIPAddr:= sInverterIPAddr,
nTCPPort:= 502,
@@ -301,6 +301,7 @@ CASE _iState OF
stCurrentValues.rActDCVoltage := LREAL_TO_REAL(WORD_TO_UINT(_awCurrentDCValues[2]) * EXPT(10,WORD_TO_INT(_awCurrentDCValues[3])));
stCurrentValues.rActDCPower := LREAL_TO_REAL(WORD_TO_INT(_awCurrentDCValues[4]) * EXPT(10,WORD_TO_INT(_awCurrentDCValues[5])));
ELSE
_iErrorInState := _iState;
// Read error register
_iState := 1000;
END_IF
@@ -309,7 +310,6 @@ CASE _iState OF
4: // Read current ac values
_iErrorInState := _iState;
_fbReadRegister(
sIPAddr:= sInverterIPAddr,
nTCPPort:= 502,
@@ -341,6 +341,7 @@ CASE _iState OF
stCurrentValues.rActReactivePower := LREAL_TO_REAL(WORD_TO_INT(_awCurrentACValues[18]) * EXPT(10,WORD_TO_INT(_awCurrentACValues[19])));
stCurrentValues.rActPowerFactor := LREAL_TO_REAL(WORD_TO_INT(_awCurrentACValues[20]) * EXPT(10,WORD_TO_INT(_awCurrentACValues[21])));
ELSE
_iErrorInState := _iState;
// Read error register
_iState := 1000;
END_IF
@@ -350,7 +351,6 @@ CASE _iState OF
5: // Send heartbeat signal
_uiPLCToInverterCounter := _uiPLCToInverterCounter + 1;
_iErrorInState := _iState;
_fbWriteRegister(
sIPAddr:= sInverterIPAddr,
nTCPPort:= 502,
@@ -372,6 +372,7 @@ CASE _iState OF
_iState := 6;
ELSE
xError := TRUE;
_iErrorInState := _iState;
// Goto error state
_iState := 1000;
END_IF
@@ -410,6 +411,7 @@ CASE _iState OF
_iState := 0;
ELSE
xError := TRUE;
_iErrorInState := _iState;
// Goto error state
_iState := 1000;
END_IF
@@ -471,7 +473,6 @@ CASE _iState OF
20: // Read inverter max power scaling
_iErrorInState := _iState;
_fbReadRegister(
sIPAddr:= sInverterIPAddr,
nTCPPort:= 502,
@@ -500,6 +501,7 @@ CASE _iState OF
END_IF
ELSE
xError := TRUE;
_iErrorInState := _iState;
// Goto error state
_iState := 1000;
END_IF
@@ -508,7 +510,6 @@ CASE _iState OF
25: // Read inverter Max power limit scaling
_iErrorInState := _iState;
_fbReadRegister(
sIPAddr:= sInverterIPAddr,
nTCPPort:= 502,
@@ -536,6 +537,7 @@ CASE _iState OF
END_IF
ELSE
xError := TRUE;
_iErrorInState := _iState;
// Goto error state
_iState := 1000;
END_IF
@@ -544,7 +546,6 @@ CASE _iState OF
26: // Read inverter scaling factor for reactive power
_iErrorInState := _iState;
_fbReadRegister(
sIPAddr:= sInverterIPAddr,
nTCPPort:= 502,
@@ -572,6 +573,7 @@ CASE _iState OF
END_IF
ELSE
xError := TRUE;
_iErrorInState := _iState;
// Goto error state
_iState := 1000;
END_IF
@@ -580,7 +582,6 @@ CASE _iState OF
30: // Read inverter max power
_iErrorInState := _iState;
_fbReadRegister(
sIPAddr:= sInverterIPAddr,
nTCPPort:= 502,
@@ -608,6 +609,7 @@ CASE _iState OF
_iWMaxLimPct := LREAL_TO_INT((_rPowerInternal*100)/(_rWMax * EXPT(10,_iWMaxLimPctSF)));
ELSE
xError := TRUE;
_iErrorInState := _iState;
// Goto error state
_iState := 1000;
END_IF
@@ -616,7 +618,6 @@ CASE _iState OF
40: // Set power limit
_iErrorInState := _iState;
_fbWriteRegister(
sIPAddr:= sInverterIPAddr,
nTCPPort:= 502,
@@ -644,6 +645,7 @@ CASE _iState OF
ELSE
_uiSetPowerLimitErrorCounter := _uiSetPowerLimitErrorCounter + 1;
IF _uiSetPowerLimitErrorCounter > 5 THEN
_iErrorInState := _iState;
// Goto error state
_iState := 1000;
ELSE
@@ -661,7 +663,6 @@ CASE _iState OF
END_IF
42: // Set max reactive power in percent
_iErrorInState := _iState;
_fbWriteRegister(
sIPAddr:= sInverterIPAddr,
nTCPPort:= 502,
@@ -683,6 +684,7 @@ CASE _iState OF
_iState := 42;
ELSE
xError := TRUE;
_iErrorInState := _iState;
// Goto error state
_iState := 1000;
END_IF
@@ -691,7 +693,6 @@ CASE _iState OF
43: // Enable reactive power percent limiting
_iErrorInState := _iState;
_fbWriteRegister(
sIPAddr:= sInverterIPAddr,
nTCPPort:= 502,
@@ -713,6 +714,7 @@ CASE _iState OF
_iState := 50;
ELSE
xError := TRUE;
_iErrorInState := _iState;
// Goto error state
_iState := 1000;
END_IF
@@ -721,7 +723,6 @@ CASE _iState OF
50: // Enable Power limiting (THROTTLED)
_iErrorInState := _iState;
_fbWriteRegister(
sIPAddr:= sInverterIPAddr,
nTCPPort:= 502,
@@ -745,6 +746,7 @@ CASE _iState OF
xActive := TRUE;
ELSE
xError := TRUE;
_iErrorInState := _iState;
// Goto error state
_iState := 1000;
END_IF
@@ -753,7 +755,6 @@ CASE _iState OF
51: // Go to started
_iErrorInState := _iState;
_fbWriteRegister(
sIPAddr:= sInverterIPAddr,
nTCPPort:= 502,
@@ -776,6 +777,7 @@ CASE _iState OF
_iState := 60;
ELSE
_uiPCSSetOperation := 1;
_iErrorInState := _iState;
// Goto error state
_iState := 1000;
END_IF
@@ -784,7 +786,6 @@ CASE _iState OF
60: // Switch to THROTTLED mode
_iErrorInState := _iState;
_fbWriteRegister(
sIPAddr:= sInverterIPAddr,
nTCPPort:= 502,
@@ -805,6 +806,7 @@ CASE _iState OF
IF NOT _fbWriteRegister.bError THEN
_iState := 65;
ELSE
_iErrorInState := _iState;
// Goto error state
_iState := 1000;
END_IF
@@ -840,7 +842,6 @@ CASE _iState OF
70: // Enabled, check for error
_iErrorInState := _iState;
_fbReadRegister(
sIPAddr:= sInverterIPAddr,
nTCPPort:= 502,
@@ -870,6 +871,8 @@ CASE _iState OF
xError := TRUE;
xActive := FALSE;
_uiPCSSetOperation := 3;
_iErrorInState := _iState;
ADSLOGDINT(msgCtrlMask:= ADSLOG_MSGTYPE_ERROR, msgFmtStr:= 'Fehler im state: %s', dintArg:= INT_TO_DINT(_iErrorInState));
// Read error register
_iState := 200;
END_IF
@@ -878,7 +881,6 @@ CASE _iState OF
80: // Read current DC values
_iErrorInState := _iState;
_fbReadRegister(
sIPAddr:= sInverterIPAddr,
nTCPPort:= 502,
@@ -903,6 +905,7 @@ CASE _iState OF
stCurrentValues.rActDCVoltage := LREAL_TO_REAL(WORD_TO_INT(_awCurrentDCValues[2]) * EXPT(10,WORD_TO_INT(_awCurrentDCValues[3])));
stCurrentValues.rActDCPower := LREAL_TO_REAL(WORD_TO_INT(_awCurrentDCValues[4]) * EXPT(10,WORD_TO_INT(_awCurrentDCValues[5])));
ELSE
_iErrorInState := _iState;
// Read error register
_iState := 1000;
END_IF
@@ -911,7 +914,6 @@ CASE _iState OF
85: // Read current ac values
_iErrorInState := _iState;
_fbReadRegister(
sIPAddr:= sInverterIPAddr,
nTCPPort:= 502,
@@ -943,6 +945,7 @@ CASE _iState OF
stCurrentValues.rActReactivePower := LREAL_TO_REAL(WORD_TO_INT(_awCurrentACValues[18]) * EXPT(10,WORD_TO_INT(_awCurrentACValues[19])));
stCurrentValues.rActPowerFactor := LREAL_TO_REAL(WORD_TO_INT(_awCurrentACValues[20]) * EXPT(10,WORD_TO_INT(_awCurrentACValues[21])));
ELSE
_iErrorInState := _iState;
// Read error register
_iState := 1000;
END_IF
@@ -972,6 +975,7 @@ CASE _iState OF
_iState := 91;
stCurrentValues.uiStatus := _uiInverterState;
ELSE
_iErrorInState := _iState;
// Read error register
_iState := 1000;
END_IF
@@ -981,7 +985,6 @@ CASE _iState OF
91: // Send heartbeat signal
_uiPLCToInverterCounter := _uiPLCToInverterCounter + 1;
_iErrorInState := _iState;
_fbWriteRegister(
sIPAddr:= sInverterIPAddr,
nTCPPort:= 502,
@@ -1002,6 +1005,7 @@ CASE _iState OF
IF NOT _fbWriteRegister.bError THEN
_iState := 92;
ELSE
_iErrorInState := _iState;
xError := TRUE;
// Goto error state
_iState := 1000;
@@ -1042,6 +1046,7 @@ CASE _iState OF
_iState := 93;
ELSE
xError := TRUE;
_iErrorInState := _iState;
// Goto error state
_iState := 1000;
END_IF
@@ -1050,7 +1055,6 @@ CASE _iState OF
END_IF
93: // Send current power demand
_iErrorInState := _iState;
_fbWriteRegister(
sIPAddr:= sInverterIPAddr,
nTCPPort:= 502,
@@ -1078,6 +1082,7 @@ CASE _iState OF
ELSE
_uiSetPowerLimitErrorCounter := _uiSetPowerLimitErrorCounter + 1;
IF _uiSetPowerLimitErrorCounter > 5 THEN
_iErrorInState := _iState;
// Goto error state
_iState := 1000;
ELSE
@@ -1089,7 +1094,6 @@ CASE _iState OF
200: // Shutdown send zero power command
_iErrorInState := _iState;
_fbWriteRegister(
sIPAddr:= sInverterIPAddr,
nTCPPort:= 502,
@@ -1111,6 +1115,7 @@ CASE _iState OF
_iState := 201;
_uiPCSSetOperation := 3;
ELSE
_iErrorInState := _iState;
// Goto error state
_iState := 1000;
END_IF
@@ -1118,7 +1123,6 @@ CASE _iState OF
END_IF
201: // Shutdown sequence
_iErrorInState := _iState;
_fbWriteRegister(
sIPAddr:= sInverterIPAddr,
nTCPPort:= 502,
@@ -1139,6 +1143,7 @@ CASE _iState OF
IF NOT _fbWriteRegister.bError THEN
_iState := 210;
ELSE
_iErrorInState := _iState;
// Goto error state
_iState := 1000;
END_IF
@@ -1184,7 +1189,6 @@ CASE _iState OF
990: // Read error register
_iErrorInState := _iState;
_fbReadRegister(
sIPAddr:= sInverterIPAddr,
nTCPPort:= 502,
@@ -1204,13 +1208,17 @@ CASE _iState OF
IF NOT _fbReadRegister.bBusy THEN
// If there was no error and the converter has no error continue
IF NOT _fbReadRegister.bError THEN
_iErrorInState := _iState;
_iState := 1000;
END_IF
_fbReadRegister(bExecute := FALSE);
END_IF
1000: // Write error state to log
ADSLOGDINT(msgCtrlMask:= ADSLOG_MSGTYPE_ERROR, msgFmtStr:= 'Fehler im state: %s', dintArg:= INT_TO_DINT(_iErrorInState));
_iState := 1001;
1000: // Error state, wait for reset
1001: // Error state, wait for reset
IF xReset AND (NOT xEnable) THEN
_iState := 1010;
END_IF

View File

@@ -269,7 +269,7 @@
</System>
<Plc>
<Project GUID="{9AE64910-5EB2-4866-93FD-EFE059C38C36}" Name="PLC" PrjFilePath="PLC\PLC.plcproj" TmcFilePath="PLC\PLC.tmc" ReloadTmc="true" AmsPort="851" FileArchiveSettings="#x000e" CopyTmcToTarget="true" CopyTpyToTarget="false" SymbolicMapping="true">
<Instance Id="#x08502000" TcSmClass="TComPlcObjDef" KeepUnrestoredLinks="2" TmcHash="{7ED08416-1955-C55E-9601-98E642FFD609}" TmcPath="PLC\PLC.tmc">
<Instance Id="#x08502000" TcSmClass="TComPlcObjDef" KeepUnrestoredLinks="2" TmcHash="{09733D5D-F6BF-9F96-D45C-805CEB906D89}" TmcPath="PLC\PLC.tmc">
<Name>PLC Instance</Name>
<CLSID ClassFactory="TcPlc30">{08500001-0000-0000-F000-000000000064}</CLSID>
<Vars VarGrpType="2" AreaNo="1">