电量计 -- BatterySimerKit DFCC仿真环境架构

概述

BatterySimerKit2.0 是一套 PC 端电量计仿真环境,用于 77561 系列燃料计芯片的 DFCC(Dynamic Fuel Gauge Compensation)离线调参和算法验证。

为什么需要仿真环境:

(1) 离线调参:无需反复烧录固件,直接在 PC 上修改 DFCC 表和参数,快速迭代
(2) 对比验证:将仿真输出(simer)与芯片实际输出(cobra)和循环仪期望值(cycle)三条曲线对比,量化误差
(3) 算法一致性:仿真环境使用与固件完全相同的 C 算法库,确保仿真结果可信

三层架构:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
┌─────────────────────────────────────────────────────────┐
│ C# WinForms GUI (BatterySimer) │
│ - SharpGL 绘图引擎 │
│ - 参数/表格编辑对话框 │
│ - COBRA日志加载 & 循环仪数据加载 │
├─────────────────────────────────────────────────────────┤
│ C DLL (SOCLib77561.dll) ← P/Invoke 调用 │
│ - 电量计核心算法 (lib_fg) │
│ - COBRA日志解析 (main.c) │
│ - 查找表: OCV, RC, DFCC, Temperature │
├─────────────────────────────────────────────────────────┤
│ Python 工具 (auto_update_table.py) │
│ - OCV/RC 表自动同步 (TableMaker → table.c) │
└─────────────────────────────────────────────────────────┘

工程目录结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
BatterySimerKit2.0/
├── BatterySimer.sln # VS解决方案(含两个项目)
├── SOCLib77561/ # C DLL - 电量计算法库
│ ├── lib/ # 核心FG算法
│ │ ├── lib_fg.h # API定义、数据结构
│ │ ├── lib_fg.c # 算法实现
│ │ └── lib_pg.c # Pack级多电芯管理
│ ├── user/ # DLL接口层
│ │ ├── main.h # DLL导出函数声明
│ │ ├── main.c # COBRA解析、FG初始化、接口实现
│ │ └── parameter.c # 电池参数(按DATA_TYPE区分)
│ ├── table/ # 查找表数据
│ │ ├── table.h # DATA_TYPE宏、维度定义
│ │ ├── table.c # OCV/RC/DFCC表数据
│ │ └── dfcc_debug.c # DFCC调试打印
│ ├── ckf/ # 卡尔曼滤波模块
│ └── vnn/ # 神经网络SOC估算模块
├── BatterySimer/ # C# WinForms GUI
│ ├── Form1.cs # 主界面、DllImport、仿真循环、绘图
│ ├── FormModifyOCV.cs # OCV表编辑
│ ├── FormModifyRC.cs # RC表编辑
│ └── FormModifyParameters.cs # 参数编辑
└── auto_update_table.py # Python表同步工具

仿真输入数据详解

COBRA CSV 日志格式

COBRA 工具通过 I2C/SBS 总线实时采集电量计芯片的寄存器数据,导出为 CSV 格式。日志包含 28 列:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
enum CobraHeader {
Time_Enum = 0,
BatteryMode_Enum,
BatteryVolt_Enum,
CellMV01_Enum, // 电芯1电压 (mV)
CellMV02_Enum, // 电芯2电压 (mV)
CellVoltMV01_Enum, // 兼容列名
CellVoltMV02_Enum,
BatteryCurr_Enum, // 电池电流 (mA)
AvgCurr_Enum,
OrigCurr_Enum,
ETHM_Enum, // 外部温度 (°C)
ITHM_Enum, // 内部温度
RSOC_Enum, // 芯片输出SOC (%)
RC_Enum, // 剩余容量 (mAh)
RCA_Enum,
CCMAH_Enum, // 库仑计累计 (mAh)
RSOC1_Enum,
RC1_Enum,
CCmAhr_Enum,
GGMEM0_Enum ~ GGMEM6_Enum, // 芯片内部状态 (hex)
CycleCount_Enum,
SOH_Enum,
};

