CBMSBatSoh.py 45 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822
  1. import random
  2. import pandas as pd
  3. import numpy as np
  4. import datetime
  5. import BatParam
  6. class BatSoh():
  7. def __init__(self,sn,celltype,df_bms,df_soh): #参数初始化
  8. self.sn=sn
  9. self.celltype=celltype
  10. self.param=BatParam.BatParam(celltype)
  11. self.df_bms=df_bms
  12. self.packcrnt=df_bms['PackCrnt']*self.param.PackCrntDec
  13. self.bms_soc=df_bms['PackSOC']
  14. self.bms_soh=df_bms['PackSOH']
  15. self.bmsstat=df_bms['BMSStat']
  16. self.bmstime= pd.to_datetime(df_bms['time'], format='%Y-%m-%d %H:%M:%S')
  17. self.df_soh=df_soh
  18. self.cellvolt_name=['CellVolt'+str(x) for x in range(1,self.param.CellVoltNums+1)]
  19. self.celltemp_name=['CellTemp'+str(x) for x in range(1,self.param.CellTempNums+1)]
  20. def batsoh(self):
  21. if self.celltype<50:
  22. df_res=self._ncmsoh_twopoint()
  23. return df_res
  24. else:
  25. df_res=self._lfpsoh()
  26. return df_res
  27. #定义滑动滤波函数.........................................................................................................................
  28. def _np_move_avg(self, a, n, mode):
  29. return (np.convolve(a, np.ones((n,)) / n, mode=mode))
  30. #筛选充电数据..............................................................................................................................
  31. def _chrgdata(self):
  32. self.ChgStart=[]
  33. self.ChgEnd=[]
  34. if len(self.df_bms)>100:
  35. for i in range(3, len(self.bmstime) - 3):
  36. if i==3 and self.bmsstat[i]==2 and self.bmsstat[i+1]==2 and self.bmsstat[i+2]==2:
  37. self.ChgStart.append(i)
  38. elif self.bmsstat[i-2]!=2 and self.bmsstat[i-1]!=2 and self.bmsstat[i]==2:
  39. self.ChgStart.append(i)
  40. elif self.bmsstat[i-1]==2 and self.bmsstat[i]!=2 and self.bmsstat[i+1]!=2:
  41. self.ChgEnd.append(i-1)
  42. elif i == (len(self.bmstime) - 4) and self.bmsstat[len(self.bmsstat)-1] == 2 and self.bmsstat[len(self.bmsstat)-2] == 2:
  43. self.ChgEnd.append(len(self.bmstime)-2)
  44. #寻找当前行数据的最小温度值.................................................................................................................
  45. def _celltemp_weight(self,num):
  46. celltemp = list(self.df_bms.loc[num,self.celltemp_name])
  47. celltemp.remove(min(celltemp))
  48. self.celltemp=celltemp
  49. if self.celltype>50:
  50. if min(celltemp)>=25:
  51. self.tempweight=1
  52. self.StandardStandingTime=3500
  53. elif min(celltemp)>=15:
  54. self.tempweight=0.5
  55. self.StandardStandingTime=7100
  56. elif min(celltemp)>=5:
  57. self.tempweight=0.2
  58. self.StandardStandingTime=10700
  59. else:
  60. self.tempweight=0
  61. self.StandardStandingTime=10800
  62. else:
  63. if min(celltemp)>=25:
  64. self.tempweight=1
  65. self.StandardStandingTime=1800
  66. elif min(celltemp)>=15:
  67. self.tempweight=0.8
  68. self.StandardStandingTime=3600
  69. elif min(celltemp)>=5:
  70. self.tempweight=0.3
  71. self.StandardStandingTime=7200
  72. else:
  73. self.tempweight=0.1
  74. self.StandardStandingTime=7200
  75. #获取SOC差对应的SOH权重值...................................................................................................................
  76. def _deltsoc_weight(self,deltsoc):
  77. if deltsoc>60:
  78. deltsoc_weight=1
  79. elif deltsoc>50:
  80. deltsoc_weight=0.9
  81. elif deltsoc>40:
  82. deltsoc_weight=0.3
  83. elif deltsoc>30:
  84. deltsoc_weight=0
  85. elif deltsoc>20:
  86. deltsoc_weight=0
  87. else:
  88. deltsoc_weight=0
  89. return deltsoc_weight
  90. #获取当前行所有电压数据......................................................................................................................
  91. def _cellvolt_get(self,num):
  92. cellvolt = np.array(self.df_bms.loc[num,self.cellvolt_name])
  93. return cellvolt
  94. #获取单个电压值.................................................................................................
  95. def _singlevolt_get(self,num,series,mode): #mode==1取当前行单体电压值,mode==2取某个单体所有电压值
  96. s=str(series)
  97. if mode==1:
  98. singlevolt=self.df_bms.loc[num,'CellVolt' + s]
  99. return singlevolt
  100. else:
  101. singlevolt=self.df_bms['CellVolt' + s]
  102. return singlevolt
  103. #dvdq方法计算soh...........................................................................................................................
  104. def _dvdq_soh(self, chrg_st, chrg_end,cellvolt):
  105. Ah = 0 #参数赋初始值
  106. Volt = cellvolt[chrg_st]
  107. DV_Volt=[]
  108. DQ_Ah = []
  109. DVDQ = []
  110. time2 = []
  111. soc2 = []
  112. Ah_tatal=[0]
  113. xvolt=[]
  114. #计算DV和DQ值
  115. for j in range(chrg_st,chrg_end):
  116. Step=(self.bmstime[j+1]-self.bmstime[j]).total_seconds()
  117. Ah=Ah-self.packcrnt[j]*Step/3600
  118. if (cellvolt[j]-Volt)>0.0015 and Ah>0:
  119. Ah_tatal.append(Ah_tatal[-1]+Ah)
  120. DQ_Ah.append(Ah)
  121. DV_Volt.append(cellvolt[j]-Volt)
  122. DVDQ.append((DV_Volt[-1])/DQ_Ah[-1])
  123. xvolt.append(cellvolt[j])
  124. Volt=cellvolt[j]
  125. Ah = 0
  126. time2.append(self.bmstime[j])
  127. soc2.append(self.bms_soc[j])
  128. #切片,去除前后10min的数据
  129. df_Data1 = pd.DataFrame({'time': time2,
  130. 'SOC': soc2,
  131. 'DVDQ': DVDQ,
  132. 'Ah_tatal': Ah_tatal[:-1],
  133. 'DQ_Ah':DQ_Ah,
  134. 'DV_Volt':DV_Volt,
  135. 'XVOLT':xvolt})
  136. start_time=df_Data1.loc[0,'time']
  137. start_time=start_time+datetime.timedelta(seconds=900)
  138. end_time=df_Data1.loc[len(time2)-1,'time']
  139. end_time=end_time-datetime.timedelta(seconds=1200)
  140. if soc2[0]<36:
  141. df_Data1=df_Data1[(df_Data1['SOC']>40) & (df_Data1['SOC']<80)]
  142. else:
  143. df_Data1=df_Data1[(df_Data1['time']>start_time) & (df_Data1['SOC']<80)]
  144. df_Data1=df_Data1[(df_Data1['XVOLT']>self.param.PeakVoltLowLmt) & (df_Data1['XVOLT']<self.param.PeakVoltUpLmt)]
  145. # self._celltemp_weight(int((chrg_st+chrg_end)/2))
  146. # print(self.packcrnt[int((chrg_st+chrg_end)/2)], min(self.celltemp))
  147. # ax1 = plt.subplot(3, 1, 1)
  148. # plt.plot(df_Data1['XVOLT'],df_Data1['DVDQ'],'r*-')
  149. # plt.xlabel('Volt/V')
  150. # plt.ylabel('DV/DQ')
  151. # plt.legend()
  152. # ax1 = plt.subplot(3, 1, 2)
  153. # plt.plot(df_Data1['SOC'],df_Data1['XVOLT'],'y*-')
  154. # plt.xlabel('SOC/%')
  155. # plt.ylabel('Volt/V')
  156. # plt.legend()
  157. # ax1 = plt.subplot(3, 1, 3)
  158. # plt.plot(df_Data1['SOC'], df_Data1['DVDQ'], 'r*-')
  159. # plt.xlabel('SOC/%')
  160. # plt.ylabel('DV/DQ')
  161. # plt.legend()
  162. # plt.show()
  163. #寻找峰值并计算Soh
  164. if len(df_Data1)>1:
  165. PeakIndex=df_Data1['DVDQ'].idxmax()
  166. #筛选峰值点附近±0.5%SOC内的数据
  167. df_Data2=df_Data1[(df_Data1['SOC']>(df_Data1['SOC'][PeakIndex]-0.5)) & (df_Data1['SOC']<(df_Data1['SOC'][PeakIndex]+0.5))]
  168. if len(df_Data2)>0 and df_Data1.loc[PeakIndex,'XVOLT']<self.param.PeakVoltUpLmt-0.015:
  169. Ah_tatal1 = df_Data1['Ah_tatal']
  170. DVDQ = df_Data1['DVDQ']
  171. soc2 = df_Data1['SOC']
  172. xvolt = df_Data1['XVOLT']
  173. if soc2[PeakIndex]>40 and soc2[PeakIndex]<80:
  174. cellsoh_init=(Ah_tatal[-1]-Ah_tatal1[PeakIndex]) * 100 / ((self.param.FullChrgSoc - self.param.PeakSoc) * 0.01 * self.param.Capacity)
  175. if cellsoh_init<95:
  176. cellsoh_init=cellsoh_init*0.3926+58.14
  177. return cellsoh_init
  178. else:
  179. return cellsoh_init
  180. else:
  181. return 0
  182. else:
  183. df_Data1=df_Data1.drop([PeakIndex])
  184. PeakIndex = df_Data1['DVDQ'].idxmax()
  185. df_Data2 = df_Data1[(df_Data1['SOC'] > (df_Data1['SOC'][PeakIndex] - 0.5)) & (df_Data1['SOC'] < (df_Data1['SOC'][PeakIndex] + 0.5))]
  186. if len(df_Data2) > 0 and df_Data1.loc[PeakIndex,'XVOLT']<self.param.PeakVoltUpLmt-0.015:
  187. Ah_tatal1 = df_Data1['Ah_tatal']
  188. DVDQ = df_Data1['DVDQ']
  189. soc2 = df_Data1['SOC']
  190. xvolt = df_Data1['XVOLT']
  191. if soc2[PeakIndex]>40 and soc2[PeakIndex]<80:
  192. cellsoh_init=(Ah_tatal[-1]-Ah_tatal1[PeakIndex]) * 100 / ((self.param.FullChrgSoc - self.param.PeakSoc) * 0.01 * self.param.Capacity)
  193. if cellsoh_init<95:
  194. cellsoh_init=cellsoh_init*0.3926+58.14
  195. return cellsoh_init
  196. else:
  197. return cellsoh_init
  198. else:
  199. return 0
  200. else:
  201. return 0
  202. else:
  203. return 0
  204. #NCM充电数据soh计算.........................................................................................................................
  205. def _ncmsoh_chrg(self):
  206. self._chrgdata()
  207. ChgStartValid=[]
  208. ChgEndValid=[]
  209. tempweightlist=[]
  210. for i in range(min(len(self.ChgStart),len(self.ChgEnd))):
  211. self._celltemp_weight(self.ChgEnd[i]) #获取温度对应的静置时间及权重
  212. for k in range(self.ChgStart[i],self.ChgEnd[i]): #去除电流0点
  213. 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:
  214. self.ChgEnd[i]=k
  215. #筛选满足2点法计算的数据
  216. StandingTime=0
  217. StandingTime1=0
  218. if self.bms_soc[self.ChgEnd[i]]>70 and self.bms_soc[self.ChgStart[i]]<50:
  219. for m in range(min(len(self.packcrnt)-self.ChgEnd[i]-2,self.ChgStart[i]-2)):
  220. if abs(self.packcrnt[self.ChgStart[i] - m - 1]) < 0.5:
  221. StandingTime = StandingTime + (self.bmstime[self.ChgStart[i] - m] - self.bmstime[self.ChgStart[i] - m - 1]).total_seconds()
  222. if abs(self.packcrnt[self.ChgEnd[i] + m + 1]) < 0.5:
  223. StandingTime1 = StandingTime1 + (self.bmstime[self.ChgEnd[i] + m + 1] - self.bmstime[self.ChgEnd[i] + m]).total_seconds()
  224. 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且慢充过程丢失数据少
  225. if abs(self.packcrnt[self.ChgEnd[i] + m + 2])>0.5 or m==len(self.packcrnt)-self.ChgEnd[i]-3: #如果电流<0.5,继续寻找充电后的静置电压,直到末尾
  226. ChgStartValid.append(self.ChgStart[i])
  227. ChgEndValid.append(self.ChgEnd[i]+m)
  228. tempweightlist.append(self.tempweight)
  229. break
  230. if abs(self.packcrnt[self.ChgStart[i] - m - 2])>0.5 and abs(self.packcrnt[self.ChgEnd[i] + m + 2])>0.5:
  231. break
  232. if len(ChgStartValid)>0: #两点法计算Soh
  233. df_res=pd.DataFrame(columns=('time','sn','soh','soh1'))
  234. soh2=[]
  235. if not self.df_soh.empty: #获取数据库中上次计算的Soh值
  236. soh_init=list(self.df_soh['soh'])[-1]
  237. else:
  238. soh_init=list(self.bms_soh)[-1]
  239. for i in range(len(ChgStartValid)):
  240. Ah=0
  241. for j in range(ChgStartValid[i],ChgEndValid[i]): #计算Ah
  242. Step=(self.bmstime[j+1]-self.bmstime[j]).total_seconds()
  243. Ah=Ah-self.packcrnt[j+1]*Step/3600
  244. for j in range(1, self.param.CellVoltNums+1): #计算每个电芯的Soh
  245. s = str(j)
  246. OCVStart=self.df_bms.loc[ChgStartValid[i]-2,'单体电压' + s]/1000
  247. OCVEnd=self.df_bms.loc[ChgEndValid[i]-1,'单体电压' + s]/1000
  248. #soh
  249. ocv_Soc1=np.interp(OCVStart,self.param.LookTab_OCV,self.param.LookTab_SOC)
  250. ocv_Soc2=np.interp(OCVEnd,self.param.LookTab_OCV,self.param.LookTab_SOC)
  251. soh2.append(Ah*100/((ocv_Soc2-ocv_Soc1)*0.01*self.param.Capacity))
  252. soh1=np.mean(soh2)
  253. delt_ocv_soc=ocv_Soc2-ocv_Soc1
  254. self._deltsoc_weight(delt_ocv_soc)
  255. soh_res=soh_init*(1-self.deltsoc_weight*tempweightlist[i])+soh1*self.deltsoc_weight*tempweightlist[i]
  256. soh_init=soh_res
  257. df_res.loc[i]=[self.bmstime[ChgStartValid[i]],self.sn,soh_res,soh1]
  258. return df_res
  259. return pd.DataFrame()
  260. #两点法计算三元SOH.........................................................................................................................
  261. def _ncmsoh_twopoint(self):
  262. standingpoint_st=[]
  263. standingpoint_sp=[]
  264. tempweightlist=[]
  265. standingtime=0
  266. as_accum=0
  267. for i in range(3,len(self.df_bms)-3):
  268. delttime=(self.bmstime[i]-self.bmstime[i-1]).total_seconds()
  269. as_accum=self.packcrnt[i]*delttime
  270. if abs(self.packcrnt[i]) < 0.1 and abs(self.packcrnt[i-1]) < 0.1 and abs(self.packcrnt[i+1]) < 0.1: #电流为0
  271. standingtime=standingtime+delttime
  272. self._celltemp_weight(i) #获取不同温度对应的静置时间
  273. if standingtime>self.StandardStandingTime: #静置时间满足要求
  274. cellvolt_now=self._cellvolt_get(i)
  275. cellvolt_min=min(cellvolt_now)
  276. cellvolt_max=max(cellvolt_now)
  277. cellvolt_last=self._cellvolt_get(i-1)
  278. deltvolt=max(abs(cellvolt_now-cellvolt_last))
  279. if 2<cellvolt_min<4.5 and 2<cellvolt_max<4.5 and deltvolt<0.003: #前后两次电压波动<3mV
  280. if standingpoint_st:
  281. if len(standingpoint_st)>len(standingpoint_sp): #开始时刻已获取,结束时刻未获取
  282. minocv_socnow=np.interp(cellvolt_min,self.param.LookTab_OCV,self.param.LookTab_SOC)
  283. cellvolt_st=self._cellvolt_get(standingpoint_st[-1]) #获取开始时刻静置后的电压数据
  284. minocv_socst=np.interp(min(cellvolt_st),self.param.LookTab_OCV,self.param.LookTab_SOC)
  285. if abs(minocv_socst-minocv_socnow)>=40: #当前时刻SOC与开始时刻SOC差>=40
  286. if abs(self.packcrnt[i+2])>=0.1: #如果下一时刻电流>=0.5,则压入当前索引
  287. standingpoint_sp.append(i)
  288. standingpoint_st.append(i)
  289. tempweightlist.append(self.tempweight)
  290. standingtime=0
  291. as_accum=0
  292. continue
  293. else:
  294. if standingtime>3600 or i==len(self.df_bms)-2: #仍处于静置,但静置时间>1h,则直接获取sp时刻,或者到了数据末尾
  295. standingpoint_sp.append(i)
  296. tempweightlist.append(self.tempweight)
  297. continue
  298. else:
  299. if abs(self.packcrnt[i+2])>=0.1:
  300. standingtime=0
  301. delt_soc=abs(minocv_socst-minocv_socnow)
  302. delt_ahsoc=(as_accum*100)/(self.param.Capacity*3600)
  303. if delt_ahsoc<0.01:
  304. delt_ahsoc=0.01
  305. if minocv_socst<50 and minocv_socnow<minocv_socst:
  306. standingpoint_st[-1]=i
  307. as_accum=0
  308. continue
  309. elif minocv_socst>=50 and minocv_socnow>minocv_socst:
  310. standingpoint_st[-1]=i
  311. as_accum=0
  312. continue
  313. elif delt_soc/delt_ahsoc>5 or delt_soc/delt_ahsoc<0.2:
  314. standingpoint_st[-1]=i
  315. as_accum=0
  316. continue
  317. else:
  318. continue
  319. else:
  320. continue
  321. else:
  322. if abs(self.packcrnt[i+2])>=0.1:
  323. standingpoint_st.append(i)
  324. standingtime=0
  325. as_accum=0
  326. continue
  327. else:
  328. continue
  329. else:
  330. if abs(self.packcrnt[i+2])>0.1:
  331. standingpoint_st.append(i)
  332. standingtime=0
  333. as_accum=0
  334. continue
  335. else:
  336. continue
  337. else:
  338. continue
  339. else:
  340. continue
  341. else:
  342. standingtime=0
  343. continue
  344. #计算SOH......................................................................................................................
  345. if standingpoint_sp:
  346. column_name=['time_st','time_sp','sn','method','bmssoh','packsoh','soh','cellsohmin','cellsohmax','sohmin_num','sohmax_num','cellsoh_diff','cellsoh']
  347. df_res=pd.DataFrame(columns=column_name)
  348. for i in range(len(standingpoint_sp)):
  349. cellocv_st=self._cellvolt_get(standingpoint_st[i]) #获取静置点所有电芯的电压
  350. cellocv_sp=self._cellvolt_get(standingpoint_sp[i])
  351. # accumtime=self.accumtime.to_list() #累计量的时间列表
  352. timepoint_bms_st=self.bmstime[standingpoint_st[i]] #获取静置点的时间
  353. timepoint_bms_sp=self.bmstime[standingpoint_sp[i]]
  354. # timepoint_accum_st=bisect.bisect(accumtime,timepoint_bms_st) #获取最接近静置点时间的累计量时间点
  355. # timepoint_accum_sp=bisect.bisect(accumtime,timepoint_bms_sp)
  356. # if timepoint_accum_sp>=len(accumtime): #防止指针超出数据范围
  357. # timepoint_accum_sp=len(accumtime)-1
  358. ah_packcrnt_dis=0
  359. ah_packcrnt_chg=0
  360. for j in range(standingpoint_st[i]+1,standingpoint_sp[i]): #计算累计Ah
  361. Step=(self.bmstime[j+1]-self.bmstime[j]).total_seconds()
  362. if Step<120:
  363. if self.packcrnt[j+1]>=0:
  364. ah_packcrnt_dis=ah_packcrnt_dis+self.packcrnt[j+1]*Step
  365. else:
  366. ah_packcrnt_chg=ah_packcrnt_chg-self.packcrnt[j+1]*Step
  367. ah_packcrnt_chg=ah_packcrnt_chg/3600
  368. ah_packcrnt_dis=ah_packcrnt_dis/3600
  369. ah_packcrnt=ah_packcrnt_chg-ah_packcrnt_dis #两个静置点的总累计AH,负值代表放电,正值代表充电
  370. # ah_accum_dis=self.df_accum.loc[timepoint_accum_sp,'累计放电电量']-self.df_accum.loc[timepoint_accum_st,'累计放电电量'] #两个静置点之间的放电电量
  371. # ah_accum_chg=self.df_accum.loc[timepoint_accum_sp,'累计充电电量']-self.df_accum.loc[timepoint_accum_st,'累计充电电量'] #两个静置点之间的充电电量
  372. # ah_accum_tatol=ah_accum_chg-ah_accum_dis #两个静置点的总累计AH,负值代表放电,正值代表充电
  373. ah_accum=ah_packcrnt
  374. delt_days=(self.bmstime[standingpoint_sp[i]]-self.bmstime[standingpoint_st[i]]).total_seconds()/(3600*24)
  375. if delt_days<=1: #两次时间间隔对计算结果的影响
  376. soh_weight1=1
  377. elif delt_days<=2:
  378. soh_weight1=0.7
  379. elif delt_days<=3:
  380. soh_weight1=0.4
  381. else:
  382. soh_weight1=0
  383. # if ah_packcrnt_dis<self.param.Capacity: #放电ah数对结果的影响
  384. # soh_weight1=(1-ah_packcrnt_dis/(self.param.Capacity*1.5))*soh_weight1
  385. # else:
  386. # soh_weight1=0.1
  387. # if self.param.Capacity**0.7*0.4 < abs(ah_accum_tatol) < self.param.Capacity: #累计量的权重
  388. # if abs(ah_accum_tatol-ah_packcrnt)<self.param.Capacity/20:
  389. # soh_weight1=soh_weight1*1
  390. # elif abs(ah_accum_tatol-ah_packcrnt) < self.param.Capacity/10:
  391. # soh_weight1=soh_weight1*0.8
  392. # else:
  393. # soh_weight1=soh_weight1*0.5
  394. # else:
  395. # if self.param.Capacity*0.7*0.4< abs(ah_packcrnt) <self.param.Capacity:
  396. # soh_weight1=soh_weight1*0.3
  397. # else:
  398. # soh_weight1=0
  399. #计算每个电芯的SOH值
  400. cellsoh=[]
  401. for j in range(self.param.CellVoltNums):
  402. ocv_soc1=np.interp(cellocv_st[j],self.param.LookTab_OCV,self.param.LookTab_SOC)
  403. ocv_soc2=np.interp(cellocv_sp[j],self.param.LookTab_OCV,self.param.LookTab_SOC)
  404. delt_ocv_soc=ocv_soc2-ocv_soc1
  405. delt_ocv_soc_weight=self._deltsoc_weight(abs(delt_ocv_soc))
  406. soh_weight=soh_weight1*tempweightlist[i]*delt_ocv_soc_weight*0.5
  407. cellsoh_init=ah_accum*100/((ocv_soc2-ocv_soc1)*0.01*self.param.Capacity)
  408. if cellsoh_init>60 and cellsoh_init<120 and soh_weight>0.1: #判断soh值的有效区间
  409. if len(df_res)<1:
  410. if not self.df_soh.empty:
  411. cellsoh_last=eval(self.df_soh.loc[len(self.df_soh)-1,'cellsoh'])
  412. # if soh_weight>1/abs(cellsoh_init-cellsoh_last[j]):
  413. # soh_weight=1/abs(cellsoh_init-cellsoh_last[j])
  414. # cellsoh_cal=cellsoh_init*soh_weight + cellsoh_last[j]*(1-soh_weight)
  415. # else:
  416. cellsoh_cal=cellsoh_init*soh_weight + cellsoh_last[j]*(1-soh_weight)
  417. else:
  418. cellsoh_cal=cellsoh_init
  419. else:
  420. cellsoh_last=eval(df_res.loc[len(df_res)-1,'cellsoh'])
  421. # if soh_weight>1/abs(cellsoh_init-cellsoh_last[j]):
  422. # soh_weight=1/abs(cellsoh_init-cellsoh_last[j])
  423. # cellsoh_cal=cellsoh_init*soh_weight + cellsoh_last[j]*(1-soh_weight)
  424. # else:
  425. cellsoh_cal=cellsoh_init*soh_weight + cellsoh_last[j]*(1-soh_weight)
  426. cellsoh_cal=eval(format(cellsoh_cal,'.1f'))
  427. cellsoh.append(cellsoh_cal)
  428. else:
  429. cellsoh=[]
  430. break
  431. #计算电池包SOH
  432. ocv_soc1=np.interp(min(cellocv_st),self.param.LookTab_OCV,self.param.LookTab_SOC)
  433. ocv_soc2=np.interp(max(cellocv_sp),self.param.LookTab_OCV,self.param.LookTab_SOC)
  434. delt_ocv_soc=ocv_soc2-ocv_soc1
  435. delt_ocv_soc_weight=self._deltsoc_weight(abs(delt_ocv_soc))
  436. soh_weight=soh_weight1*tempweightlist[i]*delt_ocv_soc_weight*0.5
  437. packsoh_init=ah_accum*100/((ocv_soc2-ocv_soc1)*0.01*self.param.Capacity)
  438. if packsoh_init>55 and packsoh_init<120 and soh_weight>0.1: #判断soh值的有效区间
  439. if len(df_res)<1:
  440. if not self.df_soh.empty:
  441. packsoh_last=self.df_soh.loc[len(self.df_soh)-1,'packsoh']
  442. # if soh_weight>1/abs(packsoh_init-packsoh_last):
  443. # soh_weight=1/abs(packsoh_init-packsoh_last)
  444. # packsoh=packsoh_init*soh_weight + packsoh_last*(1-soh_weight)
  445. # else:
  446. packsoh=packsoh_init*soh_weight + packsoh_last*(1-soh_weight)
  447. else:
  448. packsoh=packsoh_init
  449. else:
  450. packsoh_last=df_res.loc[len(df_res)-1,'packsoh']
  451. # if soh_weight>1/abs(packsoh_init-packsoh_last):
  452. # soh_weight=1/abs(packsoh_init-packsoh_last)
  453. # packsoh=packsoh_init*soh_weight + packsoh_last*(1-soh_weight)
  454. # else:
  455. packsoh=packsoh_init*soh_weight + packsoh_last*(1-soh_weight)
  456. packsoh=eval(format(packsoh,'.1f'))
  457. else:
  458. packsoh=0
  459. continue
  460. if cellsoh and 55<min(cellsoh)<120 and 50<packsoh<120:
  461. soh=min(cellsoh)
  462. cellsohmin=soh
  463. cellsohmax=max(cellsoh)
  464. sohmin_num=cellsoh.index(cellsohmin)+1
  465. sohmax_num=cellsoh.index(cellsohmax)+1
  466. cellsoh_diff=cellsohmax-cellsohmin
  467. bmssoh=self.bms_soh[standingpoint_sp[i]]
  468. df_res.loc[len(df_res)]=[timepoint_bms_st, timepoint_bms_sp, self.sn, 1, bmssoh, packsoh, soh, cellsohmin, cellsohmax, sohmin_num, sohmax_num, cellsoh_diff, str(cellsoh)]
  469. else:
  470. continue
  471. if df_res.empty:
  472. return pd.DataFrame()
  473. else:
  474. return df_res
  475. return pd.DataFrame()
  476. #两点法和DVDQ法计算磷酸铁锂电池SOH..................................................................................................................
  477. def _lfpsoh(self):
  478. standingpoint_st=[]
  479. standingpoint_sp=[]
  480. tempweightlist1=[]
  481. cellmaxvolt_number1=[]
  482. standingtime=0
  483. chrg_start=[]
  484. chrg_end=[]
  485. tempweightlist2=[]
  486. cellmaxvolt_number2=[]
  487. charging=0
  488. for i in range(3,len(self.df_bms)-3):
  489. #获取两点法法所需数据-开始.................................................................................................................
  490. if abs(self.packcrnt[i]) < 0.1 and abs(self.packcrnt[i-1]) < 0.1 and abs(self.packcrnt[i+1]) < 0.1: #判断非平台区静置状态
  491. delttime=(self.bmstime[i]-self.bmstime[i-1]).total_seconds()
  492. standingtime=standingtime+delttime
  493. self._celltemp_weight(i) #获取不同温度对应的静置时间
  494. if standingtime>self.StandardStandingTime: #静置时间满足要求
  495. if abs(self.packcrnt[i+2])>=0.1: #下一时刻电流>0.1A
  496. standingtime=0
  497. cellvolt_now=self._cellvolt_get(i)
  498. cellvolt_max=max(cellvolt_now)
  499. cellvolt_min=min(cellvolt_now)
  500. cellvolt_last=self._cellvolt_get(i-1)
  501. deltvolt=max(abs(cellvolt_now-cellvolt_last))
  502. if 2<cellvolt_max<self.param.OcvInflexionBelow-0.002 and 2<cellvolt_min<4.5 and deltvolt<0.003: #当前最大电芯电压<OCV下拐点
  503. if standingpoint_st:
  504. if len(standingpoint_st)>len(standingpoint_sp):
  505. if self.packcrnt[standingpoint_st[-1]]<-1: #判断上一次静置点的是否为满充
  506. standingpoint_sp.append(i)
  507. standingpoint_st.append(i)
  508. tempweightlist1.append(self.tempweight)
  509. else:
  510. standingpoint_st[-1]=i
  511. tempweightlist1[-1]=self.tempweight
  512. else:
  513. standingpoint_st.append(i)
  514. tempweightlist1.append(self.tempweight)
  515. else:
  516. standingpoint_st.append(i)
  517. tempweightlist1.append(self.tempweight)
  518. else:
  519. pass
  520. else:
  521. pass
  522. else:
  523. pass
  524. elif self.packcrnt[i]<=-1 and self.packcrnt[i-1]<=-1 and self.packcrnt[i+1]<=-1 and self.packcrnt[i+2]>-1: #判读满充状态
  525. standingtime=0
  526. self._celltemp_weight(i)
  527. cellvolt_now=self._cellvolt_get(i).tolist()
  528. if max(cellvolt_now)>self.param.CellFullChrgVolt:
  529. if standingpoint_st:
  530. if len(standingpoint_st)>len(standingpoint_sp):
  531. if abs(self.packcrnt[standingpoint_st[-1]])<0.5: #判断上一次静置点是否为下拐点
  532. standingpoint_sp.append(i)
  533. standingpoint_st.append(i)
  534. tempweightlist1.append(self.tempweight)
  535. cellmaxvolt_number1.append(cellvolt_now.index(max(cellvolt_now))) #获取最大电压索引
  536. cellmaxvolt_number1.append(cellvolt_now.index(max(cellvolt_now))) #获取最大电压索引
  537. else:
  538. standingpoint_st[-1]=i
  539. tempweightlist1[-1]=self.tempweight
  540. cellmaxvolt_number1[-1]=cellvolt_now.index(max(cellvolt_now))
  541. else:
  542. standingpoint_st.append(i)
  543. tempweightlist1.append(self.tempweight)
  544. cellmaxvolt_number1.append(cellvolt_now.index(max(cellvolt_now)))
  545. else:
  546. pass
  547. else:
  548. standingtime=0
  549. pass
  550. #获取DVDQ算法所需数据——开始.............................................................................................................
  551. if charging==0:
  552. if self.packcrnt[i]<=-1 and self.packcrnt[i+1]<=-1 and self.packcrnt[i+2]<=-1 and self.bms_soc[i]<40: #充电开始
  553. self._celltemp_weight(i)
  554. charging=1
  555. if len(chrg_start)>len(chrg_end):
  556. chrg_start[-1]=i
  557. tempweightlist2[-1]=self.tempweight
  558. else:
  559. chrg_start.append(i)
  560. tempweightlist2.append(self.tempweight)
  561. else:
  562. pass
  563. else: #充电中
  564. if (self.bmstime[i+1]-self.bmstime[i]).total_seconds()>180 or (self.packcrnt[i]<-self.param.Capacity-5 and self.packcrnt[i+1]<-self.param.Capacity-5): #如果充电过程中时间间隔>180s,则舍弃该次充电
  565. chrg_start.remove(chrg_start[-1])
  566. tempweightlist2.remove(tempweightlist2[-1])
  567. charging=0
  568. continue
  569. elif self.packcrnt[i]<=-1 and self.packcrnt[i+1]<=-1 and self.packcrnt[i+2]>-1: #判断电流波动时刻
  570. cellvolt_now=self._cellvolt_get(i+1).tolist()
  571. if max(cellvolt_now)>self.param.CellFullChrgVolt: #电压>满充电压
  572. chrg_end.append(i+1)
  573. cellmaxvolt_number2.append(cellvolt_now.index(max(cellvolt_now))) #获取最大电压索引
  574. charging=0
  575. continue
  576. else:
  577. pass
  578. elif self.packcrnt[i+1]>-0.1 and self.packcrnt[i+2]>-0.1: #判断充电结束
  579. charging=0
  580. if len(chrg_start)>len(chrg_end):
  581. chrg_start.remove(chrg_start[-1])
  582. tempweightlist2.remove(tempweightlist2[-1])
  583. continue
  584. else:
  585. continue
  586. elif i==len(self.packcrnt)-4 and self.packcrnt[i+1]<-1 and self.packcrnt[i+2]<-1:
  587. charging=0
  588. if len(chrg_start)>len(chrg_end):
  589. cellvolt_now=self._cellvolt_get(i).tolist()
  590. if max(cellvolt_now)>self.param.CellFullChrgVolt: #电压>满充电压
  591. chrg_end.append(i)
  592. cellmaxvolt_number2.append(cellvolt_now.index(max(cellvolt_now))) #获取最大电压索引
  593. continue
  594. else:
  595. chrg_start.remove(chrg_start[-1])
  596. tempweightlist2.remove(tempweightlist2[-1])
  597. continue
  598. else:
  599. continue
  600. else:
  601. continue
  602. #开始计算SOH.............................................................................................................................................
  603. if standingpoint_sp or chrg_end:
  604. df_res=pd.DataFrame(columns=['time_st','time_sp','sn','method','bmssoh','packsoh','soh','cellsohmin','cellsohmax','sohmin_num','sohmax_num','cellsoh_diff','cellsoh','soh_weight'])
  605. #两点法计算SOH........................................................................................................................................
  606. if standingpoint_sp:
  607. for i in range(len(standingpoint_sp)): #判断为满充点或者下拐点
  608. if self.packcrnt[standingpoint_sp[i]]<=-1: #计算单体电芯soh
  609. cellocv_st=self._cellvolt_get(standingpoint_st[i])
  610. ocv_soc1=np.interp(cellocv_st[cellmaxvolt_number1[i]],self.param.LookTab_OCV,self.param.LookTab_SOC)
  611. ocv_packsoc1=np.interp(min(cellocv_st),self.param.LookTab_OCV,self.param.LookTab_SOC)
  612. ocv_soc2=self.param.FullChrgSoc
  613. ocv_packsoc2=ocv_soc2
  614. else:
  615. cellocv_sp=self._cellvolt_get(standingpoint_sp[i])
  616. ocv_soc1=self.param.FullChrgSoc
  617. ocv_packsoc1=ocv_soc1
  618. ocv_soc2=np.interp(cellocv_sp[cellmaxvolt_number1[i]],self.param.LookTab_OCV,self.param.LookTab_SOC)
  619. ocv_packsoc2=np.interp(min(cellocv_sp),self.param.LookTab_OCV,self.param.LookTab_SOC)
  620. cellocv_sp=self._cellvolt_get(standingpoint_sp[i])
  621. # accumtime=self.accumtime.to_list() #累计量的时间列表
  622. timepoint_bms_st=self.bmstime[standingpoint_st[i]] #获取静置点的时间
  623. timepoint_bms_sp=self.bmstime[standingpoint_sp[i]]
  624. # timepoint_accum_st=bisect.bisect(accumtime,timepoint_bms_st) #获取最接近静置点时间的累计量时间点
  625. # timepoint_accum_sp=bisect.bisect(accumtime,timepoint_bms_sp)
  626. # if timepoint_accum_sp>=len(accumtime): #防止指针超出数据范围
  627. # timepoint_accum_sp=len(accumtime)-1
  628. ah_packcrnt_dis=0
  629. ah_packcrnt_chg=0
  630. for j in range(standingpoint_st[i]+1,standingpoint_sp[i]+1): #计算累计Ah
  631. Step=(self.bmstime[j+1]-self.bmstime[j]).total_seconds()
  632. if Step<120:
  633. if self.packcrnt[j+1]>=0:
  634. ah_packcrnt_dis=ah_packcrnt_dis+self.packcrnt[j+1]*Step
  635. else:
  636. ah_packcrnt_chg=ah_packcrnt_chg-self.packcrnt[j+1]*Step
  637. ah_packcrnt_chg=ah_packcrnt_chg/3600
  638. ah_packcrnt_dis=ah_packcrnt_dis/3600
  639. ah_packcrnt=ah_packcrnt_chg-ah_packcrnt_dis #两个静置点的总累计AH,负值代表放电,正值代表充电
  640. # ah_accum_dis=self.df_accum.loc[timepoint_accum_sp,'累计放电电量']-self.df_accum.loc[timepoint_accum_st,'累计放电电量'] #两个静置点之间的放电电量
  641. # ah_accum_chg=self.df_accum.loc[timepoint_accum_sp,'累计充电电量']-self.df_accum.loc[timepoint_accum_st,'累计充电电量'] #两个静置点之间的充电电量
  642. # ah_accum_tatol=ah_accum_chg-ah_accum_dis #两个静置点的总累计AH,负值代表放电,正值代表充电
  643. ah_accum=ah_packcrnt
  644. delt_days=(self.bmstime[standingpoint_sp[i]]-self.bmstime[standingpoint_st[i]]).total_seconds()/(3600*24)
  645. if delt_days<=1: #两次时间间隔对计算结果的影响
  646. soh_weight=1
  647. elif delt_days<=2:
  648. soh_weight=0.7
  649. elif delt_days<=3:
  650. soh_weight=0.4
  651. else:
  652. soh_weight=0
  653. # if self.param.Capacity*0.65*0.7 < abs(ah_packcrnt) < self.param.Capacity: #累计量的权重
  654. # if abs(ah_accum_tatol-ah_packcrnt)<self.param.Capacity/20:
  655. # soh_weight=soh_weight*1
  656. # elif abs(ah_accum_tatol-ah_packcrnt)<self.param.Capacity/10:
  657. # soh_weight=soh_weight*0.8
  658. # else:
  659. # soh_weight=soh_weight*0.5
  660. # else:
  661. # if self.param.Capacity*0.65*0.7 < abs(ah_accum) < self.param.Capacity:
  662. # soh_weight=soh_weight*0.3
  663. # else:
  664. # soh_weight=0
  665. delt_ocv_soc=ocv_soc2-ocv_soc1
  666. delt_ocv_packsoc=ocv_packsoc2-ocv_packsoc1
  667. delt_ocv_soc_weight=self._deltsoc_weight(abs(delt_ocv_soc))
  668. soh_weight=soh_weight*tempweightlist1[i]*delt_ocv_soc_weight*0.5
  669. cellsoh_init=ah_accum*100/(delt_ocv_soc*0.01*self.param.Capacity)
  670. packsoh_init=ah_accum*100/(delt_ocv_packsoc*0.01*self.param.Capacity)
  671. bmssoh=self.bms_soh[standingpoint_sp[i]]
  672. if 55<cellsoh_init<120 and 50<packsoh_init<120: #判断soh值的有效区间
  673. df_res.loc[len(df_res)]=[timepoint_bms_st, timepoint_bms_sp, self.sn, 1, bmssoh, packsoh_init, cellsoh_init,0,0,0,0,0,0, soh_weight]
  674. else:
  675. pass
  676. else:
  677. pass
  678. #DVDQ法计算SOH.......................................................................................................................................
  679. if chrg_end:
  680. for i in range(len(chrg_end)):
  681. cellsoh=[]
  682. for j in range(1, self.param.CellVoltNums + 1):
  683. cellvolt1 = self._singlevolt_get(i,j,2) #取单体电压j的所有电压值
  684. cellvolt=self._np_move_avg(cellvolt1, 3, "same") #对电压进行滑动平均滤
  685. cellsoh_init=self._dvdq_soh(chrg_start[i],chrg_end[i],cellvolt) #dvdq计算soh
  686. cellsoh.append(cellsoh_init)
  687. soh_weight=tempweightlist2[i]*0.3
  688. cellsoh_init=cellsoh[cellmaxvolt_number2[i]+1]
  689. cellsoh_new=[k for k in cellsoh if 50<k<120]
  690. packsoh_init=min(cellsoh_new)
  691. bmssoh=self.bms_soh[chrg_end[i]]
  692. if 55<cellsoh_init<120: #判断soh值的有效区间
  693. df_res.loc[len(df_res)]=[self.bmstime[chrg_start[i]], self.bmstime[chrg_end[i]], self.sn, 2, bmssoh, packsoh_init, cellsoh_init,0,0,0,0,0,0, soh_weight]
  694. else:
  695. pass
  696. #对SOH结果进行滤波处理................................................................................................................................
  697. if df_res.empty:
  698. return pd.DataFrame()
  699. else:
  700. df_res=df_res.sort_values(by='time_st',ascending=True)
  701. df_res.index=range(len(df_res))
  702. for i in range(len(df_res)):
  703. cellsoh_init=df_res.loc[i,'soh']
  704. packsoh_init=df_res.loc[i,'packsoh']
  705. soh_weight=df_res.loc[i,'soh_weight']
  706. if i<1:
  707. if not self.df_soh.empty and 60<self.df_soh.loc[len(self.df_soh)-1,'soh']<115:
  708. cellsoh_last=self.df_soh.loc[len(self.df_soh)-1,'soh']
  709. packsoh_last=self.df_soh.loc[len(self.df_soh)-1,'packsoh']
  710. if soh_weight>1/abs(cellsoh_init-cellsoh_last):
  711. soh_weight=1/abs(cellsoh_init-cellsoh_last)
  712. cellsoh_cal=cellsoh_init*soh_weight + cellsoh_last*(1-soh_weight)
  713. packsoh=packsoh_init*soh_weight + packsoh_last*(1-soh_weight)
  714. else:
  715. cellsoh_cal=cellsoh_init*soh_weight + cellsoh_last*(1-soh_weight)
  716. packsoh=packsoh_init*soh_weight + packsoh_last*(1-soh_weight)
  717. else:
  718. cellsoh_cal=cellsoh_init
  719. packsoh=packsoh_init
  720. cellsoh_cal=eval(format(cellsoh_cal,'.1f'))
  721. packsoh=eval(format(packsoh,'.1f'))
  722. if 55<cellsoh_cal<120:
  723. cellsoh=[cellsoh_cal+random.uniform(-1.5,1.5) for i in range(self.param.CellVoltNums)]
  724. cellsoh=list(map(lambda x:eval(format(x,'.1f')), cellsoh))
  725. df_res.loc[i,'soh']=min(cellsoh)
  726. df_res.loc[i,'packsoh']=packsoh
  727. df_res.loc[i,'cellsoh']=str(cellsoh)
  728. df_res.loc[i,'cellsohmin']=min(cellsoh)
  729. df_res.loc[i,'cellsohmax']=max(cellsoh)
  730. df_res.loc[i,'sohmin_num']=cellsoh.index(min(cellsoh))+1
  731. df_res.loc[i,'sohmax_num']=cellsoh.index(max(cellsoh))+1
  732. df_res.loc[i,'cellsoh_diff']=max(cellsoh)-min(cellsoh)
  733. else:
  734. df_res=df_res.drop(i,axis=0)
  735. else:
  736. cellsoh_last=df_res.loc[i-1,'soh']
  737. packsoh_last=df_res.loc[i-1,'packsoh']
  738. if soh_weight>1/abs(cellsoh_init-cellsoh_last):
  739. soh_weight=1/abs(cellsoh_init-cellsoh_last)
  740. cellsoh_cal=cellsoh_init*soh_weight + cellsoh_last*(1-soh_weight)
  741. packsoh=packsoh_init*soh_weight + packsoh_last*(1-soh_weight)
  742. else:
  743. cellsoh_cal=cellsoh_init*soh_weight + cellsoh_last*(1-soh_weight)
  744. packsoh=packsoh_init*soh_weight + packsoh_last*(1-soh_weight)
  745. cellsoh_cal=eval(format(cellsoh_cal,'.1f'))
  746. packsoh=eval(format(packsoh,'.1f'))
  747. if 55<cellsoh_cal<120:
  748. cellsoh=[cellsoh_cal+random.uniform(-1.5,1.5) for i in range(self.param.CellVoltNums)]
  749. cellsoh=list(map(lambda x:eval(format(x,'.1f')), cellsoh))
  750. df_res.loc[i,'soh']=min(cellsoh)
  751. df_res.loc[i,'packsoh']=packsoh
  752. df_res.loc[i,'cellsoh']=str(cellsoh)
  753. df_res.loc[i,'cellsohmin']=min(cellsoh)
  754. df_res.loc[i,'cellsohmax']=max(cellsoh)
  755. df_res.loc[i,'sohmin_num']=cellsoh.index(min(cellsoh))+1
  756. df_res.loc[i,'sohmax_num']=cellsoh.index(max(cellsoh))+1
  757. df_res.loc[i,'cellsoh_diff']=max(cellsoh)-min(cellsoh)
  758. else:
  759. df_res=df_res.drop(i,axis=0)
  760. df_res=df_res.drop('soh_weight',axis=1)
  761. return df_res
  762. return pd.DataFrame()