1. 基于安时积分法的船舶电池SOC基础估算实现

任务对象与约定

本节以一艘小型电动船舶的磷酸铁锂动力电池组为对象:额定容量 Q_N = 200 Ah,初始 SOC 为 SOC₀ = 0.80。给定一段 30 分钟的充放电电流序列(采样周期 60 s),用安时积分法递推 SOC,并输出 SOC 随时间变化曲线。

符号约定必须先固定,否则结果会整体反向:规定放电电流为正、充电电流为负(即电流从电池正极流出为正)。于是安时积分递推式为:

SOC(k) = SOC(k-1) - (I(k) · Δt) / (3600 · Q_N · η)

其中 I 单位为 A,Δt 单位为 s,Q_N 单位为 Ah,除以 3600 完成秒到小时的换算;η 为库仑效率,本例取 1.0 以突出积分步长与符号约定的影响。

核心函数实现

下面给出可直接运行的 Python 实现,包含安时积分核心函数、示例电流序列、SOC 递推与曲线绘制。电流序列按 60 s 采样,共 30 个点(0~29 min 的采样值),代表一段“放电—静置—充电—放电”的航行工况。

import numpy as np
import matplotlib.pyplot as plt

Q_N = 200.0      # 额定容量 Ah
SOC0 = 0.80      # 初始 SOC
ETA = 1.0        # 库仑效率

# 30 个采样点,采样周期 60 s;放电为正、充电为负
current = np.array([
    40, 40, 40, 40, 40,      # 0-4 min 放电
    0,  0,  0,  0,  0,       # 5-9 min 静置
    -30, -30, -30, -30, -30, # 10-14 min 充电
    0,  0,  0,  0,  0,       # 15-19 min 静置
    60, 60, 60, 60, 60,      # 20-24 min 大电流放电
    20, 20, 20, 20, 20       # 25-29 min 小电流放电
], dtype=float)

def ampere_hour_soc(current, dt, q_n, soc0, eta=1.0):
    """安时积分 SOC 递推。放电电流为正,充电为负。"""
    soc = np.empty(len(current) + 1)
    soc[0] = soc0
    for k, i_k in enumerate(current, start=1):
        soc[k] = soc[k-1] - (i_k * dt) / (3600.0 * q_n * eta)
    return soc

dt = 60.0
soc = ampere_hour_soc(current, dt, Q_N, SOC0, ETA)
t_min = np.arange(len(soc)) * dt / 60.0

print("末值 SOC = %.4f" % soc[-1])
print("SOC 变化量 = %.4f" % (soc[-1] - SOC0))

plt.plot(t_min, soc, marker='o', color='#1f77b4')
plt.xlabel("时间 / min")
plt.ylabel("SOC")
plt.title("安时积分法 SOC 递推曲线")
plt.grid(True, alpha=0.3)
plt.show()

结果判读与两个关键影响

按上述参数,放电段累计放出电量 (40×5 + 60×5 + 20×5) A·min = 600 A·min = 10 Ah,充电段累计充入 30×5 A·min = 2.5 Ah,净放出 7.5 Ah。因此末值 SOC 应为 0.80 − 7.5/200 = 0.7625,与程序输出一致。这一手算校核是验证实现正确性的第一道关口。

  • 积分步长的影响:安时积分本质是矩形法数值积分。若把同一段电流序列用 30 s 采样重算(电流值按段保持),由于电流在段内恒定,结果与 60 s 完全一致;但若电流在采样间隔内变化(如斜坡上升),步长越大,矩形近似误差越大。工程上应保证采样周期远小于电流变化的时间常数。
  • 电流符号约定的影响:若误把充电记为正值,则 SOC 曲线整体反向:放电段 SOC 上升、充电段 SOC 下降,末值变为 0.80 + 7.5/200 = 0.8375。数值上“看起来合理”,但物理意义完全错误,因此必须在代码入口处用注释和变量命名锁定符号约定。
验证技巧:先构造一段“纯放电 10 Ah”的电流序列,确认 SOC 下降 0.05;再构造“纯充电 10 Ah”,确认 SOC 上升 0.05。两个方向都通过,说明符号约定与积分方向一致。
安时积分 SOC 递推:电流序列 → 电量累积 → SOC 曲线 电流序列(放电为正 / 充电为负,Δt = 60 s) 0 A +40 A 静置 −30 A 静置 +60 A +20 A 0 5 10 15 20 25 30 min 电量累积(Ah) 放电:40×5 + 60×5 + 20×5 = 600 A·min = 10 Ah 充电:30×5 = 150 A·min = 2.5 Ah → 净放出 7.5 Ah 符号约定对末值 SOC 的影响 正确(放电为正):0.80 − 7.5/200 = 0.7625 反向(充电为正):0.80 + 7.5/200 = 0.8375 ✗ SOC 随时间变化(示意,非按比例) 0.80 0.76 末值 0.7625 反向约定 0.8375 放电为正 充电为正(错误)

步长敏感性验证

为验证积分步长的影响,可把同一段电流序列按 30 s 采样重算(每段电流值保持),由于段内电流恒定,结果与 60 s 一致;但若把电流改为在采样间隔内线性变化,则 60 s 与 30 s 的结果会出现差异。下面给出一个可复现的对比实验:构造一段从 0 A 线性上升到 60 A 的 10 分钟放电电流,分别用 60 s 与 10 s 步长积分,比较末值 SOC。

def ramp_current(t, t_end=600.0, i_max=60.0):
    return i_max * t / t_end

def integrate_ramp(dt):
    t = 0.0
    soc = SOC0
    while t < 600.0:
        i_k = ramp_current(t + dt/2)   # 取区间中点电流,减小矩形误差
        soc -= (i_k * dt) / (3600.0 * Q_N)
        t += dt
    return soc

print("60 s 步长末值 SOC = %.5f" % integrate_ramp(60.0))
print("10 s 步长末值 SOC = %.5f" % integrate_ramp(10.0))

该斜坡放电总电量为 ½ × 60 A × (600/3600) h = 5 Ah,理论末值 SOC 为 0.80 − 5/200 = 0.775。步长越小,结果越接近理论值;60 s 步长因矩形近似会产生可见偏差。这正是后续案例中“累积误差量化”的起点,但在本例中只需确认:实现正确、符号正确、步长合理,安时积分就能给出可信的 SOC 递推结果。

常见错误:把 Δt 以分钟代入公式却仍除以 3600,会导致 SOC 变化量缩小 60 倍。统一在函数入口把时间换算为秒,并在注释中写明单位。