锂电池放电数据分析与报告生成
何时使用
用户给出锂电池放电相关的表格数据(xlsx / csv),要求做多维分析、画曲线、出 Word 报告时使用。
本技能同时固化了「本地数据 → 图表 → DOCX」这条链路上踩过的 Windows 环境坑,通用性超出电池领域。
第 0 步:环境准备(重要,先做)
0.1 表格 MCP 在 Windows 上不可用 → 直接用 Python
tencent-docs-sheetagent(mcp__sheetagent__resolve_local_excel)只接受以 / 开头的 POSIX 路径,
Windows 盘符路径会分别报:
C:/Users/...→INVALID_LOCAL_PATH("必须是以 / 开头")/C:/Users/...→LOCAL_PATH_NOT_FOUND(被解析成c:\C:\Users\...)
结论:Windows 上分析本地 xlsx 直接走 Python,不要在这条路上浪费轮次。 (按路由规范,此时已属于「技能报告了具体失败后降级」,可以直接用 Bash/Python,但要在回复中说明已切换通道。)
0.2 Python 环境
优先用托管 venv(已预装常用库):
VPY="C:/Users/Frank-Zhang/.workbuddy/binaries/python/envs/default/Scripts/python.exe"
若缺库:
# 建 venv
"C:/Users/Frank-Zhang/.workbuddy/binaries/python/versions/3.13.12/python.exe" -m venv "C:/Users/Frank-Zhang/.workbuddy/binaries/python/envs/default"
VPY="C:/Users/Frank-Zhang/.workbuddy/binaries/python/envs/default/Scripts/python.exe"
"$VPY" -m pip install -i https://pypi.tuna.tsinghua.edu.cn/simple pandas numpy openpyxl matplotlib python-docx scipy
脚本运行务必加 PYTHONIOENCODING=utf-8,否则中文 print 会乱码/报错。
0.3 命名陷阱
不要把脚本命名为 inspect.py / types.py / csv.py 等标准库同名文件——会触发循环导入
(AttributeError: partially initialized module 'pandas' ... circular import)。用 peek.py 之类。
第 1 步:结构探查(不要跳过)
用 openpyxl(..., read_only=True, data_only=True) 逐 sheet 打印前 12 行 + 行数 + 列名。
BMS 类数据典型结构:
R1 站点/设备标识
R2 系统编号
R3 字段表头
R4–R6 最大值 / 最小值 / 平均值(统计行,必须剔除)
R7+ 时序数据(常见 10 s 采样)
判据:表头行 = 满足 row[1] == "总电压"(或类似首个字段名)的那一行;
数据行 = row[0] 是 datetime 的行。统计行靠 row[0] in {"最大值","最小值","平均值"} 排除。
第 2 步:数据质量核查(最关键,决定结论对错)
必须逐项检查,并在报告中显式披露:
| 检查项 | 方法 | 常见陷阱 |
|---|---|---|
| 列序不一致 | 按列名建映射,不要按列位 | 同一文件的各 sheet 列序会不同 |
| 伪温度列 | 看取值范围 | 曾有「平均温度」实为 740–798 的原始累加值而非 ℃ |
| 行/列错位 | 找物理不可能值:单体温度 <10 或 >60 ℃、单体电压 <2.5 V、分簇容量 >1000 Ah | 源文件导出错位,整表剔除 |
| SOC 饱和/未校准 | 比对 SOC 与「剩余容量/额定容量」 | SOC 恒显 100% 而容量实降 |
| 额定参数不一致 | 统计各 sheet 的「额定容量」「额定电量」分布 | 模块化架构下不同 sheet 配置不同 |
| 时间戳稀疏 | 统计相邻采样间隔众数与 >600 s 的大间隔 | 长时长记录含多次间歇放电+回充 |
| 时间戳单位 | pd.Timestamp(epoch_int) 默认按纳秒解析 | 必须写 pd.Timestamp(x, unit="s") |
第 3 步:放电段切分与指标计算
不要把整张 sheet 当一次放电。正确做法:
- 判定放电状态:
总电流 < -阈值(注意符号约定,通常负值 = 放电) - 按时间间隔容差(如 120 s)合并为连续放电段;跨日长记录会被切成多段
- 每段计算:
- 容量
Ah = Σ 0.5·(i_k+i_{k+1})·Δt / 3600,Δt需clip(0, 300s)防长间隔放大 - 能量
kWh = Σ u·i·Δt / 3.6e6 - 电压 起始 / 终止 / 平台(中位数)/ 最低 / 起始压降(起始 − 平台)
- 倍率
C = i_mean / 系统额定容量 - 温升
ΔT = max(最高单体温度) − 起始最高单体温度;单体间温差 = max(最高单体温度 − 最低单体温度) - 内阻(三法中位,单法在恒流工况下不可用):
- 放电前静置→放电的电压阶跃
(V_静置 − V_首) / I_首 - 放电→静置的电压回弹
(V_回弹 − V_末) / I_末 - 段内电流阶跃
median(−Δu/Δi)(要求|Δi| > 8 A,样本 ≥3) 过滤0.001 Ω < R < 1 Ω
- 放电前静置→放电的电压阶跃
- 一致性 单体电压极差、簇间压差、簇间 SOC 极差、各分簇放电量极差
- 容量
双口径交叉验证:容量同时用「电流积分」与 BMS 自带的「日放电容量 / 剩余容量差」计算, 两者偏差应 <5%;偏差大说明存在长间隔或 SOC 校准问题,需在报告中说明。 注意 BMS「日放电容量」在跨日记录中会归零,不可跨日比较。
第 4 步:绘图(matplotlib)
必须显式设置中文字体,否则全是方框:
plt.rcParams["font.sans-serif"] = ["Microsoft YaHei", "SimHei", "DejaVu Sans"]
plt.rcParams["axes.unicode_minus"] = False
经验:
- 图例一律放在坐标区外(
loc="upper center", bbox_to_anchor=(0.5, -0.30), ncol=2), 否则必然压住曲线——尤其放电电压曲线太平坦、散布全图 - 时间轴不要用
mdates.date2num混搭,直接用「距起点的天数」浮点 + 手动set_xticks/set_xticklabels,最稳 - 箱线图用
patch_artist=True配set_facecolor;ax.legend()前先取get_legend_handles_labels(),空则跳过 - 每图
fig.savefig(..., bbox_inches="tight", facecolor="white"),160 dpi - 输出:
fig尺寸 13–14 × 5 in 的双联图在 A4 竖版里最耐看
第 5 步:HTML → DOCX(用 tencent-docx 的 html-to-docx 引擎)
推荐路线:自己写 HTML(内容/数字完全可控,避免生成式写作编造数值),再用官方引擎转 DOCX。
html-to-docx 支持本地图片嵌入、封面/目录/页眉页脚/装饰组件。
5.1 Windows 环境搭建(官方 setup-html-to-docx.sh 在 Windows 会失败)
该脚本用 Unix 布局 $VENV_DIR/bin/python 且把 POSIX 路径交给 Windows 版 uv,会:
- 每次都判定「venv 不存在」→
rm -rf后重建 uv把/c/Users/...解析成c:\c\Users\...(会在 C 盘建出一个多余的C:\c目录)
手工搭建(用 Windows 原生路径):
export PATH="$HOME/.local/bin:$PATH"
VW="C:/Users/Frank-Zhang/.venv-html-to-docx"
PLUG="C:/Users/Frank-Zhang/.workbuddy/plugins/cache/workbuddy-builtin/tencent-docx/<版本号>"
uv venv --clear --python 3.12 "$VW"
UV_INDEX_URL="https://pypi.tuna.tsinghua.edu.cn/simple" \
uv pip install --quiet --python "$VW/Scripts/python.exe" --only-binary=:all: \
-r "$PLUG/skills/html-to-docx/scripts/requirements.txt"
<版本号>用 glob 找:ls -d .../tencent-docx/*/ | tail -1
5.2 转换命令
cd "$PLUG/skills/html-to-docx/scripts" # 必须 cd,html_to_docx 包在此
PYTHONIOENCODING=utf-8 "$VW/Scripts/python.exe" -m html_to_docx convert \
"C:/path/report.html" -o "C:/path/report.docx"
成功输出 {"success": true, "docx_path": ...}。
5.3 HTML 契约要点
- 顶层
<section role="cover">+<section role="body" data-page-restart="1">→ 自动分节、封面单独一页 - 目录:
<nav class="doc-toc"><p class="toc-title">目录</p><ol class="toc-list"><li><a href="#id">…</a></li></ol></nav>, 正文标题需带同名id;锚点解析不到会降级为纯文本 - 页眉页脚:
@page { @bottom-center { content: counter(page) " / " counter(pages); } @top-right { content: string(chapter); } }⚠️string-set的选择器必须是单个裸标题标签(如h2 { string-set: chapter content(text); }); 写h1, h2, h3会警告且 STYLEREF 解析失败、页眉为空 - 图片:
<img src="C:/绝对路径.png" width="660">,超宽自动等比缩放 - 装饰组件:
<div data-component="callout" data-variant="info|warning|success|danger">、<div data-component="data-card" data-title="指标名" data-color="primary">(KPI 卡片,会渲染为小表格) table+thead/tbody正常映射;支持中文字体
5.4 校验产出
from docx import Document
d = Document(path)
print(len(d.paragraphs), len(d.tables), len(d.inline_shapes)) # 图片数应等于图表数
再遍历 d.tables 的单元格文本做关键词断言(因为 callout / data-card 会落成表格,不在 paragraphs 里)。
常见坑速查
| 现象 | 原因 / 解法 |
|---|---|
| No module named 'click'/'docx' | venv 是空壳,手工 uv pip install -r requirements.txt |
| uv 在 C:\c\... 建目录 | Git Bash 的 POSIX 路径喂给了 Windows 版 uv,改用 C:/... 原生路径 |
| ValueError: assignment destination is read-only | df[c].to_numpy() 是只读视图,加 .copy() |
| partially initialized module ... circular import | 脚本名与标准库(inspect.py 等)重名 |
| 中文图全是方框 | 未设 font.sans-serif |
| 时间列全变成 1970 年 | pd.Timestamp(epoch秒) 少了 unit="s" |
| 报告里数字与表格对不上 | 数字应由脚本从数据内联计算后写入 HTML,不要手工誊抄 |
交付清单
放电分析/
├── output/
│ ├── 放电分析总结报告.docx ← 主交付物
│ ├── 放电分析总结报告.html ← 可浏览器预览
│ ├── figs/fig01..N.png ← 曲线图
│ ├── segments_final.csv ← 放电段明细(核心数据表)
│ ├── events_v2.csv ← 事件级汇总
│ └── data/*.csv ← 各 sheet 时序(便于复核)
└── *.py ← 可复跑脚本
微信扫一扫