MATLAB中文论坛 文章 技术专栏 物理建模 查看内容

Simulink 的电池建模与仿真模型合集 (二)深度学习

2022-9-9 17:47| 发布者: MsM18| 查看: 376| 评论: 0|原作者: 柚籽

摘要: 如何用 Deep Learning 工具箱方便地实现我们设计好的深度学习网络,并利用数据进行模型训练,最后把训练好的网络模型集成到 Simulink 模型里去。
重要的信息放前面

如何用 Deep Learning 工具箱方便地实现我们设计好的深度学习网络,并利用数据进行模型训练,最后把训练好的网络模型集成到 Simulink 模型里去。
完整的数据来源于 MacMaster University :LG 18650HG2 Li-ion Battery Data and Example Deep Neural Network xEV SOC Estimator Script,下载完数据真是忍不住感慨,这真是大学!共享信息,感谢贡献者:

MacMaster University 和 MathWorks 合作的一个网络视频:Battery State Estimation Using Deep Learning
MathWorks官方还提供了一套视频:How to Estimate Battery State of Charge Using Deep Learning

关于 AI
用数据来训练出电池模型,这两年很火热。
相比于“土掉渣”的等效电路模型,AI、大数据、深度学习这些词感觉挺高大上的,更容易发论文吧。毕竟AI也逐渐脱离“人工智障”的状态,机器学习的网络也越来越深度学习了。车牌识别作为热门毕业论文选题已经变成时代的眼泪,早已装备在各大停车场。

图片来自于 MathWorks,以下很多
而且,基于云计算、GPU的使用也极大了加速了训练效率。Simulink 也提供了各种代码生成工具,能很轻松的把训练好的模型生成代码发布到 CPU,GPU,ECU

小度、小爱,Siri(还有谁)的智商也越来越高;刷脸支付也从十几年前的玩笑话变成了现实;就只是无人自动驾驶的车,我从不敢坐到依然不敢坐,道远。

一般来说 AI 训练的结果都只是作为一个完整算法的一部分,比如下图的Vision Detector。

训练出来的模型完美的和其它部分配合,完整一个有意义的工作。

ADAS基于AI训练的视觉处模型
作为训练模型,那显然的准备数据,大量的数据
至于剩下的,有 MATLAB软件帮忙。


电池 BMS 闭环控制模型
用数据训练的方式来生成一个 SOC 预测模型,我现在也是在观望中。当然,未来是大数据的时代,学习未来的一个小工具和思想,也不是什么坏事。
用数据来训练电池 SOC 预测模型
  • 左侧是BMS,未来要生成嵌入式代码的;
  • 右侧是被控对象模型,用来完成闭环回路用于测试BMS的工具人;
既然右侧有了一个很精确的电池模型作为被控对象,而左侧也正好需要一个电池模型来预测SOC,为什么不直接把右侧的模型放进左侧呢?
  • 这的确是一个好主意;
  • 但有没有另一种可能,右侧的这个模型实现方式有一点点复杂,并不能生成嵌入式代码变成控制算法的一部分;
  • 用数据训练SOC估算模型,只是很多种方法中的一种,并不是唯一方法;
  • 解决问题的武器多了,保不齐哪天能用上;


SOC
既然要估算SOC,那什么是 SOC 呢?
SOC 原本是想表达电池剩下的能量和它充满的时候的能量比,类似于汽车油箱里还剩几格油。可惜的是,SOC是无法直接测量的,只能借助其它的可测量量来间接计算


电池以及训练模型
这是实验用的电池。

MacMaster University
以及各种因素下的电池实验,我发现光是要聊这个 MacMaster University 提供的所有数据实验就要费很大篇幅,因为内容太多了,于是略过。毕竟不影响后续的操作。

MacMaster University
SOC 和 电流有关系;另外 SOC 也和外电压有关;Q和温度也有关系。
所选取的输入信号,电压、电流、温度。移动平均值作为辅助信号添加。
其实通常在深度学习里,这部分是最难的地方,如何找到合适的特征量,如何设计合适的网络结构;


用到的数据说明
下载源提供了各种在不同的温度、不同的放电倍率、各种驾驶工况/测试工况数据,包括以下处理后的数据:

McMaster University:https://data.mendeley.com/datasets/cp3473x7xv/3
每个 mat 文件数据内容都一样,文件导入后:
  • X是输入信号;
  • Y是SOC
  • 这个数据量有点狠……

参考这里的说明,X的各行的物理含义。但实际上要把曲线画出来观察了才更清楚:

