This repository contains a Hardware-in-the-Loop (HIL) Simulator designed for testing and validating Energy Management Systems (EMS) software. The simulator acts as a digital twin of a Power Conversion System (PCS) and Battery Energy Storage System (BESS), modeling the physical dynamics, grid coupling, and communication latencies.
It allows an external EMS to read telemetry and write setpoints via Modbus TCP, while the simulator runs a real-time physics engine internally.
The following plots demonstrate the simulator's behavior using a Sungrow ST5015UX PCS-ESU model with the configuration:
- Sampling Time: 100ms
- Measurement Latency: 500ms
- P Time Constant: 0.1s
- Q Time Constant: 0.2s
- Max Apparent Power: 0.21 MVA
Active Power (P) tracking setpoint with first-order lag dynamics

Reactive Power (Q) response showing similar lag characteristics

Grid voltage profile during simulation

Resulting power factor calculated from P and Q measurements

Calculated current injected to/absorpted by the grid

-
Real-Time Physics Engine: Models Active (
$P$ ) and Reactive ($Q$ ) power evolution using First-Order Lag dynamics (Low Pass Filter). -
Transport Delay Simulation: Simulates realistic SCADA/metering delays (
$N$ steps) to test EMS feedback loops. - Modbus TCP Server: Acts as a slave device (Port 502), exposing measurements on Input Registers and accepting commands via Holding Registers.
-
P-Q Capability Curve: Implements complex saturation logic where reactive power limits (
$Q_{max}/Q_{min}$ ) float dynamically based on grid voltage and active power levels. -
Grid Scenarios: Includes a scenario generator that injects voltage sags (
$0.95$ pu) and swells ($1.05$ pu) to trigger capability curve changes. -
Web-Based Data Viewer: A standalone
viewer.htmltool to visualizeBessData.csvlogs with interactive charts and zoom capabilities.
The interaction between the Device Under Test (EMS) and the Simulator is cyclic:
-
EMS sends control references (
$u$ ) via Modbus. -
Simulator evolves internal physics state (
$x$ ) based on time constants. -
Simulator returns delayed feedback measurements (
$y$ ) to the EMS.
The system uses a discrete-time state-space representation.
The state vector represents the physical state of the plant at the inverter terminals:
The input vector consists of the setpoints received from the EMS:
The discrete-time dynamics are modeled as a 1st-order lag for Power, while Voltage and Frequency are scenario-driven inputs:
Where the system matrices are defined as:
To simulate realistic SCADA feedback, a transport delay
Where current
Program.cs: The main entry point. Initializes the model, starts the Modbus server, and runs the real-time simulation loop.BessPhysicsModel.cs: Encapsulates the physics engine, IIR filters, and delay buffers (Queues).ModbusServerWrapper.cs: Wraps theNModbuslogic to handle register mapping and TCP connections.PqCapabilityCurve.cs: Handles the complex saturation logic and interpolation between voltage levels (0.9pu to 1.1pu).viewer.html: A frontend tool for analyzing simulation CSV logs.BessData.csv: The output log file generated during simulation.
- .NET 6.0 SDK or later.
- A Modbus TCP Client (e.g., QModMaster) or an actual EMS to act as the Master.
- Modern Web Browser (for
viewer.html).
- Clone the repository.
- Open the solution in Visual Studio or VS Code.
- Restore NuGet packages (specifically
NModbus).
Run the application with administrative privileges (required to bind to Port 502).
dotnet run
Note: If Port 502 is blocked or in use, modify
ModbusServerWrapper.Start(502)inProgram.csto use port5020.
You can manually inject setpoints directly via the console window to test step responses without an EMS.
Format: P[MW] Q[MVAR]
Enter command (P Q) or 'exit': 1.0 0.5
>>> Command queued: P=1.00 MW, Q=0.50 MVAR
The simulator listens on Port 502 (Unit ID 1). Data is stored as 32-bit Floating Point values (spanning 2 registers each).
| Signal | Type | Register Type | Address (Offset) | Description |
|---|---|---|---|---|
| Setpoint P | Float (32-bit) | Holding (RW) | 40001 (0) | Active Power Command |
| Setpoint Q | Float (32-bit) | Holding (RW) | 40003 (2) | Reactive Power Command |
| Meas P | Float (32-bit) | Input (RO) | 30001 (0) | Active Power Feedback |
| Meas Q | Float (32-bit) | Input (RO) | 30003 (2) | Reactive Power Feedback |
| Meas V | Float (32-bit) | Input (RO) | 30005 (4) | Grid Voltage |
| Meas F | Float (32-bit) | Input (RO) | 30007 (6) | Grid Frequency |
| Meas I | Float (32-bit) | Input (RO) | 30009 (8) | Current (Derived) |
- Let the simulation run; it logs data to
BessData.csvin real-time. - Open
viewer.htmlin your browser. - Click Load CSV File and select the generated
BessData.csv. - Use the tabs to view Charts, Summary Stats, and Data Tables.
Key simulation parameters are defined in Program.cs and derived from the system design:
| Parameter | Value | Description |
|---|---|---|
| Sampling Time ( |
0.1 s | Simulation Step Size |
| Total Delay ( |
0.5 s | Physics to Feedback latency |
| P Time Constant ( |
0.1 s | Active Power Response Lag |
| Q Time Constant ( |
0.2 s | Reactive Power Response Lag |
| Max Apparent Power ( |
0.21 MVA | Inverter Capacity Limit |

