草庐IT

从IC曲线提取特征,采用随机森林对电池SOH进行估计

白木木木木 2023-04-05 原文

从IC曲线提取特征,采用随机森林对电池SOH进行估计

什么是SOH?

本文采用电池容量衰减定义SOH,,给出的 SOH定义如下:

即电池当前容量处于电池额定容量的百分数。

随机森林

随机森林可以看我的文章https://blog.csdn.net/qq_51444641/article/details/122783753?spm=1001.2014.3001.5501

本个项目意义

如今电动汽车越来越普及,对于电池的要求也越来越高。现在已经有额定续航500~600km的电池,特斯拉所推出的Roadster额定续航高达1000km。
但是电池的实际容量往往是不能达到额定容量的,所以电车的实际续航也不可能达到额定的续航里程,此时对电池容量的准确估计便十分重要。对电池容量的准确估计能够使驾驶人准确判断何时应进行充电。同时在电池回收市场中,电池的实际容量对于电池的估价有重要的参考价值。

估计方法

不废话,开始正文。

特征提取

IC曲线即电池充放电过程中电流随电压的变化曲线。
由于不同电池的放电以及电池的每个周期放电情况由于使用人的习惯会产生很大的差异,所以我们并不采用放电周期的IC曲线。我们从充电周期的IC曲线中提取特征。将不同SOH下的充电周期IC曲线画出来进行一个渐进对比,我们可以看出曲线随着容量的下降变化明显。

接着,我们对不同电压位置的dQ/dV进行计算,具体计算公式:

画出dQ/dV随电压变化的曲线:

可以看出该曲线随容量的下降变化也较明显,尤其是峰值与峰值所处位置,于是我们将峰值与峰值所处位置作为选定的两个特征。紧接着根据文献
再结合实际情况,电动车的充电范围一般在40%~90%的范围内,于是我们将选取的电压范围限定在3.85v-4.09v。0.03v作为一个选取区间,每隔0.03v取一个dQ/dV值,将其作为特征F1-F8。而后将0.04v作为一个区间,对每个区间的充电时间进行测量,将其作为特征F9-F14,最后将峰值与峰值所处位置作为F15-F16,共提取16个特征。

利用xgboost实现SOH的预测

本个项目采用马里兰数据集,我们先将马里兰数据集中的每个Cell转换为csv文件,具体转换方法见我文章https://blog.csdn.net/qq_51444641/article/details/124974293?spm=1001.2014.3001.5501
转换完成后进行数据的读取

###Cell1提取文件名
strseq = []
for i in range(10):
    strseq.append('cyc'+'0'+str(i)+'00')

for i in range(10,15):
    strseq.append('cyc'+str(i)+'00')
strseq.append('cyc1600')
for i in range(18,34):
    strseq.append('cyc' + str(i) + '00')

for i in range(35,47):
    strseq.append('cyc' + str(i) + '00')
strseq.append('cyc4800')
for i in range(50,83):
    strseq.append('cyc' + str(i) + '00')

每个Cell类似,先将文件名全部输出,每个文件名对应每个Cell充电循环中的一个circle。

path = 'Cell1.'
path_q = '.C1ch.q.xlsx'

C1ch_q = []
for i in strseq:
    C1ch_q.append(pd.read_excel(path+i+path_q,header=None))
path_v = '.C1ch.v.xlsx'

C1ch_v = []
for i in strseq:
    C1ch_v.append(pd.read_excel(path+i+path_v,header=None))

path_t = '.C1ch.t.xlsx'
C1ch_t = []
for i in strseq:
    C1ch_t.append(pd.read_excel(path+i+path_t,header=None))

将每个Cell的数据分别输出到t(充电时间)、v(充电电压)、q(电量电荷)


###计算出每个充电周期的电池容量,即训练所用y值
C1ch_SOH = np.arange(len(C1ch_q))

for i in range(len(C1ch_q)):
    C1ch_SOH[i] = float(max(C1ch_q[i].values))