输入字段到 FG_PARAM_T 的映射

DLL 的 soc_process_77561() 函数逐行解析 COBRA CSV,提取以下字段送入算法库:

COBRA 列 FG_PARAM_T 字段 转换逻辑
CellMV01 / CellMV02 volt_hi_mv / volt_lo_mv 比较两个电芯电压,取高/低值
BatteryCurr current_ma atoi(record) / CURR_DIV,CURR_DIV 为并联电芯数
ETHM cellthm atof(record) * 10,转为 0.1°C 单位
CCmAh delta_mah 差分计算 + slope 校正(见下文)
GGMEM2 byte[28-31] imode -1=放电, 0=空闲, 1=充电
GGMEM3 byte[0-3] delta_ms 相邻采样点时间差(s) × 1000 → ms

delta_mah 计算(库仑计差分 + slope 校正)

COBRA 日志的 CCmAh 列是芯片库仑计的累计值,仿真需要计算相邻两个采样点的差分,并用 slope 系数校正 ADC 非线性:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 差分计算
int diff_cc_uah = (CCmAh_now - CCmAh_last) * 1000; // mAh → µAh

// slope 校正(补偿充放电 ADC 非线性)
if (diff_cc_uah > 0) // 充电
diff_uah = diff_cc_uah * 10000 / chgslope;
else if (diff_cc_uah < 0) // 放电
diff_uah = diff_cc_uah * 10000 / dsgslope;

// 并联电芯分流
diff_uah = diff_uah / CURR_DIV;

// 累加(保留小数部分避免截断误差)
delta_uah += diff_uah;
gg_input.delta_mah = delta_uah / 1000; // 送入算法的 mAh 值
delta_uah = delta_uah % 1000; // 余数留到下次累加

其中 chgslopedsgslope 默认值为 10000(即无校正),实际项目中根据芯片 CADC trim 值调整。

Cycle Tester _Detail.csv 输入

循环仪(如新威、蓝电)导出的 _Detail.csv 文件提供 ground truth 数据,关键列:

含义
CC(mAh) 循环仪库仑计累计值
I(mA) 实测电流
U(mV) 实测电压
mode 1=充电, -1=放电, 0=静置
time 时间戳

仿真输出与三条 SOC 曲线

FG_RESULT_T 输出结构

每次调用 fg_update() 后,算法库返回以下结果:

1
2
3
4
5
6
7
8
9
10
typedef struct {
signed int rsoc; // 相对电量百分比 (0-10000 表示 0-100.00%)
signed int fcc; // 满充容量 (mAh)
signed int rc; // 剩余容量 (mAh)
unsigned int dsgcap; // 累计放电量 (mAh)
unsigned int chgcap; // 累计充电量 (mAh)
unsigned int status; // 状态标志位 (32-bit)
signed int soh; // 健康度 (10000 = 100%)
unsigned int tte; // 预估剩余时间 (min)
} FG_RESULT_T;

三条 SOC 曲线

BatterySimer GUI 同时显示三条 SOC 曲线,用于对比分析:

1
2
3
4
5
6
7
d2f.yy[0] = cycle SOC   (期望值/ground truth)
d2f.yy[1] = cobra SOC (芯片实际输出)
d2f.yy[2] = simer SOC (仿真输出)
d2f.yy[3] = |cycle - cobra| (芯片误差)
d2f.yy[4] = |cycle - simer| (仿真误差)
d2f.yy[5] = |cobra - simer| (一致性误差)
d2f.yy[6] = SOH

cycle(期望值 / ground truth)

来源:循环仪 _Detail.csv 的库仑计数据。

计算公式:

1
SOC = (cc_mah - cycle_min_cc) / (cycle_max_cc - cycle_min_cc) × 100

