CBMSBatUniform.py 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389
  1. import pandas as pd
  2. import numpy as np
  3. import datetime
  4. import bisect
  5. import matplotlib.pyplot as plt
  6. from LIB.MIDDLE.CellStateEstimation.Common import BatParam
  7. class BatUniform():
  8. def __init__(self,sn,celltype,df_bms): #参数初始化
  9. self.sn=sn
  10. self.celltype=celltype
  11. self.param=BatParam.BatParam(celltype)
  12. self.df_bms=df_bms
  13. self.packcrnt=df_bms['总电流[A]']*self.param.PackCrntDec
  14. self.packvolt=df_bms['总电压[V]']
  15. self.bms_soc=df_bms['SOC[%]']
  16. self.bmstime= pd.to_datetime(df_bms['时间戳'], format='%Y-%m-%d %H:%M:%S')
  17. self.cellvolt_name=['单体电压'+str(x) for x in range(1,self.param.CellVoltNums+1)]
  18. self.celltemp_name=['单体温度'+str(x) for x in range(1,self.param.CellTempNums+1)]
  19. def batuniform(self):
  20. if self.celltype<50:
  21. df_res=self._ncm_uniform()
  22. return df_res
  23. else:
  24. df_res=self._lfp_uniform()
  25. return df_res
  26. #定义滑动滤波函数........................................................................................................................................
  27. def _np_move_avg(self,a, n, mode="same"):
  28. return (np.convolve(a, np.ones((n,)) / n, mode=mode))
  29. #寻找当前行数据的最小温度值................................................................................................................................
  30. def _celltemp_weight(self,num):
  31. celltemp = list(self.df_bms.loc[num,self.celltemp_name])
  32. celltemp.remove(min(celltemp))
  33. self.celltemp=celltemp
  34. if self.celltype==99:
  35. if min(celltemp)>=20:
  36. self.tempweight=1
  37. self.StandardStandingTime=2400
  38. elif min(celltemp)>=10:
  39. self.tempweight=0.6
  40. self.StandardStandingTime=3600
  41. elif min(celltemp)>=5:
  42. self.tempweight=0.
  43. self.StandardStandingTime=4800
  44. else:
  45. self.tempweight=0.1
  46. self.StandardStandingTime=7200
  47. else:
  48. if min(celltemp)>=20:
  49. self.tempweight=1
  50. self.StandardStandingTime=1800
  51. elif min(celltemp)>=10:
  52. self.tempweight=0.8
  53. self.StandardStandingTime=2400
  54. elif min(celltemp)>=5:
  55. self.tempweight=0.6
  56. self.StandardStandingTime=3600
  57. else:
  58. self.tempweight=0.2
  59. self.StandardStandingTime=7200
  60. #获取当前行所有电压数据............................................................................................................................
  61. def _cellvolt_get(self,num):
  62. cellvolt = np.array(self.df_bms.loc[num,self.cellvolt_name])/1000
  63. return cellvolt
  64. #获取单个电压值.................................................................................................
  65. def _singlevolt_get(self,num,series,mode): #mode==1取当前行单体电压值,mode==2取某个单体所有电压值
  66. s=str(series)
  67. if mode==1:
  68. singlevolt=self.df_bms.loc[num,'单体电压' + s]/1000
  69. return singlevolt
  70. else:
  71. singlevolt=self.df_bms['单体电压' + s]/1000
  72. return singlevolt
  73. #寻找DVDQ的峰值点,并返回..........................................................................................................................
  74. def _dvdq_peak(self, time, soc, cellvolt, packcrnt):
  75. cellvolt = self._np_move_avg(cellvolt, 3, mode="same")
  76. Soc = 0
  77. Ah = 0
  78. Volt = cellvolt[0]
  79. DV_Volt = []
  80. DQ_Ah = []
  81. DVDQ = []
  82. time1 = []
  83. soc1 = []
  84. soc2 = []
  85. xvolt=[]
  86. for m in range(1, len(time)):
  87. Step = (time[m] - time[m - 1]).total_seconds()
  88. Soc = Soc - packcrnt[m] * Step * 100 / (3600 * self.param.Capacity)
  89. Ah = Ah - packcrnt[m] * Step / 3600
  90. if (cellvolt[m]-Volt)>0.0019 and Ah>0:
  91. DQ_Ah.append(Ah)
  92. DV_Volt.append(cellvolt[m]-Volt)
  93. DVDQ.append((DV_Volt[-1])/Ah)
  94. xvolt.append(cellvolt[m])
  95. Volt=cellvolt[m]
  96. Ah = 0
  97. soc1.append(Soc)
  98. time1.append(time[m])
  99. soc2.append(soc[m])
  100. #切片,去除前后10min的数据
  101. df_Data1 = pd.DataFrame({'time': time1,
  102. 'SOC': soc2,
  103. 'DVDQ': DVDQ,
  104. 'AhSoc': soc1,
  105. 'DQ_Ah':DQ_Ah,
  106. 'DV_Volt':DV_Volt,
  107. 'XVOLT':xvolt})
  108. start_time=df_Data1.loc[0,'time']
  109. start_time=start_time+datetime.timedelta(seconds=900)
  110. end_time=df_Data1.loc[len(time1)-1,'time']
  111. end_time=end_time-datetime.timedelta(seconds=1200)
  112. if soc2[0]<36:
  113. df_Data1=df_Data1[(df_Data1['SOC']>40) & (df_Data1['SOC']<80)]
  114. else:
  115. df_Data1=df_Data1[(df_Data1['time']>start_time) & (df_Data1['SOC']<80)]
  116. df_Data1=df_Data1[(df_Data1['XVOLT']>self.param.PeakVoltLowLmt) & (df_Data1['XVOLT']<self.param.PeakVoltUpLmt)]
  117. # print(packcrnt[int(len(time)/2)], min(self.celltemp))
  118. ax1 = plt.subplot(3, 1, 1)
  119. plt.plot(df_Data1['SOC'],df_Data1['DQ_Ah'],'g*-')
  120. plt.xlabel('SOC/%')
  121. plt.ylabel('DQ_Ah')
  122. plt.legend()
  123. ax1 = plt.subplot(3, 1, 2)
  124. plt.plot(df_Data1['SOC'],df_Data1['XVOLT'],'y*-')
  125. plt.xlabel('SOC/%')
  126. plt.ylabel('Volt/V')
  127. plt.legend()
  128. ax1 = plt.subplot(3, 1, 3)
  129. plt.plot(df_Data1['SOC'], df_Data1['DVDQ'], 'r*-')
  130. plt.xlabel('SOC/%')
  131. plt.ylabel('DV/DQ')
  132. plt.legend()
  133. # plt.show()
  134. if len(df_Data1)>2: #寻找峰值点,且峰值点个数>2
  135. PeakIndex = df_Data1['DVDQ'].idxmax()
  136. df_Data2 = df_Data1[(df_Data1['SOC'] > (df_Data1['SOC'][PeakIndex] - 0.5)) & (df_Data1['SOC'] < (df_Data1['SOC'][PeakIndex] + 0.5))]
  137. if len(df_Data2) > 2 and df_Data1.loc[PeakIndex,'XVOLT']<self.param.PeakVoltUpLmt-0.019:
  138. return df_Data1['AhSoc'][PeakIndex]
  139. else:
  140. df_Data1 = df_Data1.drop([PeakIndex])
  141. PeakIndex = df_Data1['DVDQ'].idxmax()
  142. df_Data2 = df_Data1[(df_Data1['SOC'] > (df_Data1['SOC'][PeakIndex] - 0.5)) & (df_Data1['SOC'] < (df_Data1['SOC'][PeakIndex] + 0.5))]
  143. if len(df_Data2) > 2 and df_Data1.loc[PeakIndex,'XVOLT']<self.param.PeakVoltUpLmt-0.019:
  144. return df_Data1['AhSoc'][PeakIndex]
  145. else:
  146. return 0
  147. else:
  148. return 0
  149. #三元电池一致性计算.................................................................................................................................
  150. def _ncm_uniform(self):
  151. column_name=['time','sn','cellsoc_diff','cellvolt_diff','cellmin_num','cellmax_num']
  152. df_res=pd.DataFrame(columns=column_name)
  153. standingtime=0
  154. for i in range(1,len(self.df_bms)-2):
  155. if abs(self.packcrnt[i]) < 0.1 and abs(self.packcrnt[i-1]) < 0.1 and abs(self.packcrnt[i+1]) < 0.1: #电流为0
  156. delttime=(self.bmstime[i]-self.bmstime[i-1]).total_seconds()
  157. standingtime=standingtime+delttime
  158. self._celltemp_weight(i) #获取不同温度对应的静置时间
  159. if standingtime>self.StandardStandingTime: #静置时间满足要求
  160. if abs(self.packcrnt[i+2]) >= 0.1:
  161. standingtime=0
  162. cellvolt_now=self._cellvolt_get(i)
  163. cellvolt_min=min(cellvolt_now)
  164. cellvolt_max=max(cellvolt_now)
  165. cellvolt_last=self._cellvolt_get(i-1)
  166. deltvolt=max(abs(cellvolt_now-cellvolt_last))
  167. if 3<cellvolt_min<4.5 and 3<cellvolt_max<4.5 and deltvolt<0.003:
  168. cellmin_num=list(cellvolt_now).index(cellvolt_min)+1
  169. cellmax_num=list(cellvolt_now).index(cellvolt_max)+1
  170. cellsoc_min=np.interp(cellvolt_min,self.param.LookTab_OCV,self.param.LookTab_SOC)
  171. cellsoc_max=np.interp(cellvolt_max,self.param.LookTab_OCV,self.param.LookTab_SOC)
  172. cellvolt_diff=(cellvolt_max-cellvolt_min)*1000
  173. cellsoc_diff=cellsoc_max-cellsoc_min
  174. cellsoc_diff=eval(format(cellsoc_diff,'.1f'))
  175. cellvolt_diff=eval(format(cellvolt_diff,'.0f'))
  176. df_res.loc[len(df_res)]=[self.bmstime[i], self.sn, cellsoc_diff, cellvolt_diff, cellmin_num, cellmax_num]
  177. elif standingtime>3600*12:
  178. standingtime=0
  179. cellvolt_now=self._cellvolt_get(i)
  180. cellvolt_min=min(cellvolt_now)
  181. cellvolt_max=max(cellvolt_now)
  182. cellvolt_last=self._cellvolt_get(i-1)
  183. deltvolt=max(abs(cellvolt_now-cellvolt_last))
  184. if 3<cellvolt_min<4.5 and 3<cellvolt_max<4.5 and deltvolt<0.003:
  185. cellmin_num=list(cellvolt_now).index(cellvolt_min)+1
  186. cellmax_num=list(cellvolt_now).index(cellvolt_max)+1
  187. cellsoc_min=np.interp(cellvolt_min,self.param.LookTab_OCV,self.param.LookTab_SOC)
  188. cellsoc_max=np.interp(cellvolt_max,self.param.LookTab_OCV,self.param.LookTab_SOC)
  189. cellvolt_diff=(cellvolt_max-cellvolt_min)*1000
  190. cellsoc_diff=cellsoc_max-cellsoc_min
  191. cellsoc_diff=eval(format(cellsoc_diff,'.1f'))
  192. cellvolt_diff=eval(format(cellvolt_diff,'.0f'))
  193. df_res.loc[len(df_res)]=[self.bmstime[i], self.sn, cellsoc_diff, cellvolt_diff, cellmin_num, cellmax_num]
  194. elif i>=len(self.df_bms)-4:
  195. standingtime=0
  196. cellvolt_now=self._cellvolt_get(i)
  197. cellvolt_min=min(cellvolt_now)
  198. cellvolt_max=max(cellvolt_now)
  199. cellvolt_last=self._cellvolt_get(i-1)
  200. deltvolt=max(abs(cellvolt_now-cellvolt_last))
  201. if 3<cellvolt_min<4.5 and 3<cellvolt_max<4.5 and deltvolt<0.003:
  202. cellmin_num=list(cellvolt_now).index(cellvolt_min)+1
  203. cellmax_num=list(cellvolt_now).index(cellvolt_max)+1
  204. cellsoc_min=np.interp(cellvolt_min,self.param.LookTab_OCV,self.param.LookTab_SOC)
  205. cellsoc_max=np.interp(cellvolt_max,self.param.LookTab_OCV,self.param.LookTab_SOC)
  206. cellvolt_diff=(cellvolt_max-cellvolt_min)*1000
  207. cellsoc_diff=cellsoc_max-cellsoc_min
  208. cellsoc_diff=eval(format(cellsoc_diff,'.1f'))
  209. cellvolt_diff=eval(format(cellvolt_diff,'.0f'))
  210. df_res.loc[len(df_res)]=[self.bmstime[i], self.sn, cellsoc_diff, cellvolt_diff, cellmin_num, cellmax_num]
  211. break
  212. else:
  213. continue
  214. else:
  215. continue
  216. else:
  217. standingtime=0
  218. continue
  219. if df_res.empty: #返回计算结果
  220. return pd.DataFrame()
  221. else:
  222. return df_res
  223. #磷酸铁锂电池一致性计算.........................................................................................................................
  224. def _lfp_uniform(self):
  225. column_name=['time','sn','cellsoc_diff','cellvolt_diff','cellmin_num','cellmax_num']
  226. df_res=pd.DataFrame(columns=column_name)
  227. standingtime=0
  228. chrg_start=[]
  229. chrg_end=[]
  230. charging=0
  231. for i in range(3,len(self.df_bms)-3):
  232. #静置电压法计算电芯一致性
  233. if abs(self.packcrnt[i]) < 0.1 and abs(self.packcrnt[i-1]) < 0.1: #电流为0
  234. delttime=(self.bmstime[i]-self.bmstime[i-1]).total_seconds()
  235. standingtime=standingtime+delttime
  236. self._celltemp_weight(i) #获取不同温度对应的静置时间
  237. if standingtime>self.StandardStandingTime: #静置时间满足要求
  238. if abs(self.packcrnt[i+1]) >= 0.1:
  239. standingtime=0
  240. cellvolt_now=self._cellvolt_get(i)
  241. cellvolt_min=min(cellvolt_now)
  242. cellvolt_max=max(cellvolt_now)
  243. cellvolt_last=self._cellvolt_get(i-1)
  244. deltvolt=max(abs(cellvolt_now-cellvolt_last))
  245. if 2 < cellvolt_max < self.param.OcvInflexionBelow-0.002 and 2<cellvolt_min<4.5 and deltvolt<0.003:
  246. cellmin_num=list(cellvolt_now).index(cellvolt_min)+1
  247. cellmax_num=list(cellvolt_now).index(cellvolt_max)+1
  248. cellsoc_min=np.interp(cellvolt_min,self.param.LookTab_OCV,self.param.LookTab_SOC)
  249. cellsoc_max=np.interp(cellvolt_max,self.param.LookTab_OCV,self.param.LookTab_SOC)
  250. cellvolt_diff=(cellvolt_max-cellvolt_min)*1000
  251. cellsoc_diff=cellsoc_max-cellsoc_min
  252. cellsoc_diff=eval(format(cellsoc_diff,'.1f'))
  253. cellvolt_diff=eval(format(cellvolt_diff,'.0f'))
  254. df_res.loc[len(df_res)]=[self.bmstime[i], self.sn, cellsoc_diff, cellvolt_diff, cellmin_num, cellmax_num]
  255. elif i>=len(self.df_bms)-4:
  256. standingtime=0
  257. cellvolt_now=self._cellvolt_get(i)
  258. cellvolt_min=min(cellvolt_now)
  259. cellvolt_max=max(cellvolt_now)
  260. cellvolt_last=self._cellvolt_get(i-1)
  261. deltvolt=max(abs(cellvolt_now-cellvolt_last))
  262. if 2 < cellvolt_max < self.param.OcvInflexionBelow-0.002 and 2<cellvolt_min<4.5 and deltvolt<0.003:
  263. cellmin_num=list(cellvolt_now).index(cellvolt_min)+1
  264. cellmax_num=list(cellvolt_now).index(cellvolt_max)+1
  265. cellsoc_min=np.interp(cellvolt_min,self.param.LookTab_OCV,self.param.LookTab_SOC)
  266. cellsoc_max=np.interp(cellvolt_max,self.param.LookTab_OCV,self.param.LookTab_SOC)
  267. cellvolt_diff=(cellvolt_max-cellvolt_min)*1000
  268. cellsoc_diff=cellsoc_max-cellsoc_min
  269. cellsoc_diff=eval(format(cellsoc_diff,'.1f'))
  270. cellvolt_diff=eval(format(cellvolt_diff,'.0f'))
  271. df_res.loc[len(df_res)]=[self.bmstime[i], self.sn, cellsoc_diff, cellvolt_diff, cellmin_num, cellmax_num]
  272. else:
  273. pass
  274. else:
  275. pass
  276. else:
  277. standingtime=0
  278. pass
  279. #获取DVDQ算法所需数据——开始............................................................................................................
  280. if charging==0: #判断充电开始
  281. if self.packcrnt[i]<=-1 and self.packcrnt[i+1]<=-1 and self.packcrnt[i+2]<=-1 and self.bms_soc[i]<40: #充电开始
  282. charging=1
  283. if len(chrg_start)>len(chrg_end):
  284. chrg_start[-1]=i
  285. else:
  286. chrg_start.append(i)
  287. else:
  288. pass
  289. else: #充电中
  290. if (self.bmstime[i+1]-self.bmstime[i]).total_seconds()>180 or (self.packcrnt[i]<-50 and self.packcrnt[i+1]<-50): #如果充电过程中时间间隔>180s,则舍弃该次充电
  291. chrg_start.remove(chrg_start[-1])
  292. charging=0
  293. continue
  294. elif self.packcrnt[i]<=-1 and self.packcrnt[i+1]<=-1 and self.packcrnt[i+2]>-1: #判断电流波动时刻
  295. cellvolt_now=self._cellvolt_get(i+1)
  296. if max(cellvolt_now)>self.param.CellFullChrgVolt: #电压>满充电压
  297. chrg_end.append(i+1)
  298. charging=0
  299. continue
  300. else:
  301. pass
  302. elif self.packcrnt[i+1]>-0.1 and self.packcrnt[i+2]>-0.1: #判断充电结束
  303. charging=0
  304. if len(chrg_start)>len(chrg_end):
  305. if self.bms_soc[i]>90:
  306. chrg_end.append(i)
  307. else:
  308. chrg_start.remove(chrg_start[-1])
  309. continue
  310. else:
  311. continue
  312. elif i==len(self.packcrnt)-4 and self.packcrnt[i+1]<-1 and self.packcrnt[i+2]<-1:
  313. charging=0
  314. if len(chrg_start)>len(chrg_end):
  315. if self.bms_soc[i]>90: #soc>90
  316. chrg_end.append(i)
  317. continue
  318. else:
  319. chrg_start.remove(chrg_start[-1])
  320. continue
  321. else:
  322. continue
  323. else:
  324. continue
  325. if chrg_end: #DVDQ方法计算soc差
  326. peaksoc_list=[]
  327. for i in range(len(chrg_end)):
  328. peaksoc_list = []
  329. self._celltemp_weight(chrg_start[i])
  330. if min(self.celltemp)>10:
  331. for j in range(1, self.param.CellVoltNums + 1):
  332. cellvolt = self._singlevolt_get(i,j,2) #取单体电压j的所有电压值
  333. cellvolt = list(cellvolt[chrg_start[i]:chrg_end[i]])
  334. time = list(self.bmstime[chrg_start[i]:chrg_end[i]])
  335. packcrnt = list(self.packcrnt[chrg_start[i]:chrg_end[i]])
  336. soc = list(self.bms_soc[chrg_start[i]:chrg_end[i]])
  337. peaksoc = self._dvdq_peak(time, soc, cellvolt, packcrnt)
  338. if peaksoc>1:
  339. peaksoc_list.append(peaksoc) #计算到达峰值点的累计Soc
  340. else:
  341. pass
  342. if len(peaksoc_list)>self.param.CellVoltNums/2:
  343. peaksoc_max=max(peaksoc_list)
  344. peaksoc_min=min(peaksoc_list)
  345. peaksoc_maxnum=peaksoc_list.index(peaksoc_min)+1
  346. peaksoc_minnum=peaksoc_list.index(peaksoc_max)+1
  347. cellsoc_diff=peaksoc_max-peaksoc_min
  348. cellsoc_diff=eval(format(cellsoc_diff,'.1f'))
  349. df_res.loc[len(df_res)]=[self.bmstime[chrg_start[i]], self.sn, cellsoc_diff, 0, peaksoc_minnum, peaksoc_maxnum]
  350. else:
  351. pass
  352. plt.show()
  353. else:
  354. pass
  355. if df_res.empty:
  356. return pd.DataFrame()
  357. else:
  358. df_res.sort_values(by='time', ascending=True, inplace=True)
  359. return df_res