diff --git a/docs/pages/battery-report-zh.html b/docs/pages/battery-report-zh.html new file mode 100644 index 0000000..70553f3 --- /dev/null +++ b/docs/pages/battery-report-zh.html @@ -0,0 +1,171 @@ +一百零五颗电芯 + + + + + +
+
+
Pylontech 电池组 · 诊断进行中
+

一百零五颗电芯

+

八个模块,每个十五颗电芯。其中十颗电芯限制了其余九十五颗的充电。本报告说明是哪几颗、依据何在、以及可以怎么处理。

+
最后更新 : 2026 年 9 月 5 日 17:00
+
+ +
10
105 颗中受影响的电芯
3
8 个模块中受影响的模块
5 秒
从越过阈值到电流下降
30 A
施加于整组的限值,标称为 150 A
+ +

1结论

电池组并未整体老化。八个模块中有四个毫无异常: +六十颗电芯在三百多次循环之后没有一处异常。第五个模块同样干净。

+

全部问题集中在三个模块,而在这三个模块之内只有十颗电芯。 +当其中一颗达到 3550 mV,BMS 便降低充电电流——降的不是该模块,而是 +整个电池组。五个健康模块被十颗电芯拖住。

+ +
+
200 mV400 mV600 mV0323旧电池组 · 地址 2 — 对照模块1191218831884189519061977187819091891019211191121881318714192151980322旧电池组 · 地址 11182217831784178517765487371818191791017911182126911317814273151780319旧电池组 · 地址 31183252131844186518263187183818691841047311184124431318414188154240324已隔离 (BAT3) · 地址 4113821243138413751396132713481429143101351113612137131281413515683
+
健康轻度偏离严重偏离已失效
+
图中为观测期内每颗电芯在最低与最高电压之间所扫过的电压幅度。 +第一个模块是对照模块:它与两个受损模块同在一条通信链上, +经历相同的电流与相同的日子,而它的十五颗电芯彼此幅度之差仅 +十一毫伏。容量衰减的电芯会比相邻电芯更早充满、更早放空, +因此在相同电流下扫过的电压范围明显更宽——这一判据无需拆解模块即可读出。 +已隔离模块的幅度偏小,是因为它在观测期内处于停用状态、荷电区间更窄: +应看它与自身相邻电芯之差,而非绝对数值。
+
+ +

2逐模块清单

模块的身份是它的条码,而不是地址:每条链上的模块都从 1 重新编号, +因此本系统中存在两个「模块 1」。

+
条码电池组地址受影响电芯数具体编号压差中位数过压次数欠压次数
K220803C30160322旧146 · 7 · 12 · 1410849886在运行
K220803C30160323旧20—600在运行
K220803C30160319旧352 · 6 · 10 · 12 · 1545416179在运行
K220803C30160324旧4115572959已隔离
Y230202C30371426新10—211在运行
Y230202C30371425新20—211在运行
Y230202C30371431新30—211在运行
Y230202C30371427新40—210在运行
+

请看压差中位数一列。五个健康模块在 2 至 6 毫伏之间; +另外三个分别为 45、57 与 108。过压计数呈现同样的对比:第一个模块为 +498 次,而新电池组四个模块各自仅 1 次—— +后者的循环次数还多出十倍。

+

新电池组的六十颗电芯,幅度全部落在 168 至 191 mV 之间,无一例外。 +清点至此完成。

+ +

3触发过程,逐秒记录

9 月 5 日,在告警上升过程中,控制台接入了旧电池组的三个模块。 +以下为逐秒记录。

+
时间发生了什么
13:04:48模块 …0322 的 7 号电芯 3526 mV,模块 …0319 的 6 号电芯 3541低于阈值
13:05:08同两颗电芯升至 3555 与 3575 mV越过三级阈值
13:05:13BMS 将允许充电电流由 50 A 降至 30 A五秒之后
13:15:10电芯回落至 3499 与 3522 mV,告警解除但电流限值仍保持 30 A
13:22:40限值 30 → 40 A经过 7 分 31 秒
13:26:39限值 40 → 35 A,最高电芯 3563 与 3548 mV再次触发
13:38:10限值 35 → 40 A经过 11 分 31 秒
+
+

最有价值的一处发现在第四行:告警远早于电流限值 +恢复之前就已解除。因此仅在日志中查找告警,会错过该现象的绝大部分—— +告警持续数分钟,而限流持续数小时。

+

这些阈值并非我们所选:它们由设备本身声明,读取自 LV-HUB 的 +sysinfo。

+ +

4是内阻,还是荷电不均衡?

这个问题决定了均衡能够挽回多少。同一颗电芯在两种电流下的两个测点 +即可判定。

+ + +
单芯电流压差
13:04:486.33 A163 mV
14:05:420.04 A46 mV
+

斜率给出最高电芯的额外内阻约 18.6 mΩ,为健康电芯的 +二十至四十倍。截距 约 46 mV,才是真正的荷电不均衡。

+

独立验证:该直线预测在整组电流 100 A 时压差为 312 mV;三十七天记录中 +实际出现的最大压差为 280 mV。

+

因此在充电时约 190 mV 的压差中,只有 46 mV 属于不均衡, +其余为容量损失。即便完成理想均衡,触发点也只能从 3555 mV 降至约 3510 mV: +低于阈值,但余量仅四十毫伏。

+ +

5损伤的来源

并非老化所致。新电池组一年内完成三百四十次循环,六十颗电芯完好无损: +循环本身不会损坏这些模块。对照模块与两个受损模块同链、同电流、同工况, +亦毫发无伤。

+

两颗已失效电芯曾降至 2953 与 +2939 mV,低于低压告警阈值。磷酸铁锂电芯在带载状态下 +被放至该电压,会发生不可逆的容量损失。这些电芯不是老化,而是被过放。

+ +

6向 Pylontech 提出的三个问题

+
  1. 这些电芯能否通过长时间保持高荷电状态而恢复,还是 18.6 mΩ 的内阻分量已使该尝试失去意义?
  2. 对处于此状态的模块,是否有推荐的处理流程?超过何种程度必须更换模块?
  3. soh 命令返回的单芯 SOHCount 具体统计的是什么?从多大数值起,Pylontech 认为该电芯需要处理?
+
+ + +
diff --git a/docs/pages/battery-report.html b/docs/pages/battery-report.html new file mode 100644 index 0000000..dc96952 --- /dev/null +++ b/docs/pages/battery-report.html @@ -0,0 +1,181 @@ +The Hundred and Five Cells + + + + + +
+
+
Pylontech pack · diagnosis in progress
+

The Hundred and Five Cells

+

Eight modules, fifteen cells each. Ten of those cells throttle the charging of the other ninety-five. Here is which ones, how we know, and what can be done about it.

+
Last updated : 5 September 2026, 17:00
+
+ +
10
affected cells out of 105
3
modules involved out of 8
5 s
from threshold crossing to current cut
30 A
limit imposed on the whole pack, against 150 A nominal
+ +

1The verdict

The pack is not worn out. Four modules out of eight are flawless: +sixty cells without a single anomaly, after more than three hundred cycles. A +fifth is just as clean.

+

Three modules carry the entire problem, and within those three, ten +cells. When one of them reaches 3550 mV the BMS reduces the charge +current — not for its own module, but for the whole pack. Five +healthy modules are throttled by ten cells.

+ +
+
200 mV400 mV600 mV0323older pack · address 2 — THE CONTROL1191218831884189519061977187819091891019211191121881318714192151980322older pack · address 11182217831784178517765487371818191791017911182126911317814273151780319older pack · address 31183252131844186518263187183818691841047311184124431318414188154240324isolated (BAT3) · address 4113821243138413751396132713481429143101351113612137131281413515683
+
healthydriftingbadly affectedterminal
+
Voltage span swept by each cell between its minimum and maximum over the +observation period. The first module is the control: it lives on +the same chain as the two damaged ones, has seen the same currents and the same +days, and its fifteen cells sit within eleven millivolts of span of each +other. A cell that has lost capacity fills and empties before its +neighbours, so it sweeps a far wider range at identical current — the sign +requires no disassembly. The isolated module shows smaller spans because it spent +the period out of service, over a narrower state-of-charge window: what matters +is the gap to its own neighbours, not the absolute value.
+
+ +

2The pack, module by module

A module's identity is its barcode, never its address: each chain renumbers +its modules from 1, so there are two “module 1” in this installation.

+
BarcodePackAddr.Affected cellsWhich onesMedian spreadOvervoltageUndervoltage
K220803C30160322older146 · 7 · 12 · 1410849886in service
K220803C30160323older20—600in service
K220803C30160319older352 · 6 · 10 · 12 · 1545416179in service
K220803C30160324older4115572959isolated
Y230202C30371426newer10—211in service
Y230202C30371425newer20—211in service
Y230202C30371431newer30—211in service
Y230202C30371427newer40—210in service
+

