锂电池SOC估计的一种算法(改进无迹卡尔曼滤波)

论文解读

来自:《基于改进无迹卡尔曼滤波的锂电池SOC估计方法》

基于改进无迹卡尔曼滤波的锂电池SOC估计方法

本文解读的算法来自论文《基于改进无迹卡尔曼滤波的锂电池SOC估计方法》。该工作提出了一种融合鹦鹉优化算法(Parrot Optimizer, PO)与H∞\text{H}_\inftyH∞鲁棒准则的新型滤波器------PO-RUKF,用于高精度、自适应地估计锂离子电池的荷电状态(SOC)。

给你的手机/电动车"读心术":如何用"鹦鹉优化"的卡尔曼滤波,精准预测电池还剩多少电?

从安时积分到PO-RUKF,一篇让你看懂SOC估计黑科技的保姆级教程

你有没有过这样的经历?

  • 手机显示还有20%的电,结果下一秒就自动关机了?
  • 电动车仪表盘上明明还有100公里续航,开出去没多久就亮起了"龟速"红灯?

这背后,都是因为一个看似简单、实则极其复杂的工程难题:如何精确知道一块锂电池当前的"荷电状态"(State of Charge, SOC)?

SOC就像是电池的"油表",但它比汽车油箱复杂得多。你不能直接"看"到电池内部的电子储量,而且电池性能会随着温度、老化程度、充放电倍率动态变化。想要精准估算SOC,既要懂电池的物理化学特性,又要会用智能算法------这是一个典型的"交叉学科难题"。


第一幕:传统方法为何频频"翻车"?

先来看看工业界和学术界早期的SOC估计方法,以及它们各自的"致命痛点"。

1. 安时积分法(Coulomb Counting)------ "粗心的记账员"

这是最直观、最常用的方法,原理等同于"记账":记录每一分每一秒的电流,累加计算总电量变化

  • 核心公式
    SOC(t)=SOC0−ηCn×3600∫0tI(τ)dτ \text{SOC}(t) = \text{SOC}_0 - \frac{\eta}{C_n \times 3600} \int_0^t I(\tau)d\tau SOC(t)=SOC0−Cn×3600η∫0tI(τ)dτ
    其中,SOC0\text{SOC}_0SOC0是初始SOC,η\etaη是库伦效率,CnC_nCn是电池额定容量(Ah),I(τ)I(\tau)I(τ)是充放电电流(放电为正,充电为负)。
  • 优点:计算简单、实时性强,嵌入式芯片也能轻松运行。
  • 致命问题
    1. 累积误差:电流传感器的微小误差会随时间不断放大,几小时后SOC估计就会严重偏离真实值;
    2. 初始值依赖 :必须精准知道SOC0\text{SOC}_0SOC0,否则从一开始就错了;
    3. 容量衰减:电池老化后实际容量会下降,但算法通常沿用额定容量,进一步加剧误差。

2. 开路电压法(OCV Method)------ "只能冷静时工作的观察员"

这个方法利用了电池的核心物理特性:完全静置后的开路电压(OCV)与SOC呈一一对应的函数关系

  • 操作流程:让电池静置1~2小时(消除极化效应)→ 测量OCV → 查预设的"OCV-SOC"曲线表 → 得到SOC。
  • 优点:精度高(实验室条件下误差可小于1%),无需复杂计算。
  • 致命问题动态场景完全失效
    电动车行驶、手机玩游戏时,电池处于高频充放电状态,端电压受极化电压、欧姆压降影响极大,此时测量的电压和OCV毫无关系。这个方法只能用于"离线校准",无法实时估计。

显然,我们需要一个既能实时计算,又能自我修正误差 的智能方法。这时候,大名鼎鼎的卡尔曼滤波(Kalman Filter) 就登场了!


第二幕:卡尔曼滤波------那个"猜得越来越准"的天才算法

卡尔曼滤波被誉为"20世纪最伟大的算法之一",它的核心思想不是"猜",而是**"基于模型预测+基于传感器修正"的最优融合**:

"我既相信电池模型算出的预测值,也相信电压传感器的测量值,但我会根据两者的可信度,动态分配权重,得到最靠谱的结果。"

对于锂电池这种强非线性系统 ,线性卡尔曼滤波(KALF)效果很差,于是它的"升级版"------无迹卡尔曼滤波(UKF) 成为了SOC估计的主流算法。

UKF的核心魔法:用"Sigma点"破解非线性难题

UKF的精妙之处在于:不直接对非线性函数做近似,而是用一组精心挑选的采样点(Sigma点)来近似状态的概率分布

举个通俗的例子:

你要预测一个不规则气球在风中的运动轨迹(非线性),不需要解复杂的流体力学方程,只需要在气球表面贴几个标记点(Sigma点),跟踪这些点的运动,就能推断出整个气球的轨迹。

UKF估计SOC的关键步骤(四步走)

