LFPSoh_20210711.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304
  1. # 获取数据
  2. from LIB.BACKEND import DBManager
  3. import os
  4. import pandas as pd
  5. import numpy as np
  6. import datetime
  7. # import matplotlib.pyplot as plt
  8. #参数输入
  9. Capacity = 54
  10. PackFullChrgVolt=69.99
  11. CellFullChrgVolt=3.5
  12. CellVoltNums=20
  13. CellTempNums=4
  14. FullChrgSoc=98
  15. PeakSoc=57
  16. # #40Ah-OCV
  17. # LookTab_SOC = [0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65, 70, 75, 80, 85, 90, 95, 100]
  18. # LookTab_OCV = [3.3159, 3.4502, 3.4904, 3.5277, 3.5590, 3.5888, 3.6146, 3.6312, 3.6467, 3.6642, 3.6865, 3.7171, 3.7617,
  19. # 3.8031, 3.8440, 3.8888, 3.9376, 3.9891, 4.0451, 4.1068, 4.1830]
  20. #55Ah-OCV
  21. LookTab_SOC = [0.00, 2.40, 6.38, 10.37, 14.35, 18.33, 22.32, 26.30, 30.28, 35.26, 40.24, 45.22, 50.20, 54.19, 58.17, 60.16, 65.14, 70.12, 75.10, 80.08, 84.06, 88.05, 92.03, 96.02, 100.00]
  22. LookTab_OCV = [2.7151, 3.0298, 3.1935, 3.2009, 3.2167, 3.2393, 3.2561, 3.2703, 3.2843, 3.2871, 3.2874, 3.2868, 3.2896, 3.2917, 3.2967, 3.3128, 3.3283, 3.3286, 3.3287, 3.3288, 3.3289, 3.3296, 3.3302, 3.3314, 3.3429]
  23. #定义滑动滤波函数
  24. def np_move_avg(a, n, mode="same"):
  25. return (np.convolve(a, np.ones((n,)) / n, mode=mode))
  26. #参数初始化
  27. dvdq_soh=[]
  28. dvdq_soh_err=[]
  29. bms_soh=[]
  30. dvdq_time=[]
  31. dvdq_sohcfd=[]
  32. sn_list=[]
  33. #输入一个含有‘SN号’的xlsx
  34. def cal_soh(sn, end_time, start_time):
  35. #获取数据时间段
  36. end_time = end_time
  37. strat_time = start_time
  38. SNnum=str(sn)
  39. sn = SNnum
  40. st = start_time
  41. et = end_time
  42. dbManager = DBManager.DBManager()
  43. df_data = dbManager.get_data(sn=sn, start_time=st, end_time=et, data_groups=['bms'])
  44. data = df_data['bms']
  45. packcrnt=data['总电流[A]']
  46. packvolt=data['总电压[V]']
  47. SOC=data['SOC[%]']
  48. SOH=data['SOH[%]']
  49. bmsstat=data['充电状态']
  50. time= pd.to_datetime(data['时间戳'], format='%Y-%m-%d %H:%M:%S')
  51. #第一步:筛选充电数据
  52. ChgStart=[]
  53. ChgEnd=[]
  54. for i in range(3, len(time) - 3):
  55. if i==3 and bmsstat[i]==2 and bmsstat[i+1]==2 and bmsstat[i+2]==2:
  56. ChgStart.append(i)
  57. elif bmsstat[i-2]!=2 and bmsstat[i-1]!=2 and bmsstat[i]==2:
  58. ChgStart.append(i)
  59. elif bmsstat[i-1]==2 and bmsstat[i]!=2 and bmsstat[i+1]!=2:
  60. ChgEnd.append(i)
  61. elif i == (len(time) - 4) and bmsstat[len(bmsstat)-1] == 2 and bmsstat[len(bmsstat)-2] == 2:
  62. ChgEnd.append(len(time)-1)
  63. #第二步:筛选充电起始Soc<48%,电芯温度>15℃,且满充的数据
  64. ChgStartValid1=[]
  65. ChgEndValid1=[]
  66. ChgStartValid2=[]
  67. ChgEndValid2=[]
  68. for i in range(min(len(ChgStart),len(ChgEnd))):
  69. #获取最小温度值
  70. celltemp = []
  71. for j in range(1, CellTempNums+1):
  72. s = str(j)
  73. temp = data['单体温度' + s]
  74. celltemp.append(temp[ChgEnd[i]])
  75. #寻找最大电压值
  76. cellvolt = []
  77. for j in range(1, CellVoltNums+1):
  78. s = str(j)
  79. volt = max(data['单体电压' + s][ChgStart[i]:ChgEnd[i]]/1000)
  80. cellvolt.append(volt)
  81. #筛选满足2点法计算的数据
  82. StandingTime=0
  83. if max(cellvolt)>CellFullChrgVolt and SOC[ChgStart[i]]<30 and min(celltemp)>5:
  84. for k in reversed(range(ChgStart[i])):
  85. if abs(packcrnt[k - 2]) < 0.01:
  86. StandingTime = StandingTime + (time[k] - time[k-1]).total_seconds()
  87. if StandingTime > 600: # 筛选静置时间>10min
  88. ChgStartValid1.append(ChgStart[i])
  89. ChgEndValid1.append(ChgEnd[i])
  90. break
  91. else:
  92. break
  93. #筛选满足DV/DQ方法的数据
  94. if max(cellvolt)>CellFullChrgVolt and SOC[ChgStart[i]]<45 and min(celltemp)>5:
  95. if ((time[ChgEnd[i]]-time[ChgStart[i]]).total_seconds())/(ChgEnd[i]-ChgStart[i])<60:
  96. ChgStartValid2.append(ChgStart[i])
  97. ChgEndValid2.append(ChgEnd[i])
  98. #第三步:计算充电Soc和Soh
  99. # 两点法计算soh
  100. Soc=[]
  101. Time=[]
  102. Soc_Err=[]
  103. Bms_Soc=[]
  104. Soh1=[]
  105. Time1=[]
  106. Bms_Soh1=[]
  107. Soh_Err1=[]
  108. for i in range(len(ChgStartValid1)):
  109. #寻找最大电压值
  110. cellvolt = []
  111. for j in range(1, CellVoltNums+1):
  112. s = str(j)
  113. volt = max(data['单体电压' + s])
  114. cellvolt.append(volt)
  115. voltmax_index = cellvolt.index(max(cellvolt)) + 1
  116. cellvolt = data['单体电压' + str(voltmax_index)] / 1000
  117. #soc
  118. Soc.append(np.interp(cellvolt[ChgStartValid1[i]-3],LookTab_OCV,LookTab_SOC))
  119. Time.append(time[ChgStartValid1[i]-3])
  120. Bms_Soc.append(SOC[ChgStartValid1[i]-3])
  121. Soc_Err.append(Bms_Soc[-1]-Soc[-1])
  122. #soh
  123. Ocv_Soc=np.interp(cellvolt[ChgStartValid1[i]-3],LookTab_OCV,LookTab_SOC)
  124. Ah=0
  125. for j in range(ChgStartValid1[i],ChgEndValid1[i]):
  126. #计算soc
  127. Step=(time[j]-time[j-1]).total_seconds()
  128. Time.append(time[j])
  129. Bms_Soc.append(SOC[j])
  130. if Soc[-1]-(packcrnt[j]*Step*100)/(3600*Capacity)<100:
  131. Soc.append(Soc[-1]-(packcrnt[j]*Step*100)/(3600*Capacity))
  132. else:
  133. Soc.append(100)
  134. Soc_Err.append(Bms_Soc[-1] - Soc[-1])
  135. #两点法计算soh
  136. Ah=Ah-packcrnt[j]*Step/3600
  137. Soh1.append(Ah*100/((FullChrgSoc-Ocv_Soc)*0.01*Capacity))
  138. Bms_Soh1.append(SOH[i])
  139. Soh_Err1.append(Bms_Soh1[-1]-Soh1[-1])
  140. Time1.append(time[ChgStartValid1[i]])
  141. # DV/DQ法计算soh
  142. Soh2=[]
  143. Time2=[]
  144. Bms_Soh2=[]
  145. Soh_Err2=[]
  146. SohCfd = []
  147. sn_list=[]
  148. for i in range(len(ChgStartValid2)):
  149. #寻找最大电压值
  150. cellvolt1 = []
  151. cellvolt=[]
  152. for j in range(1, CellVoltNums+1):
  153. s = str(j)
  154. volt = data['单体电压' + s]
  155. cellvolt1.append(volt[ChgEndValid2[i]])
  156. voltmax1_index = cellvolt1.index(max(cellvolt1)) + 1
  157. cellvolt1 = data['单体电压' + str(voltmax1_index)] / 1000
  158. #电压采用滑动平均滤波
  159. cellvolt=np_move_avg(cellvolt1, 3, mode="same")
  160. #参数赋初始值
  161. Ah = 0
  162. Volt = cellvolt[ChgStartValid2[i]]
  163. DV_Volt=[]
  164. DQ_Ah = []
  165. DVDQ = []
  166. time2 = []
  167. soc2 = []
  168. Ah_tatal=[0]
  169. xvolt=[]
  170. #计算DV和DQ值
  171. for j in range(ChgStartValid2[i],ChgEndValid2[i]):
  172. Step=(time[j+1]-time[j]).total_seconds()
  173. Ah=Ah-packcrnt[j]*Step/3600
  174. if (cellvolt[j]-Volt)>0.0009 and Ah>0:
  175. Ah_tatal.append(Ah_tatal[-1]+Ah)
  176. DQ_Ah.append(Ah)
  177. DV_Volt.append(cellvolt[j]-Volt)
  178. DVDQ.append((DV_Volt[-1])/DQ_Ah[-1])
  179. xvolt.append(cellvolt[j])
  180. Volt=cellvolt[j]
  181. Ah = 0
  182. time2.append(time[j])
  183. soc2.append(SOC[j])
  184. #切片Soc>50且Soc<80
  185. Data1 = pd.DataFrame({'SOC': soc2,
  186. 'DVDQ': DVDQ,
  187. 'Ah_tatal': Ah_tatal[:-1],
  188. 'DQ_Ah':DQ_Ah,
  189. 'DV_Volt':DV_Volt,
  190. 'XVOLT':xvolt})
  191. Data1=Data1[(Data1['SOC']>50) & (Data1['SOC']<80)]
  192. #寻找峰值并计算Soh和置信度
  193. # 获取最小温度值
  194. celltemp = []
  195. for j in range(1, CellTempNums+1):
  196. s = str(j)
  197. temp = data['单体温度' + s]
  198. celltemp.append(temp[ChgStartValid2[i]])
  199. if len(Data1['DVDQ'])>1:
  200. PeakIndex=Data1['DVDQ'].idxmax()
  201. #筛选峰值点附近±0.5%SOC内的数据
  202. Data2=Data1[(Data1['SOC']>(Data1['SOC'][PeakIndex]-0.5)) & (Data1['SOC']<(Data1['SOC'][PeakIndex]+0.5))]
  203. if len(Data2)>2:
  204. Ah_tatal1 = Data1['Ah_tatal']
  205. DVDQ = Data1['DVDQ']
  206. soc2 = Data1['SOC']
  207. xvolt = Data1['XVOLT']
  208. if soc2[PeakIndex]>50 and soc2[PeakIndex]<80:
  209. DVDQ_SOH=(Ah_tatal[-1]-Ah_tatal1[PeakIndex]) * 100 / ((FullChrgSoc - PeakSoc) * 0.01 * Capacity)
  210. if DVDQ_SOH<95:
  211. DVDQ_SOH=DVDQ_SOH*0.3926+58.14
  212. if DVDQ_SOH>70 and DVDQ_SOH<120:
  213. Soh2.append(DVDQ_SOH)
  214. Bms_Soh2.append(SOH[ChgStartValid2[i]])
  215. Soh_Err2.append(Bms_Soh2[-1] - Soh2[-1])
  216. Time2.append(time[ChgStartValid2[i]])
  217. sn_list.append(SNnum)
  218. #计算置信度
  219. if min(celltemp)<10:
  220. SohCfd.append(50)
  221. elif min(celltemp)<20:
  222. SohCfd.append(80)
  223. else:
  224. SohCfd.append(100)
  225. else:
  226. Data1=Data1.drop([PeakIndex])
  227. PeakIndex = Data1['DVDQ'].idxmax()
  228. Data2 = Data1[(Data1['SOC'] > (Data1['SOC'][PeakIndex] - 0.5)) & (Data1['SOC'] < (Data1['SOC'][PeakIndex] + 0.5))]
  229. if len(Data2) > 3:
  230. Ah_tatal1 = Data1['Ah_tatal']
  231. DVDQ = Data1['DVDQ']
  232. soc2 = Data1['SOC']
  233. xvolt = Data1['XVOLT']
  234. if soc2[PeakIndex]>50 and soc2[PeakIndex]<80:
  235. DVDQ_SOH=(Ah_tatal[-1]-Ah_tatal1[PeakIndex]) * 100 / ((FullChrgSoc - PeakSoc) * 0.01 * Capacity)
  236. if DVDQ_SOH<95:
  237. DVDQ_SOH=DVDQ_SOH*0.3926+58.14
  238. if DVDQ_SOH>70 and DVDQ_SOH<120:
  239. Soh2.append(DVDQ_SOH)
  240. Bms_Soh2.append(SOH[ChgStartValid2[i]])
  241. Soh_Err2.append(Bms_Soh2[-1] - Soh2[-1])
  242. Time2.append(time[ChgStartValid2[i]])
  243. sn_list.append(SNnum)
  244. #计算置信度
  245. if min(celltemp)<10:
  246. SohCfd.append(50)
  247. elif min(celltemp)<20:
  248. SohCfd.append(80)
  249. else:
  250. SohCfd.append(100)
  251. #处理数据
  252. if len(Soh2)>5:
  253. Soh2=np_move_avg(Soh2,5,mode="valid")
  254. result_soh2={'时间': Time2[4::],
  255. 'SN号':sn_list[4::],
  256. 'BMS_SOH': Bms_Soh2[4::],
  257. 'SOH': Soh2,
  258. 'SOH误差': Soh_Err2[4::]}
  259. else:
  260. result_soh2={'时间': Time2,
  261. 'SN号':sn_list,
  262. 'BMS_SOH': Bms_Soh2,
  263. 'SOH': Soh2,
  264. 'SOH误差': Soh_Err2}
  265. #第四步:将数据存入Excel
  266. Result_Soh2=pd.DataFrame(result_soh2)
  267. # Result_Soh2.to_csv('BMS_SOH_'+SNnum+'.csv',encoding='GB18030')
  268. return Result_Soh2