← Back to skills
extension
Category: Data & AnalyticsNo API key required

电池放电数据汇总分析技能

从锂电池 BMS / 测试台的 Excel 放电数据出发,完成「多维度放电分析 → 曲线图 → Word 汇报报告」全链路。 适用场景:用户提供锂电模组/电芯放电数据(BMS 运行日志、充放电测试台导出、HVDC/UPS 后备电池记录等), 要求做容量/能量/电压/内阻/温度/倍率/一致性/衰减等维度的深度分析并出图、产出可汇报的 Word 文档。 触发词:锂电池放电分析、放电数据、BMS 数据、容量分析、放电曲线、SOH、DOD、化成数据、放电报告。 也适用于任何「本地 xlsx/csv 数值数据 → 统计分析 + matplotlib 出图 + HTML→DOCX 报告」的通用链路。

personAuthor: user_052aadc3hubcommunity

锂电池放电数据分析与报告生成

何时使用

用户给出锂电池放电相关的表格数据(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 当一次放电。正确做法:

  1. 判定放电状态:总电流 < -阈值(注意符号约定,通常负值 = 放电)
  2. 按时间间隔容差(如 120 s)合并为连续放电段;跨日长记录会被切成多段
  3. 每段计算:
    • 容量 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(最高单体温度 − 最低单体温度)
    • 内阻(三法中位,单法在恒流工况下不可用):
      1. 放电前静置→放电的电压阶跃 (V_静置 − V_首) / I_首
      2. 放电→静置的电压回弹 (V_回弹 − V_末) / I_末
      3. 段内电流阶跃 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                           ← 可复跑脚本