我们定义电池的状态向量为 x=[SOC, Vp1, Vp2]T\boldsymbol{x} = [\text{SOC},\ V_{p1},\ V_{p2}]^Tx=[SOC, Vp1, Vp2]T(包含SOC和两个极化电压,对应二阶RC模型),观测向量为端电压 VVV。

  1. 步骤1:生成Sigma点
    围绕当前最优估计值 x^k−1∣k−1\hat{\boldsymbol{x}}{k-1|k-1}x^k−1∣k−1,生成 2n+12n+12n+1 个Sigma点(nnn是状态维度,这里n=3n=3n=3,共7个点):
    χk−1(i)={x^k−1∣k−1,i=0x^k−1∣k−1+(n+λ)Pk−1∣k−1i,i=1,2,...,nx^k−1∣k−1−(n+λ)Pk−1∣k−1i,i=n+1,...,2n \chi
    {k-1}^{(i)} = \begin{cases} \hat{\boldsymbol{x}}{k-1|k-1}, & i=0 \\ \hat{\boldsymbol{x}}{k-1|k-1} + \sqrt{(n+\lambda) \boldsymbol{P}{k-1|k-1}}i, & i=1,2,...,n \\ \hat{\boldsymbol{x}}{k-1|k-1} - \sqrt{(n+\lambda) \boldsymbol{P}{k-1|k-1}}_i, & i=n+1,...,2n \end{cases} χk−1(i)=⎩ ⎨ ⎧x^k−1∣k−1,x^k−1∣k−1+(n+λ)Pk−1∣k−1 i,x^k−1∣k−1−(n+λ)Pk−1∣k−1 i,i=0i=1,2,...,ni=n+1,...,2n
    其中,λ\lambdaλ是缩放系数,P\boldsymbol{P}P是协方差矩阵(表示状态的不确定性)。

解读 这个公式就是 UKF 的**"撒侦察兵"大法**!咱们用"猜电池状态"的例子,把它掰扯得明明白白:

  1. 核心主角

    • x^k−1∣k−1\hat{\boldsymbol{x}}_{k-1|k-1}x^k−1∣k−1:你上一时刻最靠谱的"猜测结果",比如猜电池状态是 [SOC=0.8,Vp1=0.02V,Vp2=0.005V][SOC=0.8, Vp1=0.02V, Vp2=0.005V][SOC=0.8,Vp1=0.02V,Vp2=0.005V],这就是侦察兵的大本营
    • n=3n=3n=3:状态维度是3(SOC+两个极化电压),所以要派 2×3+1=72×3+1=72×3+1=7 个侦察兵。
  2. 协方差矩阵 P\boldsymbol{P}P 的作用
    P\boldsymbol{P}P 就是**"不确定范围地图"**------P\boldsymbol{P}P 里的数值越大,说明你对这个状态的猜测越没底。比如 SOC 的不确定性大,地图上 SOC 对应的方向就越宽。

  3. (n+λ)P\sqrt{(n+\lambda)\boldsymbol{P}}(n+λ)P 的魔法

    这一步是把"不规则的不确定地图"拉成方方正正的坐标轴 (就像把椭圆操场压成矩形跑道),方便侦察兵按方向出发。λ\lambdaλ 是个"缩放调节器",能控制侦察兵的出发距离------λ\lambdaλ 越大,侦察兵跑得越远,探索范围越广。

  4. 7个侦察兵的出发路线

    • i=0i=0i=0:1个侦察兵留守大本营,不挪窝,代表最基础的猜测;
    • i=1,2,3i=1,2,3i=1,2,3:3个侦察兵分别沿着3个坐标轴正方向跑一步,探索"状态偏高"的情况;
    • i=4,5,6i=4,5,6i=4,5,6:另外3个侦察兵分别沿着3个坐标轴反方向跑一步,探索"状态偏低"的情况。

最后这7个侦察兵,就带着你的猜测,去电池模型里"走一趟",帮你摸清所有可能的状态------比硬解复杂方程轻松多啦!

代码对应

python 复制代码
n = x.shape[0]
lam = alpha**2 * (n + kappa) - n  # alpha,kappa是UKF参数
sqrt_term = np.linalg.cholesky((n + lam) * P)
sigma = np.tile(x, (2*n+1, 1))
for i in range(n):
    sigma[i+1] += sqrt_term[:, i]   # 正方向采样
    sigma[i+1+n] -= sqrt_term[:, i] # 负方向采样
  1. 步骤2:时间更新(预测)
    让每个Sigma点通过电池非线性状态方程 ,得到预测的Sigma点 χk∣k−1(i)\chi_{k|k-1}^{(i)}χk∣k−1(i),再加权平均得到先验状态估计 x^k∣k−1\hat{\boldsymbol{x}}{k|k-1}x^k∣k−1 和先验协方差 Pk∣k−1\boldsymbol{P}{k|k-1}Pk∣k−1:
    x^k∣k−1=∑i=02nWm(i)χk∣k−1(i) \hat{\boldsymbol{x}}{k|k-1} = \sum{i=0}^{2n} W_m^{(i)} \chi_{k|k-1}^{(i)} x^k∣k−1=i=0∑2nWm(i)χk∣k−1(i)
    Pk∣k−1=∑i=02nWc(i)(χk∣k−1(i)−x^k∣k−1)(χk∣k−1(i)−x^k∣k−1)T+Q \boldsymbol{P}{k|k-1} = \sum{i=0}^{2n} W_c^{(i)} \left( \chi_{k|k-1}^{(i)} - \hat{\boldsymbol{x}}{k|k-1} \right) \left( \chi{k|k-1}^{(i)} - \hat{\boldsymbol{x}}_{k|k-1} \right)^T + \boldsymbol{Q} Pk∣k−1=i=0∑2nWc(i)(χk∣k−1(i)−x^k∣k−1)(χk∣k−1(i)−x^k∣k−1)T+Q
    其中,Wm(i)W_m^{(i)}Wm(i)和Wc(i)W_c^{(i)}Wc(i)是Sigma点的权重,Q\boldsymbol{Q}Q是过程噪声协方差。

解读 这一步就是让咱们派出去的 7个侦察兵(Sigma点)去执行任务,再把它们的反馈汇总成"作战地图"!

  1. 第一步:侦察兵去前线(Sigma点传播)

    每个Sigma点 χk−1(i)\chi_{k-1}^{(i)}χk−1(i) 都要钻进电池非线性状态方程 里"走一遭"------这个方程就像电池的"真实世界规则",侦察兵进去后,会根据当前的电流、时间,变成下一时刻的预测状态 χk∣k−1(i)\chi_{k|k-1}^{(i)}χk∣k−1(i)。

    比如留守大本营的侦察兵(i=0i=0i=0),进去后会变成"按上一时刻最优猜测算出来的下一时刻状态";跑出去的侦察兵,会变成"状态偏高/偏低时,下一时刻可能的样子"。

  2. 第二步:给侦察兵的反馈打分(加权平均)

    公式里的 Wm(i)W_m^{(i)}Wm(i) 是**"信任分"**------不是所有侦察兵的话都同等可信!

    • 留守大本营的侦察兵(i=0i=0i=0)通常权重最高,因为它代表最靠谱的初始猜测;
    • 跑出去的侦察兵权重较低,毕竟它们探索的是"边缘情况"。
      把每个侦察兵的预测状态 χk∣k−1(i)\chi_{k|k-1}^{(i)}χk∣k−1(i) 乘以对应的信任分,再加起来,就得到了 先验状态估计 x^k∣k−1\hat{\boldsymbol{x}}_{k|k-1}x^k∣k−1 ------这就是"综合所有侦察兵情报后,猜出来的下一时刻电池状态"。
  3. 第三步:画误差范围(计算先验协方差 Pk∣k−1\boldsymbol{P}_{k|k-1}Pk∣k−1)

    光有猜测还不够,得知道这个猜测"有多靠谱"!

    • (χk∣k−1(i)−x^k∣k−1)\left( \chi_{k|k-1}^{(i)} - \hat{\boldsymbol{x}}_{k|k-1} \right)(χk∣k−1(i)−x^k∣k−1) 就是每个侦察兵的反馈和综合猜测的差距------差距越大,说明猜测的不确定性越高;
    • Wc(i)W_c^{(i)}Wc(i) 是计算不确定性时的"信任分"(和 Wm(i)W_m^{(i)}Wm(i) 类似,但数值略有不同);
    • 最后加的 Q\boldsymbol{Q}Q 是**"意外风险值"**------代表电池模型本身的误差(比如电池老化、温度变化这些没考虑到的因素),就像侦察兵执行任务时遇到的"突发天气"。
      整个公式算出来的 Pk∣k−1\boldsymbol{P}_{k|k-1}Pk∣k−1,就是"这次预测的误差范围地图"------地图上的数值越大,说明预测越没底。
  1. 步骤3:观测更新(预测端电压)
    把先验Sigma点通过观测方程 (由RC模型计算端电压),得到预测的观测点 Υk∣k−1(i)\Upsilon_{k|k-1}^{(i)}Υk∣k−1(i),再加权平均得到预测端电压 V^k∣k−1\hat{V}{k|k-1}V^k∣k−1 和观测协方差 Pzz\boldsymbol{P}{zz}Pzz:
    V^k∣k−1=∑i=02nWm(i)Υk∣k−1(i) \hat{V}{k|k-1} = \sum{i=0}^{2n} W_m^{(i)} \Upsilon_{k|k-1}^{(i)} V^k∣k−1=i=0∑2nWm(i)Υk∣k−1(i)
    Pzz=∑i=02nWc(i)(Υk∣k−1(i)−V^k∣k−1)(Υk∣k−1(i)−V^k∣k−1)T+R \boldsymbol{P}{zz} = \sum{i=0}^{2n} W_c^{(i)} \left( \Upsilon_{k|k-1}^{(i)} - \hat{V}{k|k-1} \right) \left( \Upsilon{k|k-1}^{(i)} - \hat{V}_{k|k-1} \right)^T + \boldsymbol{R} Pzz=i=0∑2nWc(i)(Υk∣k−1(i)−V^k∣k−1)(Υk∣k−1(i)−V^k∣k−1)T+R
    其中,R\boldsymbol{R}R是量测噪声协方差。

解读 这一步就是让侦察兵们**"变身成电压侦探"**,专门预测电池的端电压,再汇总出"电压预测报告"!

  1. 第一步:侦察兵变身电压侦探

    上一轮得到的先验Sigma点 χk∣k−1(i)\chi_{k|k-1}^{(i)}χk∣k−1(i)(每个点都是一组完整的电池状态:SOC+两个极化电压),现在要钻进观测方程 这个"转换器"。

    观测方程就是咱们之前说的电池"电压账本",它会根据每组状态算出一个对应的端电压------这些算出来的电压,就是预测观测点 Υk∣k−1(i)\Upsilon_{k|k-1}^{(i)}Υk∣k−1(i)。

    简单说:每个电池状态侦察兵,都摇身一变成了一个"电压预测值"

  2. 第二步:汇总出最靠谱的电压预测值

    公式 V^k∣k−1=∑i=02nWm(i)Υk∣k−1(i)\hat{V}{k|k-1} = \sum{i=0}^{2n} W_m^{(i)} \Upsilon_{k|k-1}^{(i)}V^k∣k−1=∑i=02nWm(i)Υk∣k−1(i) 还是老规矩------给每个电压侦探的预测值发"信任分" Wm(i)W_m^{(i)}Wm(i)。

    大本营侦察兵对应的电压预测值权重最高,边缘侦察兵的权重较低。把所有预测值乘以信任分再相加,就得到了最靠谱的预测端电压 V^k∣k−1\hat{V}_{k|k-1}V^k∣k−1 ------这就是算法"猜"出来的,电池此刻应该有的电压。

  3. 第三步:给电压预测画"误差圈圈"

    光猜电压还不够,得知道这个猜测准不准!公式 Pzz=∑i=02nWc(i)(Υk∣k−1(i)−V^k∣k−1)(Υk∣k−1(i)−V^k∣k−1)T+R\boldsymbol{P}{zz} = \sum{i=0}^{2n} W_c^{(i)} \left( \Upsilon_{k|k-1}^{(i)} - \hat{V}{k|k-1} \right) \left( \Upsilon{k|k-1}^{(i)} - \hat{V}_{k|k-1} \right)^T + \boldsymbol{R}Pzz=∑i=02nWc(i)(Υk∣k−1(i)−V^k∣k−1)(Υk∣k−1(i)−V^k∣k−1)T+R 就是干这个的:

    • (Υk∣k−1(i)−V^k∣k−1)\left( \Upsilon_{k|k-1}^{(i)} - \hat{V}_{k|k-1} \right)(Υk∣k−1(i)−V^k∣k−1):每个电压侦探的预测值,和综合预测值的差距------差距越大,说明电压预测的不确定性越高;
    • Wc(i)W_c^{(i)}Wc(i):计算不确定性时的信任分,和之前状态预测的信任分逻辑一致;
    • 最后加的 R\boldsymbol{R}R 是**"传感器误差红包"------代表电压传感器本身的测量误差(比如传感器老化、电磁干扰),就像侦探们手里的测量工具本身就有点不准。
      算出来的 Pzz\boldsymbol{P}_{zz}Pzz 就是
      电压预测的误差范围**------数值越大,说明我们对这个预测电压越没把握。
  1. 步骤4:卡尔曼增益计算与状态修正
    计算状态和观测的互协方差 Pxz\boldsymbol{P}{xz}Pxz,再计算卡尔曼增益 Kk\boldsymbol{K}kKk,最终得到后验状态估计(最优SOC)
    Pxz=∑i=02nWc(i)(χk∣k−1(i)−x^k∣k−1)(Υk∣k−1(i)−V^k∣k−1)T \boldsymbol{P}
    {xz} = \sum
    {i=0}^{2n} W_c^{(i)} \left( \chi_{k|k-1}^{(i)} - \hat{\boldsymbol{x}}{k|k-1} \right) \left( \Upsilon{k|k-1}^{(i)} - \hat{V}{k|k-1} \right)^T Pxz=i=0∑2nWc(i)(χk∣k−1(i)−x^k∣k−1)(Υk∣k−1(i)−V^k∣k−1)T
    Kk=PxzPzz−1 \boldsymbol{K}k = \boldsymbol{P}{xz} \boldsymbol{P}
    {zz}^{-1} Kk=PxzPzz−1
    x^k∣k=x^k∣k−1+Kk(Vk−V^k∣k−1) \hat{\boldsymbol{x}}{k|k} = \hat{\boldsymbol{x}}{k|k-1} + \boldsymbol{K}k \left( V_k - \hat{V}{k|k-1} \right) x^k∣k=x^k∣k−1+Kk(Vk−V^k∣k−1)
    Pk∣k=Pk∣k−1−KkPzzKkT \boldsymbol{P}{k|k} = \boldsymbol{P}{k|k-1} - \boldsymbol{K}k \boldsymbol{P}{zz} \boldsymbol{K}k^T Pk∣k=Pk∣k−1−KkPzzKkT
    其中,VkV_kVk是传感器实测端电压,Vk−V^k∣k−1V_k - \hat{V}
    {k|k-1}Vk−V^k∣k−1 被称为新息(衡量预测和测量的差距)。

解读 这一步就是UKF的**"终极决策环节"**------把"模型预测的状态"和"传感器测的电压"放在一起比对,最终算出最准的电池状态!咱们拆成4个小步骤,用"侦探破案"的思路讲明白:

  1. 第一步:算"状态和电压的关联度"------互协方差 Pxz\boldsymbol{P}_{xz}Pxz

    公式里的两个差值,咱们早就认识了:

    • χk∣k−1(i)−x^k∣k−1\chi_{k|k-1}^{(i)} - \hat{\boldsymbol{x}}_{k|k-1}χk∣k−1(i)−x^k∣k−1:状态侦察兵和平均预测状态的差距(状态偏差);
    • Υk∣k−1(i)−V^k∣k−1\Upsilon_{k|k-1}^{(i)} - \hat{V}_{k|k-1}Υk∣k−1(i)−V^k∣k−1:电压侦探和平均预测电压的差距(电压偏差)。

    这个公式的本质,就是给每一组偏差配对,再用权重加权求和 ------目的是搞清楚:"状态猜得不准"和"电压猜得不准"之间有没有关系?关系多大?

    举个例子:如果状态偏差大的时候,电压偏差也跟着大,那Pxz\boldsymbol{P}_{xz}Pxz的数值就大,说明两者"强相关";反之则数值小,说明"没啥关系"。

    简单说,Pxz\boldsymbol{P}_{xz}Pxz就是**"状态和电压的相关关系报告单"**。

  2. 第二步:算"信任分配系数"------卡尔曼增益 Kk\boldsymbol{K}_kKk

    公式 Kk=PxzPzz−1\boldsymbol{K}k = \boldsymbol{P}{xz} \boldsymbol{P}_{zz}^{-1}Kk=PxzPzz−1 看起来复杂,其实就是**"权衡模型和传感器的信任度"**:

    • 分母 Pzz\boldsymbol{P}{zz}Pzz 是电压预测的误差范围------Pzz\boldsymbol{P}{zz}Pzz 越小,说明电压预测越准,传感器越靠谱;
    • 分子 Pxz\boldsymbol{P}_{xz}Pxz 是状态和电压的关联度------关联度越高,说明"电压不准"大概率是"状态没猜对"导致的。

    所以 Kk\boldsymbol{K}_kKk 的物理意义超直白:当传感器测的电压和预测电压不一样时,我该用多大的力度去修正模型预测的状态?

    • Kk\boldsymbol{K}_kKk 大 → 多信传感器,使劲修正状态;
    • Kk\boldsymbol{K}_kKk 小 → 多信模型,稍微修正一下就行。
  3. 第三步:修正状态,得到最优解------后验估计 x^k∣k\hat{\boldsymbol{x}}_{k|k}x^k∣k

    公式 x^k∣k=x^k∣k−1+Kk(Vk−V^k∣k−1)\hat{\boldsymbol{x}}{k|k} = \hat{\boldsymbol{x}}{k|k-1} + \boldsymbol{K}k \left( V_k - \hat{V}{k|k-1} \right)x^k∣k=x^k∣k−1+Kk(Vk−V^k∣k−1)

    就是**"最终的修正公式",咱们把它翻译成大白话:
    最优状态=模型预测状态+信任系数×电压差距 \text{最优状态} = \text{模型预测状态} + \text{信任系数} \times \text{电压差距} 最优状态=模型预测状态+信任系数×电压差距
    这里的 (Vk−V^k∣k−1)\left( V_k - \hat{V}_{k|k-1} \right)(Vk−V^k∣k−1) 就是
    新息**------可以理解成"传感器给模型的纠错提示":

    • 新息为正 → 实测电压比预测高,说明模型猜的SOC可能偏低,得往上调一点;
    • 新息为负 → 实测电压比预测低,说明模型猜的SOC可能偏高,得往下调一点;
    • 调多少?全看 Kk\boldsymbol{K}_kKk 这个"信任系数"说了算!
  4. 第四步:更新误差地图------后验协方差 Pk∣k\boldsymbol{P}_{k|k}Pk∣k

    修正完状态还不算完,得更新一下"误差范围",为下一轮预测做准备。

    公式 Pk∣k=Pk∣k−1−KkPzzKkT\boldsymbol{P}{k|k} = \boldsymbol{P}{k|k-1} - \boldsymbol{K}k \boldsymbol{P}{zz} \boldsymbol{K}_k^TPk∣k=Pk∣k−1−KkPzzKkT

    的意思是:经过传感器的修正,我们对状态的猜测变得更靠谱了,误差范围缩小了!

    缩小的幅度,正好是"信任系数×电压误差×信任系数"------修正越到位,误差缩小得越多。

    最后得到的 Pk∣k\boldsymbol{P}_{k|k}Pk∣k 就是**"本轮最优状态的误差地图"**,下一轮的Sigma点,就要围绕这个新地图来撒啦!

--- 总结一下这一步的核心逻辑:用电压传感器的"实测值",去修正模型的"预测值",最终得到又准又靠谱的电池状态------这就是卡尔曼滤波"越猜越准"的秘诀!

UKF的两大"软肋"

尽管UKF是SOC估计的利器,但它有两个难以解决的问题:

  1. 参数敏感性 :过程噪声Q\boldsymbol{Q}Q和量测噪声R\boldsymbol{R}R需要人工设定,参数选得不好,估计结果会抖动或滞后;
  2. 鲁棒性不足:遇到极端工况(如急加速、低温)或模型失配时,UKF的估计误差会急剧增大,甚至发散。

这就是论文提出PO-RUKF的原因------给UKF装上"鲁棒防弹衣"和"自适应调参大脑"。


第三幕:PO-RUKF------让UKF变得更稳、更准

PO-RUKF的名字可以拆解为两部分:

  • RUKF(Robust UKF) :引入H∞\text{H}_\inftyH∞鲁棒准则,解决鲁棒性问题;
  • PO(Parrot Optimizer) :用鹦鹉优化算法,自适应优化过程噪声Q\boldsymbol{Q}Q。

Part 1: RUKF------引入H∞\text{H}_\inftyH∞准则,打造"抗干扰防弹衣"

RUKF的这个改进,就是给咱们的滤波算法穿上了一件"防坑防弹衣"!专治标准UKF"容易被传感器忽悠"的毛病,咱们用"买东西砍价"的故事讲明白:

先回顾标准UKF的"小缺点"

标准UKF算卡尔曼增益的公式是 Kk=PxzPzz−1\boldsymbol{K}k = \boldsymbol{P}{xz} \boldsymbol{P}{zz}^{-1}Kk=PxzPzz−1,这里的 Pzz\boldsymbol{P}{zz}Pzz 是电压预测的误差值

  • 如果 Pzz\boldsymbol{P}_{zz}Pzz 很小 → 算法觉得"我预测的电压超准",潜台词就是"传感器你可得靠谱点";
  • 这时候 Pzz−1\boldsymbol{P}_{zz}^{-1}Pzz−1 会变得超大 → 卡尔曼增益 Kk\boldsymbol{K}_kKk 直接"爆表";
  • 后果就是:算法无脑相信传感器------哪怕传感器突然抽风(比如电压突变、电磁干扰),算法也会跟着"跑偏",SOC估计值直接"飞上天"。

简单说,标准UKF就是个**"别人说啥都信的老实人"**,容易踩坑!

RUKF的"防坑秘诀":加个"安全垫"

RUKF的改进就俩公式,核心就一个字------"稳"
Re,k=Pzz+γ−2 \boldsymbol{R}{e,k} = \boldsymbol{P}{zz} + \gamma^{-2} Re,k=Pzz+γ−2
Kk=PxzRe,k−1 \boldsymbol{K}k = \boldsymbol{P}{xz} \boldsymbol{R}_{e,k}^{-1} Kk=PxzRe,k−1

  1. γ−2\gamma^{-2}γ−2 是啥?------ 算法的"冷静缓冲垫"
    γ\gammaγ 是个调节参数(一般取1.0~1.5),γ−2\gamma^{-2}γ−2 就是一个固定的正数小尾巴

    不管 Pzz\boldsymbol{P}{zz}Pzz 变得多小,加上这个小尾巴后,Re,k\boldsymbol{R}{e,k}Re,k 都不会变成0!

    这就像你砍价时,卖家说"这东西成本100,我只赚1块",你不会直接信------而是心里加个"缓冲":"他肯定赚了不止1块,我再砍20试试"。这个"缓冲"就是 γ−2\gamma^{-2}γ−2,让你不会被卖家的话牵着走。

  2. γ\gammaγ 怎么调?------ 调节"谨慎程度"的开关

    • γ\gammaγ 越小 → γ−2\gamma^{-2}γ−2 越大 → "缓冲垫"越厚 → 卡尔曼增益 Kk\boldsymbol{K}_kKk 越小 → 算法越保守,不信传感器的"鬼话",抗干扰能力拉满;
    • γ\gammaγ 越大 → γ−2\gamma^{-2}γ−2 越小 → "缓冲垫"越薄 → 算法越大胆,越来越接近标准UKF;
    • γ=1\gamma=1γ=1 → 是"保守"和"大胆"的平衡点,大部分场景都能用!

** 一句话总结**

RUKF不是"老实人",它是个**"有主见的聪明人"**------哪怕传感器说的天花乱坠,它也会留个心眼,不会轻易被带偏。这层"防弹衣",让它在复杂工况下也能稳稳输出精准的SOC估计!

Part 2: PO------让"数字鹦鹉"帮你调参,告别人工试错

过程噪声Q\boldsymbol{Q}Q决定了算法对"模型预测"的信任程度(Q\boldsymbol{Q}Q越大,越信任测量;Q\boldsymbol{Q}Q越小,越信任模型)。传统方法靠人工试错调Q\boldsymbol{Q}Q,而PO算法能自适应寻找最优Q\boldsymbol{Q}Q

鹦鹉优化算法(PO)的核心思想

PO算法就是一群超聪明的**"数学鹦鹉"**,它们的目标只有一个------帮咱们找到最完美的Q\boldsymbol{Q}Q矩阵(过程噪声),让RUKF的SOC估计精准到离谱!

这群鹦鹉不靠公式硬算,而是靠四种"生存技能",在Q\boldsymbol{Q}Q的搜索空间里"找宝藏",咱们一个个拆开来唠:

  1. 觅食行为 ------ "跟着大佬有肉吃"

    刚开始,鹦鹉们会在Q\boldsymbol{Q}Q的搜索范围里随机乱飞(相当于随机选几组Q\boldsymbol{Q}Q值)。

    飞着飞着,它们发现:有的Q\boldsymbol{Q}Q值代入RUKF后,新息特别小(预测电压和实测电压几乎一致) ------这就是"好吃的食物"!

    于是,所有鹦鹉都会朝着这个"最优食物位置"靠拢,优先探索大佬附近的区域。

    这就像你找奶茶店,看到一条街的人都往一家店跑,你肯定也会跟着去凑热闹~

  2. 停留行为 ------ "蹲在宝地慢慢挖"

    找到优质食物后,鹦鹉们不会立刻飞走,而是在附近"转圈溜达"------微调Q\boldsymbol{Q}Q的数值,比如把某个元素从1e−81e-81e−8改成1.2e−81.2e-81.2e−8、0.9e−80.9e-80.9e−8。

    这个操作就是**"精细搜索"**,目的是避免错过"藏在角落的极品美食"。

    好比你挖到一个金矿,不会只挖表面一层就走,而是会在周围深挖,争取把所有金子都带走!

  3. 沟通行为 ------ "鹦鹉之间不藏私"

    这群鹦鹉超有爱,它们会实时分享自己找到的食物位置

    如果一只鹦鹉在某个角落发现了更优的Q\boldsymbol{Q}Q值,它会立刻"喊"其他同伴过来;反之,如果一片区域啥都没有,大家也会互相提醒"别来这儿浪费时间"。

    这就像同学之间分享考试重点,一人找到,全班受益,效率直接翻倍!

  4. 避险行为 ------ "遇到陷阱赶紧跑"

    有时候,鹦鹉们会遇到**"假宝藏"------某个Q\boldsymbol{Q}Q值在小范围里看起来很好,但换个工况就不行了(这就是数学里的"局部最优解")。
    这时候,鹦鹉们不会死磕,而是会
    随机飞离这片区域**,去探索新的地方。

    好比你走进一条死胡同,不会硬着头皮撞墙,而是赶紧掉头,换条路继续找目的地~
    一句话总结

    PO算法的这群鹦鹉,就是**"自带导航的调参小能手"**------它们靠"跟风、深挖、分享、避险"四大技能,帮咱们找到那个能让RUKF封神的最优Q\boldsymbol{Q}Q矩阵,彻底告别"人工试错调参"的痛苦!

PO与RUKF的融合流程(关键!)

PO是外层优化算法 ,不参与每一步滤波,而是周期性迭代优化,流程如下:

python 复制代码
# 1. 初始化参数
Q_init = np.diag([1e-8, 1e-8, 1e-8])  # 初始Q矩阵
gamma = 1.2                           # H∞鲁棒参数
window_size = 100                     # 优化窗口(每100步优化一次)
PO_pop_size = 20                      # 鹦鹉种群数量
PO_max_iter = 50                      # PO最大迭代次数

# 2. 主循环:滤波+周期性优化
for k in range(total_steps):
    # 2.1 执行RUKF滤波,得到SOC估计和新息序列
    soc_est, innov = RUKF_step(x_prev, P_prev, I_k, V_k, Q_curr, gamma)
    
    # 2.2 每积累window_size个新息,启动PO优化Q
    if (k+1) % window_size == 0:
        # 定义适应度函数:新息的均方误差(越小越好)
        def fitness(Q):
            _, innov_list = run_RUKF_window(Q, gamma, I_window, V_window)
            return np.mean(np.square(innov_list))
        
        # 启动PO算法优化Q
        Q_opt = ParrotOptimizer(fitness, Q_search_range, PO_pop_size, PO_max_iter)
        
        # 更新Q为最优值
        Q_curr = Q_opt
    
    # 2.3 更新状态,进入下一步
    x_prev, P_prev = update_state(x_prev, soc_est, P_prev)

核心逻辑 :用新息的均方误差(MSE) 作为适应度函数------新息越小,说明模型预测和测量越吻合,对应的Q\boldsymbol{Q}Q就是最优的。PO算法通过迭代搜索,找到能最小化新息MSE的Q\boldsymbol{Q}Q,让RUKF始终处于最佳工作状态。


第四幕:效果如何?数据说话!

论文在FUDS联邦城市驾驶循环数据集上进行了实验(模拟真实城市路况,电流波动剧烈),对比了标准UKF、RUKF和PO-RUKF三种算法的性能,结果如下表所示:

算法 平均绝对误差 (AAE) 均方根误差 (RMSE) 稳定性(误差方差)
标准UKF 1.23% 1.58% 0.024
RUKF 0.87% 1.12% 0.012
PO-RUKF (本文) 0.52% 0.68% 0.005

实验结论

  1. 精度翻倍:PO-RUKF的误差比标准UKF降低了超过一半,完全满足工业级SOC估计的精度要求(误差<1%);
  2. 稳定性更强:误差方差仅为标准UKF的1/5,即使在电流剧烈波动的工况下,SOC估计曲线也平滑无抖动;
  3. 自适应能力突出 :PO算法能根据工况变化动态调整Q\boldsymbol{Q}Q,无需人工干预,适合复杂的实际应用场景。

第五幕:核心公式与代码实现------从理论到实践

1. 锂电池二阶RC模型(状态方程+观测方程)

这是SOC估计的核心物理模型,包含SOC和两个极化电压的更新公式。

状态方程(SOC+极化电压更新)

{SOCk=SOCk−1−ηIkΔtCn×3600Vp1,k=Vp1,k−1e−Δtτ1+R1Ik(1−e−Δtτ1)Vp2,k=Vp2,k−1e−Δtτ2+R2Ik(1−e−Δtτ2) \begin{cases} \text{SOC}k = \text{SOC}{k-1} - \frac{\eta I_k \Delta t}{C_n \times 3600} \\ V_{p1,k} = V_{p1,k-1} e^{-\frac{\Delta t}{\tau_1}} + R_1 I_k \left(1 - e^{-\frac{\Delta t}{\tau_1}}\right) \\ V_{p2,k} = V_{p2,k-1} e^{-\frac{\Delta t}{\tau_2}} + R_2 I_k \left(1 - e^{-\frac{\Delta t}{\tau_2}}\right) \end{cases} ⎩ ⎨ ⎧SOCk=SOCk−1−Cn×3600ηIkΔtVp1,k=Vp1,k−1e−τ1Δt+R1Ik(1−e−τ1Δt)Vp2,k=Vp2,k−1e−τ2Δt+R2Ik(1−e−τ2Δt)

其中,τ1=R1C1\tau_1 = R_1 C_1τ1=R1C1,τ2=R2C2\tau_2 = R_2 C_2τ2=R2C2 是RC网络的时间常数。

解读

  • SOC更新公式 :就像你钱包里的钱------SOCk−1SOC_{k-1}SOCk−1是你昨天剩的钱,IkΔtI_k ΔtIkΔt是今天花的钱(放电=花钱,充电=赚钱),ηηη是"手续费"(充电时电池不能100%存电,会耗一点),Cn×3600C_n×3600Cn×3600是你钱包的总容量。为啥乘3600?因为钱包容量按"小时"算(Ah),而花钱速度按"秒"算(A·s),得统一单位才能算清楚!
  • 极化电压公式 :想象极化电压是个弹簧 ------电流IkI_kIk是拉弹簧的力。第一项Vp1,k−1e−Δt/τ1V_{p1,k-1}e^{-Δt/τ_1}Vp1,k−1e−Δt/τ1是弹簧的"回弹"(电流消失后,极化电压会慢慢衰减);第二项R1Ik(1−e−Δt/τ1)R_1I_k(1-e^{-Δt/τ_1})R1Ik(1−e−Δt/τ1)是弹簧的"拉伸"(电流越大、拉的时间越长,弹簧伸得越长)。τ1τ_1τ1就是弹簧的"弹性系数"------τ1τ_1τ1越大,弹簧回弹越慢,极化电压变化越平缓。
观测方程(端电压计算)

Vk=OCV(SOCk)−R0Ik−Vp1,k−Vp2,k V_k = \text{OCV}(\text{SOC}k) - R_0 I_k - V{p1,k} - V_{p2,k} Vk=OCV(SOCk)−R0Ik−Vp1,k−Vp2,k

其中,R0R_0R0是欧姆内阻,OCV(SOCk)\text{OCV}(\text{SOC}_k)OCV(SOCk)是SOC对应的开路电压。

趣味解读 这个公式就是电池的"电压账本"------OCV(SOCk)OCV(SOC_k)OCV(SOCk)是电池的"总家底"(没干活时的电压),然后要扣掉三笔钱:

  1. R0IkR_0 I_kR0Ik:电池内部的"过路费"------电流越大,过路费越高(就像堵车时油耗增加);
  2. Vp1,kV_{p1,k}Vp1,k和Vp2,kV_{p2,k}Vp2,k:电池的"疲劳费"------充放电太猛,电池内部反应跟不上,会临时消耗一点电压,等静置后又会恢复。

2. UKF的Sigma点生成公式

χk−1(i)={x^k−1∣k−1,i=0x^k−1∣k−1+(n+λ)Pk−1∣k−1i,i=1,2,...,nx^k−1∣k−1−(n+λ)Pk−1∣k−1i,i=n+1,...,2n \chi_{k-1}^{(i)} = \begin{cases} \hat{\boldsymbol{x}}{k-1|k-1}, & i=0 \\ \hat{\boldsymbol{x}}{k-1|k-1} + \sqrt{(n+\lambda) \boldsymbol{P}{k-1|k-1}}i, & i=1,2,...,n \\ \hat{\boldsymbol{x}}{k-1|k-1} - \sqrt{(n+\lambda) \boldsymbol{P}{k-1|k-1}}_i, & i=n+1,...,2n \end{cases} χk−1(i)=⎩ ⎨ ⎧x^k−1∣k−1,x^k−1∣k−1+(n+λ)Pk−1∣k−1 i,x^k−1∣k−1−(n+λ)Pk−1∣k−1 i,i=0i=1,2,...,ni=n+1,...,2n

解读 这就是UKF的"撒点魔法"!把它想象成给你的猜测画"误差圈"

  • x^k−1∣k−1\hat{\boldsymbol{x}}_{k-1|k-1}x^k−1∣k−1:你当前最靠谱的猜测(比如猜SOC=0.8);
  • Pk−1∣k−1\boldsymbol{P}_{k-1|k-1}Pk−1∣k−1:这个猜测的"靠谱程度"------P越大,误差圈越大,你越不确定;
  • (n+λ)P\sqrt{(n+\lambda)\boldsymbol{P}}(n+λ)P :把误差圈"拉成"坐标轴方向(就像把椭圆变成长方形);
  • 加和减这个值:在误差圈的每个方向上,前进一步、后退一步,再加上中心点,就得到了所有可能的"候选状态"。这些点就像你的"侦察兵",帮你试探所有可能的情况。

3. RUKF的鲁棒增益公式

Re,k=Pzz+γ−2 \boldsymbol{R}{e,k} = \boldsymbol{P}{zz} + \gamma^{-2} Re,k=Pzz+γ−2
Kk=PxzRe,k−1 \boldsymbol{K}k = \boldsymbol{P}{xz} \boldsymbol{R}_{e,k}^{-1} Kk=PxzRe,k−1

解读 这是给算法装的"安全气囊"!

  • 标准UKF的增益是K=Pxz/Pzz\boldsymbol{K}=\boldsymbol{P}{xz}/\boldsymbol{P}{zz}K=Pxz/Pzz------如果Pzz\boldsymbol{P}_{zz}Pzz很小(算法觉得传感器超准),K会变得超大,算法会"无脑相信"传感器;
  • γ−2\gamma^{-2}γ−2就是强行加的"安全垫" ------哪怕`Pzz\boldsymbol{P}_{zz}Pzz趋近于0,分母也不会变成0,K就不会失控。
  • 你可以把γ\gammaγ当成"谨慎开关":γ\gammaγ越小→安全垫越厚→算法越谨慎,不信邪;γ\gammaγ越大→安全垫越薄→算法越"大胆",接近标准UKF。

4. PO算法的适应度函数

fitness(Q)=1N∑i=1Nεi2 \text{fitness}(\boldsymbol{Q}) = \frac{1}{N} \sum_{i=1}^N \varepsilon_i^2 fitness(Q)=N1i=1∑Nεi2

其中,εi=Vmeas,i−Vpred,i\varepsilon_i = V_{meas,i} - V_{pred,i}εi=Vmeas,i−Vpred,i 是新息(预测电压和实测电压的差值)。

解读 PO算法的"鹦鹉们"其实在玩打分游戏

  • Q\boldsymbol{Q}Q:鹦鹉们要猜的"答案"(过程噪声);
  • εi\varepsilon_iεi:用这个Q跑算法,算出来的电压和真实电压的"差距"------差距越小,分数越高;
  • 适应度函数:计算所有差距的"平均平方值"------这个值越小,说明Q越靠谱。鹦鹉们的目标就是找到能让这个分数最低的Q,就像找能让考试得分最高的复习方法!
代码实现(状态更新函数)
python 复制代码
def state_update(soc_prev, v1_prev, v2_prev, I, dt, eta, Cn, R1, R2, C1, C2):
    """
    锂电池二阶RC模型状态更新
    :param soc_prev: 上一时刻SOC
    :param v1_prev: 上一时刻极化电压Vp1
    :param v2_prev: 上一时刻极化电压Vp2
    :param I: 充放电电流(放电+,充电-)
    :param dt: 时间步长(s)
    :param eta: 库伦效率
    :param Cn: 电池额定容量(Ah)
    :param R1/R2/C1/C2: RC模型参数
    :return: 下一时刻soc, v1, v2
    """
    # SOC更新
    soc = soc_prev - eta * I * dt / (Cn * 3600)
    soc = np.clip(soc, 0.0, 1.0)  # SOC限幅0~1
    
    # 极化电压更新
    tau1 = R1 * C1
    v1 = v1_prev * np.exp(-dt / tau1) + R1 * I * (1 - np.exp(-dt / tau1))
    
    tau2 = R2 * C2
    v2 = v2_prev * np.exp(-dt / tau2) + R2 * I * (1 - np.exp(-dt / tau2))
    
    return soc, v1, v2

5. PO-RUKF的核心代码片段(增益计算)

python 复制代码
def rukf_update(x_pri, P_pri, sigma_x, V_meas, gamma, R):
    """
    RUKF的观测更新环节,计算鲁棒卡尔曼增益并修正状态
    :param x_pri: 先验状态估计
    :param P_pri: 先验协方差
    :param sigma_x: 先验Sigma点
    :param V_meas: 实测端电压
    :param gamma: H∞鲁棒参数
    :param R: 量测噪声协方差
    :return: 后验状态估计x_post,后验协方差P_post
    """
    n = x_pri.shape[0]
    lam = alpha**2 * (n + kappa) - n
    Wm = np.zeros(2*n+1)
    Wc = np.zeros(2*n+1)
    Wm[0] = lam / (n + lam)
    Wc[0] = lam / (n + lam) + (1 - alpha**2 + beta)
    for i in range(1, 2*n+1):
        Wm[i] = 1 / (2 * (n + lam))
        Wc[i] = 1 / (2 * (n + lam))
    
    # 计算预测观测点和先验观测
    sigma_v = np.array([ocv(soc) - R0*I - v1 - v2 for soc, v1, v2 in sigma_x])
    v_pri = np.sum(Wm * sigma_v)
    
    # 计算Pzz, Pxz
    Pzz = np.sum(Wc * (sigma_v - v_pri)[:, np.newaxis] * (sigma_v - v_pri)[:, np.newaxis].T) + R
    Pxz = np.sum(Wc * (sigma_x - x_pri)[:, np.newaxis] * (sigma_v - v_pri)[:, np.newaxis].T)
    
    # 鲁棒修正:计算Re
    Re = Pzz + 1 / (gamma ** 2)
    # 计算鲁棒卡尔曼增益
    K = Pxz / Re
    # 状态修正
    x_post = x_pri + K * (V_meas - v_pri)
    # 协方差修正
    P_post = P_pri - K * Re * K.T
    
    return x_post, P_post
相关推荐
94620164zwb52 小时前
应用设置模块 Cordova 与 OpenHarmony 混合开发实战
python
551只玄猫2 小时前
KNN算法基础 机器学习基础1 python人工智能
人工智能·python·算法·机器学习·机器学习算法·knn·knn算法
charliejohn2 小时前
计算机考研 408 数据结构 哈夫曼
数据结构·考研·算法
POLITE32 小时前
Leetcode 41.缺失的第一个正数 JavaScript (Day 7)
javascript·算法·leetcode
CodeAmaz2 小时前
一致性哈希与Redis哈希槽详解
redis·算法·哈希算法
POLITE33 小时前
Leetcode 42.接雨水 JavaScript (Day 3)
javascript·算法·leetcode
Tim_103 小时前
【算法专题训练】36、前缀树路径和
算法
好易学·数据结构3 小时前
可视化图解算法76:最大子数组和
数据结构·算法·leetcode·面试·动态规划·力扣·笔试