Read the median spread column. The five healthy modules sit between +2 and 6 millivolts; the other three are at 45, 57 and 108. The +same contrast shows in the overvoltage counters: 498 for the +first module against one for each of the four in the newer pack, +which have nonetheless taken ten times more cycles.

+

All sixty cells of the newer pack sweep between 168 and 191 mV, without +exception. The inventory is closed.

+ +

3The trip, timed

On 5 September the console was connected to the three modules of the older +pack while the alarm was rising. What follows is the record, to the second.

+
TimeWhat happens
13:04:48cell 7 of module …0322 at 3526 mV, cell 6 of module …0319 at 3541below threshold
13:05:08the same two at 3555 and 3575 mVlevel 3 crossed
13:05:13the BMS drops the allowed charge current from 50 to 30 Afive seconds later
13:15:10cells fall back to 3499 and 3522 mV, alarm clearsthe limit stays at 30 A
13:22:40limit 30 → 40 Aafter 7 min 31 s
13:26:39limit 40 → 35 A, leading cells at 3563 and 3548 mVit trips again
13:38:10limit 35 → 40 Aafter 11 min 31 s
+
+

The most useful finding is in the fourth row: the alarm +clears well before the limit recovers. Looking for the alarm in the logs therefore +misses the bulk of the phenomenon — the alarm lasts minutes, the throttling lasts +hours.

+

The thresholds are not values we chose: they are the ones the hardware +declares, read from sysinfo on the LV-HUB.

+ +

4Internal resistance, or charge imbalance?

The answer decides what balancing can give back. Two measurement points on +the same cell at two different currents settle it.

+ + +
current per cellspread
13:04:486.33 A163 mV
14:05:420.04 A46 mV
+

The slope gives ≈ 18.6 mΩ of excess internal resistance on the +leading cell, twenty to forty times that of a healthy one. The intercept, +≈ 46 mV, is the true charge imbalance.

+

Independent check: this line predicts 312 mV at 100 A pack current. The largest +spread actually recorded over thirty-seven days is 280 mV.

+

So of the ~190 mV of spread under charge, only 46 mV are an +imbalance — the rest is lost capacity. A perfect rebalance would bring the +trip from 3555 down to about 3510 mV: below the threshold, but with only forty +millivolts of margin.

+ +

5Where the damage comes from

Not from wear. The newer pack has taken three hundred and forty cycles in a +year and its sixty cells are intact: cycling does not damage these modules. And +the control module shared the chain, the currents and the days of the two damaged +ones without a scratch.

+

The two terminal cells went down to 2953 and +2939 mV, below the low-voltage alarm threshold. A +lithium iron phosphate cell taken there under load loses capacity irreversibly. +These cells did not age, they were discharged too deep.

+ +

6Three questions for Pylontech

+
  1. Are these cells recoverable by a prolonged hold at high state of charge, or does the 18.6 mΩ resistive component make the attempt pointless?
  2. Is there a recommended procedure for a module in this condition, and a limit beyond which module replacement is the only option?
  3. What exactly does the per-cell SOHCount returned by the soh command count, and from what value does Pylontech consider a cell to require attention?