C1ch_SOH = C1ch_SOH.astype(float)
for i in range(1,len(C1ch_q)):
    C1ch_SOH[i] = C1ch_SOH[i]/C1ch_SOH[0]
C1ch_SOH[0] = C1ch_SOH[0]/C1ch_SOH[0]

C1ch_SOH = pd.DataFrame(C1ch_SOH)

y_Cell1 = C1ch_SOH

而后提取出每个电池电荷量中的电量最大值(基本是第一个circle的第一个电荷量值)作为每个circle的电池容量。将其输出到y_Cell中,每个Cell的操作一样。

而后将每个circle对应的容量都转化为百分数,即SOH。

##将电池剩余电量转化为百分比
C1ch_q_m = []
for q in C1ch_q:
    q = np.array(q,dtype=float)
    qmax = q[-1]

    for i in range(len(q)):
        q[i] = q[i]/qmax
    q = pd.DataFrame(q)
    C1ch_q_m.append(q)

而后将电池的产量与容量整理为一个Dataframe.

而后进行特征的提取

##选取电压范围:3.85~4.09,提取特征

highest_v = []
for i in range(len(C1ch_q)):
    highest_v.append(C1ch_v_q_1[i].iloc[-1,0])
min_highest_v = min(highest_v)

###提取不同电压段电压上升所需时间
feature1 = []
for i in range(len(C1ch_q)):
    count_1 = []
    for j in range(6):
        count_1.append(len(C1ch_v_q_1[i][(C1ch_v_q_1[i].v > 3.85 + 0.04*j) & (C1ch_v_q_1[i].v < (3.85+0.04*(j+1)) )]))

    feature1.append(count_1)

###提取IC曲线的特征
feature2 = []
for i in range(len(C1ch_q)):
    count_2 = []
    for j in range(8):
        d = C1ch_v_q_1[i][(C1ch_v_q_1[i].v > 3.85 + 0.03*j) & (C1ch_v_q_1[i].v < (3.85+0.03*(j+1) ) ) ]
        d_q_v = (d.iloc[-1 ,1]-d.iloc[0 ,1])/(d.iloc[-1 ,0]-d.iloc[0 ,0])
        count_2.append(d_q_v)
    max_d_q_v = max(count_2)
    max_d_q_v_id = count_2.index(max_d_q_v)
    print(max_d_q_v_id)
    count_2.append(max_d_q_v)
    feature2.append(count_2)

feature = []
for i in range(len(C1ch_q)):
    feature.append(feature1[i]+feature2[i])

feature = pd.DataFrame(feature) #样本特征

将样本特征全部提取完毕后带入xgboost进行预测:

###随机森林
X_train, X_test, y_train, y_test = train_test_split(feature,y,test_size=0.2,random_state=0)

regressor = RandomForestRegressor(n_estimators=200, random_state=0)
regressor.fit(X_train, y_train)
y_pred = regressor.predict(X_test)

from sklearn import metrics

print('Mean Absolute Error:', metrics.mean_absolute_error(y_test, y_pred))
print('Mean Squared Error:', metrics.mean_squared_error(y_test, y_pred))
print('Root Mean Squared Error:',
      np.sqrt(metrics.mean_squared_error(y_test, y_pred)))

最终所的误差小于0.4%,采用随机取样所得预测与实际值对比图:

由图可见预测效果不错。

下面是寻找最优参数代码:


##以下代码用于寻找最优参数
def build_model(optimizer):
    grid_model = Sequential()
    grid_model.add(LSTM(100,return_sequences=True,input_shape=(12000,3)))
    grid_model.add(LSTM(50))
    grid_model.add(Dropout(0.2))
    grid_model.add(Dense(1))
    

    grid_model.compile(loss = 'mse',optimizer = optimizer)

    return grid_model

grid_model = KerasRegressor(build_fn=build_model, verbose=1, validation_data=(testX, testY))
parameters = {'batch_size' : [16,20],
              'epochs' : [8,10],
              'optimizer' : ['adam','Adadelta'] }

