# Virtual EMS
**Repository Path**: PandaX-Go/virtual-ems
## Basic Information
- **Project Name**: Virtual EMS
- **Description**: **Virtual EMS** 是一个用 Go 语言构建的**完整储能能量管理系统(EMS)仿真环境**。它在一个进程中虚拟了电池组、BMU、BMS、PCS 等全部设备,通过真实的 Modbus TCP 协议进行交互,并提供直观的 Web 可视化界面,让你能够清晰看到每一条控制指令的流动、电流方向以及设备状态变化。
- **Primary Language**: Go
- **License**: MulanPSL-2.0
- **Default Branch**: master
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 0
- **Forks**: 1
- **Created**: 2026-07-10
- **Last Updated**: 2026-07-10
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
# Virtual EMS — 基于 Go 的储能系统全链路仿真与可视化平台
[](https://go.dev/)
[](LICENSE)
[](https://www.docker.com/)
**Virtual EMS** 是一个用 Go 语言构建的**完整储能能量管理系统(EMS)仿真环境**。它在一个进程中虚拟了电池组、BMU、BMS、PCS 等全部设备,通过标准的 Modbus TCP 协议进行交互,并提供直观的 Web 可视化界面。你可以清晰看到每一条控制指令的流动、电流方向以及设备状态变化。
无论是学习 Modbus 通信、验证 EMS 控制策略、进行系统集成测试,还是作为教学演示工具,它都能让你在没有任何硬件的情况下快速开始。**现已支持 Docker 一键部署,开发环境零配置。**
---
## 特性
- **全栈仿真**:单进程运行电池模型、BMU、BMS、PCS,完全模拟真实电气层级。
- **纯 Go 实现的 Modbus TCP 协议栈**:不依赖第三方 Modbus 库,完全自主编写服务器与客户端,兼容各类标准 Modbus 工具(Modbus Poll、qModMaster 等)。
- **指令流与能量流双模式可视化**:
- 黄色发光粒子表示控制指令(`EMS → PCS → BMS → 电池组`)
- 绿色/红色粒子表示充电/放电能量流动(电网 ↔ PCS ↔ BMS ↔ 电池组),方向明确,实时刷新。
- **电气状态实时看板**:SOC、电压、电流、功率、温度等数据实时显示,充放电状态、效率模型自动计算。
- **寄存器即配置**:通过 JSON 文件定义每个设备的寄存器映射(点位文件),无需修改代码即可调整仿真行为。
- **事件总线与指令追踪**:所有设备间的 Modbus 写操作均生成带时间戳的事件,前端可显示完整指令链路。
- **手动/自动 EMS 控制**:支持 Web 界面按钮手动充放电,也可嵌入自动策略(可扩展)。
- **可扩展设备模型**:预留 `Device` 接口,轻松添加光伏逆变器、环境监测仪等新设备。
- **混合仿真就绪**:可将某个仿真设备替换为真实硬件(真实 Modbus 设备),其余部分仍保持虚拟化,实现半实物仿真。
- **Docker 容器化部署**:一键启动,无需安装 Go 或 Node.js,开箱即用。
---
## 系统架构
```mermaid
graph TD
subgraph “浏览器可视化”
UI[Web 前端
拓扑图 / 粒子动画 / 数据看板]
end
subgraph “Go 仿真引擎 (单进程)”
WS[WebSocket Hub]
EB[事件总线]
subgraph “设备仿真”
EMS[EMS 控制器
Modbus 主站]
PCS[PCS 仿真器
Modbus 从站 + BMS 客户端]
BMS[BMS 仿真器
Modbus 从站 + 内部 BMU 聚合]
BMU1[BMU #1]
BMU2[BMU #2]
BATT1[电池包 #1]
BATT2[电池包 #2]
end
REG[寄存器配置加载器]
end
UI <-->|WebSocket| WS
WS --> EB
EB -.->|指令/状态事件| EMS
EB -.->|指令/状态事件| PCS
EB -.->|指令/状态事件| BMS
EMS -->|Modbus TCP 读写| PCS
PCS -->|Modbus TCP 读写| BMS
BMS -->|内部调用| BMU1
BMS -->|内部调用| BMU2
BMU1 -->|状态更新| BATT1
BMU2 -->|状态更新| BATT2
```
---
## 快速开始
### 环境要求(本地开发)
- Go 1.21 或更高版本
- (前端已预编译为静态文件,无需 Node.js)
### 方式一:Docker 容器运行(推荐)
**0. 前提**:安装 [Docker](https://www.docker.com/) 和 Docker Compose。
**1. 克隆仓库**
```bash
git clone https://gitee.com/cmx1998/virtual-ems.git
cd virtual-ems
```
**2. 构建并启动**
```bash
docker-compose up -d
```
首次运行会自动构建镜像,稍等片刻即可。
**3. 访问**
打开浏览器访问 `http://localhost:8080`,你将看到完整的系统拓扑图与实时数据。
**4. 停止**
```bash
docker-compose down
```
> **说明**:Web 端口映射为 8080;Modbus 仿真端口(5020、5021)仅在容器内部使用,无需暴露。若需使用外部 Modbus 工具调试,可自行修改 `docker-compose.yml` 增加端口映射。
### 方式二:本地编译运行
**1. 克隆仓库并进入目录**
```bash
git clone https://gitee.com/cmx1998/virtual-ems.git
cd virtual-ems
```
**2. 下载依赖并运行**
```bash
go mod tidy
go run ./cmd/simulator
```
**3. 访问**
打开浏览器访问 `http://localhost:8080`。
### 使用 Modbus 工具单独调试
仿真器启动后,各设备 Modbus 从站默认地址:
| 设备 | IP:Port |
|------|----------------|
| BMS | 127.0.0.1:5020 |
| PCS | 127.0.0.1:5021 |
你可以使用 **Modbus Poll** 或 **qModMaster** 连接到这些端口,按照 `configs/` 中的寄存器表进行读写测试,所有操作都会实时反映到 Web 界面上。
---
## 配置与寄存器点位文件
所有设备寄存器通过 JSON 文件定义,位于 `configs/` 目录。示例 `configs/pcs.json`:
```json
{
"device": "PCS",
"slave_id": 2,
"registers": [
{"address": 200, "name": "运行状态", "type": "uint16", "rw": "r", "desc": "0=待机 1=充电 2=放电 3=故障"},
{"address": 201, "name": "有功功率设定", "type": "int16", "rw": "rw", "desc": "单位 kW,正充电负放电"},
{"address": 202, "name": "直流电压", "type": "uint16", "rw": "r", "desc": "0.1V 精度"},
{"address": 203, "name": "直流电流", "type": "int16", "rw": "r", "desc": "0.1A 精度"},
{"address": 204, "name": "交流电压", "type": "uint16", "rw": "r", "desc": "0.1V 精度"},
{"address": 205, "name": "交流电流", "type": "int16", "rw": "r", "desc": "0.1A 精度"},
{"address": 206, "name": "交流功率", "type": "int16", "rw": "r", "desc": "0.1kW 精度"}
]
}
```
修改这些文件即可自定义设备接口,无需重新编译。程序启动时会自动加载并构建内存寄存器映射。
---
## 目录结构
```
.
├── cmd
│ └── simulator # 主程序入口,启动所有仿真服务
├── internal
│ ├── config # 寄存器配置加载(JSON/Excel)
│ ├── devices # 设备仿真器实现
│ │ ├── battery # 电池电芯模型(SOC/电压/温度)
│ │ ├── bmu # 电池管理单元
│ │ ├── bms # 电池管理系统(聚合+Modbus 从站)
│ │ ├── pcs # 储能变流器(从站+ BMS 客户端)
│ │ └── ems # 能量管理系统(控制命令处理)
│ ├── modbus # Modbus TCP 协议栈(服务器/客户端)
│ ├── event # 指令事件定义与事件总线
│ └── web # WebSocket Hub,全局状态管理
├── web # 前端静态文件(HTML/CSS/JS,已内嵌拓扑图动画)
├── configs # 寄存器配置文件
│ ├── bms.json
│ └── pcs.json
├── vendor # Go 依赖离线缓存(用于 Docker 构建)
├── Dockerfile
├── docker-compose.yml
├── go.mod
├── go.sum
└── README.md
```
---
## 指令流可视化原理
1. 当 EMS 通过 Modbus 向 PCS 写入寄存器时,PCS 从站捕获到写操作。
2. PCS 仿真器内部触发回调,生成事件 `Event{From:"EMS", To:"PCS", Register:"有功功率设定", Value:50}`。
3. 事件通过事件总线广播,WebSocket Hub 将其推送到前端。
4. 前端收到事件后,在对应连线上创建黄色发光粒子,粒子沿连线移动并在到达后消散,同时目标设备高亮。
5. 若 PCS 随后向 BMS 写寄存器(转发指令),同样产生新事件,形成完整的指令链条。
6. 在充电/放电状态下,前端还会持续产生绿色或红色能量流粒子,模拟实际电能的流动方向。
最终呈现效果:用户点击“充电 50kW”按钮后,界面上一颗金色光点从 EMS 节点出发,到达 PCS 再跳到 BMS,最后分发至电池图标;同时电网节点也参与能量流动动画。
---
## 可扩展性设计
### 添加新设备仿真
1. 实现 `Device` 接口:
```go
type Device interface {
Name() string
Init(regMap *modbus.RegisterMap, eventBus *event.Bus) error
Start(ctx context.Context) error
Stop() error
}
```
2. 在 `configs/` 中提供对应的寄存器 JSON。
3. 在 `cmd/simulator` 中注册设备并指定 Modbus 端口。
4. 前端组件增加新设备节点并订阅事件。
### 接入真实设备
将某个仿真设备替换为真实 Modbus TCP 设备时,只需在配置中将其 `driver` 设置为 `"real"` 并提供 IP 地址,系统会跳过仿真逻辑,直接向真实设备读写寄存器,其余虚拟部分照常工作。
### 自定义 EMS 策略
在 `internal/devices/ems` 中实现 `Strategy` 接口:
```go
type Strategy interface {
Evaluate(bmsData, pcsData map[string]float64) (controlAction string, params map[string]interface{})
}
```
然后在 Web 界面或配置文件中选择当前运行的策略,支持热切换。
---
## 迭代路线图
- [x] 基础电池模型与 BMS/PCS 仿真
- [x] 纯 Go 手写 Modbus TCP 协议栈
- [x] 寄存器 JSON 配置驱动
- [x] WebSocket 实时数据推送与状态看板
- [x] 指令粒子动画 + 能量流动可视化
- [x] 手动 EMS 控制与功率自动限制
- [x] **Docker 一键部署**
- [ ] Excel 寄存器文件支持
- [ ] SOC-OCV 曲线电池模型(更高精度)
- [ ] 故障注入与告警模拟
- [ ] 历史数据存储与回放(SQLite / InfluxDB)
- [ ] 策略脚本引擎(Lua / JavaScript)
- [ ] 多簇电池并联/串联复杂拓扑仿真
- [ ] 增加电网等更多设备节点
---
## 贡献
欢迎提交 Issue 和 PR!在开始大的改动之前,建议先开 Issue 讨论设计方案。请遵循项目的 Go 编码规范和前端 ESLint 规则。
---
## 许可证
本项目采用 [木兰宽松许可证,第2版](https://license.coscl.org.cn/MulanPSL2)(Mulan PSL v2)。
您可以自由复制、使用、修改及分发本软件,但需遵守该许可证的条款。
详见 [LICENSE](LICENSE) 文件。
---
**告别硬件依赖,在代码中自由探索储能系统。**
*如有任何问题或建议,欢迎在 Gitee / GitHub 提交反馈。*