Photonicat 2 SOC 计算:修订间差异
Point cross-links at the renamed SOC pages directly instead of via redirects |
Tighten prose: state how each stage works rather than motivating it; note k(T) is a piecewise-linear NCA approximation clamped outside -20..40C; link the MCU protocol page |
||
| 第9行: | 第9行: | ||
English: [[Photonicat 2 SOC Calculation]]。 | English: [[Photonicat 2 SOC Calculation]]。 | ||
== | == 电压与电量的关系 == | ||
锂电池的"电压—电量"曲线中段非常平。下面取固件里的'''放电 OCV 表''' | |||
(去 IR 后的整包电压): | |||
{| class="wikitable" | {| class="wikitable" | ||
| 第28行: | 第28行: | ||
|} | |} | ||
从 70 % 掉到 30 %,电压只降了 '''0.30 V''' | 从 70 % 掉到 30 %,电压只降了 '''0.30 V''',平均每 1 % 电量约 '''6 mV'''; | ||
'''6 mV''' | 而带负载时内阻压降(I×R)可达几十毫伏,误差会超过 10 %,负载越大偏差越大。 | ||
两端则很陡:这张表里 8.34 V 是 100 %、6.34 V 是 0 %。因此固件'''两端用电压定位, | |||
''' | 中间靠电流积分推进'''。 | ||
== | == 总体结构 == | ||
电量 = 起点电量 − ∫(放电电流)dt + ∫(充电电流)dt | 电量 = 起点电量 − ∫(放电电流)dt + ∫(充电电流)dt | ||
积分需要一个起点,且误差会随时间累积,因此还要配合一套修正机制。 | |||
光影猫2 分为以下几层: | |||
{| class="wikitable" | {| class="wikitable" | ||
| 第60行: | 第59行: | ||
|} | |} | ||
== | == 一、硬件测量 == | ||
测量芯片是 '''TPA626'''(I²C 电流/电压监测器,与 INA226 同类)。它串一个 | 测量芯片是 '''TPA626'''(I²C 电流/电压监测器,与 INA226 同类)。它串一个 | ||
| 第67行: | 第66行: | ||
* '''采样周期''':约 100 ms 一次(<code>VD1_Collect</code>) | * '''采样周期''':约 100 ms 一次(<code>VD1_Collect</code>) | ||
* '''中值滤波''':电压和电流各过一个中值滤波器,滤掉 I²C 偶发抖动和负载尖峰 | * '''中值滤波''':电压和电流各过一个中值滤波器,滤掉 I²C 偶发抖动和负载尖峰 | ||
* '''原始值另存一份''':中值滤波有约 3 s | * '''原始值另存一份''':中值滤波有约 3 s 滞后,会抹平充电器插拔的电压台阶; | ||
内阻自学习需要该瞬间台阶,故固件同时保留未滤波的原始值 | |||
=== 电流校准 === | === 电流校准 === | ||
| 第88行: | 第87行: | ||
|} | |} | ||
实现细节: | |||
* '''先夹再算''' | * '''先夹再算''':抛物线在标定范围外会反向弯(顶点约 +11.7 A),故先把输入夹到 | ||
±3 A,域外按端点斜率线性延伸。 | |||
* '''int64 运算''':x² 最大到 | * '''int64 运算''':x² 最大到 9×10⁶,乘系数后会溢出 int32。 | ||
* '''55 mA 死区''' | * '''55 mA 死区''':小于该值按 0 处理,避免待机时噪声累积到账本。 | ||
== | == 二、库仑积分 == | ||
每 '''200 ms''' 调一次 <code>coulomb_counter_update()</code>,把电流乘以时间 | 每 '''200 ms''' 调一次 <code>coulomb_counter_update()</code>,把电流乘以时间 | ||
累加进一个 '''µAs(微安秒)'''的 64 | 累加进一个 '''µAs(微安秒)'''的 64 位累加器。使用 µAs 而非 mAh,可避免定点 | ||
除法的舍入误差长期累积。 | |||
* 放电减、充电加 | * 放电减、充电加 | ||
| 第105行: | 第104行: | ||
* 充电方向另有一个自学习的'''增益系数'''(充电效率不是 100 %) | * 充电方向另有一个自学习的'''增益系数'''(充电效率不是 100 %) | ||
== 三、OCV | == 三、OCV 融合 == | ||
在'''电池接近静置'''时,用电压反查电量表对积分做小幅修正。触发条件如下, | |||
全部满足才执行: | |||
{| class="wikitable" | {| class="wikitable" | ||
! 条件 !! 阈值 !! | ! 条件 !! 阈值 !! 说明 | ||
|- | |- | ||
| 电流足够小 || |I| ≤ 340 mA(约 0.05C) || 电流大时内阻压降的模型误差会放大 | | 电流足够小 || |I| ≤ 340 mA(约 0.05C) || 电流大时内阻压降的模型误差会放大 | ||
| 第125行: | 第123行: | ||
|} | |} | ||
修正采用'''加权融合'''而非直接覆盖: | |||
* 权重上限 '''α = 0.07'''(OCV 最多只占 7 %),且在 8 s 内从 0 线性爬升 | * 权重上限 '''α = 0.07'''(OCV 最多只占 7 %),且在 8 s 内从 0 线性爬升 | ||
* 单次修正最多 '''0.20 %''' | * 单次修正最多 '''0.20 %''' | ||
因此 OCV 只对积分做渐进修正,显示电量不会跳变。 | |||
=== 三张 OCV 表 === | === 三张 OCV 表 === | ||
锂电池存在'''滞回''':同一电量下充电时端电压偏高、放电时偏低(约 30 mV/芯, | |||
30 mV/ | 2S 约 ±60 mV)。固件为此设三张表: | ||
* '''放电表''':进入放电模式(|I| > 900 mA)时用 | * '''放电表''':进入放电模式(|I| > 900 mA)时用 | ||
| 第142行: | 第139行: | ||
* '''静置表(REST)''':充放两表的平均,小电流(|I| ≤ 600 mA)时用 | * '''静置表(REST)''':充放两表的平均,小电流(|I| ≤ 600 mA)时用 | ||
模式切换带'''滞回'''(进 900 mA / 出 600 | 模式切换带'''滞回'''(进 900 mA / 出 600 mA),避免在阈值附近反复切换。 | ||
查表前先'''去 IR''':OCV = 端电压 − I×R_pack,其中内阻取自下面的自学习值。 | |||
== | == 四、锚点 == | ||
中段靠积分,两端由锚点直接置位: | |||
* '''满电锚点''' | * '''满电锚点''':充电器在位、vbus ≥ 8 V 且电流已很小 → 电量 = 100 %。 | ||
必须要求充电器在位:'''95 % 的静置 OCV 约 8.16 V''',高电量静置电池易被误判为满电; | |||
劣质或欠压充电器无法充到真满,同样不给锚。 | |||
* '''欠压锚点''':放到欠压关机点 → 电量 = 0 %,同时触发空点学习。 | * '''欠压锚点''':放到欠压关机点 → 电量 = 0 %,同时触发空点学习。 | ||
== | == 五、自学习 == | ||
同一批电池和板子的个体差异可超过 10 %,因此固件'''为每台机器单独学习''' | |||
10 % | 四个量,结果存入 data flash,掉电不丢。 | ||
=== 5.1 满容量学习(电池老化) === | === 5.1 满容量学习(电池老化) === | ||
标称 6800 mAh | 标称 6800 mAh 的电池用两年可能只剩 5500 mAh。满容量若写死,老化后 SOC 会偏乐观。 | ||
SOC | 学习流程: | ||
# 满电锚点触发时开窗口,累计放电清零 | # 满电锚点触发时开窗口,累计放电清零 | ||
| 第173行: | 第167行: | ||
# 中途检测到充电 → 本次窗口作废 | # 中途检测到充电 → 本次窗口作废 | ||
其中 '''700 mAh''' 是关机点余量:整机不会把电池放到 0 V,在关机点 | |||
(约 6.3–6.45 | (约 6.3–6.45 V)包内仍剩约 700 mAh,需补回。 | ||
学到的值换算成'''健康度''': | |||
健康度 = 学习容量 / 6800 mAh | 健康度 = 学习容量 / 6800 mAh | ||
健康度按 EMA 缓慢融入,单次测量不会使其大幅变化。 | |||
=== 5.2 内阻学习(电压台阶法) === | === 5.2 内阻学习(电压台阶法) === | ||
内阻由'''充电器插拔瞬间'''的电压台阶测得:电流突变 ΔI,电压随之突变 ΔV, | |||
R = ΔV / ΔI | R = ΔV / ΔI | ||
| 第193行: | 第186行: | ||
范围限制在 10–200 mΩ,默认值 50 mΩ。 | 范围限制在 10–200 mΩ,默认值 50 mΩ。 | ||
该值是'''整包回路电阻''' = 电芯本体 + 线缆/接插件(20 mΩ)+ 保护板 MOSFET(15 mΩ)。 | |||
保护板 MOSFET(15 | 去 IR 使用整包串联值,不做扣减;上位机显示"电芯本体内阻"时才减去这 35 mΩ 板级寄生。 | ||
=== 5.3 显示空点学习 === | === 5.3 显示空点学习 === | ||
每台机器的电流计增益与实际容量存在偏差,内部 SOC 归零的时刻与硬件实际断电的 | |||
时刻可能不一致(表现为显示尚有 20 % 时断电)。固件不修改电流链路,而是在'''显示层''' | |||
套一个每台机器自学习的线性映射: | |||
显示值 = (内部SOC − 空点) × 100 / (100 − 空点) | 显示值 = (内部SOC − 空点) × 100 / (100 − 空点) | ||
| 第210行: | 第200行: | ||
低压关机命令、低压下主机意外死机、欠压锚点。 | 低压关机命令、低压下主机意外死机、欠压锚点。 | ||
并带'''向下自纠''':放电时电压仍健康(> 6.8 V)而内部 SOC 已低于空点, | |||
说明空点偏高,空点随之下调。换电池或学习过头可在一个放电周期内恢复。 | |||
=== 5.4 充电增益学习 === | === 5.4 充电增益学习 === | ||
充电效率不足 100 %,充入电荷与实际存入电荷存在差异。该增益在满电锚点学习, | |||
变化 ≥1 % 时落盘。 | |||
== 六、温度补偿 == | == 六、温度补偿 == | ||
板载 NTC 每 200 ms 把温度送入库仑计,用于两项修正: | |||
=== 6.1 内阻温度倍率 k(T) === | === 6.1 内阻温度倍率 k(T) === | ||
内阻随温度变化,直接影响去 IR 的准确性。倍率取 NCA 典型温度特性的分段线性近似: | |||
{| class="wikitable" | {| class="wikitable" | ||
| 第243行: | 第232行: | ||
|} | |} | ||
即 −20 °C 时内阻约为常温的'''三倍'''。学习到的内阻归一到 25 °C,使用时再乘 k(T)。 | |||
超出 −20…40 °C 的部分按端点截断。 | |||
=== 6.2 低温可用容量补偿 === | === 6.2 低温可用容量补偿 === | ||
低温下电池可用容量下降(电量并未消失,只是低温下无法取出)。固件据此 | |||
'''把显示空点上移''' | '''把显示空点上移''',使显示电量提前走低: | ||
{| class="wikitable" | {| class="wikitable" | ||
| 第266行: | 第254行: | ||
NTC 读数超出 −30…70 °C 视为故障,保持上一次有效温度(初值 25 °C)。 | NTC 读数超出 −30…70 °C 视为故障,保持上一次有效温度(初值 25 °C)。 | ||
== | == 七、显示曲线 == | ||
内部 SOC 不直接显示,中间还有一层映射: | |||
* '''4 % → 显示 1 %''' | * '''4 % → 显示 1 %''' | ||
| 第274行: | 第262行: | ||
* 中段线性(斜率 99/92),两端截断 | * 中段线性(斜率 99/92),两端截断 | ||
因此'''显示 0 % 时电池仍留有约 4 % 余量'''(另加空点以下的硬件余量), | |||
可供保存工作并正常关机;充到 96 % 即显示 100 %。这是消费电子的常规做法。 | |||
96 % | |||
固件上报的百分比'''下限钳在 1 %''':未关机时不显示 0 %。 | |||
== | == 八、关机后的电量保持 == | ||
共三条路径: | |||
=== 8.1 SOC 快照 === | === 8.1 SOC 快照 === | ||
| 第293行: | 第279行: | ||
* '''≤12 小时''':SOC = 快照 − 2.0 mA × Δt(按待机电流扣减),并'''禁止''' | * '''≤12 小时''':SOC = 快照 − 2.0 mA × Δt(按待机电流扣减),并'''禁止''' | ||
OCV | OCV 重估覆盖。 | ||
* '''>12 小时''':快照过期,走冷启动 OCV 重估。 | * '''>12 小时''':快照过期,走冷启动 OCV 重估。 | ||
=== 8.2 深睡放电估计 === | === 8.2 深睡放电估计 === | ||
电池深睡时 TPA626 | 电池深睡时 TPA626 掉电,无法采集电流。固件进睡前记录 RTC 分钟戳, | ||
唤醒后按整机待机约 '''2.0 mA''' 恒流扣减。 | |||
=== 8.3 换电池识别 === | === 8.3 换电池识别 === | ||
电池拔出后 RTC 掉电重置,时间倒退。固件据此判定为换电池,快照作废并重新冷启动估计。 | |||
判定留有 10 分钟容差,避免升级重启时误判丢弃有效快照。 | |||
== | == 九、数据如何到达 Linux == | ||
MCU 算完,通过 '''UART''' 把状态帧发给主机(RK3576)。主机侧有两个消费者: | MCU 算完,通过 '''UART''' 把状态帧发给主机(RK3576)。主机侧有两个消费者: | ||
| 第319行: | 第304行: | ||
* '''RTC''' 类 → <code>/dev/rtc</code> | * '''RTC''' 类 → <code>/dev/rtc</code> | ||
由于注册为'''标准子系统''',现成 Linux 工具可直接使用,无需了解私有协议: | |||
<syntaxhighlight lang="bash"> | <syntaxhighlight lang="bash"> | ||
| 第340行: | 第324行: | ||
</syntaxhighlight> | </syntaxhighlight> | ||
'''MCU | '''MCU 即硬件 RTC。'''板上没有独立 RTC 芯片,MCU 常供电并自行走时, | ||
时间随状态帧上送。驱动将其注册为 <code>rtc_class_ops</code>,标准工具可直接使用: | |||
<syntaxhighlight lang="bash"> | <syntaxhighlight lang="bash"> | ||
| 第349行: | 第332行: | ||
</syntaxhighlight> | </syntaxhighlight> | ||
因此'''断网断电数日后开机时间仍准确'''。8.3 的换电池识别也依赖它: | |||
RTC 掉电重置后时间会倒退。 | |||
=== 9.2 用户态守护进程:pcat-manager === | === 9.2 用户态守护进程:pcat-manager === | ||
内核驱动只处理状态帧中的电源部分。'''其余 UART 协议由 pcat-manager 处理''': | |||
调制解调器管理、看门狗、开关机策略、按键事件、充电阈值下发、MCU 固件升级等。 | 调制解调器管理、看门狗、开关机策略、按键事件、充电阈值下发、MCU 固件升级等。 | ||
完整协议见 [[Photonicat 2 MCU 通讯协议]]。 | |||
分工如下: | |||
{| class="wikitable" | {| class="wikitable" | ||
| 第369行: | 第353行: | ||
|} | |} | ||
以'''充电上限'''为例:网页设置 80 % 后,pcat-manager 通过 UART 下发给 MCU, | |||
MCU 存入 data flash 并直接控制充电 MOSFET。自固件 '''RA2E1260726005''' 起该设置在 | |||
'''RA2E1260726005''' | MCU 侧持久化,复位或 OTA 后不丢失;pcat-manager 同时保留镜像,检测到 MCU 重启时重新下发。 | ||
== 十、常见问题 == | == 十、常见问题 == | ||
; | ;电量偶尔小幅跳动 | ||
: | :OCV 融合或锚点正在修正积分漂移。单次修正上限 0.20 %,正常情况下不会出现大跳变; | ||
若出现大跳变,通常是快照过期后走了冷启动重估。 | |||
; | ;冬天电量下降更快 | ||
: | :低温下可用容量下降,同时固件把显示空点上移(−10 °C 约 +8 %)。回暖后恢复。 | ||
; | ;充到 96 % 即显示 100 % | ||
: | :显示曲线的设定,见第七节。同理显示 0 % 时电池仍有余量。 | ||
; | ;新电池是否需要校准 | ||
: | :不需要专门操作。完整放电一次(满电到自动关机)可让固件学到真实满容量和空点, | ||
日常使用也会逐步学到。 | |||
; | ;健康度的更新频率 | ||
: | :需要一次完整放电窗口(满电锚点 → 欠压),中途充电则本次作废,因此更新较慢, | ||
且按 EMA 缓变。 | |||
2026年7月26日 (日) 20:01的最新版本
光影猫2 不靠"看电压猜电量",而是用一套完整的库仑计(Coulomb counter,又叫电量计 / fuel gauge)来算电量:像给电池装了一个水表, 进出的每一毫安秒都记账。
本页讲清楚这套系统怎么工作——从硬件怎么测,到固件怎么积分、怎么自学习、 怎么做温度补偿,再到 Linux 内核怎么把它变成 sysfs 文件。
指示灯说明见 Photonicat 2 LED 指示灯,sysfs 路径见 Photonicat 2 sysfs。 English: Photonicat 2 SOC Calculation。
电压与电量的关系
锂电池的"电压—电量"曲线中段非常平。下面取固件里的放电 OCV 表 (去 IR 后的整包电压):
| 整包 OCV(开路电压) | 对应电量 |
|---|---|
| 7.58 V | 70 % |
| 7.50 V | 60 % |
| 7.44 V | 50 % |
| 7.36 V | 40 % |
| 7.28 V | 30 % |
从 70 % 掉到 30 %,电压只降了 0.30 V,平均每 1 % 电量约 6 mV; 而带负载时内阻压降(I×R)可达几十毫伏,误差会超过 10 %,负载越大偏差越大。
两端则很陡:这张表里 8.34 V 是 100 %、6.34 V 是 0 %。因此固件两端用电压定位, 中间靠电流积分推进。
总体结构
电量 = 起点电量 − ∫(放电电流)dt + ∫(充电电流)dt
积分需要一个起点,且误差会随时间累积,因此还要配合一套修正机制。 光影猫2 分为以下几层:
| 层 | 作用 |
|---|---|
| 硬件测量 | TPA626 每 100 ms 测一次电压/电流 |
| 库仑积分 | 每 200 ms 把电流累加进 µAs 账本 |
| OCV 融合 | 静置时用电压表反查电量,轻轻拉回积分漂移 |
| 锚点 | 充满 / 欠压时直接把电量钉死到 100 % / 0 % |
| 自学习 | 满容量、内阻、空点、充电增益,每台机器各学各的 |
| 温度补偿 | 冷天内阻变大、可用容量变少,都要修正 |
| 显示层 | 把内部电量映射成用户看到的 1–100 % |
一、硬件测量
测量芯片是 TPA626(I²C 电流/电压监测器,与 INA226 同类)。它串一个 毫欧级采样电阻在电池回路上,测电阻两端压降换算电流,同时测总线电压。
- 采样周期:约 100 ms 一次(
VD1_Collect) - 中值滤波:电压和电流各过一个中值滤波器,滤掉 I²C 偶发抖动和负载尖峰
- 原始值另存一份:中值滤波有约 3 s 滞后,会抹平充电器插拔的电压台阶;
内阻自学习需要该瞬间台阶,故固件同时保留未滤波的原始值
电流校准
采样电阻和运放都有个体误差,出厂靠精密万用表实测标定,拟合成二次多项式:
I_真实(mA) = C2·x² + C1·x + C0
当前系数(2026-07-24 二轮标定,四点残差 ≤3 mA):
| 项 | 值 | 说明 |
|---|---|---|
| C2 | −4.444×10⁻⁵ | 二次项,修大电流段的非线性 |
| C1 | 1.0411 | 一次项(增益) |
| C0 | +20 mA(放电)/ 0 mA(充电) | 零点偏移,分方向 |
实现细节:
- 先夹再算:抛物线在标定范围外会反向弯(顶点约 +11.7 A),故先把输入夹到
±3 A,域外按端点斜率线性延伸。
- int64 运算:x² 最大到 9×10⁶,乘系数后会溢出 int32。
- 55 mA 死区:小于该值按 0 处理,避免待机时噪声累积到账本。
二、库仑积分
每 200 ms 调一次 coulomb_counter_update(),把电流乘以时间
累加进一个 µAs(微安秒)的 64 位累加器。使用 µAs 而非 mAh,可避免定点
除法的舍入误差长期累积。
- 放电减、充电加
- 电流绝对值小于 30 mA 的部分不计入(死区,防噪声漂移)
- 充电方向另有一个自学习的增益系数(充电效率不是 100 %)
三、OCV 融合
在电池接近静置时,用电压反查电量表对积分做小幅修正。触发条件如下, 全部满足才执行:
| 条件 | 阈值 | 说明 |
|---|---|---|
| 电流足够小 | |I| ≤ 340 mA(约 0.05C) | 电流大时内阻压降的模型误差会放大 |
| 持续足够久 | ≥ 30 s | 让极化电压和表面电荷弛豫掉 |
| 电压变化率 | ≤ 20 mV/s | 还在动态过程中就不算静置 |
| 电流变化率 | ≤ 2000 mA/s | 同上 |
| 插拔宽限期 | 10 s | 充电器刚插拔,电压还没稳 |
修正采用加权融合而非直接覆盖:
- 权重上限 α = 0.07(OCV 最多只占 7 %),且在 8 s 内从 0 线性爬升
- 单次修正最多 0.20 %
因此 OCV 只对积分做渐进修正,显示电量不会跳变。
三张 OCV 表
锂电池存在滞回:同一电量下充电时端电压偏高、放电时偏低(约 30 mV/芯, 2S 约 ±60 mV)。固件为此设三张表:
- 放电表:进入放电模式(|I| > 900 mA)时用
- 充电表:进入充电模式时用
- 静置表(REST):充放两表的平均,小电流(|I| ≤ 600 mA)时用
模式切换带滞回(进 900 mA / 出 600 mA),避免在阈值附近反复切换。
查表前先去 IR:OCV = 端电压 − I×R_pack,其中内阻取自下面的自学习值。
四、锚点
中段靠积分,两端由锚点直接置位:
- 满电锚点:充电器在位、vbus ≥ 8 V 且电流已很小 → 电量 = 100 %。
必须要求充电器在位:95 % 的静置 OCV 约 8.16 V,高电量静置电池易被误判为满电; 劣质或欠压充电器无法充到真满,同样不给锚。
- 欠压锚点:放到欠压关机点 → 电量 = 0 %,同时触发空点学习。
五、自学习
同一批电池和板子的个体差异可超过 10 %,因此固件为每台机器单独学习 四个量,结果存入 data flash,掉电不丢。
5.1 满容量学习(电池老化)
标称 6800 mAh 的电池用两年可能只剩 5500 mAh。满容量若写死,老化后 SOC 会偏乐观。 学习流程:
- 满电锚点触发时开窗口,累计放电清零
- 一路放电到欠压关机点
- 满容量 = 累计放出的电量 + 700 mAh
- 中途检测到充电 → 本次窗口作废
其中 700 mAh 是关机点余量:整机不会把电池放到 0 V,在关机点 (约 6.3–6.45 V)包内仍剩约 700 mAh,需补回。
学到的值换算成健康度:
健康度 = 学习容量 / 6800 mAh
健康度按 EMA 缓慢融入,单次测量不会使其大幅变化。
5.2 内阻学习(电压台阶法)
内阻由充电器插拔瞬间的电压台阶测得:电流突变 ΔI,电压随之突变 ΔV,
R = ΔV / ΔI
条件:电压 > 7.8 V(近满电区,否则 OCV 本身会随电量移动而测不准)、 电流台阶 ≥ 300 mA、电压台阶 ≥ 15 mV。测量结果按 3/10 的权重 EMA 融入, 范围限制在 10–200 mΩ,默认值 50 mΩ。
该值是整包回路电阻 = 电芯本体 + 线缆/接插件(20 mΩ)+ 保护板 MOSFET(15 mΩ)。 去 IR 使用整包串联值,不做扣减;上位机显示"电芯本体内阻"时才减去这 35 mΩ 板级寄生。
5.3 显示空点学习
每台机器的电流计增益与实际容量存在偏差,内部 SOC 归零的时刻与硬件实际断电的 时刻可能不一致(表现为显示尚有 20 % 时断电)。固件不修改电流链路,而是在显示层 套一个每台机器自学习的线性映射:
显示值 = (内部SOC − 空点) × 100 / (100 − 空点)
空点(empty)= 上一次"电池真的没电了"时的内部 SOC。学习事件包括: 低压关机命令、低压下主机意外死机、欠压锚点。
并带向下自纠:放电时电压仍健康(> 6.8 V)而内部 SOC 已低于空点, 说明空点偏高,空点随之下调。换电池或学习过头可在一个放电周期内恢复。
5.4 充电增益学习
充电效率不足 100 %,充入电荷与实际存入电荷存在差异。该增益在满电锚点学习, 变化 ≥1 % 时落盘。
六、温度补偿
板载 NTC 每 200 ms 把温度送入库仑计,用于两项修正:
6.1 内阻温度倍率 k(T)
内阻随温度变化,直接影响去 IR 的准确性。倍率取 NCA 典型温度特性的分段线性近似:
| 温度 | 内阻倍率 k(T) |
|---|---|
| −20 °C | ×3.00 |
| −10 °C | ×2.30 |
| 0 °C | ×1.80 |
| 10 °C | ×1.35 |
| 25 °C | ×1.00(基准) |
| 40 °C | ×0.85 |
即 −20 °C 时内阻约为常温的三倍。学习到的内阻归一到 25 °C,使用时再乘 k(T)。 超出 −20…40 °C 的部分按端点截断。
6.2 低温可用容量补偿
低温下电池可用容量下降(电量并未消失,只是低温下无法取出)。固件据此 把显示空点上移,使显示电量提前走低:
| 温度 | 空点上移 |
|---|---|
| ≥ 15 °C | 0(不补偿) |
| 0 °C | 约 +4 % |
| −10 °C | 约 +8 % |
| ≤ −20 °C | +12 %(封顶) |
NTC 读数超出 −30…70 °C 视为故障,保持上一次有效温度(初值 25 °C)。
七、显示曲线
内部 SOC 不直接显示,中间还有一层映射:
- 4 % → 显示 1 %
- 96 % → 显示 100 %
- 中段线性(斜率 99/92),两端截断
因此显示 0 % 时电池仍留有约 4 % 余量(另加空点以下的硬件余量), 可供保存工作并正常关机;充到 96 % 即显示 100 %。这是消费电子的常规做法。
固件上报的百分比下限钳在 1 %:未关机时不显示 0 %。
八、关机后的电量保持
共三条路径:
8.1 SOC 快照
运行时 SOC 每漂移 ≥5 % 就自动落盘;关机、主机断电、进待机、OTA 复位前 强制落盘并刷新时间戳。
开机恢复策略:
- ≤12 小时:SOC = 快照 − 2.0 mA × Δt(按待机电流扣减),并禁止
OCV 重估覆盖。
- >12 小时:快照过期,走冷启动 OCV 重估。
8.2 深睡放电估计
电池深睡时 TPA626 掉电,无法采集电流。固件进睡前记录 RTC 分钟戳, 唤醒后按整机待机约 2.0 mA 恒流扣减。
8.3 换电池识别
电池拔出后 RTC 掉电重置,时间倒退。固件据此判定为换电池,快照作废并重新冷启动估计。 判定留有 10 分钟容差,避免升级重启时误判丢弃有效快照。
九、数据如何到达 Linux
MCU 算完,通过 UART 把状态帧发给主机(RK3576)。主机侧有两个消费者:
9.1 内核驱动:photonicat-pm
这是一个 serdev 驱动(990-photonicat-pm-add-driver.patch,
内核 6.12),直接挂在串口上解析状态帧,然后注册成标准 Linux 设备:
- power_supply 类 →
/sys/class/power_supply/battery - RTC 类 →
/dev/rtc
由于注册为标准子系统,现成 Linux 工具可直接使用,无需了解私有协议:
# 电量(%),这就是上面整套算法的最终输出
cat /sys/class/power_supply/battery/capacity
# 电压 µV / 电流 µA / 功率 µW
cat /sys/class/power_supply/battery/voltage_now
cat /sys/class/power_supply/battery/current_now
cat /sys/class/power_supply/battery/power_now
# 能量 µWh:energy_full 是学习到的满容量,对比 energy_full_design 看老化
cat /sys/class/power_supply/battery/energy_now
cat /sys/class/power_supply/battery/energy_full
cat /sys/class/power_supply/battery/energy_full_design
# 充电器在线
cat /sys/class/power_supply/charger/online
MCU 即硬件 RTC。板上没有独立 RTC 芯片,MCU 常供电并自行走时,
时间随状态帧上送。驱动将其注册为 rtc_class_ops,标准工具可直接使用:
hwclock -r # 读 MCU 里的时间
hwclock -w # 把系统时间写回 MCU
因此断网断电数日后开机时间仍准确。8.3 的换电池识别也依赖它: RTC 掉电重置后时间会倒退。
9.2 用户态守护进程:pcat-manager
内核驱动只处理状态帧中的电源部分。其余 UART 协议由 pcat-manager 处理: 调制解调器管理、看门狗、开关机策略、按键事件、充电阈值下发、MCU 固件升级等。 完整协议见 Photonicat 2 MCU 通讯协议。
分工如下:
| 组件 | 负责 |
|---|---|
| MCU 固件 | 测量、积分、学习、补偿——所有算法 |
| photonicat-pm(内核) | 电源/RTC 数据 → 标准 sysfs 接口 |
| pcat-manager(用户态) | 其余协议:调制解调器、看门狗、电源策略、OTA |
以充电上限为例:网页设置 80 % 后,pcat-manager 通过 UART 下发给 MCU, MCU 存入 data flash 并直接控制充电 MOSFET。自固件 RA2E1260726005 起该设置在 MCU 侧持久化,复位或 OTA 后不丢失;pcat-manager 同时保留镜像,检测到 MCU 重启时重新下发。
十、常见问题
- 电量偶尔小幅跳动
- OCV 融合或锚点正在修正积分漂移。单次修正上限 0.20 %,正常情况下不会出现大跳变;
若出现大跳变,通常是快照过期后走了冷启动重估。
- 冬天电量下降更快
- 低温下可用容量下降,同时固件把显示空点上移(−10 °C 约 +8 %)。回暖后恢复。
- 充到 96 % 即显示 100 %
- 显示曲线的设定,见第七节。同理显示 0 % 时电池仍有余量。
- 新电池是否需要校准
- 不需要专门操作。完整放电一次(满电到自动关机)可让固件学到真实满容量和空点,
日常使用也会逐步学到。
- 健康度的更新频率
- 需要一次完整放电窗口(满电锚点 → 欠压),中途充电则本次作废,因此更新较慢,
且按 EMA 缓变。