导入Train文件夹下的数据,根据上面的提示标注各个数据。
load TRAIN_LGHG2@n10degC_to_25degC_Norm_5Inputs   % 查看其它文件修改文件名即可
X = X';
Y = Y';
在后面训练模型时,数据需要以列向量的方式导入,而mat文件里每个信号是以行的方式存的,所以代码里可以看到做了个转置运算。

从刚刚截图显示的数据值就有些奇怪,画出来就更清楚了。
  • 这里的数据都是归一化后的数据;
  • 至于说为什么归一化,或许就是让各个输入因素的训练权重都是归一化的,为了提高训练的效果;
  • 因为后面选择的回归模型,会建议输入进行归一化。

归一化后的数据
%数据顺序: V,I,T,Vavg,Iavg
% 按归一化比例 还原原值,只是为了画图用
load LGHG2_Min_Max_25degC_to_n10degC  % 假设这里的数据都是统一的归一化数据边界映射值
clear X_original
for i = 1:3
    X_original(:,i) = (X_MAX(i)-X_MIN(i)).*X(:,i)+X_MIN(i);
    X_original(:,3) =  X(:,3)  + 273.15; % convert to K
end
X_original(:,4) = (X_MAX(1)-X_MIN(1)).*X(:,4)+X_MIN(1);
X_original(:,5) = (X_MAX(2)-X_MIN(2)).*X(:,5)+X_MIN(2);

Voltage = X_original(:,1);
Current = X_original(:,2); % 放电为正
Temperature =X_original(:,3);
Mov_Voltage  = X_original(:,4);
Move_Current = X_original(:,5);
SOC = Y';
目前,其实并没有时间信息,所以直接画信号向量。不过MacMaster University 的说句说明里有提到,信号按照 1Hz 进行了重采样,但这个训练模型其实是不使用带时间戳的信号的。
figure
yyaxis left
plot(Current);
hold on
yyaxis right
plot(Voltage);
legend({'Current','Voltage'})
xlabel('Index');

放大部分,可以看清楚些,当前导入的实验数据就是:
  1. 先静置一段时间,稍稍恒流冲了些电(蓝色),电压上升(红色);
  2. 工况放电,电压逐渐降低;
  3. 停止放电;
  4. 静置一段时间后,恒流充电;
  5. 如此往复;

其实如果把抖动的部分细节再放大,会看到电流电压信号,其实是吻合的:

其它信号查看也是刚刚类似的代码:


A-B 的全部代码
先来看个简单版本的流程:实现网络-训练网络-验证。
% 导入数据
load 01_TEST_LGHG2@n10degC_Norm_(05_Inputs)
valX = X';
valY = Y';

load TRAIN_LGHG2@n10degC_to_25degC_Norm_5Inputs
X = X';
Y = Y';

%定义层
numFeatures = 5;       % Number of inputs features (V, I, Temp, V_avg, I_avg)
numHiddenUnits = 64;   % Number of hidden units 'N', where each hidden unit for FNN represents a Neuron.
numResponses = 1;      % Number of outputs (SOC)

layers = [...
    featureInputLayer(numFeatures,"Normalization","zerocenter")
    fullyConnectedLayer(numHiddenUnits)
    reluLayer 
    fullyConnectedLayer(numResponses)
    reluLayer
    regressionLayer];
% 定义
options = trainingOptions("adam", ...                    % Adam optimizer
    MaxEpochs = 10,...
    ExecutionEnvironment = "auto", ...
    InitialLearnRate = 0.1, ...
    Shuffle = "every-epoch",...
    Verbose = false,...
    ValidationFrequency = 10, ...
    MiniBatchSize = 1e5, ...
    Plots = "training-progress", ... 
    ValidationData = {valX,valY});
% 训练网络
tic
net = trainNetwork(X,Y,layers,options);
toc
% 测试结果
%load net.mat
load 01_TEST_LGHG2@n10degC_Norm_(05_Inputs)
% S = load(fullfile(testFolder,testFiles(1).name));
testX = X';
testY = Y';

Y_Pred = predict(net,testX);

residual = testY - Y_Pred; 
RMSE = sqrt(mean(residual).^2);
% figure
% plot(residual./testY,title('err%'));
figure
plot(residual); title("RMSE: "+RMSE);

figure
plot(testY); 
hold on;
plot(Y_Pred);  hold off
legend("SOC from lab","Data estimated with NN");

