123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802 |
- import pandas as pd
- import numpy as np
- import datetime
- import bisect
- import matplotlib.pyplot as plt
- from LIB.MIDDLE.CellStateEstimation.Common import BatParam
- class BatSoh():
- def __init__(self,sn,celltype,df_bms,df_accum,df_soh): #参数初始化
- self.sn=sn
- self.celltype=celltype
- self.param=BatParam.BatParam(celltype)
- self.df_bms=df_bms
- self.packcrnt=df_bms['总电流[A]']*self.param.PackCrntDec
- self.packvolt=df_bms['总电压[V]']
- self.bms_soc=df_bms['SOC[%]']
- self.bms_soh=df_bms['SOH[%]']
- self.bmsstat=df_bms['充电状态']
- self.bmstime= pd.to_datetime(df_bms['时间戳'], format='%Y-%m-%d %H:%M:%S')
- self.df_accum=df_accum
- self.accumtime=pd.to_datetime(df_accum['时间戳'], format='%Y-%m-%d %H:%M:%S')
- self.df_soh=df_soh
- self.cellvolt_name=['单体电压'+str(x) for x in range(1,self.param.CellVoltNums+1)]
- self.celltemp_name=['单体温度'+str(x) for x in range(1,self.param.CellTempNums+1)]
- def batsoh(self):
- if self.celltype==1 or self.celltype==2 or self.celltype==3 or self.celltype==4:
- df_res=self._ncmsoh_twopoint()
- return df_res
-
- elif self.celltype==99:
- df_res=self._lfpsoh()
- return df_res
- else:
- return pd.DataFrame()
- #定义滑动滤波函数.........................................................................................................................
- def _np_move_avg(self,a, n, mode="same"):
- return (np.convolve(a, np.ones((n,)) / n, mode=mode))
- #筛选充电数据..............................................................................................................................
- def _chrgdata(self):
- self.ChgStart=[]
- self.ChgEnd=[]
- if len(self.packvolt)>100:
- for i in range(3, len(self.bmstime) - 3):
- if i==3 and self.bmsstat[i]==2 and self.bmsstat[i+1]==2 and self.bmsstat[i+2]==2:
- self.ChgStart.append(i)
- elif self.bmsstat[i-2]!=2 and self.bmsstat[i-1]!=2 and self.bmsstat[i]==2:
- self.ChgStart.append(i)
- elif self.bmsstat[i-1]==2 and self.bmsstat[i]!=2 and self.bmsstat[i+1]!=2:
- self.ChgEnd.append(i-1)
- elif i == (len(self.bmstime) - 4) and self.bmsstat[len(self.bmsstat)-1] == 2 and self.bmsstat[len(self.bmsstat)-2] == 2:
- self.ChgEnd.append(len(self.bmstime)-2)
-
- #寻找当前行数据的最小温度值.................................................................................................................
- def _celltemp_weight(self,num):
- celltemp = list(self.df_bms.loc[num,self.celltemp_name])
- celltemp.remove(min(celltemp))
- self.celltemp=celltemp
- if self.celltype>50:
- if min(celltemp)>=25:
- self.tempweight=1
- self.StandardStandingTime=2400
- elif min(celltemp)>=15:
- self.tempweight=0.6
- self.StandardStandingTime=3600
- elif min(celltemp)>=5:
- self.tempweight=0.3
- self.StandardStandingTime=7200
- else:
- self.tempweight=0.1
- self.StandardStandingTime=10800
- else:
- if min(celltemp)>=25:
- self.tempweight=1
- self.StandardStandingTime=1800
- elif min(celltemp)>=15:
- self.tempweight=0.8
- self.StandardStandingTime=3600
- elif min(celltemp)>=5:
- self.tempweight=0.3
- self.StandardStandingTime=4800
- else:
- self.tempweight=0.1
- self.StandardStandingTime=7200
- #获取SOC差对应的SOH权重值...................................................................................................................
- def _deltsoc_weight(self,deltsoc):
- if deltsoc>60:
- deltsoc_weight=1
- elif deltsoc>50:
- deltsoc_weight=0.9
- elif deltsoc>40:
- deltsoc_weight=0.7
- elif deltsoc>30:
- deltsoc_weight=0
- elif deltsoc>20:
- deltsoc_weight=0
- else:
- deltsoc_weight=0
- return deltsoc_weight
- #获取当前行所有电压数据......................................................................................................................
- def _cellvolt_get(self,num):
- cellvolt = np.array(self.df_bms.loc[num,self.cellvolt_name])/1000
- return cellvolt
-
- #获取单个电压值.................................................................................................
- def _singlevolt_get(self,num,series,mode): #mode==1取当前行单体电压值,mode==2取某个单体所有电压值
- s=str(series)
- if mode==1:
- singlevolt=self.df_bms.loc[num,'单体电压' + s]/1000
- return singlevolt
- else:
- singlevolt=self.df_bms['单体电压' + s]/1000
- return singlevolt
-
- #dvdq方法计算soh...........................................................................................................................
- def _dvdq_soh(self, chrg_st, chrg_end,cellvolt):
- Ah = 0 #参数赋初始值
- Volt = cellvolt[chrg_st]
- DV_Volt=[]
- DQ_Ah = []
- DVDQ = []
- time2 = []
- soc2 = []
- Ah_tatal=[0]
- xvolt=[]
- #计算DV和DQ值
- for j in range(chrg_st,chrg_end):
- Step=(self.bmstime[j+1]-self.bmstime[j]).total_seconds()
- Ah=Ah-self.packcrnt[j]*Step/3600
- if (cellvolt[j]-Volt)>0.0015 and Ah>0:
- Ah_tatal.append(Ah_tatal[-1]+Ah)
- DQ_Ah.append(Ah)
- DV_Volt.append(cellvolt[j]-Volt)
- DVDQ.append((DV_Volt[-1])/DQ_Ah[-1])
- xvolt.append(cellvolt[j])
- Volt=cellvolt[j]
- Ah = 0
- time2.append(self.bmstime[j])
- soc2.append(self.bms_soc[j])
- #切片,去除前后10min的数据
- df_Data1 = pd.DataFrame({'time': time2,
- 'SOC': soc2,
- 'DVDQ': DVDQ,
- 'Ah_tatal': Ah_tatal[:-1],
- 'DQ_Ah':DQ_Ah,
- 'DV_Volt':DV_Volt,
- 'XVOLT':xvolt})
- start_time=df_Data1.loc[0,'time']
- start_time=start_time+datetime.timedelta(seconds=900)
- end_time=df_Data1.loc[len(time2)-1,'time']
- end_time=end_time-datetime.timedelta(seconds=1200)
- if soc2[0]<36:
- df_Data1=df_Data1[(df_Data1['SOC']>40) & (df_Data1['SOC']<80)]
- else:
- df_Data1=df_Data1[(df_Data1['time']>start_time) & (df_Data1['SOC']<80)]
- df_Data1=df_Data1[(df_Data1['XVOLT']>self.param.PeakVoltLowLmt) & (df_Data1['XVOLT']<self.param.PeakVoltUpLmt)]
- # self._celltemp_weight(int((chrg_st+chrg_end)/2))
- # print(self.packcrnt[int((chrg_st+chrg_end)/2)], min(self.celltemp))
- # ax1 = plt.subplot(3, 1, 1)
- # plt.plot(df_Data1['XVOLT'],df_Data1['DVDQ'],'r*-')
- # plt.xlabel('Volt/V')
- # plt.ylabel('DV/DQ')
- # plt.legend()
- # ax1 = plt.subplot(3, 1, 2)
- # plt.plot(df_Data1['SOC'],df_Data1['XVOLT'],'y*-')
- # plt.xlabel('SOC/%')
- # plt.ylabel('Volt/V')
- # plt.legend()
- # ax1 = plt.subplot(3, 1, 3)
- # plt.plot(df_Data1['SOC'], df_Data1['DVDQ'], 'r*-')
- # plt.xlabel('SOC/%')
- # plt.ylabel('DV/DQ')
- # plt.legend()
- # plt.show()
- #寻找峰值并计算Soh
- if len(df_Data1)>2:
- PeakIndex=df_Data1['DVDQ'].idxmax()
- #筛选峰值点附近±0.5%SOC内的数据
- df_Data2=df_Data1[(df_Data1['SOC']>(df_Data1['SOC'][PeakIndex]-0.5)) & (df_Data1['SOC']<(df_Data1['SOC'][PeakIndex]+0.5))]
- if len(df_Data2)>1 and df_Data1.loc[PeakIndex,'XVOLT']<self.param.PeakVoltUpLmt-0.015:
- Ah_tatal1 = df_Data1['Ah_tatal']
- DVDQ = df_Data1['DVDQ']
- soc2 = df_Data1['SOC']
- xvolt = df_Data1['XVOLT']
- if soc2[PeakIndex]>40 and soc2[PeakIndex]<80:
- cellsoh_init=(Ah_tatal[-1]-Ah_tatal1[PeakIndex]) * 100 / ((self.param.FullChrgSoc - self.param.PeakSoc) * 0.01 * self.param.Capacity)
- if cellsoh_init<95:
- cellsoh_init=cellsoh_init*0.3926+58.14
- return cellsoh_init
- else:
- return cellsoh_init
- else:
- return 0
- else:
- df_Data1=df_Data1.drop([PeakIndex])
- PeakIndex = df_Data1['DVDQ'].idxmax()
- df_Data2 = df_Data1[(df_Data1['SOC'] > (df_Data1['SOC'][PeakIndex] - 0.5)) & (df_Data1['SOC'] < (df_Data1['SOC'][PeakIndex] + 0.5))]
- if len(df_Data2) > 1 and df_Data1.loc[PeakIndex,'XVOLT']<self.param.PeakVoltUpLmt-0.015:
- Ah_tatal1 = df_Data1['Ah_tatal']
- DVDQ = df_Data1['DVDQ']
- soc2 = df_Data1['SOC']
- xvolt = df_Data1['XVOLT']
- if soc2[PeakIndex]>40 and soc2[PeakIndex]<80:
- cellsoh_init=(Ah_tatal[-1]-Ah_tatal1[PeakIndex]) * 100 / ((self.param.FullChrgSoc - self.param.PeakSoc) * 0.01 * self.param.Capacity)
- if cellsoh_init<95:
- cellsoh_init=cellsoh_init*0.3926+58.14
- return cellsoh_init
- else:
- return cellsoh_init
- else:
- return 0
- else:
- return 0
- else:
- return 0
-
- #NCM充电数据soh计算.........................................................................................................................
- def _ncmsoh_chrg(self):
- self._chrgdata()
- ChgStartValid=[]
- ChgEndValid=[]
- tempweightlist=[]
- for i in range(min(len(self.ChgStart),len(self.ChgEnd))):
- self._celltemp_weight(self.ChgEnd[i]) #获取温度对应的静置时间及权重
- for k in range(self.ChgStart[i],self.ChgEnd[i]): #去除电流0点
- if self.packcrnt[k]<-0.5 and self.packcrnt[k+1]>-0.5 and self.packcrnt[k+2]>-0.5 and self.packcrnt[k+3]>-0.5:
- self.ChgEnd[i]=k
-
- #筛选满足2点法计算的数据
- StandingTime=0
- StandingTime1=0
- if self.bms_soc[self.ChgEnd[i]]>70 and self.bms_soc[self.ChgStart[i]]<50:
- for m in range(min(len(self.packcrnt)-self.ChgEnd[i]-2,self.ChgStart[i]-2)):
- if abs(self.packcrnt[self.ChgStart[i] - m - 1]) < 0.5:
- StandingTime = StandingTime + (self.bmstime[self.ChgStart[i] - m] - self.bmstime[self.ChgStart[i] - m - 1]).total_seconds()
- if abs(self.packcrnt[self.ChgEnd[i] + m + 1]) < 0.5:
- StandingTime1 = StandingTime1 + (self.bmstime[self.ChgEnd[i] + m + 1] - self.bmstime[self.ChgEnd[i] + m]).total_seconds()
- if StandingTime > self.StandardStandingTime and StandingTime1>self.StandardStandingTime and ((self.bmstime[self.ChgEnd[i]]-self.bmstime[self.ChgStart[i]]).total_seconds())/(self.ChgEnd[i]-self.ChgStart[i])<60: #筛选静置时间>15min且慢充过程丢失数据少
- if abs(self.packcrnt[self.ChgEnd[i] + m + 2])>0.5 or m==len(self.packcrnt)-self.ChgEnd[i]-3: #如果电流<0.5,继续寻找充电后的静置电压,直到末尾
- ChgStartValid.append(self.ChgStart[i])
- ChgEndValid.append(self.ChgEnd[i]+m)
- tempweightlist.append(self.tempweight)
- break
- if abs(self.packcrnt[self.ChgStart[i] - m - 2])>0.5 and abs(self.packcrnt[self.ChgEnd[i] + m + 2])>0.5:
- break
- if len(ChgStartValid)>0: #两点法计算Soh
- df_res=pd.DataFrame(columns=('time','sn','soh','soh1'))
- soh2=[]
- if not self.df_soh.empty: #获取数据库中上次计算的Soh值
- soh_init=list(self.df_soh['soh'])[-1]
- else:
- soh_init=list(self.bms_soh)[-1]
- for i in range(len(ChgStartValid)):
- Ah=0
- for j in range(ChgStartValid[i],ChgEndValid[i]): #计算Ah
- Step=(self.bmstime[j+1]-self.bmstime[j]).total_seconds()
- Ah=Ah-self.packcrnt[j+1]*Step/3600
- for j in range(1, self.param.CellVoltNums+1): #计算每个电芯的Soh
- s = str(j)
- OCVStart=self.df_bms.loc[ChgStartValid[i]-2,'单体电压' + s]/1000
- OCVEnd=self.df_bms.loc[ChgEndValid[i]-1,'单体电压' + s]/1000
- #soh
- ocv_Soc1=np.interp(OCVStart,self.param.LookTab_OCV,self.param.LookTab_SOC)
- ocv_Soc2=np.interp(OCVEnd,self.param.LookTab_OCV,self.param.LookTab_SOC)
-
- soh2.append(Ah*100/((ocv_Soc2-ocv_Soc1)*0.01*self.param.Capacity))
- soh1=np.mean(soh2)
- delt_ocv_soc=ocv_Soc2-ocv_Soc1
- self._deltsoc_weight(delt_ocv_soc)
- soh_res=soh_init*(1-self.deltsoc_weight*tempweightlist[i])+soh1*self.deltsoc_weight*tempweightlist[i]
- soh_init=soh_res
- df_res.loc[i]=[self.bmstime[ChgStartValid[i]],self.sn,soh_res,soh1]
-
- return df_res
- return pd.DataFrame()
- #两点法计算三元SOH.........................................................................................................................
- def _ncmsoh_twopoint(self):
- standingpoint_st=[]
- standingpoint_sp=[]
- tempweightlist=[]
- standingtime=0
- for i in range(3,len(self.df_bms)-3):
- if abs(self.packcrnt[i]) < 0.1 and abs(self.packcrnt[i-1]) < 0.1 and abs(self.packcrnt[i+1]) < 0.1: #电流为0
- delttime=(self.bmstime[i]-self.bmstime[i-1]).total_seconds()
- standingtime=standingtime+delttime
- self._celltemp_weight(i) #获取不同温度对应的静置时间
- if standingtime>self.StandardStandingTime: #静置时间满足要求
- cellvolt_now=self._cellvolt_get(i)
- cellvolt_min=min(cellvolt_now)
- cellvolt_max=max(cellvolt_now)
- cellvolt_last=self._cellvolt_get(i-1)
- deltvolt=max(abs(cellvolt_now-cellvolt_last))
- if 3<cellvolt_min<4.5 and 3<cellvolt_max<4.5 and deltvolt<0.003: #前后两次电压波动<3mV
- if standingpoint_st:
- if len(standingpoint_st)>len(standingpoint_sp): #开始时刻已获取,结束时刻未获取
- minocv_socnow=np.interp(cellvolt_min,self.param.LookTab_OCV,self.param.LookTab_SOC)
- cellvolt_st=self._cellvolt_get(standingpoint_st[-1]) #获取开始时刻静置后的电压数据
- minocv_socst=np.interp(min(cellvolt_st),self.param.LookTab_OCV,self.param.LookTab_SOC)
- if abs(minocv_socst-minocv_socnow)>=40: #当前时刻SOC与开始时刻SOC差>=40
- if abs(self.packcrnt[i+2])>=0.1: #如果下一时刻电流>=0.5,则压入当前索引
- standingpoint_sp.append(i)
- standingpoint_st.append(i)
- tempweightlist.append(self.tempweight)
- standingtime=0
- continue
- else:
- if standingtime>3600 or i==len(self.df_bms)-2: #仍处于静置,但静置时间>1h,则直接获取sp时刻,或者到了数据末尾
- standingpoint_sp.append(i)
- tempweightlist.append(self.tempweight)
- continue
- else:
- if abs(self.packcrnt[i+2])>=0.1:
- standingtime=0
- if minocv_socst<50 and minocv_socnow<minocv_socst:
- standingpoint_st[-1]=i
- continue
- elif abs(self.packcrnt[i+2])>=0.1:
- standingtime=0
- if minocv_socst>=50 and minocv_socnow>minocv_socst:
- standingpoint_st[-1]=i
- continue
- else:
- continue
- else:
- if abs(self.packcrnt[i+2])>=0.1:
- standingpoint_st.append(i)
- standingtime=0
- continue
- else:
- continue
- else:
- if abs(self.packcrnt[i+2])>0.1:
- standingpoint_st.append(i)
- standingtime=0
- continue
- else:
- continue
- else:
- continue
- else:
- continue
- else:
- standingtime=0
- continue
- #计算SOH......................................................................................................................
- if standingpoint_sp:
- column_name=['time_st','time_sp','sn','method','bmssoh','packsoh','soh','cellsoh']
- df_res=pd.DataFrame(columns=column_name)
- for i in range(len(standingpoint_sp)):
- cellocv_st=self._cellvolt_get(standingpoint_st[i]) #获取静置点所有电芯的电压
- cellocv_sp=self._cellvolt_get(standingpoint_sp[i])
- accumtime=self.accumtime.to_list() #累计量的时间列表
- timepoint_bms_st=self.bmstime[standingpoint_st[i]] #获取静置点的时间
- timepoint_bms_sp=self.bmstime[standingpoint_sp[i]]
- timepoint_accum_st=bisect.bisect(accumtime,timepoint_bms_st) #获取最接近静置点时间的累计量时间点
- timepoint_accum_sp=bisect.bisect(accumtime,timepoint_bms_sp)
- if timepoint_accum_sp>=len(accumtime): #防止指针超出数据范围
- timepoint_accum_sp=len(accumtime)-1
-
- ah_packcrnt_dis=0
- ah_packcrnt_chg=0
- for j in range(standingpoint_st[i]+1,standingpoint_sp[i]): #计算累计Ah
- Step=(self.bmstime[j+1]-self.bmstime[j]).total_seconds()
- if Step<120:
- if self.packcrnt[j+1]>=0:
- ah_packcrnt_dis=ah_packcrnt_dis+self.packcrnt[j+1]*Step
- else:
- ah_packcrnt_chg=ah_packcrnt_chg-self.packcrnt[j+1]*Step
- ah_packcrnt_chg=ah_packcrnt_chg/3600
- ah_packcrnt_dis=ah_packcrnt_dis/3600
- ah_packcrnt=ah_packcrnt_chg-ah_packcrnt_dis #两个静置点的总累计AH,负值代表放电,正值代表充电
-
- ah_accum_dis=self.df_accum.loc[timepoint_accum_sp,'累计放电电量']-self.df_accum.loc[timepoint_accum_st,'累计放电电量'] #两个静置点之间的放电电量
- ah_accum_chg=self.df_accum.loc[timepoint_accum_sp,'累计充电电量']-self.df_accum.loc[timepoint_accum_st,'累计充电电量'] #两个静置点之间的充电电量
- ah_accum_tatol=ah_accum_chg-ah_accum_dis #两个静置点的总累计AH,负值代表放电,正值代表充电
- ah_accum=ah_packcrnt
- delt_days=(self.bmstime[standingpoint_sp[i]]-self.bmstime[standingpoint_st[i]]).total_seconds()/(3600*24)
- if delt_days<=1: #两次时间间隔对计算结果的影响
- soh_weight1=1
- elif delt_days<=2:
- soh_weight1=0.7
- elif delt_days<=3:
- soh_weight1=0.4
- else:
- soh_weight1=0
-
- if ah_packcrnt_dis<self.param.Capacity: #放电ah数对结果的影响
- soh_weight1=(1-ah_packcrnt_dis/(self.param.Capacity*1.5))*soh_weight1
- else:
- soh_weight1=0.1
-
- if self.param.Capacity**0.7*0.4 < abs(ah_accum_tatol) < self.param.Capacity: #累计量的权重
- if abs(ah_accum_tatol-ah_packcrnt)<self.param.Capacity/20:
- soh_weight1=soh_weight1*1
- elif abs(ah_accum_tatol-ah_packcrnt) < self.param.Capacity/10:
- soh_weight1=soh_weight1*0.8
- else:
- soh_weight1=soh_weight1*0.5
- else:
- if self.param.Capacity*0.7*0.4< abs(ah_packcrnt) <self.param.Capacity:
- soh_weight1=soh_weight1*0.3
- else:
- soh_weight1=0
- #计算每个电芯的SOH值
- cellsoh=[]
- for j in range(self.param.CellVoltNums):
- ocv_soc1=np.interp(cellocv_st[j],self.param.LookTab_OCV,self.param.LookTab_SOC)
- ocv_soc2=np.interp(cellocv_sp[j],self.param.LookTab_OCV,self.param.LookTab_SOC)
- delt_ocv_soc=ocv_soc2-ocv_soc1
- delt_ocv_soc_weight=self._deltsoc_weight(abs(delt_ocv_soc))
- soh_weight=soh_weight1*tempweightlist[i]*delt_ocv_soc_weight*0.5
- cellsoh_init=ah_accum*100/((ocv_soc2-ocv_soc1)*0.01*self.param.Capacity)
- if cellsoh_init>55 and cellsoh_init<120 and soh_weight>0.05: #判断soh值的有效区间
- if len(df_res)<1:
- if not self.df_soh.empty:
- cellsoh_last=eval(self.df_soh.loc[len(self.df_soh)-1,'cellsoh'])
- if soh_weight>1/abs(cellsoh_init-cellsoh_last[j]):
- soh_weight=1/abs(cellsoh_init-cellsoh_last[j])
- cellsoh_cal=cellsoh_init*soh_weight + cellsoh_last[j]*(1-soh_weight)
- else:
- cellsoh_cal=cellsoh_init*soh_weight + cellsoh_last[j]*(1-soh_weight)
- else:
- cellsoh_cal=cellsoh_init
- else:
- cellsoh_last=eval(df_res.loc[len(df_res)-1,'cellsoh'])
- if soh_weight>1/abs(cellsoh_init-cellsoh_last[j]):
- soh_weight=1/abs(cellsoh_init-cellsoh_last[j])
- cellsoh_cal=cellsoh_init*soh_weight + cellsoh_last[j]*(1-soh_weight)
- else:
- cellsoh_cal=cellsoh_init*soh_weight + cellsoh_last[j]*(1-soh_weight)
- cellsoh_cal=eval(format(cellsoh_cal,'.1f'))
- cellsoh.append(cellsoh_cal)
- else:
- cellsoh=[]
- break
- #计算电池包SOH
- ocv_soc1=np.interp(min(cellocv_st),self.param.LookTab_OCV,self.param.LookTab_SOC)
- ocv_soc2=np.interp(max(cellocv_sp),self.param.LookTab_OCV,self.param.LookTab_SOC)
- delt_ocv_soc=ocv_soc2-ocv_soc1
- delt_ocv_soc_weight=self._deltsoc_weight(abs(delt_ocv_soc))
- soh_weight=soh_weight1*tempweightlist[i]*delt_ocv_soc_weight*0.5
- packsoh_init=ah_accum*100/((ocv_soc2-ocv_soc1)*0.01*self.param.Capacity)
- if packsoh_init>55 and packsoh_init<120 and soh_weight>0.05: #判断soh值的有效区间
- if len(df_res)<1:
- if not self.df_soh.empty:
- packsoh_last=self.df_soh.loc[len(self.df_soh)-1,'packsoh']
- if soh_weight>1/abs(packsoh_init-packsoh_last):
- soh_weight=1/abs(packsoh_init-packsoh_last)
- packsoh=packsoh_init*soh_weight + packsoh_last*(1-soh_weight)
- else:
- packsoh=packsoh_init*soh_weight + packsoh_last*(1-soh_weight)
- else:
- packsoh=packsoh_init
- else:
- packsoh_last=df_res.loc[len(df_res)-1,'packsoh']
- if soh_weight>1/abs(packsoh_init-packsoh_last):
- soh_weight=1/abs(packsoh_init-packsoh_last)
- packsoh=packsoh_init*soh_weight + packsoh_last*(1-soh_weight)
- else:
- packsoh=packsoh_init*soh_weight + packsoh_last*(1-soh_weight)
- packsoh=eval(format(packsoh,'.1f'))
- else:
- packsoh=0
- continue
- if cellsoh and 55<min(cellsoh)<120 and 50<packsoh<120:
- soh=min(cellsoh)
- bmssoh=self.bms_soh[standingpoint_sp[i]]
- soh_list=[timepoint_bms_st, timepoint_bms_sp, self.sn, 1, bmssoh, packsoh, soh, str(cellsoh)]
- df_res.loc[len(df_res)]=soh_list
- else:
- continue
- if df_res.empty:
- return pd.DataFrame()
- else:
- return df_res
- return pd.DataFrame()
- #两点法和DVDQ法计算磷酸铁锂电池SOH..................................................................................................................
- def _lfpsoh(self):
- standingpoint_st=[]
- standingpoint_sp=[]
- tempweightlist1=[]
- cellmaxvolt_number1=[]
- standingtime=0
- chrg_start=[]
- chrg_end=[]
- tempweightlist2=[]
- cellmaxvolt_number2=[]
- charging=0
- for i in range(3,len(self.df_bms)-3):
- #获取两点法法所需数据-开始.................................................................................................................
- if abs(self.packcrnt[i]) < 0.1 and abs(self.packcrnt[i-1]) < 0.1 and abs(self.packcrnt[i+1]) < 0.1: #判断非平台区静置状态
- delttime=(self.bmstime[i]-self.bmstime[i-1]).total_seconds()
- standingtime=standingtime+delttime
- self._celltemp_weight(i) #获取不同温度对应的静置时间
- if standingtime>self.StandardStandingTime: #静置时间满足要求
- if abs(self.packcrnt[i+2])>=0.1: #下一时刻电流>0.1A
- standingtime=0
- cellvolt_now=self._cellvolt_get(i)
- cellvolt_max=max(cellvolt_now)
- cellvolt_min=min(cellvolt_now)
- cellvolt_last=self._cellvolt_get(i-1)
- deltvolt=max(abs(cellvolt_now-cellvolt_last))
- if 2<cellvolt_max<self.param.OcvInflexionBelow-0.002 and 2<cellvolt_min<4.5 and deltvolt<0.003: #当前最大电芯电压<OCV下拐点
- if standingpoint_st:
- if len(standingpoint_st)>len(standingpoint_sp):
- if self.packcrnt[standingpoint_st[-1]]<-1: #判断上一次静置点的是否为满充
- standingpoint_sp.append(i)
- standingpoint_st.append(i)
- tempweightlist1.append(self.tempweight)
- else:
- standingpoint_st[-1]=i
- tempweightlist1[-1]=self.tempweight
- else:
- standingpoint_st.append(i)
- tempweightlist1.append(self.tempweight)
- else:
- standingpoint_st.append(i)
- tempweightlist1.append(self.tempweight)
- else:
- pass
- else:
- pass
- else:
- pass
-
- elif self.packcrnt[i]<=-1 and self.packcrnt[i-1]<=-1 and self.packcrnt[i+1]<=-1 and self.packcrnt[i+2]>-1: #判读满充状态
- standingtime=0
- self._celltemp_weight(i)
- cellvolt_now=self._cellvolt_get(i).tolist()
- if max(cellvolt_now)>self.param.CellFullChrgVolt:
- if standingpoint_st:
- if len(standingpoint_st)>len(standingpoint_sp):
- if abs(self.packcrnt[standingpoint_st[-1]])<0.5: #判断上一次静置点是否为下拐点
- standingpoint_sp.append(i)
- standingpoint_st.append(i)
- tempweightlist1.append(self.tempweight)
- cellmaxvolt_number1.append(cellvolt_now.index(max(cellvolt_now))) #获取最大电压索引
- cellmaxvolt_number1.append(cellvolt_now.index(max(cellvolt_now))) #获取最大电压索引
- else:
- standingpoint_st[-1]=i
- tempweightlist1[-1]=self.tempweight
- cellmaxvolt_number1[-1]=cellvolt_now.index(max(cellvolt_now))
- else:
- standingpoint_st.append(i)
- tempweightlist1.append(self.tempweight)
- cellmaxvolt_number1.append(cellvolt_now.index(max(cellvolt_now)))
- else:
- pass
-
- else:
- standingtime=0
- pass
- #获取DVDQ算法所需数据——开始.............................................................................................................
- if charging==0:
- if self.packcrnt[i]<=-1 and self.packcrnt[i+1]<=-1 and self.packcrnt[i+2]<=-1 and self.bms_soc[i]<40: #充电开始
- self._celltemp_weight(i)
- charging=1
- if len(chrg_start)>len(chrg_end):
- chrg_start[-1]=i
- tempweightlist2[-1]=self.tempweight
- else:
- chrg_start.append(i)
- tempweightlist2.append(self.tempweight)
- else:
- pass
- else: #充电中
- if (self.bmstime[i+1]-self.bmstime[i]).total_seconds()>180 or (self.packcrnt[i]<-self.param.Capacity/2 and self.packcrnt[i+1]<-self.param.Capacity/2): #如果充电过程中时间间隔>180s,则舍弃该次充电
- chrg_start.remove(chrg_start[-1])
- tempweightlist2.remove(tempweightlist2[-1])
- charging=0
- continue
- elif self.packcrnt[i]<=-1 and self.packcrnt[i+1]<=-1 and self.packcrnt[i+2]>-1: #判断电流波动时刻
- cellvolt_now=self._cellvolt_get(i+1).tolist()
- if max(cellvolt_now)>self.param.CellFullChrgVolt: #电压>满充电压
- chrg_end.append(i+1)
- cellmaxvolt_number2.append(cellvolt_now.index(max(cellvolt_now))) #获取最大电压索引
- charging=0
- continue
- else:
- pass
- elif self.packcrnt[i+1]>-0.1 and self.packcrnt[i+2]>-0.1: #判断充电结束
- charging=0
- if len(chrg_start)>len(chrg_end):
- chrg_start.remove(chrg_start[-1])
- tempweightlist2.remove(tempweightlist2[-1])
- continue
- else:
- continue
- elif i==len(self.packcrnt)-4 and self.packcrnt[i+1]<-1 and self.packcrnt[i+2]<-1:
- charging=0
- if len(chrg_start)>len(chrg_end):
- cellvolt_now=self._cellvolt_get(i).tolist()
- if max(cellvolt_now)>self.param.CellFullChrgVolt: #电压>满充电压
- chrg_end.append(i)
- cellmaxvolt_number2.append(cellvolt_now.index(max(cellvolt_now))) #获取最大电压索引
- continue
- else:
- chrg_start.remove(chrg_start[-1])
- tempweightlist2.remove(tempweightlist2[-1])
- continue
- else:
- continue
- else:
- continue
-
- #开始计算SOH.............................................................................................................................................
- if standingpoint_sp or chrg_end:
- column_name=['time_st','time_sp','sn','method','bmssoh','packsoh','soh','cellsoh','weight']
- df_res=pd.DataFrame(columns=column_name)
-
- #两点法计算SOH........................................................................................................................................
- if standingpoint_sp:
- for i in range(len(standingpoint_sp)): #判断为满充点或者下拐点
- if self.packcrnt[standingpoint_sp[i]]<=-1: #计算单体电芯soh
- cellocv_st=self._cellvolt_get(standingpoint_st[i])
- ocv_soc1=np.interp(cellocv_st[cellmaxvolt_number1[i]],self.param.LookTab_OCV,self.param.LookTab_SOC)
- ocv_packsoc1=np.interp(min(cellocv_st),self.param.LookTab_OCV,self.param.LookTab_SOC)
- ocv_soc2=self.param.FullChrgSoc
- ocv_packsoc2=ocv_soc2
- else:
- cellocv_sp=self._cellvolt_get(standingpoint_sp[i])
- ocv_soc1=self.param.FullChrgSoc
- ocv_packsoc1=ocv_soc1
- ocv_soc2=np.interp(cellocv_sp[cellmaxvolt_number1[i]],self.param.LookTab_OCV,self.param.LookTab_SOC)
- ocv_packsoc2=np.interp(min(cellocv_sp),self.param.LookTab_OCV,self.param.LookTab_SOC)
- cellocv_sp=self._cellvolt_get(standingpoint_sp[i])
- accumtime=self.accumtime.to_list() #累计量的时间列表
- timepoint_bms_st=self.bmstime[standingpoint_st[i]] #获取静置点的时间
- timepoint_bms_sp=self.bmstime[standingpoint_sp[i]]
- timepoint_accum_st=bisect.bisect(accumtime,timepoint_bms_st) #获取最接近静置点时间的累计量时间点
- timepoint_accum_sp=bisect.bisect(accumtime,timepoint_bms_sp)
- if timepoint_accum_sp>=len(accumtime): #防止指针超出数据范围
- timepoint_accum_sp=len(accumtime)-1
-
- ah_packcrnt_dis=0
- ah_packcrnt_chg=0
- for j in range(standingpoint_st[i]+1,standingpoint_sp[i]+1): #计算累计Ah
- Step=(self.bmstime[j+1]-self.bmstime[j]).total_seconds()
- if Step<120:
- if self.packcrnt[j+1]>=0:
- ah_packcrnt_dis=ah_packcrnt_dis+self.packcrnt[j+1]*Step
- else:
- ah_packcrnt_chg=ah_packcrnt_chg-self.packcrnt[j+1]*Step
- ah_packcrnt_chg=ah_packcrnt_chg/3600
- ah_packcrnt_dis=ah_packcrnt_dis/3600
- ah_packcrnt=ah_packcrnt_chg-ah_packcrnt_dis #两个静置点的总累计AH,负值代表放电,正值代表充电
-
- ah_accum_dis=self.df_accum.loc[timepoint_accum_sp,'累计放电电量']-self.df_accum.loc[timepoint_accum_st,'累计放电电量'] #两个静置点之间的放电电量
- ah_accum_chg=self.df_accum.loc[timepoint_accum_sp,'累计充电电量']-self.df_accum.loc[timepoint_accum_st,'累计充电电量'] #两个静置点之间的充电电量
- ah_accum_tatol=ah_accum_chg-ah_accum_dis #两个静置点的总累计AH,负值代表放电,正值代表充电
- ah_accum=ah_accum_tatol
- delt_days=(self.bmstime[standingpoint_sp[i]]-self.bmstime[standingpoint_st[i]]).total_seconds()/(3600*24)
- if delt_days<=1: #两次时间间隔对计算结果的影响
- soh_weight=1
- elif delt_days<=2:
- soh_weight=0.7
- elif delt_days<=3:
- soh_weight=0.4
- else:
- soh_weight=0
-
- if self.param.Capacity*0.65*0.7 < abs(ah_packcrnt) < self.param.Capacity: #累计量的权重
- if abs(ah_accum_tatol-ah_packcrnt)<self.param.Capacity/20:
- soh_weight=soh_weight*1
- elif abs(ah_accum_tatol-ah_packcrnt)<self.param.Capacity/10:
- soh_weight=soh_weight*0.8
- else:
- soh_weight=soh_weight*0.5
- else:
- if self.param.Capacity*0.65*0.7 < abs(ah_accum) < self.param.Capacity:
- soh_weight=soh_weight*0.3
- else:
- soh_weight=0
- delt_ocv_soc=ocv_soc2-ocv_soc1
- delt_ocv_packsoc=ocv_packsoc2-ocv_packsoc1
- delt_ocv_soc_weight=self._deltsoc_weight(abs(delt_ocv_soc))
- soh_weight=soh_weight*tempweightlist1[i]*delt_ocv_soc_weight*0.5
- cellsoh_init=ah_accum*100/(delt_ocv_soc*0.01*self.param.Capacity)
- packsoh_init=ah_accum*100/(delt_ocv_packsoc*0.01*self.param.Capacity)
- bmssoh=self.bms_soh[standingpoint_sp[i]]
- if 55<cellsoh_init<120 and 50<packsoh_init<120: #判断soh值的有效区间
- soh_list=[timepoint_bms_st, timepoint_bms_sp, self.sn, 1, bmssoh, packsoh_init, cellsoh_init, str(cellsoh_init), soh_weight]
- df_res.loc[len(df_res)]=soh_list
- else:
- pass
- else:
- pass
- #DVDQ法计算SOH.......................................................................................................................................
- if chrg_end:
- for i in range(len(chrg_end)):
- cellsoh=[]
- for j in range(1, self.param.CellVoltNums + 1):
- cellvolt1 = self._singlevolt_get(i,j,2) #取单体电压j的所有电压值
- cellvolt=self._np_move_avg(cellvolt1, 3, mode="same") #对电压进行滑动平均滤
- cellsoh_init=self._dvdq_soh(chrg_start[i],chrg_end[i],cellvolt) #dvdq计算soh
- cellsoh.append(cellsoh_init)
-
- soh_weight=tempweightlist2[i]*0.3
- cellsoh_init=cellsoh[cellmaxvolt_number2[i]+1]
- cellsoh_new=[k for k in cellsoh if 50<k<120]
- packsoh_init=min(cellsoh_new)
- bmssoh=self.bms_soh[chrg_end[i]]
- if 55<cellsoh_init<120: #判断soh值的有效区间
- soh_list=[self.bmstime[chrg_start[i]], self.bmstime[chrg_end[i]], self.sn, 2, bmssoh, packsoh_init, cellsoh_init, str(cellsoh_init), soh_weight]
- df_res.loc[len(df_res)]=soh_list
- else:
- pass
-
- #对SOH结果进行滤波处理................................................................................................................................
- if df_res.empty:
- return pd.DataFrame()
- else:
- df_res=df_res.sort_values(by='time_st',ascending=True)
- df_res.index=range(len(df_res))
- for i in range(len(df_res)):
- cellsoh_init=df_res.loc[i,'soh']
- packsoh_init=df_res.loc[i,'packsoh']
- soh_weight=df_res.loc[i,'weight']
- if i<1:
- if not self.df_soh.empty and 60<self.df_soh.loc[len(self.df_soh)-1,'soh']<115:
- cellsoh_last=self.df_soh.loc[len(self.df_soh)-1,'soh']
- packsoh_last=self.df_soh.loc[len(self.df_soh)-1,'packsoh']
- if soh_weight>1/abs(cellsoh_init-cellsoh_last):
- soh_weight=1/abs(cellsoh_init-cellsoh_last)
- cellsoh_cal=cellsoh_init*soh_weight + cellsoh_last*(1-soh_weight)
- packsoh=packsoh_init*soh_weight + packsoh_last*(1-soh_weight)
- else:
- cellsoh_cal=cellsoh_init*soh_weight + cellsoh_last*(1-soh_weight)
- packsoh=packsoh_init*soh_weight + packsoh_last*(1-soh_weight)
- else:
- cellsoh_cal=cellsoh_init
- packsoh=packsoh_init
- cellsoh_cal=eval(format(cellsoh_cal,'.1f'))
- packsoh=eval(format(packsoh,'.1f'))
- if 55<cellsoh_cal<120:
- df_res.loc[i,'soh']=cellsoh_cal
- df_res.loc[i,'cellsoh']=str(cellsoh_cal)
- df_res.loc[i,'packsoh']=packsoh
- else:
- df_res=df_res.drop(i,axis=0)
- else:
- cellsoh_last=df_res.loc[i-1,'soh']
- packsoh_last=df_res.loc[i-1,'packsoh']
- if soh_weight>1/abs(cellsoh_init-cellsoh_last):
- soh_weight=1/abs(cellsoh_init-cellsoh_last)
- cellsoh_cal=cellsoh_init*soh_weight + cellsoh_last*(1-soh_weight)
- packsoh=packsoh_init*soh_weight + packsoh_last*(1-soh_weight)
- else:
- cellsoh_cal=cellsoh_init*soh_weight + cellsoh_last*(1-soh_weight)
- packsoh=packsoh_init*soh_weight + packsoh_last*(1-soh_weight)
- cellsoh_cal=eval(format(cellsoh_cal,'.1f'))
- packsoh=eval(format(packsoh,'.1f'))
- if 55<cellsoh_cal<120:
- df_res.loc[i,'soh']=cellsoh_cal
- df_res.loc[i,'cellsoh']=str([cellsoh_cal])
- df_res.loc[i,'packsoh']=packsoh
- else:
- df_res=df_res.drop(i,axis=0)
- df_res=df_res.drop('weight',axis=1)
- return df_res
- return pd.DataFrame()
|