+
+ + +
diff --git a/docs/pages/rapport-batteries.html b/docs/pages/rapport-batteries.html index 5752f40..50db1d9 100644 --- a/docs/pages/rapport-batteries.html +++ b/docs/pages/rapport-batteries.html @@ -25,55 +25,50 @@ } * { box-sizing:border-box; } body { background:var(--fond); color:var(--encre); - font:400 17px/1.62 "Source Serif 4", Georgia, serif; margin:0; - -webkit-font-smoothing:antialiased; } + font:400 17px/1.62 "Source Serif 4", Georgia, serif; margin:0; -webkit-font-smoothing:antialiased; } .page { max-width:1000px; margin:0 auto; padding:0 24px 96px; } .mesure { max-width:64ch; } h1, h2, h3, .fm, .etiq { font-family:Chivo, "Helvetica Neue", Arial, sans-serif; } -h1 { font-weight:900; font-size:clamp(2.4rem,6.5vw,4rem); line-height:1.02; +h1 { font-weight:900; font-size:clamp(2.2rem,6vw,3.7rem); line-height:1.08; letter-spacing:-.025em; margin:0 0 .4em; text-wrap:balance; } -h2 { font-weight:700; font-size:1.5rem; letter-spacing:-.012em; - margin:4.2rem 0 .2em; text-wrap:balance; } +h2 { font-weight:700; font-size:1.5rem; margin:4.2rem 0 .2em; text-wrap:balance; } h2 .num { color:var(--sourd); font-weight:400; margin-right:.5em; font-variant-numeric:tabular-nums; } -h3 { font-weight:700; font-size:1.05rem; margin:2.4rem 0 .4em; } p { margin:0 0 1.05em; } -a { color:var(--sain); } strong, b { font-weight:600; } .mono { font-family:"JetBrains Mono", ui-monospace, monospace; font-size:.86em; font-variant-numeric:tabular-nums; } header { padding:72px 0 8px; border-bottom:1px solid var(--trait); margin-bottom:2.5rem; } .etiq { font-size:.72rem; font-weight:700; letter-spacing:.14em; text-transform:uppercase; color:var(--sourd); margin-bottom:1.6rem; } -.chapeau { font-size:1.24rem; line-height:1.5; color:var(--sourd); +.chapeau { font-size:1.2rem; line-height:1.55; color:var(--sourd); max-width:56ch; margin-bottom:2rem; } .maj { font-size:.8rem; color:var(--sourd); padding-bottom:1.4rem; } .verdict { display:grid; gap:1px; background:var(--trait); - grid-template-columns:repeat(auto-fit,minmax(150px,1fr)); + grid-template-columns:repeat(auto-fit,minmax(155px,1fr)); border:1px solid var(--trait); border-radius:10px; overflow:hidden; margin:2rem 0 0; } .verdict div { background:var(--carte); padding:18px 20px; } -.verdict .n { font-family:Chivo,sans-serif; font-weight:900; font-size:2.3rem; - line-height:1; letter-spacing:-.03em; font-variant-numeric:tabular-nums; } +.verdict .n { font-family:Chivo, "Helvetica Neue", Arial, sans-serif; font-weight:900; font-size:2.3rem; + line-height:1; font-variant-numeric:tabular-nums; } .verdict .l { font-size:.86rem; color:var(--sourd); margin-top:.45em; line-height:1.35; } figure { margin:2rem 0; } .cadre { background:var(--carte); border:1px solid var(--trait); border-radius:10px; padding:22px 20px; overflow-x:auto; } svg { display:block; min-width:640px; width:100%; height:auto; } .fm { font-size:12.5px; font-weight:700; fill:var(--encre); } -.fp { font-size:10.5px; fill:var(--sourd); font-family:"Source Serif 4",serif; } -.fc { font-size:9.5px; fill:var(--sourd); text-anchor:end; - font-family:"JetBrains Mono",monospace; } +.fp { font-size:10.5px; fill:var(--sourd); font-family:"Source Serif 4", Georgia, serif; } +.fc { font-size:9.5px; fill:var(--sourd); text-anchor:end; font-family:"JetBrains Mono",monospace; } .fv { font-size:9.5px; fill:var(--sourd); font-family:"JetBrains Mono",monospace; } -.fr { font-size:9.5px; fill:var(--sourd); text-anchor:middle; - font-family:"JetBrains Mono",monospace; } +.fr { font-size:9.5px; fill:var(--sourd); text-anchor:middle; font-family:"JetBrains Mono",monospace; } .rep { stroke:var(--trait-fort); stroke-width:1; stroke-dasharray:2 4; } -figcaption { font-size:.88rem; color:var(--sourd); margin-top:.9rem; max-width:62ch; } +figcaption { font-size:.88rem; color:var(--sourd); margin-top:.9rem; + max-width:62ch; line-height:1.65; } .tabl { overflow-x:auto; margin:1.6rem 0; border:1px solid var(--trait); border-radius:10px; background:var(--carte); } -table { border-collapse:collapse; width:100%; min-width:660px; - font-family:"Source Serif 4",serif; font-size:.94rem; } -th { font-family:Chivo,sans-serif; font-size:.7rem; font-weight:700; - letter-spacing:.09em; text-transform:uppercase; color:var(--sourd); +table { border-collapse:collapse; width:100%; min-width:680px; + font-family:"Source Serif 4", Georgia, serif; font-size:.94rem; } +th { font-family:Chivo, "Helvetica Neue", Arial, sans-serif; font-size:.7rem; font-weight:700; + letter-spacing:.08em; text-transform:uppercase; color:var(--sourd); text-align:left; padding:13px 14px; border-bottom:1px solid var(--trait); } td { padding:11px 14px; border-bottom:1px solid var(--trait); vertical-align:top; } tr:last-child td { border-bottom:none; } @@ -85,6 +80,9 @@ tr.mal td { background:var(--lueur); } .etat { font-size:.85rem; color:var(--sourd); } .h { font-weight:700; white-space:nowrap; } .note { color:var(--sourd); font-size:.88rem; } +table.mini { min-width:0; width:auto; margin:1.2rem 0; border:1px solid var(--trait); + border-radius:8px; } +table.mini th { padding:9px 16px; } table.mini td { padding:8px 16px; } .legende { display:flex; flex-wrap:wrap; gap:18px; font-size:.85rem; color:var(--sourd); margin-top:1rem; } .legende span { display:inline-flex; align-items:center; gap:7px; } @@ -92,149 +90,95 @@ tr.mal td { background:var(--lueur); } .encart { border-left:3px solid var(--grave); background:var(--carte); padding:16px 20px; margin:1.8rem 0; border-radius:0 8px 8px 0; } .encart p:last-child { margin-bottom:0; } -ul { padding-left:1.2em; } li { margin-bottom:.45em; } +ol, ul { padding-left:1.3em; } li { margin-bottom:.7em; } footer { margin-top:5rem; padding-top:1.6rem; border-top:1px solid var(--trait); - font-size:.85rem; color:var(--sourd); } + font-size:.85rem; color:var(--sourd); line-height:1.6; }
Parc Pylontech · diagnostic en cours

Les cent cinq cellules

-

Huit modules, quinze cellules chacun. Dix de ces cellules - brident la charge des quatre-vingt-quinze autres. Voici lesquelles, comment on le sait, - et ce qu’on peut en faire.

+

Huit modules, quinze cellules chacun. Dix de ces cellules brident la charge des quatre-vingt-quinze autres. Voici lesquelles, comment on le sait, et ce qu’on peut en faire.

Dernière mise à jour : 5 septembre 2026, 17 h
-
-
10
cellules atteintes sur 105
-
3
modules concernés sur 8
-
5 s
entre le franchissement du seuil et la coupure
-
30 A
plafond imposé au parc entier, pour 150 A nominaux
-
+
10
cellules atteintes sur 105
3
modules concernés sur 8
5 s
entre le franchissement du seuil et la coupure
30 A
plafond imposé au parc entier, pour 150 A nominaux
-
-

1Le verdict

-

Le parc n’est pas usé. Quatre modules sur huit sont irréprochables : +

1Le verdict

Le parc n’est pas usé. Quatre modules sur huit sont irréprochables : soixante cellules sans une seule anomalie, après plus de trois cents cycles. Un cinquième l’est tout autant.

Trois modules portent l’ensemble du problème, et à l’intérieur de ces trois, dix cellules. Quand l’une d’elles atteint 3550 mV, le BMS réduit le courant de charge — non pas de son module, mais de tout le parc. -Cinq modules sains sont bridés par dix cellules.

-
+Cinq modules sains sont bridés par dix cellules.

-
200 mV400 mV600 mV0323pack ancien · adresse 2 — LE TÉMOIN1191218831884189519061977187819091891019211191121881318714192151980322pack ancien · adresse 11182217831784178517765487371818191791017911182126911317814273151780319pack ancien · adresse 31183252131844186518263187183818691841047311184124431318414188154240324isolée (BAT3) · adresse 4113821243138413751396132713481429143101351113612137131281413515683
-
- saine - décalée - gravement atteinte - terminale -
-
Amplitude balayée par chaque cellule entre sa tension minimale et - sa tension maximale sur la période d’observation. Le premier module est le - témoin : il vit dans la même chaîne que les deux abîmés, - a subi les mêmes courants et les mêmes journées, et ses quinze cellules tiennent - dans onze millivolts d’amplitude les unes des autres. C’est à - cela que ressemble un module sain, et c’est ce qui rend les autres barres - lisibles. Une cellule qui a perdu de la capacité se remplit et se vide avant ses - voisines, donc elle balaye une plage bien plus large à courant identique — le - signe ne demande aucun démontage. Les amplitudes du module isolé sont plus - faibles parce qu’il a passé la période hors service, sur une plage de charge - plus étroite : c’est l’écart à ses propres voisines qui compte, pas la - valeur absolue.
+
200 mV400 mV600 mV0323pack ancien · adresse 2 — LE TÉMOIN1191218831884189519061977187819091891019211191121881318714192151980322pack ancien · adresse 11182217831784178517765487371818191791017911182126911317814273151780319pack ancien · adresse 31183252131844186518263187183818691841047311184124431318414188154240324isolée (BAT3) · adresse 4113821243138413751396132713481429143101351113612137131281413515683
+
sainedécaléegravement atteinteterminale
+
Amplitude balayée par chaque cellule entre sa tension minimale et sa +tension maximale sur la période d’observation. Le premier module est le +témoin : il vit dans la même chaîne que les deux abîmés, a subi +les mêmes courants et les mêmes journées, et ses quinze cellules tiennent dans +onze millivolts d’amplitude les unes des autres. Une cellule qui +a perdu de la capacité se remplit et se vide avant ses voisines, donc elle balaye +une plage bien plus large à courant identique — le signe ne demande aucun +démontage. Les amplitudes du module isolé sont plus faibles parce qu’il a passé +la période hors service, sur une plage de charge plus étroite : c’est l’écart à +ses propres voisines qui compte, pas la valeur absolue.
-
-

2Le parc, module par module

-

L’identité d’un module est son code-barres, jamais son adresse : chaque -chaîne renumérote ses modules à partir de 1, il existe donc deux « module -1 » dans cette installation.

-
- -
- - - -
Code-barresPackAdr.Cellules atteintesLesquellesÉcart médianSurtensionsSous-tensions
K220803C30160322ancien146 · 7 · 12 · 1410849886en service
K220803C30160323ancien20—600en service
K220803C30160319ancien352 · 6 · 10 · 12 · 1545416179en service
K220803C30160324ancien4115572959isolée
Y230202C30371426récent10—211en service
Y230202C30371425récent20—211en service
Y230202C30371431récent30—211en service
Y230202C30371427récent40—210en service
- -
-

Lisez la colonne des écarts médians. Les cinq modules sains tiennent entre -2 et 6 millivolts ; les trois autres sont à 45, 57 et 108. -Le même contraste apparaît sur les surtensions : 498 pour -le premier module contre une seule pour chacun des quatre du -pack récent, qui ont pourtant encaissé dix fois plus de cycles.

+

2Le parc, module par module

L’identité d’un module est son code-barres, jamais son adresse : chaque +chaîne renumérote ses modules à partir de 1, il existe donc deux « module 1 » +dans cette installation.

+
Code-barresPackAdr.Cellules atteintesLesquellesÉcart médianSurtensionsSous-tensions
K220803C30160322ancien146 · 7 · 12 · 1410849886en service
K220803C30160323ancien20—600en service
K220803C30160319ancien352 · 6 · 10 · 12 · 1545416179en service
K220803C30160324ancien4115572959isolée
Y230202C30371426récent10—211en service
Y230202C30371425récent20—211en service
Y230202C30371431récent30—211en service
Y230202C30371427récent40—210en service
+

Lisez la colonne des écarts médians. Les cinq modules sains tiennent entre +2 et 6 millivolts ; les trois autres sont à 45, 57 et 108. Le même +contraste apparaît sur les surtensions : 498 pour le premier +module contre une seule pour chacun des quatre du pack récent, +qui ont pourtant encaissé dix fois plus de cycles.

Les soixante cellules du pack récent balayent toutes entre 168 et 191 mV -d’amplitude, sans exception. L’inventaire est clos : il n’y a rien à -chercher ailleurs que dans les trois modules déjà nommés.

- -

3Le déclenchement, chronométré

-

Le 5 septembre, la console a été branchée sur les trois modules du pack ancien -pendant que l’alarme montait. Ce qui suit est le relevé, à la seconde.

-
- -
- - -
HeureCe qui se passe
13:04:48cellule 7 du module …0322 à 3526 mV, cellule 6 du module …0319 à 3541sous le seuil
13:05:08les mêmes à 3555 et 3575 mVniveau 3 franchi
13:05:13le BMS abaisse le courant de charge autorisé de 50 à 30 Acinq secondes plus tard
13:15:10les cellules redescendent à 3499 et 3522 mV, l’alarme repasse au vertle plafond, lui, reste à 30 A
13:22:40plafond 30 → 40 Aaprès 7 min 31 s
13:26:39plafond 40 → 35 A, meneuses à 3563 et 3548 mVil redéclenche
13:38:10plafond 35 → 40 Aaprès 11 min 31 s
+d’amplitude, sans exception. L’inventaire est clos.

+

3Le déclenchement, chronométré

Le 5 septembre, la console a été branchée sur les trois modules du pack +ancien pendant que l’alarme montait. Ce qui suit est le relevé, à la seconde.

+
HeureCe qui se passe
13:04:48cellule 7 du module …0322 à 3526 mV, cellule 6 du module …0319 à 3541sous le seuil
13:05:08les mêmes à 3555 et 3575 mVniveau 3 franchi
13:05:13le BMS abaisse le courant de charge autorisé de 50 à 30 Acinq secondes plus tard
13:15:10les cellules redescendent à 3499 et 3522 mV, l’alarme repasse au vertle plafond, lui, reste à 30 A
13:22:40plafond 30 → 40 Aaprès 7 min 31 s
13:26:39plafond 40 → 35 A, meneuses à 3563 et 3548 mVil redéclenche
13:38:10plafond 35 → 40 Aaprès 11 min 31 s
-
-

La découverte la plus utile de la journée tient dans la -quatrième ligne : l’alarme repasse au vert bien avant que le plafond ne -remonte. Chercher l’alarme dans les journaux fait donc manquer l’essentiel du -phénomène — elle dure quelques minutes, le bridage dure des heures.

+

La découverte la plus utile tient dans la quatrième +ligne : l’alarme repasse au vert bien avant que le plafond ne remonte. Chercher +l’alarme dans les journaux fait donc manquer l’essentiel du phénomène — elle dure +quelques minutes, le bridage dure des heures.

+

Les seuils ne sont pas des valeurs choisies : ce sont ceux que le matériel +déclare lui-même, lus dans sysinfo du LV-HUB.

  • 3480 mV niveau 1
  • 3520 mV niveau 2
  • 3550 mV niveau 3 — le courant se réduit
  • 3600 mV niveau 4
+ +

4Résistance interne ou décalage de charge ?

La question décide de ce qu’un rééquilibrage peut rendre. Deux points de +mesure suffisent à la trancher, sur la même cellule à deux courants différents.

+ + +
courant par celluleécart
13:04:486,33 A163 mV
14:05:420,04 A46 mV
+

La pente donne ≈ 18,6 mΩ de résistance excédentaire sur la +cellule meneuse, vingt à quarante fois celle d’une cellule saine. L’ordonnée à +l’origine, ≈ 46 mV, est le vrai décalage de charge.

+

Contrôle indépendant : cette droite prédit 312 mV à 100 A de courant parc. +L’écart maximal réellement enregistré sur trente-sept jours est de 280 mV.

+

Donc sur les ~190 mV d’écart en charge, 46 mV seulement sont un +décalage — le reste est de la capacité perdue. Un rééquilibrage parfait +ramènerait le déclenchement de 3555 à environ 3510 mV : sous le seuil, mais avec +quarante millivolts de marge seulement.

+ +

5D’où viennent les dégâts

Pas de l’usure. Le pack récent a encaissé trois cent quarante cycles en un +an et ses soixante cellules sont intactes : cycler n’abîme pas ces modules. Et le +module témoin a partagé la chaîne, les courants et les journées des deux abîmés +sans une égratignure.

+

Les deux cellules terminales du parc sont descendues à 2953 +et 2939 mV, sous le seuil d’alarme basse. Un accumulateur +au lithium-fer-phosphate mené là sous charge perd de la capacité de façon +irréversible. Ces cellules n’ont pas vieilli, elles ont été vidées trop +bas.

+ +

6Les trois questions posées à Pylontech

+
  1. Ces cellules sont-elles récupérables par un maintien prolongé en haut de charge, ou la composante résistive de 18,6 mΩ rend-elle l’opération vaine ?
  2. Existe-t-il une procédure recommandée pour un module dans cet état, et une limite au-delà de laquelle le remplacement du module s’impose ?
  3. Que compte exactement le SOHCount par cellule rendu par la commande soh, et à partir de quelle valeur Pylontech considère qu’une cellule doit être traitée ?
-

Les seuils ne sont pas des valeurs choisies : ce sont ceux que le -matériel déclare lui-même.

- - -

4Ce qui a été essayé

-

La consigne de tension de charge a été abaissée de 51,3 -à 51,0 V pour voir si le contournement suffisait à ne -plus jamais déclencher.

-

Il ne suffit pas. À 92 % de charge, les cellules meneuses -ont atteint 3563 et 3548 mV malgré la consigne abaissée, et le plafond a -recommencé à osciller entre 35 et 40 A. Le système ne reste pas « bloqué » -à une valeur : il cherche en permanence le courant maximal que la cellule la -plus faible tolère.

-

Une mesure explique pourquoi la tension seule ne peut pas régler la question. -À tension de parc identique, la cellule meneuse était à 3555 mV -sous 50 A et à 3496 mV sous 30 A. Son excursion -est donc largement produite par sa résistance interne multipliée par le courant, -et pas seulement par son état de charge.

- -

5D’où viennent les dégâts

-

Pas de l’usure. Le pack récent a encaissé trois cent quarante cycles en un an -et ses soixante cellules sont intactes : cycler n’abîme pas ces modules.

-

Les deux cellules terminales du parc sont descendues à -2953 et 2939 mV, sous le seuil -d’alarme basse. Un accumulateur au lithium-fer-phosphate mené là sous charge perd -de la capacité de façon irréversible. Ces cellules n’ont pas vieilli, -elles ont été vidées trop bas.

-
-

Et il y a une boucle à rompre. La jauge d’état de charge du parc suit la -cellule la plus faible. Quand le système croit toucher son plancher de sécurité -à 22 %, cette cellule est déjà bien plus bas que le chiffre ne le laisse -croire. Le plancher est exprimé dans une grandeur que la cellule -endommagée fausse elle-même : il ne protège pas ce qu’il croit -protéger, et rien n’empêche le même mécanisme d’atteindre le pack sain.

-
- -

6Ce qui vient

-

Une alimentation stabilisée permettra de reprendre le rééquilibrage au banc, -en commençant par le module isolé : il n’a qu’une cellule à traiter, il est -hors service, et rien de ce qu’on y tentera ne peut nuire au parc.

-

Une limite à garder en tête : un rééquilibrage corrige un décalage -de charge, pas une perte de capacité. Les cellules modérément décalées -sont de bonnes candidates. Les deux cellules terminales le sont beaucoup moins — -et c’est précisément ce que l’essai sur le module isolé dira, sans risque.

-
- - +
diff --git a/tools/__pycache__/rapport_textes.cpython-314.pyc b/tools/__pycache__/rapport_textes.cpython-314.pyc new file mode 100644 index 0000000..e2daeee Binary files /dev/null and b/tools/__pycache__/rapport_textes.cpython-314.pyc differ diff --git a/tools/gen-rapport-batteries.py b/tools/gen-rapport-batteries.py index c343d91..2c8b029 100644 --- a/tools/gen-rapport-batteries.py +++ b/tools/gen-rapport-batteries.py @@ -1,101 +1,101 @@ # -*- coding: utf-8 -*- -"""Genere le rapport sur l'etat du parc batterie. +"""Genere le rapport sur l'etat du parc batterie, en trois langues. -Rejouable : les mesures vivent dans les constantes en tete, la mise en page -en dessous. Mettre a jour une mesure, relancer, republier. +Les MESURES vivent ici, les MOTS dans tools/rapport_textes.py. Une correction +de traduction ne peut donc jamais toucher un chiffre, ni l'inverse. + + python3 tools/gen-rapport-batteries.py """ -import pathlib, datetime +import importlib.util, pathlib -MAJ = "5 septembre 2026, 17 h" +_sp = importlib.util.spec_from_file_location( + "rt", str(pathlib.Path(__file__).with_name("rapport_textes.py"))) +rt = importlib.util.module_from_spec(_sp); _sp.loader.exec_module(rt) -# Amplitude min->max de chaque cellule, relevee sur l'historique du BMS. -# C'est LA grandeur qui separe une cellule saine d'une cellule morte : une -# cellule qui a perdu sa capacite se remplit et se vide avant ses voisines, -# donc elle balaye une plage bien plus large a courant identique. -MODULES = [ - ("K220803C30160323", "pack ancien · adresse 2 — LE TÉMOIN", "1793 relevés, 30/07 → 05/09", [ - 191,188,188,189,190,197,187,190,189,192,191,188,187,192,198]), - ("K220803C30160322", "pack ancien · adresse 1", "1793 relevés, 14/08 → 05/09", [ - 182,178,178,178,177,548,371,181,179,179,182,691,178,273,178]), - ("K220803C30160319", "pack ancien · adresse 3", "1476 relevés, 30/07 → 05/09", [ - 183,521,184,186,182,318,183,186,184,473,184,443,184,188,424]), - ("K220803C30160324", "isolée (BAT3) · adresse 4", "322 relevés, 23/07 → 05/09", [ - 138,124,138,137,139,132,134,142,143,135,136,137,128,135,683]), +MAJ = {"fr": "5 septembre 2026, 17 h", "en": "5 September 2026, 17:00", + "zh": "2026 年 9 月 5 日 17:00"} + +# Amplitude min->max de chaque cellule, relevee sur l'historique du BMS. C'est +# LA grandeur qui separe une cellule saine d'une cellule morte sans demontage. +CODES = ["K220803C30160323", "K220803C30160322", "K220803C30160319", "K220803C30160324"] +AMPL = [ + [191,188,188,189,190,197,187,190,189,192,191,188,187,192,198], + [182,178,178,178,177,548,371,181,179,179,182,691,178,273,178], + [183,521,184,186,182,318,183,186,184,473,184,443,184,188,424], + [138,124,138,137,139,132,134,142,143,135,136,137,128,135,683], ] - +# code-barres, pack, adresse, nb atteintes, lesquelles, HV, OV, ecart median, etat PARC = [ - ("K220803C30160322", "ancien", "1", "4", "6 · 7 · 12 · 14", 498, 86, 108, "en service"), - ("K220803C30160323", "ancien", "2", "0", "—", 0, 0, 6, "en service"), - ("K220803C30160319", "ancien", "3", "5", "2 · 6 · 10 · 12 · 15", 416, 179, 45, "en service"), - ("K220803C30160324", "ancien", "4", "1", "15", 29, 59, 57, "isolée"), - ("Y230202C30371426", "récent", "1", "0", "—", 1, 1, 2, "en service"), - ("Y230202C30371425", "récent", "2", "0", "—", 1, 1, 2, "en service"), - ("Y230202C30371431", "récent", "3", "0", "—", 1, 1, 2, "en service"), - ("Y230202C30371427", "récent", "4", "0", "—", 1, 0, 2, "en service"), + ("K220803C30160322","a","1","4","6 · 7 · 12 · 14", 498, 86,108,"s"), + ("K220803C30160323","a","2","0","—", 0, 0, 6,"s"), + ("K220803C30160319","a","3","5","2 · 6 · 10 · 12 · 15", 416,179, 45,"s"), + ("K220803C30160324","a","4","1","15", 29, 59, 57,"i"), + ("Y230202C30371426","r","1","0","—", 1, 1, 2,"s"), + ("Y230202C30371425","r","2","0","—", 1, 1, 2,"s"), + ("Y230202C30371431","r","3","0","—", 1, 1, 2,"s"), + ("Y230202C30371427","r","4","0","—", 1, 0, 2,"s"), ] -CHRONO = [ - ("13:04:48", "cellule 7 du module …0322 à 3526 mV, cellule 6 du module …0319 à 3541", "sous le seuil"), - ("13:05:08", "les mêmes à 3555 et 3575 mV", "niveau 3 franchi"), - ("13:05:13", "le BMS abaisse le courant de charge autorisé de 50 à 30 A", "cinq secondes plus tard"), - ("13:15:10", "les cellules redescendent à 3499 et 3522 mV, l’alarme repasse au vert", "le plafond, lui, reste à 30 A"), - ("13:22:40", "plafond 30 → 40 A", "après 7 min 31 s"), - ("13:26:39", "plafond 40 → 35 A, meneuses à 3563 et 3548 mV", "il redéclenche"), - ("13:38:10", "plafond 35 → 40 A", "après 11 min 31 s"), -] - -SEUILS = [(3480, "niveau 1"), (3520, "niveau 2"), (3550, "niveau 3 — le courant se réduit"), (3600, "niveau 4")] - def couleur(a): - if a >= 600: return "var(--mort)" - if a >= 400: return "var(--grave)" - if a >= 250: return "var(--attention)" - return "var(--sain)" + return ("var(--mort)" if a >= 600 else "var(--grave)" if a >= 400 + else "var(--attention)" if a >= 250 else "var(--sain)") -def figure(): - """Barres horizontales : amplitude de chaque cellule, par module.""" - LARG, LIGNE, GAUCHE, HAUT = 760, 15, 96, 26 - ech = 0.86 # px par mV +def figure(lang): + LARG, LIGNE, GAUCHE, HAUT, ech = 760, 15, 96, 26, 0.86 out, y = [], 0 - for code, place, _n, amps in MODULES: + for code, place, nrec, amps in zip(CODES, rt.PLACES[lang], rt.RELEVES[lang], AMPL): out.append(f'{code[-4:]}' f'{place}') y += HAUT for i, a in enumerate(amps, 1): - larg = a * ech - out.append( - f'{i}' - f'' - f'{a}') + l = a * ech + out.append(f'{i}' + f'' + f'{a}') y += LIGNE y += 14 - reperes = [] + rep = [] for mv in (200, 400, 600): x = GAUCHE + mv * ech - reperes.append(f'' - f'{mv} mV') - return (f'' - + "".join(reperes) + "".join(out) + '') + rep.append(f'' + f'{mv} mV') + return (f'' + "".join(rep) + "".join(out) + '') -lignes_parc = "".join( - f'{c}{p}' - f'{a}{n}{d}' - f'{med}{hv}{ov}' - f'{e}' - for c, p, a, n, d, hv, ov, med, e in PARC) +def page(lang): + t = rt.T[lang] + cjk = lang == "zh" + police = ('"Noto Sans SC", "PingFang SC", "Microsoft YaHei", sans-serif' if cjk + else '"Source Serif 4", Georgia, serif') + lien = ("https://fonts.googleapis.com/css2?family=Chivo:wght@400;700;900" + "&family=Noto+Sans+SC:wght@400;500;700&family=JetBrains+Mono:wght@400;700&display=swap" + if cjk else + "https://fonts.googleapis.com/css2?family=Chivo:wght@400;700;900" + "&family=Source+Serif+4:opsz,wght@8..60,400;8..60,600" + "&family=JetBrains+Mono:wght@400;700&display=swap") + titre_pol = ('"Noto Sans SC", sans-serif' if cjk else 'Chivo, "Helvetica Neue", Arial, sans-serif') -lignes_chrono = "".join( - f'{h}{q}{n}' - for h, q, n in CHRONO) + verdict = "".join(f'
{n}
{l}
' + for n, l in t["v"]) + parc = "".join( + f'{c}' + f'{rt.PACKS[lang][p]}{a}{n}' + f'{d}{med}{hv}' + f'{ov}{rt.ETATS[lang][e]}' + for c, p, a, n, d, hv, ov, med, e in PARC) + chrono = "".join(f'{h}{q}{x}' + for h, q, x in rt.CHRONO_L[lang]) + seuils = "".join(f'
  • {mv} mV {nom}
  • ' + for mv, nom in rt.SEUILS_L[lang]) + ths = "".join(f"{x}" for x in t["th"]) + thc = "".join(f"{x}" for x in t["thc"]) + leg = "".join(f'{n}' + for v, n in zip(("sain","attention","grave","mort"), t["leg"])) + quest = "".join(f"
  • {q}
  • " for q in t["q"]) -lignes_seuils = "".join( - f'
  • {mv} mV {nom}
  • ' for mv, nom in SEUILS) - -HTML = f"""Les cent cinq cellules + return f"""{t['titre']} - +
    -
    Parc Pylontech · diagnostic en cours
    -

    Les cent cinq cellules

    -

    Huit modules, quinze cellules chacun. Dix de ces cellules - brident la charge des quatre-vingt-quinze autres. Voici lesquelles, comment on le sait, - et ce qu’on peut en faire.

    -
    Dernière mise à jour : {MAJ}
    +
    {t['etiq']}
    +

    {t['titre']}

    +

    {t['chapeau']}

    +
    {t['maj']} : {MAJ[lang]}
    -
    -
    10
    cellules atteintes sur 105
    -
    3
    modules concernés sur 8
    -
    5 s
    entre le franchissement du seuil et la coupure
    -
    30 A
    plafond imposé au parc entier, pour 150 A nominaux
    -
    +
    {verdict}
    -
    -

    1Le verdict

    -

    Le parc n’est pas usé. Quatre modules sur huit sont irréprochables : -soixante cellules sans une seule anomalie, après plus de trois cents cycles. Un -cinquième l’est tout autant.

    -

    Trois modules portent l’ensemble du problème, et à l’intérieur de ces trois, -dix cellules. Quand l’une d’elles atteint 3550 mV, le BMS réduit -le courant de charge — non pas de son module, mais de tout le parc. -Cinq modules sains sont bridés par dix cellules.

    -
    +

    1{t['h1']}

    {t['p1']}
    -
    {figure()}
    -
    - saine - décalée - gravement atteinte - terminale -
    -
    Amplitude balayée par chaque cellule entre sa tension minimale et - sa tension maximale sur la période d’observation. Le premier module est le - témoin : il vit dans la même chaîne que les deux abîmés, - a subi les mêmes courants et les mêmes journées, et ses quinze cellules tiennent - dans onze millivolts d’amplitude les unes des autres. C’est à - cela que ressemble un module sain, et c’est ce qui rend les autres barres - lisibles. Une cellule qui a perdu de la capacité se remplit et se vide avant ses - voisines, donc elle balaye une plage bien plus large à courant identique — le - signe ne demande aucun démontage. Les amplitudes du module isolé sont plus - faibles parce qu’il a passé la période hors service, sur une plage de charge - plus étroite : c’est l’écart à ses propres voisines qui compte, pas la - valeur absolue.
    +
    {figure(lang)}
    +
    {leg}
    +
    {t['figcap']}
    +

    2{t['h2']}

    {t['p2']}
    +
    {ths}{parc}
    +
    {t['p3']} + +

    3{t['h3']}

    {t['p4']}
    +
    {thc}{chrono}
    -

    2Le parc, module par module

    -

    L’identité d’un module est son code-barres, jamais son adresse : chaque -chaîne renumérote ses modules à partir de 1, il existe donc deux « module -1 » dans cette installation.

    +
    {t['enc1']}
    +{t['p5']}
      {seuils}
    + +

    4{t['h4']}

    {t['p6']} + +

    5{t['h5']}

    {t['p7']} + +

    6{t['h6']}

    +
      {quest}
    -
    - - -{lignes_parc} -
    Code-barresPackAdr.Cellules atteintesLesquellesÉcart médianSurtensionsSous-tensions
    - -
    -

    Lisez la colonne des écarts médians. Les cinq modules sains tiennent entre -2 et 6 millivolts ; les trois autres sont à 45, 57 et 108. -Le même contraste apparaît sur les surtensions : 498 pour -le premier module contre une seule pour chacun des quatre du -pack récent, qui ont pourtant encaissé dix fois plus de cycles.

    -

    Les soixante cellules du pack récent balayent toutes entre 168 et 191 mV -d’amplitude, sans exception. L’inventaire est clos : il n’y a rien à -chercher ailleurs que dans les trois modules déjà nommés.

    - -

    3Le déclenchement, chronométré

    -

    Le 5 septembre, la console a été branchée sur les trois modules du pack ancien -pendant que l’alarme montait. Ce qui suit est le relevé, à la seconde.

    -
    - -
    - -{lignes_chrono} -
    HeureCe qui se passe
    - -
    -
    -

    La découverte la plus utile de la journée tient dans la -quatrième ligne : l’alarme repasse au vert bien avant que le plafond ne -remonte. Chercher l’alarme dans les journaux fait donc manquer l’essentiel du -phénomène — elle dure quelques minutes, le bridage dure des heures.

    -
    - -

    Les seuils ne sont pas des valeurs choisies : ce sont ceux que le -matériel déclare lui-même.

    -
      {lignes_seuils}
    - -

    4Ce qui a été essayé

    -

    La consigne de tension de charge a été abaissée de 51,3 -à 51,0 V pour voir si le contournement suffisait à ne -plus jamais déclencher.

    -

    Il ne suffit pas. À 92 % de charge, les cellules meneuses -ont atteint 3563 et 3548 mV malgré la consigne abaissée, et le plafond a -recommencé à osciller entre 35 et 40 A. Le système ne reste pas « bloqué » -à une valeur : il cherche en permanence le courant maximal que la cellule la -plus faible tolère.

    -

    Une mesure explique pourquoi la tension seule ne peut pas régler la question. -À tension de parc identique, la cellule meneuse était à 3555 mV -sous 50 A et à 3496 mV sous 30 A. Son excursion -est donc largement produite par sa résistance interne multipliée par le courant, -et pas seulement par son état de charge.

    - -

    5D’où viennent les dégâts

    -

    Pas de l’usure. Le pack récent a encaissé trois cent quarante cycles en un an -et ses soixante cellules sont intactes : cycler n’abîme pas ces modules.

    -

    Les deux cellules terminales du parc sont descendues à -2953 et 2939 mV, sous le seuil -d’alarme basse. Un accumulateur au lithium-fer-phosphate mené là sous charge perd -de la capacité de façon irréversible. Ces cellules n’ont pas vieilli, -elles ont été vidées trop bas.

    -
    -

    Et il y a une boucle à rompre. La jauge d’état de charge du parc suit la -cellule la plus faible. Quand le système croit toucher son plancher de sécurité -à 22 %, cette cellule est déjà bien plus bas que le chiffre ne le laisse -croire. Le plancher est exprimé dans une grandeur que la cellule -endommagée fausse elle-même : il ne protège pas ce qu’il croit -protéger, et rien n’empêche le même mécanisme d’atteindre le pack sain.

    -
    - -

    6Ce qui vient

    -

    Une alimentation stabilisée permettra de reprendre le rééquilibrage au banc, -en commençant par le module isolé : il n’a qu’une cellule à traiter, il est -hors service, et rien de ce qu’on y tentera ne peut nuire au parc.

    -

    Une limite à garder en tête : un rééquilibrage corrige un décalage -de charge, pas une perte de capacité. Les cellules modérément décalées -sont de bonnes candidates. Les deux cellules terminales le sont beaucoup moins — -et c’est précisément ce que l’essai sur le module isolé dira, sans risque.

    -
    - -
    Relevés faits sur la console série des BMS Pylontech, que ni Home -Assistant ni le GX Victron n’exposent : le système ne publie que l’agrégat -du parc. Ce rapport évolue au fil des travaux.
    +
    {t['meth']}
    """ -p = pathlib.Path("docs/pages/rapport-batteries.html") -p.parent.mkdir(parents=True, exist_ok=True) -p.write_text(HTML, encoding="utf-8") -print(f"{p} {len(HTML)} caracteres") +SORTIES = {"fr": "rapport-batteries.html", "en": "battery-report.html", + "zh": "battery-report-zh.html"} +base = pathlib.Path("docs/pages"); base.mkdir(parents=True, exist_ok=True) +for lang, nom in SORTIES.items(): + h = page(lang) + (base / nom).write_text(h, encoding="utf-8") + print(f" {nom:26s} {lang} {len(h):6d} caracteres") diff --git a/tools/rapport_textes.py b/tools/rapport_textes.py new file mode 100644 index 0000000..6c816f9 --- /dev/null +++ b/tools/rapport_textes.py @@ -0,0 +1,316 @@ +# -*- coding: utf-8 -*- +"""Libelles et corps redactionnel du rapport batteries, en francais, anglais +et chinois. Les MESURES vivent dans gen-rapport-batteries.py : ce fichier ne +contient que des mots, pour qu'une correction de traduction ne puisse jamais +toucher un chiffre.""" + +# La chronologie et les libelles de module dependent de la langue. +CHRONO_L = { +"fr": [ + ("13:04:48", "cellule 7 du module …0322 à 3526 mV, cellule 6 du module …0319 à 3541", "sous le seuil"), + ("13:05:08", "les mêmes à 3555 et 3575 mV", "niveau 3 franchi"), + ("13:05:13", "le BMS abaisse le courant de charge autorisé de 50 à 30 A", "cinq secondes plus tard"), + ("13:15:10", "les cellules redescendent à 3499 et 3522 mV, l’alarme repasse au vert", "le plafond, lui, reste à 30 A"), + ("13:22:40", "plafond 30 → 40 A", "après 7 min 31 s"), + ("13:26:39", "plafond 40 → 35 A, meneuses à 3563 et 3548 mV", "il redéclenche"), + ("13:38:10", "plafond 35 → 40 A", "après 11 min 31 s")], +"en": [ + ("13:04:48", "cell 7 of module …0322 at 3526 mV, cell 6 of module …0319 at 3541", "below threshold"), + ("13:05:08", "the same two at 3555 and 3575 mV", "level 3 crossed"), + ("13:05:13", "the BMS drops the allowed charge current from 50 to 30 A", "five seconds later"), + ("13:15:10", "cells fall back to 3499 and 3522 mV, alarm clears", "the limit stays at 30 A"), + ("13:22:40", "limit 30 → 40 A", "after 7 min 31 s"), + ("13:26:39", "limit 40 → 35 A, leading cells at 3563 and 3548 mV", "it trips again"), + ("13:38:10", "limit 35 → 40 A", "after 11 min 31 s")], +"zh": [ + ("13:04:48", "模块 …0322 的 7 号电芯 3526 mV,模块 …0319 的 6 号电芯 3541", "低于阈值"), + ("13:05:08", "同两颗电芯升至 3555 与 3575 mV", "越过三级阈值"), + ("13:05:13", "BMS 将允许充电电流由 50 A 降至 30 A", "五秒之后"), + ("13:15:10", "电芯回落至 3499 与 3522 mV,告警解除", "但电流限值仍保持 30 A"), + ("13:22:40", "限值 30 → 40 A", "经过 7 分 31 秒"), + ("13:26:39", "限值 40 → 35 A,最高电芯 3563 与 3548 mV", "再次触发"), + ("13:38:10", "限值 35 → 40 A", "经过 11 分 31 秒")], +} + +PLACES = { + "fr": ["pack ancien · adresse 2 — LE TÉMOIN", "pack ancien · adresse 1", + "pack ancien · adresse 3", "isolée (BAT3) · adresse 4"], + "en": ["older pack · address 2 — THE CONTROL", "older pack · address 1", + "older pack · address 3", "isolated (BAT3) · address 4"], + "zh": ["旧电池组 · 地址 2 — 对照模块", "旧电池组 · 地址 1", + "旧电池组 · 地址 3", "已隔离 (BAT3) · 地址 4"], +} +RELEVES = { + "fr": ["1793 relevés, 30/07 → 05/09", "1793 relevés, 14/08 → 05/09", + "1476 relevés, 30/07 → 05/09", "322 relevés, 23/07 → 05/09"], + "en": ["1793 records, 30 Jul → 5 Sep", "1793 records, 14 Aug → 5 Sep", + "1476 records, 30 Jul → 5 Sep", "322 records, 23 Jul → 5 Sep"], + "zh": ["1793 条记录,7/30 → 9/5", "1793 条记录,8/14 → 9/5", + "1476 条记录,7/30 → 9/5", "322 条记录,7/23 → 9/5"], +} +ETATS = {"fr": {"s": "en service", "i": "isolée"}, + "en": {"s": "in service", "i": "isolated"}, + "zh": {"s": "在运行", "i": "已隔离"}} +PACKS = {"fr": {"a": "ancien", "r": "récent"}, + "en": {"a": "older", "r": "newer"}, + "zh": {"a": "旧", "r": "新"}} +SEUILS_L = { + "fr": [(3480, "niveau 1"), (3520, "niveau 2"), (3550, "niveau 3 — le courant se réduit"), (3600, "niveau 4")], + "en": [(3480, "level 1"), (3520, "level 2"), (3550, "level 3 — current is reduced"), (3600, "level 4")], + "zh": [(3480, "一级"), (3520, "二级"), (3550, "三级 — 充电电流开始下降"), (3600, "四级")], +} + +T = {} + +T["fr"] = dict( + titre="Les cent cinq cellules", + etiq="Parc Pylontech · diagnostic en cours", + chapeau="Huit modules, quinze cellules chacun. Dix de ces cellules brident la " + "charge des quatre-vingt-quinze autres. Voici lesquelles, comment on le " + "sait, et ce qu’on peut en faire.", + maj="Dernière mise à jour", + v=[("10","cellules atteintes sur 105"),("3","modules concernés sur 8"), + ("5 s","entre le franchissement du seuil et la coupure"), + ("30 A","plafond imposé au parc entier, pour 150 A nominaux")], + h1="Le verdict", + p1="""

    Le parc n’est pas usé. Quatre modules sur huit sont irréprochables : +soixante cellules sans une seule anomalie, après plus de trois cents cycles. Un +cinquième l’est tout autant.

    +

    Trois modules portent l’ensemble du problème, et à l’intérieur de ces trois, +dix cellules. Quand l’une d’elles atteint 3550 mV, le BMS réduit +le courant de charge — non pas de son module, mais de tout le parc. +Cinq modules sains sont bridés par dix cellules.

    """, + leg=["saine","décalée","gravement atteinte","terminale"], + figcap="""Amplitude balayée par chaque cellule entre sa tension minimale et sa +tension maximale sur la période d’observation. Le premier module est le +témoin : il vit dans la même chaîne que les deux abîmés, a subi +les mêmes courants et les mêmes journées, et ses quinze cellules tiennent dans +onze millivolts d’amplitude les unes des autres. Une cellule qui +a perdu de la capacité se remplit et se vide avant ses voisines, donc elle balaye +une plage bien plus large à courant identique — le signe ne demande aucun +démontage. Les amplitudes du module isolé sont plus faibles parce qu’il a passé +la période hors service, sur une plage de charge plus étroite : c’est l’écart à +ses propres voisines qui compte, pas la valeur absolue.""", + h2="Le parc, module par module", + p2="""

    L’identité d’un module est son code-barres, jamais son adresse : chaque +chaîne renumérote ses modules à partir de 1, il existe donc deux « module 1 » +dans cette installation.

    """, + th=["Code-barres","Pack","Adr.","Cellules atteintes","Lesquelles","Écart médian", + "Surtensions","Sous-tensions",""], + p3="""

    Lisez la colonne des écarts médians. Les cinq modules sains tiennent entre +2 et 6 millivolts ; les trois autres sont à 45, 57 et 108. Le même +contraste apparaît sur les surtensions : 498 pour le premier +module contre une seule pour chacun des quatre du pack récent, +qui ont pourtant encaissé dix fois plus de cycles.

    +

    Les soixante cellules du pack récent balayent toutes entre 168 et 191 mV +d’amplitude, sans exception. L’inventaire est clos.

    """, + h3="Le déclenchement, chronométré", + p4="""

    Le 5 septembre, la console a été branchée sur les trois modules du pack +ancien pendant que l’alarme montait. Ce qui suit est le relevé, à la seconde.

    """, + thc=["Heure","Ce qui se passe",""], + enc1="""

    La découverte la plus utile tient dans la quatrième +ligne : l’alarme repasse au vert bien avant que le plafond ne remonte. Chercher +l’alarme dans les journaux fait donc manquer l’essentiel du phénomène — elle dure +quelques minutes, le bridage dure des heures.

    """, + p5="""

    Les seuils ne sont pas des valeurs choisies : ce sont ceux que le matériel +déclare lui-même, lus dans sysinfo du LV-HUB.

    """, + h4="Résistance interne ou décalage de charge ?", + p6="""

    La question décide de ce qu’un rééquilibrage peut rendre. Deux points de +mesure suffisent à la trancher, sur la même cellule à deux courants différents.

    + + +
    courant par celluleécart
    13:04:486,33 A163 mV
    14:05:420,04 A46 mV
    +

    La pente donne ≈ 18,6 mΩ de résistance excédentaire sur la +cellule meneuse, vingt à quarante fois celle d’une cellule saine. L’ordonnée à +l’origine, ≈ 46 mV, est le vrai décalage de charge.

    +

    Contrôle indépendant : cette droite prédit 312 mV à 100 A de courant parc. +L’écart maximal réellement enregistré sur trente-sept jours est de 280 mV.

    +

    Donc sur les ~190 mV d’écart en charge, 46 mV seulement sont un +décalage — le reste est de la capacité perdue. Un rééquilibrage parfait +ramènerait le déclenchement de 3555 à environ 3510 mV : sous le seuil, mais avec +quarante millivolts de marge seulement.

    """, + h5="D’où viennent les dégâts", + p7="""

    Pas de l’usure. Le pack récent a encaissé trois cent quarante cycles en un +an et ses soixante cellules sont intactes : cycler n’abîme pas ces modules. Et le +module témoin a partagé la chaîne, les courants et les journées des deux abîmés +sans une égratignure.

    +

    Les deux cellules terminales du parc sont descendues à 2953 +et 2939 mV, sous le seuil d’alarme basse. Un accumulateur +au lithium-fer-phosphate mené là sous charge perd de la capacité de façon +irréversible. Ces cellules n’ont pas vieilli, elles ont été vidées trop +bas.

    """, + h6="Les trois questions posées à Pylontech", + q=["Ces cellules sont-elles récupérables par un maintien prolongé en haut de " + "charge, ou la composante résistive de 18,6 mΩ rend-elle l’opération vaine ?", + "Existe-t-il une procédure recommandée pour un module dans cet état, et une " + "limite au-delà de laquelle le remplacement du module s’impose ?", + "Que compte exactement le SOHCount par cellule " + "rendu par la commande soh, et à partir de quelle " + "valeur Pylontech considère qu’une cellule doit être traitée ?"], + meth="Relevés faits sur la console série des BMS Pylontech, que ni Home Assistant " + "ni le GX Victron n’exposent : le système ne publie que l’agrégat du parc. " + "Installation en autoconsommation sans injection, deux MultiPlus-II 48/5000 " + "en parallèle. Ce rapport évolue au fil des travaux.", +) + +T["en"] = dict( + titre="The Hundred and Five Cells", + etiq="Pylontech pack · diagnosis in progress", + chapeau="Eight modules, fifteen cells each. Ten of those cells throttle the " + "charging of the other ninety-five. Here is which ones, how we know, " + "and what can be done about it.", + maj="Last updated", + v=[("10","affected cells out of 105"),("3","modules involved out of 8"), + ("5 s","from threshold crossing to current cut"), + ("30 A","limit imposed on the whole pack, against 150 A nominal")], + h1="The verdict", + p1="""

    The pack is not worn out. Four modules out of eight are flawless: +sixty cells without a single anomaly, after more than three hundred cycles. A +fifth is just as clean.

    +

    Three modules carry the entire problem, and within those three, ten +cells. When one of them reaches 3550 mV the BMS reduces the charge +current — not for its own module, but for the whole pack. Five +healthy modules are throttled by ten cells.

    """, + leg=["healthy","drifting","badly affected","terminal"], + figcap="""Voltage span swept by each cell between its minimum and maximum over the +observation period. The first module is the control: it lives on +the same chain as the two damaged ones, has seen the same currents and the same +days, and its fifteen cells sit within eleven millivolts of span of each +other. A cell that has lost capacity fills and empties before its +neighbours, so it sweeps a far wider range at identical current — the sign +requires no disassembly. The isolated module shows smaller spans because it spent +the period out of service, over a narrower state-of-charge window: what matters +is the gap to its own neighbours, not the absolute value.""", + h2="The pack, module by module", + p2="""

    A module's identity is its barcode, never its address: each chain renumbers +its modules from 1, so there are two “module 1” in this installation.

    """, + th=["Barcode","Pack","Addr.","Affected cells","Which ones","Median spread", + "Overvoltage","Undervoltage",""], + p3="""

    Read the median spread column. The five healthy modules sit between +2 and 6 millivolts; the other three are at 45, 57 and 108. The +same contrast shows in the overvoltage counters: 498 for the +first module against one for each of the four in the newer pack, +which have nonetheless taken ten times more cycles.

    +

    All sixty cells of the newer pack sweep between 168 and 191 mV, without +exception. The inventory is closed.

    """, + h3="The trip, timed", + p4="""

    On 5 September the console was connected to the three modules of the older +pack while the alarm was rising. What follows is the record, to the second.

    """, + thc=["Time","What happens",""], + enc1="""

    The most useful finding is in the fourth row: the alarm +clears well before the limit recovers. Looking for the alarm in the logs therefore +misses the bulk of the phenomenon — the alarm lasts minutes, the throttling lasts +hours.

    """, + p5="""

    The thresholds are not values we chose: they are the ones the hardware +declares, read from sysinfo on the LV-HUB.

    """, + h4="Internal resistance, or charge imbalance?", + p6="""

    The answer decides what balancing can give back. Two measurement points on +the same cell at two different currents settle it.

    + + +
    current per cellspread
    13:04:486.33 A163 mV
    14:05:420.04 A46 mV
    +

    The slope gives ≈ 18.6 mΩ of excess internal resistance on the +leading cell, twenty to forty times that of a healthy one. The intercept, +≈ 46 mV, is the true charge imbalance.

    +

    Independent check: this line predicts 312 mV at 100 A pack current. The largest +spread actually recorded over thirty-seven days is 280 mV.

    +

    So of the ~190 mV of spread under charge, only 46 mV are an +imbalance — the rest is lost capacity. A perfect rebalance would bring the +trip from 3555 down to about 3510 mV: below the threshold, but with only forty +millivolts of margin.

    """, + h5="Where the damage comes from", + p7="""

    Not from wear. The newer pack has taken three hundred and forty cycles in a +year and its sixty cells are intact: cycling does not damage these modules. And +the control module shared the chain, the currents and the days of the two damaged +ones without a scratch.

    +

    The two terminal cells went down to 2953 and +2939 mV, below the low-voltage alarm threshold. A +lithium iron phosphate cell taken there under load loses capacity irreversibly. +These cells did not age, they were discharged too deep.

    """, + h6="Three questions for Pylontech", + q=["Are these cells recoverable by a prolonged hold at high state of charge, or " + "does the 18.6 mΩ resistive component make the attempt pointless?", + "Is there a recommended procedure for a module in this condition, and a limit " + "beyond which module replacement is the only option?", + "What exactly does the per-cell SOHCount returned " + "by the soh command count, and from what value " + "does Pylontech consider a cell to require attention?"], + meth="Measurements taken on the serial console of the Pylontech BMS units, which " + "neither Home Assistant nor the Victron GX exposes: the system publishes only " + "the pack aggregate. Self-consumption installation without grid feed-in, two " + "MultiPlus-II 48/5000 in parallel. This report evolves as the work proceeds.", +) + +T["zh"] = dict( + titre="一百零五颗电芯", + etiq="Pylontech 电池组 · 诊断进行中", + chapeau="八个模块,每个十五颗电芯。其中十颗电芯限制了其余九十五颗的充电。" + "本报告说明是哪几颗、依据何在、以及可以怎么处理。", + maj="最后更新", + v=[("10","105 颗中受影响的电芯"),("3","8 个模块中受影响的模块"), + ("5 秒","从越过阈值到电流下降"), + ("30 A","施加于整组的限值,标称为 150 A")], + h1="结论", + p1="""

    电池组并未整体老化。八个模块中有四个毫无异常: +六十颗电芯在三百多次循环之后没有一处异常。第五个模块同样干净。

    +

    全部问题集中在三个模块,而在这三个模块之内只有十颗电芯。 +当其中一颗达到 3550 mV,BMS 便降低充电电流——降的不是该模块,而是 +整个电池组。五个健康模块被十颗电芯拖住。

    """, + leg=["健康","轻度偏离","严重偏离","已失效"], + figcap="""图中为观测期内每颗电芯在最低与最高电压之间所扫过的电压幅度。 +第一个模块是对照模块:它与两个受损模块同在一条通信链上, +经历相同的电流与相同的日子,而它的十五颗电芯彼此幅度之差仅 +十一毫伏。容量衰减的电芯会比相邻电芯更早充满、更早放空, +因此在相同电流下扫过的电压范围明显更宽——这一判据无需拆解模块即可读出。 +已隔离模块的幅度偏小,是因为它在观测期内处于停用状态、荷电区间更窄: +应看它与自身相邻电芯之差,而非绝对数值。""", + h2="逐模块清单", + p2="""

    模块的身份是它的条码,而不是地址:每条链上的模块都从 1 重新编号, +因此本系统中存在两个「模块 1」。

    """, + th=["条码","电池组","地址","受影响电芯数","具体编号","压差中位数", + "过压次数","欠压次数",""], + p3="""

    请看压差中位数一列。五个健康模块在 2 至 6 毫伏之间; +另外三个分别为 45、57 与 108。过压计数呈现同样的对比:第一个模块为 +498 次,而新电池组四个模块各自仅 1 次—— +后者的循环次数还多出十倍。

    +

    新电池组的六十颗电芯,幅度全部落在 168 至 191 mV 之间,无一例外。 +清点至此完成。

    """, + h3="触发过程,逐秒记录", + p4="""

    9 月 5 日,在告警上升过程中,控制台接入了旧电池组的三个模块。 +以下为逐秒记录。

    """, + thc=["时间","发生了什么",""], + enc1="""

    最有价值的一处发现在第四行:告警远早于电流限值 +恢复之前就已解除。因此仅在日志中查找告警,会错过该现象的绝大部分—— +告警持续数分钟,而限流持续数小时。

    """, + p5="""

    这些阈值并非我们所选:它们由设备本身声明,读取自 LV-HUB 的 +sysinfo。

    """, + h4="是内阻,还是荷电不均衡?", + p6="""

    这个问题决定了均衡能够挽回多少。同一颗电芯在两种电流下的两个测点 +即可判定。

    + + +
    单芯电流压差
    13:04:486.33 A163 mV
    14:05:420.04 A46 mV
    +

    斜率给出最高电芯的额外内阻约 18.6 mΩ,为健康电芯的 +二十至四十倍。截距 约 46 mV,才是真正的荷电不均衡。

    +

    独立验证:该直线预测在整组电流 100 A 时压差为 312 mV;三十七天记录中 +实际出现的最大压差为 280 mV。

    +

    因此在充电时约 190 mV 的压差中,只有 46 mV 属于不均衡, +其余为容量损失。即便完成理想均衡,触发点也只能从 3555 mV 降至约 3510 mV: +低于阈值,但余量仅四十毫伏。

    """, + h5="损伤的来源", + p7="""

    并非老化所致。新电池组一年内完成三百四十次循环,六十颗电芯完好无损: +循环本身不会损坏这些模块。对照模块与两个受损模块同链、同电流、同工况, +亦毫发无伤。

    +

    两颗已失效电芯曾降至 2953 与 +2939 mV,低于低压告警阈值。磷酸铁锂电芯在带载状态下 +被放至该电压,会发生不可逆的容量损失。这些电芯不是老化,而是被过放。

    """, + h6="向 Pylontech 提出的三个问题", + q=["这些电芯能否通过长时间保持高荷电状态而恢复,还是 18.6 mΩ 的内阻分量" + "已使该尝试失去意义?", + "对处于此状态的模块,是否有推荐的处理流程?超过何种程度必须更换模块?", + "soh 命令返回的单芯 SOHCount " + "具体统计的是什么?从多大数值起,Pylontech 认为该电芯需要处理?"], + meth="数据取自 Pylontech BMS 的串口控制台——Home Assistant 与 Victron GX 均不提供" + "这些数据,系统仅发布整组聚合值。系统为无馈网自发自用,两台 MultiPlus-II " + "48/5000 并机。本报告将随工作进展持续更新。", +)