资讯动态

锂电池二阶RC参数辨识:HPPC工况与递推贝叶斯算法Matlab实现

发布时间:2026/10/22 10:50:36 来源:尧图企业网站定制
做电池建模仿真的人十有八九都跟HPPC工况打过交道。我第一次跑二阶RC参数辨识时最困惑的不是模型公式而是为什么同样的电压曲线有的做法能一次拆出五个参数有的做法却算出负电容。后来把离线初值、递推贝叶斯更新、数据预处理这几个环节拆开看才发现坑全藏在细节里。这篇文章围绕“锂离子电池二阶RC参数辨识HPPC工况递推贝叶斯算法(RB)Matlab代码”这条主线展开说清楚三件事第一HPPC工况和二阶RC模型为什么是默认组合第二递推贝叶斯算法怎么把一组离线初值变成在线收敛的参数估计第三拿到代码之后怎么把自己的电池数据换进去以及哪些地方最容易翻车。适合正在做BMS算法、电池建模课程设计、或者刚接触参数辨识想快速上手的工程师和学生参考。1. HPPC工况与二阶RC模型为什么这对组合是参数辨识的默认起点1.1 二阶RC模型方程与离散化锂离子电池在恒流充放电和静置切换时端电压会出现“瞬跳两段指数恢复”的典型响应。瞬跳由欧姆内阻引起两段指数恢复分别对应电化学极化和浓差极化时间常数通常从几秒到几十秒不等。一阶RC模型只能表达一个时间常数在高倍率脉冲和宽SOC范围内往往拟合不足三阶以上模型虽然拟合精度更高但参数辨识的病态性明显增加工程上性价比反而不高。所以二阶RC是BMS领域最常见的选择。二阶RC模型的电气方程如下U_L(t) OCV(SOC) - U_1 - U_2 - R_0 I其中U_1、U_2是两个RC支路的极化电压满足dU_1/dt -U_1/(R_1 C_1) I/C_1dU_2/dt -U_2/(R_2 C_2) I/C_2离散化时常用零阶保持近似。设采样周期为Ts定义α_1 exp(-Ts/τ_1)α_2 exp(-Ts/τ_2)其中τ_1 R_1C_1τ_2 R_2C_2则递推式写成U_1(k) α_1 U_1(k-1) (1 - α_1) R_1 I(k)U_2(k) α_2 U_2(k-1) (1 - α_2) R_2 I(k)这个离散化看似简单却是后面所有参数辨识的基础。需要特别注意的是这里的U_1、U_2是状态量R_1、R_2、τ_1、τ_2是待辨识参数。参数辨识和状态估计在这里是耦合的不知道参数就算不准U_1、U_2不知道U_1、U_2又很难单独分离参数。后面会用“离线初值递推贝叶斯在线更新”的组合来解耦先把初值搞定再让在线算法在初值附近做动态修正。1.2 HPPC工况的设计逻辑HPPCHybrid Pulse Power Characterization的全称是混合脉冲功率特性测试基本套路是在某个SOC点先静置足够长时间然后施加一个短时恒流脉冲再静置再施加反向脉冲再静置。一个点测完调整SOC到下一个点重复整个流程。典型参数是1C或2C放电脉冲10s静置40s再充电脉冲10s静置40sSOC步长10%。脉冲宽度的选择不是拍脑袋定的。脉冲宽度需要能激发起快慢两个RC支路的动态如果脉冲只有1s慢时间常数基本没响应如果脉冲太长极化电压接近稳态静置段的辨识信息反而被压缩。10s脉冲对多数三元和磷酸铁锂电池来说能覆盖常用的快慢时间常数范围。静置时间则要足够长建议至少是慢时间常数τ_2的3~5倍否则静置段还没恢复到接近OCV长时支路的参数估计会明显偏小。HPPC能让参数辨识变得容易是因为它在每个SOC点提供了三种信息电流阶跃瞬间的电压突变量对应R_0静置段的电压恢复曲线对应RC支路的幅值和时间常数静置末端的电压直接读取OCV点。这三种信息在同一个脉冲段内就能全部拿到不需要额外做OCV测试或EIS阻抗测试。1.3 参数可辨识性的直观理解参数可辨识性说白了就是“给一条电压曲线能不能唯一确定一组参数”。二阶RC模型在数据充足时需要辨识R_0、R_1、R_2、τ_1、τ_2五个参数看起来自由度很高但HPPC数据天然把自由度拆开了R_0只影响阶跃瞬间的电压突变和RC动态几乎无关两个时间常数分别控制静置段恢复曲线的快慢段两个幅值由静置段各支路的初始极化电压决定。只要脉冲宽度和静置时间选得合适五个参数基本可以看成“解耦”的。反过来如果只用一段很短的动态工况数据比如随机跳变的城市工况R_0和RC支路的贡献经常混在一起辨识出来的参数相关性很高换个初值可能就得到另一组合理但不唯一的解。这也是我坚持推荐“HPPC初值递推贝叶斯微调”的原因——先用解耦性好的数据把参数锁在合理区间再用在线算法适应实际工况的变化。2. 数据预处理与离线初值辨识先把路走通再谈递推2.1 电流符号、重采样与脉冲片段提取拿到HPPC实验数据后第一件要统一的事是电流符号。建议约定“放电为正充电为负”因为绝大多数电池测试设备的原始定义就是这个方向代码里也能避免正负号混乱。如果数据里充电是正先做一次符号翻转。然后是采样时间。HPPC数据常见的采样率有1Hz、10Hz和不等间隔采样。递推算法要求等间隔时间序列如果采样间隔不一致建议先做线性重采样到统一时间轴。我的习惯是1Hz或10Hz重采样到1Hz因为HPPC本身是分钟级的工况1Hz足够还能平滑一部分噪声。片段提取可以自动化根据电流绝对值是否超过阈值来判断脉冲开始和结束。阈值一般取当前SOC点标称电流的50%比如1C脉冲就取0.5C作为门槛。每个HPPC周期包含四段静置、放电脉冲、静置、充电脉冲用电流正负号再区分放充电。建议用findchangepts或简单的diff检测电流跳变沿把各段起始点保存下来。2.2 R0分离用阶跃前后的外推避免虚高R_0的辨识思路很直接电流阶跃瞬间OCV和RC极化电压都来不及突变端电压的变化量只由欧姆内阻产生所以R_0 ΔU / ΔI。但实际采样时有个大坑。如果采样率只有1Hz阶跃后第一个采样点已经包含了部分RC响应直接用这一点算ΔU会把R_0高估。更稳的做法是阶跃前取最后3~5个点的均值作为U_before阶跃后取前3~5个点做一阶指数外推得到t0时刻的U_after。指数外推公式很简单假设响应按指数衰减用前几个点拟合一个初始斜率回推到阶跃发生的时刻。我实际测试过低频采样下不做外推R_0可能虚高10%~20%对外推后的在线辨识收敛过程会产生明显偏差。所以这个预处理步骤不要省。2.3 指数剥谱法同时拆出两个RC支路拿到静置段电压曲线后先减去该SOC点的OCV得到U_1和U_2的叠加响应。放电脉冲结束后极化电压为负值所以定义ΔV(t) V(t) - OCV且ΔV 0。剥谱法分两步。第一步取时间轴末段例如t 2τ_1估计值认为快时间常数支路已经衰减完剩下慢支路主导。对ln(-ΔV)做线性拟合斜率的负倒数就是τ_2截距的指数就是U_2(0)。第二步从原始ΔV中减去慢支路贡献再对剩余部分做线性拟合得到τ_1和U_1(0)。注意剥谱法并不完美它会受噪声和“两段时间常数不够分离”的影响。好在它只需要提供一个合理的初值后续可以用带约束的非线性最小二乘精调。精调的目标函数是静置段整体拟合误差用lsqcurvefit或fminsearch都可以。精调后得到R_1 |U_1(0)| / |I_pulse|C_1 τ_1/R_1R_2和C_2同理。这里的物理关系要理解清楚在放电脉冲持续10s接近结束时U_1已经趋近-R_1 I_pulseU_2趋近-R_2 I_pulse所以静置段开始时刻的极化电压幅值直接对应RC支路的电阻乘电流。这也是HPPC数据比任意动态工况更好辨识的根本原因。2.4 初值质量对递推贝叶斯的影响很多人以为递推算法有初值就行随便填一个合理的数也能收敛。对递推贝叶斯/EKF这类局部算法来说这个想法很危险。EKF的线性化是在当前状态估计点附近的泰勒展开初值如果离真值太远线性化误差大可能出现参数发散或者收敛到错误极值。特别是R_1、R_2、τ_1、τ_2之间的强相关性初值差一个数量级时滤波器可能花很长时间“爬”回真值段甚至把τ_1和τ_2互换。所以我强烈建议递推贝叶斯之前一定要先用HPPC静置段做离线拟合给出每个SOC点的参数初值。初值质量好的时候RB算法的收敛过程只是在初值附近的小幅修正初值差的时候算法会变成一场灾难。后面代码部分我会按这个流程组织。3. 递推贝叶斯辨识的正式建模从后验递推到EKF落地3.1 RB算法基本递推式与“参数随机游走”假设递推贝叶斯估计Recursive Bayesian Estimation的核心思想是不断用新的量测数据更新参数的后验概率分布。设参数向量为θ到k时刻为止的量测为y_1:k则预测步和更新步分别为p(θ_k | y_1:k-1) ∫ p(θ_k | θ_k-1) p(θ_k-1 | y_1:k-1) dθ_k-1p(θ_k | y_1:k) p(y_k | θ_k) p(θ_k | y_1:k-1) / p(y_k | y_1:k-1)如果状态转移和量测方程都是线性的、噪声都是高斯的这套递推式可以解析化为卡尔曼滤波。对我们这个参数辨识问题未知参数随时间缓慢变化因此设θ_k θ_k-1 w_k这就是“随机游走”假设。它允许参数有小幅漂移但不会像常参数最小二乘那样拒绝新信息。大量BMS文献说的“递推贝叶斯辨识”实现上多数等价于EKF或UKF。本文给出的Matlab代码用线性化EKF实现好处是代码量小、速度极快适合长数据批次处理。如果后续想进一步提高非线性响应精度可以在同一框架内换成无迹变换。3.2 状态空间改造把未知参数变成状态变量要进行在线辨识需要把待辨识参数作为状态变量量测方程仍然用端电压。定义状态向量θ [R_0; R_1; R_2; τ_1; τ_2]状态方程为θ_k θ_k-1 w_k这里的w_k是过程噪声协方差矩阵Q要专门设计。量测方程不能直接写成“端电压OCV-极化电压-R_0 I”这么简单因为U_1、U_2本身需要递推更新。在EKF的每次时间更新中先用上一时刻的参数计算α_1、α_2更新U_1、U_2再计算预测端电压。因此严格来说这是一个半隐式的递推结构也是代码里最需要仔细处理的部分。为了正定性也可以把状态参数换为对数形式比如log(R_0)、log(τ_1)等再做EKF。这样参数永远为正不会出现负内阻的荒谬结果。但线性化的雅可比会更绕。初学者建议先用线性参数边界投影每步更新后把参数约束到合理区间跑通流程后再升级。3.3 雅可比矩阵推导与一步更新流程EKF的雅可比矩阵C ∂V_pred / ∂θ。推导时只取一阶近似U_1、U_2的状态更新依赖上一时刻值。最终得到C_1 -IC_2 -(1 - α_1) IC_3 -(1 - α_2) IC_4 -α_1 Ts / τ_1² · (U_1_prev - R_1 I)C_5 -α_2 Ts / τ_2² · (U_2_prev - R_2 I)如果状态改成对数参数雅可比需要乘上相应的链式偏导。线性参数的形式已经够用把每个时刻的五个偏导凑成行向量C然后走标准EKF更新S C P C^T RK P C^T S^{-1}θ θ_prev K (U_measured - V_pred)P (I - K C) P_prev (I - K C)^T K R K^T Q这里协方差更新用了Joseph形式它比简单形式更稳定即便K计算有舍入误差P也能保持半正定。3.4 Q/R噪声方差的取值经验EKF里两个噪声方差矩阵取值直接影响收敛速度和稳定。R是量测电压噪声方差可以从传感器精度和实验数据噪声水平推算。如果电压噪声标准差是5mVR就取(0.005)²。不要为了追求滤波平滑而把R设得特别大那样参数更新会非常迟钝。Q是参数随机游走的协方差矩阵代表“你相信参数会漂移多快”。Q取太小参数收敛后几乎冻结Q取太大参数被量测噪声带着乱跑。我的经验是先设Q R × 1e-6 × I再看参数收敛后是否还有持续抖动如果抖动明显但幅度可接受说明Q合适如果需要更强跟踪能力可以适当增大τ_1、τ_2对应的Q项因为时间常数随温度和SOC漂移最明显。初值协方差P_0设大一点对角线取离线初值平方的10倍左右让滤波器在初期有较大的搜索自由度。我见过P_0设得太小的案例参数更新走了很久都没走出初值误差的阴影。4. Matlab实现与数据替换拿到代码后怎么改成自己的电池4.1 代码文件结构与数据接口约定整套代码推荐按以下目录组织逻辑清晰、好复用main_batch.m主脚本负责读数据、循环调用各模块、输出结果图表extract_pulse.m从HPPC数据中提取脉冲区间fit_offline.m对单个SOC点的静置段做离线参数拟合rb_update.m递推贝叶斯/EKF单步更新函数rb_run.m对完整数据集跑在线辨识gen_test_data.m生成一组已知参数的仿真HPPC数据用于验证代码数据接口约定为统一的表格格式至少三列time_s、current_A、voltage_V电流方向统一为放电为正。如果还有SOC列也可以一并读入没有SOC列则用安时积分估算各点SOC。所有HPPC数据都按这个格式整理后main_batch.m只需要修改文件名、采样周期、各SOC点OCV表其他都不用动。4.2 离线初值辨识核心代码下面给出一段剥离精调的核心示意代码实际使用时可以放进fit_offline.m。假设输入是单个SOC点的静置段时间向量t_r和电压向量v_r以及脉冲电流幅值I_pulse、OCV值ocv。function [R0, R1, R2, C1, C2] fit_offline(t_r, v_r, I_pulse, ocv, Ts) % 去除OCV得到负的极化电压叠加 dv v_r - ocv; dv(abs(dv) 1e-8) 1e-8; % 末段线性拟合得到慢时间常数 n length(dv); n_slow max(3, round(n*0.4)); % 取末段40% t_slow t_r(end-n_slow1:end) - t_r(1); y_slow log(-dv(end-n_slow1:end)); p_slow polyfit(t_slow, y_slow, 1); tau2 -1/p_slow(1); U2_0 -exp(p_slow(2)); % 减掉慢支路在初始段拟合快时间常数 t_full t_r - t_r(1); res dv - U2_0 * exp(-t_full / tau2); n_fast max(3, round(n*0.25)); % 取前25% res res(1:n_fast); res(res 0) -1e-8; t_fast t_full(1:n_fast); p_fast polyfit(t_fast, log(-res), 1); tau1 -1/p_fast(1); U1_0 -exp(p_fast(2)); % 由幅值恢复电阻和电容 R1 abs(U1_0 / I_pulse); R2 abs(U2_0 / I_pulse); C1 tau1 / R1; C2 tau2 / R2; R0 0; % 由阶跃段另行计算不在本函数中 end注意剥谱拟合容易受噪声影响所以建议精调环节里用lsqcurvefit把tau1,tau2,U1_0,U2_0整体优化一遍。精调目标函数就是把上面静置段的指数叠加模板套回去让拟合残差平方和最小。4.3 RB/EKF在线辨识核心代码rb_update.m是整条代码链里最核心的文件。它的输入是上一时刻的参数、协方差、极化电压以及当前时刻的电流、端电压、OCV。函数内部完成极化电压递推、雅可比计算、EKF更新和边界约束。function [theta, P, U_p] rb_update(theta, P, U_p, I, Vt, OCV, Ts, Q, R) % theta [R0; R1; R2; tau1; tau2] tau1 max(theta(4), 0.5); tau2 max(theta(5), 1.0); a1 exp(-Ts / tau1); a2 exp(-Ts / tau2); % 用上一时刻参数更新极化电压 U1 a1 * U_p(1) (1 - a1) * theta(2) * I; U2 a2 * U_p(2) (1 - a2) * theta(3) * I; % 预测端电压 V_pred OCV - U1 - U2 - theta(1) * I; % 雅可比行向量 C dV/dtheta C zeros(1, 5); C(1) -I; C(2) -(1 - a1) * I; C(3) -(1 - a2) * I; C(4) -(a1 * Ts / tau1^2) * (U_p(1) - theta(2) * I); C(5) -(a2 * Ts / tau2^2) * (U_p(2) - theta(3) * I); % EKF更新 S C * P * C R; K P * C / S; innov Vt - V_pred; theta theta K * innov; P (eye(5) - K*C) * P * (eye(5) - K*C) K*R*K Q; P 0.5 * (P P); U_p [U1; U2]; % 边界投影防止参数物理离谱 theta(1) max(min(theta(1), 1e-2), 1e-4); % R0: 0.1mOhm~10mOhm theta(2) max(min(theta(2), 2e-2), 1e-4); % R1 theta(3) max(min(theta(3), 5e-2), 1e-4); % R2 theta(4) max(min(theta(4), 100), 0.5); % tau1: 0.5~100s theta(5) max(min(theta(5), 500), 1); % tau2: 1~500s end这个函数每一步都做了边界投影。实际数据里R_0辨识经常出现瞬时跳变如果不做约束误差会通过协方差传播污染其他参数。投影不是最优做法但胜在稳、适合初学者。想更精细就改成对数参数化让参数天然保持正数。4.4 用自己的HPPC数据替换三步到位拿到别人的代码第一件事不是跑而是先检查数据格式和电流符号。我建议按三步操作把自己的实验数据整理成time_s, current_A, voltage_V三列的CSV或Excel用readtable读入。如果测试设备导出的电流方向是充电为正统一乘-1。在main_batch.m中设置Ts为实际采样周期。如果数据不是等间隔先插值到等间隔。设置各SOC点的OCV表优先用自己的静置末端电压不要套用网上别人电池的OCV值。先跑gen_test_data.m生成一组已知参数的仿真数据验证代码流程无误后再把数据路径改成自己的HPPC文件。最后比较输出参数随SOC的变化曲线确认没有异常跳变。这里特别强调OCV表。很多人直接忽略OCV或随便用一个不准确的开路电压值结果端电压预测误差全部灌进参数更新里。HPPC数据本身就有静置末端电压直接用实验静置段的末端平均值作为每个SOC点的OCV是最可靠的做法。5. 结果验证与参数判读拟合误差、收敛性和鲁棒性5.1 端电压拟合评估指标递推贝叶斯跑完后把辨识得到的参数代回模型用同一个电流序列重算端电压和实测端电压对比。常用指标有三个均方根误差RMSE、最大绝对误差MAE、相对误差。RMSE是整体拟合精度的核心指标对BMS应用来说端电压拟合RMSE能压到10mV以内就算不错。需要注意区分训练误差和验证误差。直接用同一段数据计算误差通常会偏乐观因为EKF的参数更新是“看着这段电压调的”。如果要做严谨验证应该把数据切成两半前半段做参数辨识后半段代入模型计算后半段端电压预测误差。5.2 参数收敛过程怎么看参数收敛过程比最终的误差数字更能说明问题。看R_0的收敛轨迹运行开始时参数从初值附近开始前几百秒内会有较大波动然后逐步稳定。如果收敛曲线在真值附近反复震荡可能是Q/R比值太高如果参数长时间单调缓慢漂移说明Q太小、滤波器反应迟钝。时间常数τ_1和τ_2的收敛通常最慢因为它们和RC动态是相乘关系信息含量比R_0更“间接”。如果τ的初值偏差大收敛可能需要几百秒。不要期待所有参数在几个脉冲内就完全收敛这是很正常的现象。5.3 三种数据下的鲁棒性表现用三类数据做鲁棒性检查很有价值。第一类是仿真数据参数完全已知用来验证代码实现是否正确第二类是加噪仿真数据在端电压上叠加5mV左右的高斯噪声用来评估干扰下的参数抖动第三类是真实HPPC数据重点看参数是否随SOC平滑变化。我实测下来的经验是仿真无噪数据能让RMSE降到1mV以下加噪后RMSE上升到5~8mV参数曲线出现轻微抖动但均值仍在真值附近真实数据上如果OCV表准确、电流符号统一RMSE通常能控制在10mV以内参数随SOC的变化也有物理解释。如果某个SOC点的参数出现异常大跳变优先检查那个点附近的脉冲提取和数据拼接是否出了问题。6. 参数辨识里的坑与我的处理习惯6.1 采样点延迟导致R0虚高这是新手最容易踩的坑。有些电池测试设备的电流和电压采样不是完全同步电压信号还可能经过滤波导致电流阶跃发生后电压响应被“推迟”和“抹平”。如果直接用阶跃前后第一个采样点做差R_0会带上明显的RC分量。我的处理习惯是在每条脉冲前后各取若干点分别平均后做差值并辅以外推。如果条件允许优先选采样率不低于10Hz的设备。数据采集质量决定了R_0的真实性而后面的所有在线辨识都建立在这组初值上。6.2 RC时间常数过近导致参数退化当τ_1和τ_2相差不到3倍时指数剥谱法很难把两支路彻底分开拟合结果会随机偏向某一边有时候快支路吃掉慢支路有时候反过来。这种退化不是算法问题而是模型可辨识性本身的边界。如果一定要在时间常数接近的场景下辨识建议更换激励方式把HPPC脉冲宽度调整为能拉开响应速度的数值或者降低模型阶数用一阶RC模型。强行用二阶模型只会得到一组相关性极强的参数换一组初值可能得到同样好的拟合但完全不同的一组参数。6.3 电流噪声对极化电压递推的影响极化电压递推公式里直接使用电流信号如果电流噪声大U_1、U_2的高频抖动会传导到端电压预测和雅可比里使参数估计方差增大。有些情况下甚至能看到R_1和τ_1同步抖动看起来像两个参数“抱团”振荡。解决思路是预处理对电流做轻度滑动平均或Savitzky-Golay滤波滤掉高频噪声但不要过度平滑到改变脉冲的上升沿。上升沿对R_0辨识至关重要所以滤波窗口控制在几个采样点以内。6.4 低SOC段的在线抖动处理电池在低SOC段内阻极化明显增大参数值本身变化快EKF的Q如果还是按全SOC范围统一设定会出现过度跟踪或相位滞后。低SOC段的电压平台特性也会让OCV曲线变平导致同样的电压残差对SOC和参数都更敏感。我的做法是分段设置QSOC小于20%时把Q适当放大到中SOC段的1.5~2倍允许参数跟随真实变化同时把R设得略大抑制平台段的噪声放大效应。这样跑出来的低SOC参数曲线明显更稳定也不容易出现负内阻或参数穿边界的情况。如果你打算把这套代码用于后续的SOC估计算法比如扩展卡尔曼滤波或无迹卡尔曼滤波的联合估计建议把参数辨识的输出另存为一张“参数-SOC”插值表。在线运行时先查表得到初值再让RB算法做慢速修正整套方案会稳健很多。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