grid_search  = GridSearchCV(estimator = grid_model,
                            param_grid = parameters,
                            cv = 2)
grid_search = grid_search.fit(trainX,trainY)
my_model=grid_search.best_estimator_.model

所需导入库:

import numpy as np
import pandas as pd
from matplotlib import pyplot as plt
from tensorflow.python.keras.models import Sequential
from tensorflow.python.keras.layers import LSTM
from tensorflow.python.keras.layers import Dense, Dropout
from sklearn.preprocessing import MinMaxScaler
from keras.wrappers.scikit_learn import KerasRegressor
from sklearn.model_selection import GridSearchCV
import os
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.ensemble import RandomForestRegressor

其实最终所得结果较好在意料之中,因为特征中包含了dQ/dv,一个电池容量有关的特征,相当于特征中包含了预测对象中的某些属性。这在实际中并不实用,因为你不能在预测前就知道一个电池的容量。本文仅供参考,引用请标注来源。

有关从IC曲线提取特征,采用随机森林对电池SOH进行估计的更多相关文章

  1. ruby-on-rails - 使用 Ruby on Rails 进行自动化测试 - 最佳实践 - 2

    很好奇,就使用ruby​​onrails自动化单元测试而言,你们正在做什么?您是否创建了一个脚本来在cron中运行rake作业并将结果邮寄给您?git中的预提交Hook?只是手动调用?我完全理解测试,但想知道在错误发生之前捕获错误的最佳实践是什么。让我们理所当然地认为测试本身是完美无缺的,并且可以正常工作。下一步是什么以确保他们在正确的时间将可能有害的结果传达给您? 最佳答案 不确定您到底想听什么,但是有几个级别的自动代码库控制:在处理某项功能时,您可以使用类似autotest的内容获得关于哪些有效,哪些无效的即时反馈。要确保您的提

  2. ruby-on-rails - 按天对 Mongoid 对象进行分组 - 2

    在控制台中反复尝试之后,我想到了这种方法,可以按发生日期对类似activerecord的(Mongoid)对象进行分组。我不确定这是完成此任务的最佳方法,但它确实有效。有没有人有更好的建议,或者这是一个很好的方法?#eventsisanarrayofactiverecord-likeobjectsthatincludeatimeattributeevents.map{|event|#converteventsarrayintoanarrayofhasheswiththedayofthemonthandtheevent{:number=>event.time.day,:event=>ev

  3. ruby - 使用 C 扩展开发 ruby​​gem 时,如何使用 Rspec 在本地进行测试? - 2

    我正在编写一个包含C扩展的gem。通常当我写一个gem时,我会遵循TDD的过程,我会写一个失败的规范,然后处理代码直到它通过,等等......在“ext/mygem/mygem.c”中我的C扩展和在gemspec的“扩展”中配置的有效extconf.rb,如何运行我的规范并仍然加载我的C扩展?当我更改C代码时,我需要采取哪些步骤来重新编译代码?这可能是个愚蠢的问题,但是从我的gem的开发源代码树中输入“bundleinstall”不会构建任何native扩展。当我手动运行rubyext/mygem/extconf.rb时,我确实得到了一个Makefile(在整个项目的根目录中),然后当

  4. ruby - 如何进行排列以有效地定制输出 - 2

    这是一道面试题,我没有答对,但还是很好奇怎么解。你有N个人的大家庭,分别是1,2,3,...,N岁。你想给你的大家庭拍张照片。所有的家庭成员都排成一排。“我是家里的friend,建议家庭成员安排如下:”1岁的家庭成员坐在这一排的最左边。每两个坐在一起的家庭成员的年龄相差不得超过2岁。输入:整数N,1≤N≤55。输出:摄影师可以拍摄的照片数量。示例->输入:4,输出:4符合条件的数组:[1,2,3,4][1,2,4,3][1,3,2,4][1,3,4,2]另一个例子:输入:5输出:6符合条件的数组:[1,2,3,4,5][1,2,3,5,4][1,2,4,3,5][1,2,4,5,3][

  5. ruby - 如何根据特征实现 FactoryGirl 的条件行为 - 2

    我有一个用户工厂。我希望默认情况下确认用户。但是鉴于unconfirmed特征,我不希望它们被确认。虽然我有一个基于实现细节而不是抽象的工作实现,但我想知道如何正确地做到这一点。factory:userdoafter(:create)do|user,evaluator|#unwantedimplementationdetailshereunlessFactoryGirl.factories[:user].defined_traits.map(&:name).include?(:unconfirmed)user.confirm!endendtrait:unconfirmeddoenden

  6. ruby - 即使失败也继续进行多主机测试 - 2

    我已经构建了一些serverspec代码来在多个主机上运行一组测试。问题是当任何测试失败时,测试会在当前主机停止。即使测试失败,我也希望它继续在所有主机上运行。Rakefile:namespace:specdotask:all=>hosts.map{|h|'spec:'+h.split('.')[0]}hosts.eachdo|host|begindesc"Runserverspecto#{host}"RSpec::Core::RakeTask.new(host)do|t|ENV['TARGET_HOST']=hostt.pattern="spec/cfengine3/*_spec.r

  7. ruby - 是否可以覆盖 gemfile 进行本地开发? - 2

    我们的git存储库中目前有一个Gemfile。但是,有一个gem我只在我的环境中本地使用(我的团队不使用它)。为了使用它,我必须将它添加到我们的Gemfile中,但每次我checkout到我们的master/dev主分支时,由于与跟踪的gemfile冲突,我必须删除它。我想要的是类似Gemfile.local的东西,它将继承从Gemfile导入的gems,但也允许在那里导入新的gems以供使用只有我的机器。此文件将在.gitignore中被忽略。这可能吗? 最佳答案 设置BUNDLE_GEMFILE环境变量:BUNDLE_GEMFI

  8. ruby - 在 Windows 机器上使用 Ruby 进行开发是否会适得其反? - 2

    这似乎非常适得其反,因为太多的gem会在window上破裂。我一直在处理很多mysql和ruby​​-mysqlgem问题(gem本身发生段错误,一个名为UnixSocket的类显然在Windows机器上不能正常工作,等等)。我只是在浪费时间吗?我应该转向不同的脚本语言吗? 最佳答案 我在Windows上使用Ruby的经验很少,但是当我开始使用Ruby时,我是在Windows上,我的总体印象是它不是Windows原生系统。因此,在主要使用Windows多年之后,开始使用Ruby促使我切换回原来的系统Unix,这次是Linux。Rub

  9. ruby-on-rails - Rails - 从命名路由中提取 HTTP 动词 - 2

    Rails中有没有一种方法可以提取与路由关联的HTTP动词?例如,给定这样的路线:将“users”匹配到:“users#show”,通过:[:get,:post]我能实现这样的目标吗?users_path.respond_to?(:get)(显然#respond_to不是正确的方法)我最接近的是通过执行以下操作,但它似乎并不令人满意。Rails.application.routes.routes.named_routes["users"].constraints[:request_method]#=>/^GET$/对于上下文,我有一个设置cookie然后执行redirect_to:ba

  10. ruby-on-rails - Ruby - 如何从 ruby​​ 上的 .pfx 文件中提取公钥、rsa 私钥和 CA key - 2

    我有一个.pfx格式的证书,我需要使用ruby​​提取公共(public)、私有(private)和CA证书。使用shell我可以这样做:#ExtractPublicKey(askforpassword)opensslpkcs12-infile.pfx-outfile_public.pem-clcerts-nokeys#ExtractCertificateAuthorityKey(askforpassword)opensslpkcs12-infile.pfx-outfile_ca.pem-cacerts-nokeys#ExtractPrivateKey(askforpassword)o

随机推荐