其中:

  • cycle_min_cc:该放电段起点的 CC 值
  • cycle_max_cc:电压降到 EOD(End of Discharge,如 3200mV)时的 CC 值

关键理解:cycle SOC 是基于实际放出容量的线性归一化,不是电压线性插值。 循环仪精确测量了电池从满电到 EOD 实际放出的总容量,以此为基准将每个时刻的已放容量映射到 0-100%。

cobra(芯片实际输出)

来源:COBRA 日志的 RSOC 列,直接读取芯片寄存器值。

这是芯片内部 FG 算法的实时输出,反映固件在真实硬件上的运行结果。

simer(仿真输出)

来源:DLL soc_process_77561() 返回的 rsoc 字段。

使用与固件相同的 C 算法库,输入相同的电压/电流/温度数据,在 PC 上重新计算 SOC。

调参目标

  • cyc-sim 误差(yy[4])全程 < 3%
  • 误差曲线在 GUI 中 ×10 放大显示,便于观察细微偏差

SOCLib77561 DLL 层

DLL 导出接口

main.h 声明了所有 DLL 导出函数,供 C# GUI 通过 P/Invoke 调用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
// 初始化与处理
__declspec(dllexport) char* soc_init_77561(char* line); // 用COBRA首行初始化
__declspec(dllexport) char* soc_process_77561(char* line); // 逐行处理,返回结果

// OCV 表读写
__declspec(dllexport) char* get_ocv_table_77561(int index);
__declspec(dllexport) void set_ocv_table_77561(int index, int x, int y);

// RC 表读写(三维)
__declspec(dllexport) char* get_rc_table_77561(int index1, int index2);
__declspec(dllexport) void set_rc_table_77561(int index1, int index2, int x);

// RC 轴数据
__declspec(dllexport) char* get_x_table_77561(int index); // 电压轴
__declspec(dllexport) char* get_y_table_77561(int index); // 电流轴
__declspec(dllexport) char* get_z_table_77561(int index); // 温度轴

// 温度表
__declspec(dllexport) char* get_temp_table_77561(int index);
__declspec(dllexport) void set_temp_table_77561(int index, int x, int y);

// 参数管理
__declspec(dllexport) char* get_parameters_77561();
__declspec(dllexport) void set_parameters_77561(char* line);

// DFCC 相关
__declspec(dllexport) char* get_dfcc_ratio_77561();
__declspec(dllexport) void set_dfcc_ratio_77561(int v1, int v2, int v3, int v4, int v5, int v6, int v7);

// COBRA 列头解析
__declspec(dllexport) void set_cobratable_header_77561(char* line, int logformat);

// FCC 初始值
__declspec(dllexport) char* get_init_fcc_77561();
__declspec(dllexport) void set_init_fcc_77561(int value1, int value2);

FG 库初始化流程

1
2
3
4
5
6
7
8
9
soc_init_77561(first_line)

├── 解析 GGMEM0-6 → 恢复芯片内部状态
├── fg_setup(ram, devlst, num) → 分配句柄
├── fg_init(handle, cfg, lut, seo) → 绑定配置和查找表
│ ├── cfg: FG_CONFIG_T (dsncap, eocmv, eodmv, sysim...)
│ ├── lut: FG_LUT_T (OCV表, RC表, DFCC表, 温度表)
│ └── seo: FG_AUTO_SEO_T (自学习参数)
└── soc_sync_status_77561() → 同步初始SOC状态

FG_PARAM_T 输入结构

1
2
3
4
5
6
7
8
9
10
typedef struct {
signed int delta_ms; // 调用间隔 (ms)
signed int imode; // 电池状态: -1放电, 0空闲, 1充电
signed int current_ma; // 电流 (mA)
signed int cellthm; // 电芯温度 (0.1°C)
signed int delta_mah; // 库仑计增量 (mAh)
signed int volt_hi_mv; // 最高电芯电压 (mV)
signed int volt_lo_mv; // 最低电芯电压 (mV)
signed int chgr_state; // 充电器状态: 0无, 1接入, -1断开, 3满充
} FG_PARAM_T;