A. Deep Network Designer
有了数据之后,就到了我惶恐的深度学习网络环节,好在,有这个 App 可以帮我。我发实现用这个App,实现 Network 就跟Simulink搭框图一样简单,前提是如果已经有了设计。
从刚刚代码段的 layers 定义可以看到是个6层的网络,现在就用 App 的方式来定义它。
deepNetworkDesigner % 打开App界面


1.Input Layer
输入层,将各种类型的数据输入网络,并做归一化处理。
这里的输入数据是和时间轴无关的数值向量,这里选用了 featureInputLayer。按照代码里的方式定义输入层,其它保持默认。
featureInputLayer(numFeatures,"Normalization","zerocenter")

featureInputLayer
顺便看了下输入层的类型,其它的图像或者时序输入类型的数据。


2.Fully connected layer
A fully connected layer multiplies the input by a weight matrix W and then adds a bias vector b.
layer = fullyConnectedLayer(outputSize)
fullyConnectedLayer(numHiddenUnits)
按照说明设置 outputSize = numHiddenUnits。只是这没有指定额外的权重矩阵和偏置向量。


3. Relu Layner
Rectified Linear Unit (ReLU) layer
A ReLU layer performs a threshold operation to each element of the input, where any value less than zero is set to zero. This operation is equivalent to

reluLayer,它就是做了一个预定义的限值处理,且不需要设置任何参数。


4. 接着又是一层 Fully connected layer
fullyConnectedLayer(numResponses)
只不过此时是要把输出维度转为1,因为这个网络只有一个响应就是SOC。


5. Relu Layner 再次做了一个限值处理
6. OUTPUT
A regression layer computes the half-mean-squared-error loss for regression tasks.

这里的R是输出响应的个数。


B. 训练设置
options = trainingOptions("adam", ...                    % Adam optimizer
    MaxEpochs = 10,...
    ExecutionEnvironment = "auto", ...
    InitialLearnRate = 0.1, ...
    Shuffle = "every-epoch",...
    Verbose = false,...
    ValidationFrequency = 10, ...
    MiniBatchSize = 1e5, ...
    Plots = "training-progress", ... 
    ValidationData = {valX,valY});
app 本身是可以设置训练配置的,这里其实还有三个配置项和界面是对不上的:
Plots = "training-progress", ValidationData = {valX,valY}); Verbose = false,...

但待到导入数据时,才发现问题的严重性,需要定义Datastore,查了一下Datastore,觉得还挺麻烦的。
而且Datastore主要适用于海量文件需要参与数据训练,可节约大量资源,但这里作为一个案例,并不会涉及到这些,因此此刻表示放弃。只使用 App的网络设计功能。

于是导出刚刚用App定义好的网络为 Layer_my


训练
调用命令 trainNetwork 开始训练后,由于设置了Plots = "training-progress", 所以下图实际上是个动态更新过程。
net = trainNetwork(X,Y,layers,options);
下面是结果图。
  • RMSE: root mean square error
  • 给我的感觉是每一个 Epoch是全数据集每一轮,而每一个Iteration是训练每一组数据;
  • 新版本的界面右上角有一个【停止】的按钮可以随时停止。

训练结束后,右上角得到最终的精度。
以及停止迭代的原因是:Max epochs completed,之前设置了 last-iteration,达到了定义的最大迭代10轮。

  • 白灰相间的是每一轮Epoch的结果;
  • 而一轮内每一个点则对应 Iteration,它对应 Mini-batch RMSE;
  • 因为指定了 validation数据,所以也画出了 Validation 的结果曲线;

调用刚刚用 app 定义的网络来训练:
net = trainNetwork(X,Y,layers_my,options);

差不多。

验证
加载 Test 文件夹下的独立实验数据来进行验证,predict 函数调用训练好的网络。
load 01_TEST_LGHG2@n10degC_Norm_(05_Inputs)
% S = load(fullfile(testFolder,testFiles(1).name));
testX = X';
testY = Y';

Y_Pred = predict(net,testX);

residual = testY - Y_Pred; 
RMSE = sqrt(mean(residual).^2);
% figure
% plot(residual./testY,title('err%'));
figure
plot(residual); title("RMSE: "+RMSE)

小讨论
如果只是单纯的看RMSE的数据范围,感觉还挺好的。

但看到两条 SOC 对比曲线,就觉得有点莫名的怪异。
NN预估的 SOC 抖动的很厉害,于是把输入X也一起画了一下。

如果把曲线放大,上面两条抖动的很厉害的是电流和电压,但从趋势判断并不是无意义的噪音,而是正常的放电波形得到对应的电压。
这里 NN 训练的结果,怎么说呢,吻合度可以,但就觉得哪里不对劲,放大了这种抖动。