DFCC 表结构

三维查找表定义

DFCC 是一个三维查找表,用于在不同工况下补偿 RC(剩余容量)估算的偏差:

含义 默认维度 单位
X 轴 SOC 区间 DFCC_X = 11 %
Y 轴 电流区间 DFCC_Y = 2~4(按项目) mA
Z 轴 温度区间 DFCC_Z = 4 0.1°C

存储格式:short DFCC_table[DFCC_Y * DFCC_Z][DFCC_X]

维度由 DATA_TYPE 宏控制

1
2
3
4
5
6
7
8
9
10
11
12
// table.h 中按项目定义不同维度
#if (DATA_TYPE == 1027)
#define DFCC_Y 1 // 电流区间:1档
#define DFCC_Z 4 // 温度区间:4档
#elif (DATA_TYPE == 1028)
#define DFCC_Y 2 // 电流区间:2档
#define DFCC_Z 4 // 温度区间:4档
#endif

#ifndef DFCC_X
#define DFCC_X 11 // SOC区间:固定11档
#endif

DFCC 补偿原理

DFCC 表中的值为权重百分比:

  • 值 = 100:无补偿(RC 估算准确)
  • 值 > 100:实际容量比 RC 表估算值大,需上调
  • 值 < 100:实际容量比 RC 表估算值小,需下调

算法在放电过程中,根据当前 SOC、电流、温度三个维度查 DFCC 表,得到补偿系数,修正 RC 表的输出值。

DLL 访问接口

1
2
3
4
5
6
7
8
// 读取 DFCC 轴数据
__declspec(dllexport) short get_dfcc_xdata_77561(int index); // SOC 断点
__declspec(dllexport) short get_dfcc_ydata_77561(int index); // 电流断点
__declspec(dllexport) short get_dfcc_zdata_77561(int index); // 温度断点

// 读写 DFCC 表值
__declspec(dllexport) short get_dfcc_table_77561(int index1, int index2);
__declspec(dllexport) void set_dfcc_table_77561(int index1, int index2, short value);

C# GUI 层(BatterySimer)

P/Invoke 调用模式

C# 通过 DllImport 调用 C DLL,所有字符串返回值使用 IntPtr + Marshal.PtrToStringAnsi() 模式:

1
2
3
4
5
6
7
8
9
[DllImport(@"SOCLib77561.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr soc_init_77561(string line);

[DllImport(@"SOCLib77561.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr soc_process_77561(string line);

// 调用示例
IntPtr ptr = soc_process_77561(csv_line);
string result = Marshal.PtrToStringAnsi(ptr); // "rsoc,fcc,rc,dsgcap,chgcap,status,soh,tte"

仿真主循环

1
2
3
4
5
6
7
8
9
10
11
// 1. 初始化:用COBRA日志第一行数据初始化FG库
soc_init_77561(lines[1]);

// 2. 逐行处理
for (int i = 2; i < lines.Length; i++)
{
IntPtr ptr = soc_process_77561(lines[i]);
string result = Marshal.PtrToStringAnsi(ptr);
// 解析结果: rsoc, fcc, rc, dsgcap, chgcap, status, soh, tte
// 存入 d2f.yy[2] (simer SOC)
}

SharpGL 绘图数据结构

1
2
3
4
5
6
7
8
9
10
11
struct d2f {
public float xx; // X轴:数据点索引
public List<float> yy; // Y轴数组:
// yy[0] = cycle SOC (期望值)
// yy[1] = cobra SOC (芯片输出)
// yy[2] = simer SOC (仿真输出)
// yy[3] = |cycle - cobra| × 10
// yy[4] = |cycle - simer| × 10
// yy[5] = |cobra - simer| × 10
// yy[6] = SOH
}

GUI 支持:

  • 各曲线独立显示/隐藏
  • 鼠标缩放和平移
  • 实时数据 tooltip
  • OCV/RC/DFCC/温度表的可视化编辑

端到端数据流

从电池测试到 DFCC 调参完成的完整链路:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
(1) 电池循环测试
循环仪按指定温度/电流执行充放电
输出: _Detail.csv (CC, I, U, mode, time)

(2) COBRA 实时采集
COBRA工具通过I2C读取芯片寄存器
输出: COBRA_xxx.csv (28列, 含GGMEM状态)

(3) BatterySimer 加载数据
├── 加载 COBRA CSV → 解析列头 → set_cobratable_header_77561()
└── 自动关联 _Detail.csv → 计算 cycle SOC

(4) DLL 仿真处理
soc_init_77561(line[1]) → 初始化FG库
for each line:
soc_process_77561(line[i]) → fg_update() → 返回 rsoc/fcc/rc/soh

(5) 三曲线对比
cycle SOC vs cobra SOC vs simer SOC
误差曲线: cyc-sim 是否 < 3%?

(6) DFCC 调整
├── 误差大 → 修改 DFCC 表值 → 重新仿真
└── 误差小 → 调参完成 → 同步到固件

关键点:步骤 (4)-(6) 可以反复迭代,无需重新测试电池,这就是仿真环境的核心价值。

Python 自动化工具

auto_update_table.py 用于将 TableMaker 生成的 OCV/RC 数据自动同步到仿真代码:

1
2
3
4
5
6
# 使用示例
# BatterySimerKit2.0> python auto_update_table.py
# 请输入 table.c 中待替换区域的 DATA_TYPE 值: 1024
# 请输入 table.c 文件的路径: .\SOCLib77561\table\table.c
# 请输入 OCV.txt 文件的路径: .\OCV_PS_805769_5140mAhr_4400mV_3000mV_V007.txt
# 请输入 RC.txt 文件的路径: .\RC_PS_805769_5140mAhr_4400mV_3000mV_V002.txt

工具流程:
(1) 解析 OCV.txt → 提取 65 个 (电压, SOC) 数据点
(2) 解析 RC.txt → 提取三维数据(X轴 80点, Y轴 5点, Z轴 5点)
(3) 用正则表达式定位 table.c 中对应 DATA_TYPE 的代码块
(4) 替换 OCV 和 RC 数组数据

注意事项:

  • 此脚本不更新 DFCC 表,DFCC 需要根据客户定义的温度/电流测试条件手动配置 table.h 的 DFCC_Y/DFCC_Z 维度
  • 更新后需人工确认 table.c 是否与 FW PRJ 一致
  • parameter.c 的电池参数需手动与固件同步

多项目支持与仿真-固件关系

DATA_TYPE 系统

单代码库通过 DATA_TYPE 宏支持 20+ 不同电池项目,每个项目定义独立的:

配置项 说明 示例
XAxis / YAxis / ZAxis RC 表维度 80 / 5 / 5
DFCC_Y / DFCC_Z DFCC 表维度 2 / 4
CURR_DIV 并联电芯数(电流分流比) 1(单芯)或 6(6并)
dsncap 设计容量 (mAh) 5000
eocmv / eodmv 满充/截止电压 (mV) 4400 / 3200

切换项目只需修改 table.h 中的 #define DATA_TYPE 值,重新编译 DLL 即可。

仿真与固件的一致性要求

仿真结果可信的前提是算法代码与固件完全一致:

(1) lib_fg.c 必须与固件版本相同(用 WinMerge 对比确认)
(2) table.c / table.h 的 OCV、RC、DFCC 数据必须与固件 PRJ 匹配
(3) parameter.c 的电池参数(dsncap, eocmv, eodmv, sysim, slope)必须与固件一致

如果 cobra 曲线与 simer 曲线存在系统性偏差(而非局部 DFCC 误差),通常说明算法代码或参数不同步,需要先排查一致性问题再进行 DFCC 调参。