C. 放入 Simulink
验证数据

训练好的网络:net.mat
用于对比 EKF 模型的:R和ocv
XT 是实验数据,一共四组,对应不同的电芯温度,但实际上只能测试到电池盒的温度,并不能真的测试到电芯温度。
  • % Load inputs and outputs (true observered values) at different temperatures
  • % XT{1,1} - data collected at -10deg
  • % XT{2,1} - data collected at 0deg
  • % XT{3,1} - data collected at 10deg
  • % XT{4,1} - data collected at 25deg
  • %数据顺序: V,I,T,Vavg,Iavg
如之前所说,这里的数据是归一化的,不过可以借助 Max-Min_vectors 给的范围给反算回来。
  • Max-Min_vectors 数据顺序: V,I,T
%% 用于信号观察
load("XT.mat");
load('Max_Min_vectors.mat');
ts = 1; 
TempIndex = 1;% 取第一个温度数据
timeVector = (0:ts:length(XT{TempIndex}.X)-1)';
%数据顺序: V,I,T,Vavg,Iavg
X = XT{TempIndex}.X';
% 按归一化比例 还原原值
clear X_original
for i = 1:3
    X_original(:,i) = (X_MAX(i)-X_MIN(i)).*X(:,i)+X_MIN(i);
    X_original(:,3) =  X(:,3)  + 273.15; % convert to K
end
X_original(:,4) = (X_MAX(1)-X_MIN(1)).*X(:,4)+X_MIN(1);
X_original(:,5) = (X_MAX(2)-X_MIN(2)).*X(:,5)+X_MIN(2);

Voltage = X_original(:,1);
Current = X_original(:,2); % 放电为正
Temperature =X_original(:,3);
Mov_Voltage  = X_original(:,4);
Move_Current = X_original(:,5);
SOC = XT{TempIndex,1}.Y';

查看数据,画图
figure
yyaxis left
plot(timeVector,Current);
hold on
yyaxis right
plot(timeVector,Voltage);
legend({'Current','Voltage'})
xlabel('Time');

从曲线观察上来看,似乎是个:
  • 第一个抖动区域,驾驶循环不断放电又能量回收的阶段,电压逐渐降低;
  • 接着就充电,然后又驾驶循环放电;
放大局部
  • 电流和电压变化是吻合的;

进行了移动平均值处理后的数据:
figure
yyaxis left
plot(timeVector,Move_Current);
hold on
yyaxis right
plot(timeVector,Mov_Voltage);
legend({'Mov Current','Mov Voltage'})
xlabel('Time');

感觉消失了一些动态特性。


温度和SOC,
figure
yyaxis left
plot(timeVector,Temperature);
hold on
yyaxis right
plot(timeVector,SOC);
legend({'Temperature','SOC'})
xlabel('Time');
温度如果看坐标轴的话,其实基本上没怎么变化。只是在充电的时候稍微上升了一点点。


NN模型,Deep Learning Toolbox
Deep Learning Toolbox 提供了模块,可以很方便的把训练好的网路导入到 Simulink。

因为FNN的训练输入时基于归一化处理后的数据,所以这里的 nnInput/nnOutput 如果是归一化数据的话,那么输入给EKF的数据,就需要反算回原数据。

把训练好的网络模型添加到 Simulink,相对容易,指定Network的mat文件就行了。


EKF 对比,Control System Toolbox
如果没有对应的参数,把这部分忽略,直接和实验数据对比就行了。
Control System Toolbox 提供了 EKF 模块。

作为对比的EKF模型,直接提供了对应的查表表格,不过能看的出来,模型不考虑RC环节。

输出函数相对简单,的确是没有RC环节。

状态量,直接根据电流算出dSOC,然后累加。


仿真结果
还行


系统级模型验证
回到最初的模型:

系统也提供了一个预先训练好的模型作为演示,流程大致和以前应该一样。

把训练的模型替换进去,这里添加进去作为对比。


小讨论
基于之前的网络的训练方式:
  • 是基于归一化数据训练的,所以这里的数据也做了归一化处理,才输入到NN模块;
  • 添加两个移动平均值信号;
  • 信号顺序和之前训练保持一致,V,T,T,V_ave,I_avg
但这里有个问题,真实的模型中如何能实现这种归一化处理?除非所有的量都按照了许可工况范围来进行归一化,而不是根据实验曲线的最大最小值,那么才能提前知道归一化映射边界Max,Min。


从这个闭环模型运行的结果来看,NN(神经网络)和EKF(扩展卡尔曼滤波)的结果和真值吻合得较好,相比之下CC(安时积分)就差了很多。
但这里有个让我很在意得信息,在案例提供的模型,右侧 true信号的来源,是这里的电池模型的仿真结果。让我不禁考虑一个问题,这里的神经网络是用仿真数据来训练的?
  • 用仿真数据来训练的大前提是,当前这个电池模型“认可”为可以表达所需要的精度;
  • 这里训练电池模型方法,和原右侧的建模方法并不是一个竞争关系,而是一个互补关系;
  • 那也就意味着,这里训练出来的模型目标是为原右侧电池模型生成一个“可嵌入”的替代;
另外就是,这种基于数据训练的模型,如何应对老化?

喜欢看视频的可以看视频:


其它
所以,其实目前为止,只是大概走了一下流程。真正有技术含量的地方,其实是如何设计一个更好的深度学习网络。软件工具的作用,只是更好更方便更快的实现和测试你的想法。

Simulink也支持导入TensorFlow训练好的网络
安装插件
导入之前,需要安装支持包。点击 提示的 Add-On Explorer 就会自动打开下载页面,按照步骤安装就行了。

但有时候也会遇到这样,那可能就需要联系一下官方技术支持。

或者是检查一下自己的网络连接,比如我这里就是因为连着公司的VPN导致的,断开后很快就安装好了。


导入网络
importTensorFlowNetwork 导入网络,然而我竟然遇到了报错。
暂时未能解决是因为什么引起的,大概是Add-On没装好的缘故。


扯一句:代数环
我发现我看模型总有奇怪(和主线剧情无关)的关注点。
有时候,我们可能会遇到一些很诡异的问题。比如说明明模型之前跑起来很正常,但就只是把某个部分变成Model Reference 了就会报代数环的错误。那没办法 Model Reference 是个独立的单元,Solve在求解的时候,不会再把它内部模块掏出来和其它模块一起排序求解。就会发生之前明明没问题,之后“模型似乎什么都没改”却运行出问题了。

如果遇到代数换警告信息:
  • Simulink.BlockDiagram.getAlgebraicLoops(bdroot) 可以用来检查代数环发生在哪;
  • 有时候 Simulink 只是在输出窗口给一个黄色的警告,仿真还是能继续。但,如果出现代数环的部分 (比如这个代数环完全发生在BMS ECU模块内部) 是要生成代码的话,还是需要消除这个代数环警告;
  • 消除代数环,通常就是添加1/z的方式打断;
  • 除了算法本身搭的有问题之外,很多时候由于反馈或者耦合而不可避免;
  • 用刚才的命令检查,经常会发现黄了一大片,各种线路都被高亮了出来。这时候,在分支路线上添加1/z模块事倍功半,需要把1/z添加在主干道上。
下一节,再来讨论一下传统的等效电路建模方式
技术文章就是这么长,你们忍一下。
1

鲜花

握手

雷人

路过

鸡蛋

刚表态过的朋友 (1 人)

最新评论

基于 Simulink/Simscape 的动力总成建模与仿真 1

虚拟车辆,动力总成,Simulink,Simscape Driveline, SimDriveline,建模、仿真,无论怎么排列组合都好。这一个系列主要用物理建模的方式来搭。

Simulink 的电池建模与仿真模型合集(一)

这个系列主要讨论基于Simulink搭建电池模型的各种方法。如果用到具体的模型,会在对应的章节提供下载链接。

Simulink 的电池建模与仿真模型合集 (二)深度学习

如何用 Deep Learning 工具箱方便地实现我们设计好的深度学习网络,并利用数据进行模型训练,最后把训练好的网络模型集成到 Simulink 模型里去。

Simulink 的电池建模与仿真模型合集 (二)深度学习

如何用 Deep Learning 工具箱方便地实现我们设计好的深度学习网络,并利用数据进行模型训练,最后把训练好的网络模型集成到 Simulink 模型里去。

Simulink 的电池建模与仿真模型合集(一)

这个系列主要讨论基于Simulink搭建电池模型的各种方法。如果用到具体的模型,会在对应的章节提供下载链接。

基于 Simulink/Simscape 的动力总成建模与仿真 1

虚拟车辆,动力总成,Simulink,Simscape Driveline, SimDriveline,建模、仿真,无论怎么排列组合都好。这一个系列主要用物理建模的方式来搭。
关闭

站长推荐上一条 /5 下一条

返回顶部