Pārlūkot izejas kodu

Merge branch 'dev' of http://git.fast-fun.cn:92/lmstack/data_analyze_platform into dev

shangguanlie23 3 gadi atpakaļ
vecāks
revīzija
632864b4ab

+ 3 - 3
LIB/BACKEND/DBManager.py

@@ -172,9 +172,9 @@ class DBManager():
     def _convert_to_dataframe_gps(data, mode=0):
         if mode == 0:
             if data['info']['subType'] == 1:
-                data_block = np.array([data['info']['obdTime'],data['ffGps']['locationType'], data['ffGps']['satellites'],
-                                       data['ffGps']['latitude'],data['ffGps']['longitude'],data['ffGps']['speed'], 
-                                       data['ffGps']['altitude'], data['ffGps']['direction'], data['ffGps']['mileage']]).reshape(1,9)
+                data_block = np.array([data['info'].get('obdTime',None),data['ffGps'].get('locationType',None), data['ffGps'].get('satellites',None),
+                                       data['ffGps'].get('latitude',None),data['ffGps'].get('longitude',None),data['ffGps'].get('speed',None), 
+                                       data['ffGps'].get('altitude',None), data['ffGps'].get('direction', None), data['ffGps'].get('mileage',None)]).reshape(1,9)
                 df = pd.DataFrame(
                     columns=['时间戳','定位类型', '卫星数','纬度','经度','速度[km/h]','海拔','航向', '里程/m'],data=data_block)
             elif data['info']['subType'] == 2:

+ 8 - 4
LIB/MIDDLE/CellStateEstimation/BatSafetyWarning/V1_0_1/CBMSBatInterShort.py

@@ -236,9 +236,11 @@ class BatInterShort():
         if df_ram_last3.empty:
             standingtime=0
             standingtime1=0
+            standingtime2=0
         else:
             standingtime=df_ram_last3.loc[0,'standingtime']
             standingtime1=df_ram_last3.loc[0,'standingtime1']
+            standingtime2=df_ram_last3.loc[0,'standingtime2']
             dict_bal1={}
             if abs(self.packcrnt[0])<0.01 and standingtime>1 and standingtime1>1:
                 standingtime=standingtime+(self.bmstime[0]-df_ram_last3.loc[0,'time3']).total_seconds()
@@ -383,7 +385,7 @@ class BatInterShort():
                 pass
         
         #更新RAM的standingtime
-        df_ram_last3.loc[0]=[self.sn,self.bmstime[len(self.bmstime)-1],standingtime,standingtime1]
+        df_ram_last3.loc[0]=[self.sn,self.bmstime[len(self.bmstime)-1],standingtime,standingtime1,standingtime2]
 
         #返回计算结果
         if df_res.empty:    
@@ -439,9 +441,11 @@ class BatInterShort():
         if df_ram_last3.empty:
             standingtime=0
             standingtime1=0
+            standingtime2=0
         else:
             standingtime=df_ram_last3.loc[0,'standingtime']
             standingtime1=df_ram_last3.loc[0,'standingtime1']
+            standingtime2=df_ram_last3.loc[0,'standingtime2']
             dict_bal1={}
             if abs(self.packcrnt[0])<0.01 and standingtime>1 and standingtime1>1:
                 standingtime=standingtime+(self.bmstime[0]-df_ram_last3.loc[0,'time3']).total_seconds()
@@ -562,7 +566,7 @@ class BatInterShort():
                             time_now1=self.bmstime[i]
                             df_ram_last1.loc[0]=[self.sn,time_now1,deltsoc_now1]    #更新RAM信息
 
-                            if (time_now1-time_last1).total_seconds()>3600*12:
+                            if (time_now1-time_last1).total_seconds()>3600*24:
                                 list_sub1=deltsoc_now1-deltsoc_last1
                                 list_pud1=(0.01*capacity*3600*1000)/(time_now1-time_last1).total_seconds()
                                 leak_current1=list_sub1*list_pud1
@@ -624,7 +628,7 @@ class BatInterShort():
                         continue
                     elif min(cellvolt_now)>self.param.CellFullChrgVolt-0.13:   #电压>满充电压-0.13V,即3.37V
                         self._celltemp_weight(i)
-                        if i-chrg_start>10 and self.celltemp>10:
+                        if i-chrg_start>10 and self.celltemp>15:
                             chrg_end=i+1
                             charging=0
 
@@ -665,7 +669,7 @@ class BatInterShort():
 
     
         #更新RAM
-        df_ram_last3.loc[0]=[self.sn,self.bmstime[len(self.bmstime)-1],standingtime,standingtime1]
+        df_ram_last3.loc[0]=[self.sn,self.bmstime[len(self.bmstime)-1],standingtime,standingtime1,standingtime2]
 
         #返回结果
         if df_res.empty:    

+ 506 - 0
LIB/MIDDLE/CellStateEstimation/BatSafetyWarning/V1_0_1/CBMSBatUniform.py

@@ -0,0 +1,506 @@
+import pandas as pd
+import numpy as np
+import datetime
+from LIB.MIDDLE.CellStateEstimation.Common import BatParam
+
+class BatUniform():
+    def __init__(self,sn,celltype,df_bms,df_uniform,df_last3,df_lfp1):  #参数初始化
+
+        if (not df_lfp1.empty) and celltype>50:
+            df_lfp1.drop(['sn'],axis=1)
+            df_bms=pd.concat([df_lfp1, df_bms], ignore_index=True)
+        else:
+            pass
+        
+        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.bmstime= pd.to_datetime(df_bms['时间戳'], format='%Y-%m-%d %H:%M:%S')
+        # df_uniform['time']=pd.to_datetime(df_uniform['time'], format='%Y-%m-%d %H:%M:%S')
+
+        self.df_uniform=df_uniform
+        self.df_last3=df_last3
+        self.df_lfp1=df_lfp1
+
+        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 batuniform(self):
+        if self.celltype<50:
+            df_res, df_ram_last3=self._ncm_uniform()
+            return df_res, df_ram_last3, self.df_lfp1
+        else:
+            df_res, df_ram_last3, df_ram_lfp1=self._lfp_uniform()
+            return df_res, df_ram_last3, df_ram_lfp1
+    
+    #定义滑动滤波函数........................................................................................................................................
+    def _np_move_avg(self,a, n, mode="same"): 
+        return (np.convolve(a, np.ones((n,)) / n, mode=mode))
+    
+    #寻找当前行数据的最小温度值................................................................................................................................
+    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.2
+                self.StandardStandingTime=4800
+            else:
+                self.tempweight=0.1
+                self.StandardStandingTime=7200
+        else:
+            if min(celltemp)>=25:
+                self.tempweight=1
+                self.StandardStandingTime=1800
+            elif min(celltemp)>=15:
+                self.tempweight=0.8
+                self.StandardStandingTime=2400
+            elif min(celltemp)>=5:
+                self.tempweight=0.6
+                self.StandardStandingTime=3600
+            else:
+                self.tempweight=0.2
+                self.StandardStandingTime=7200
+        
+    #获取当前行所有电压数据............................................................................................................................
+    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的峰值点,并返回..........................................................................................................................
+    def _dvdq_peak(self, time, soc, cellvolt, packcrnt):
+        cellvolt = self._np_move_avg(cellvolt, 3, mode="same")
+        Soc = 0
+        Ah = 0
+        Volt = cellvolt[0]
+        DV_Volt = []
+        DQ_Ah = []
+        DVDQ = []
+        time1 = []
+        soc1 = []
+        soc2 = []
+        xvolt=[]
+
+        for m in range(1, len(time)):
+            Step = (time[m] - time[m - 1]).total_seconds()
+            Soc = Soc - packcrnt[m] * Step * 100 / (3600 * self.param.Capacity)
+            Ah = Ah - packcrnt[m] * Step / 3600
+            if (cellvolt[m]-Volt)>0.0019 and Ah>0:
+                DQ_Ah.append(Ah)
+                DV_Volt.append(cellvolt[m]-Volt)
+                DVDQ.append((DV_Volt[-1])/Ah)
+                xvolt.append(cellvolt[m])
+                Volt=cellvolt[m]
+                Ah = 0
+                soc1.append(Soc)
+                time1.append(time[m])
+                soc2.append(soc[m])
+
+        #切片,去除前后10min的数据
+        df_Data1 = pd.DataFrame({'time': time1,
+                                'SOC': soc2,
+                                'DVDQ': DVDQ,
+                                'AhSoc': soc1,
+                                '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(time1)-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)]
+
+        # print(packcrnt[int(len(time)/2)], min(self.celltemp))
+        # ax1 = plt.subplot(3, 1, 1)
+        # plt.plot(df_Data1['SOC'],df_Data1['DQ_Ah'],'g*-')
+        # plt.xlabel('SOC/%')
+        # plt.ylabel('DQ_Ah')
+        # 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()
+
+        if len(df_Data1)>2:     #寻找峰值点,且峰值点个数>2
+            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) > 2 and df_Data1.loc[PeakIndex,'XVOLT']<self.param.PeakVoltUpLmt-0.019:
+                return df_Data1['AhSoc'][PeakIndex]
+            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) > 2 and df_Data1.loc[PeakIndex,'XVOLT']<self.param.PeakVoltUpLmt-0.019:
+                    return df_Data1['AhSoc'][PeakIndex]
+                else:
+                    return 0
+        else:
+            return 0
+ 
+    #三元电池一致性计算.................................................................................................................................
+    def _ncm_uniform(self):
+        column_name=['time','sn','cellsoc_diff','cellvolt_diff','cellmin_num','cellmax_num','cellvolt_rank']
+        df_res=pd.DataFrame(columns=column_name)
+
+        df_ram_last3=self.df_last3
+        if df_ram_last3.empty:
+            standingtime=0
+            standingtime1=0
+            standingtime2=0
+        else:
+            standingtime=df_ram_last3.loc[0,'standingtime']
+            standingtime1=df_ram_last3.loc[0,'standingtime1']
+            standingtime2=df_ram_last3.loc[0,'standingtime2']
+            if abs(self.packcrnt[0])<0.01 and standingtime2>1:
+                standingtime2=standingtime2+(self.bmstime[0]-df_ram_last3.loc[0,'time3']).total_seconds()
+            else:
+                pass
+        
+
+        for i in range(1,len(self.df_bms)-2):
+
+            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()
+                standingtime2=standingtime2+delttime
+                self._celltemp_weight(i)     #获取不同温度对应的静置时间
+
+                if standingtime2>self.StandardStandingTime:      #静置时间满足要求
+                    if abs(self.packcrnt[i+2]) >= 0.1:
+                        standingtime2=0  
+                        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.005:
+                            cellvolt_sort=np.argsort(cellvolt_now)
+                            cellvolt_rank=list(np.argsort(cellvolt_sort)+1)
+                            cellmin_num=list(cellvolt_now).index(cellvolt_min)+1
+                            cellmax_num=list(cellvolt_now).index(cellvolt_max)+1
+                            cellsoc_min=np.interp(cellvolt_min,self.param.LookTab_OCV,self.param.LookTab_SOC)
+                            cellsoc_max=np.interp(cellvolt_max,self.param.LookTab_OCV,self.param.LookTab_SOC)
+                            cellvolt_diff=(cellvolt_max-cellvolt_min)*1000
+                            cellsoc_diff=cellsoc_max-cellsoc_min
+                            cellsoc_diff=eval(format(cellsoc_diff,'.1f'))
+                            cellvolt_diff=eval(format(cellvolt_diff,'.0f'))
+                            df_res.loc[len(df_res)]=[self.bmstime[i], self.sn, cellsoc_diff, cellvolt_diff, cellmin_num, cellmax_num, str(cellvolt_rank)]
+                    elif standingtime2>3600*6:
+                        standingtime2=0
+                        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.005:
+                            cellvolt_sort=np.argsort(cellvolt_now)
+                            cellvolt_rank=list(np.argsort(cellvolt_sort)+1)
+                            cellmin_num=list(cellvolt_now).index(cellvolt_min)+1
+                            cellmax_num=list(cellvolt_now).index(cellvolt_max)+1
+                            cellsoc_min=np.interp(cellvolt_min,self.param.LookTab_OCV,self.param.LookTab_SOC)
+                            cellsoc_max=np.interp(cellvolt_max,self.param.LookTab_OCV,self.param.LookTab_SOC)
+                            cellvolt_diff=(cellvolt_max-cellvolt_min)*1000
+                            cellsoc_diff=cellsoc_max-cellsoc_min
+                            cellsoc_diff=eval(format(cellsoc_diff,'.1f'))
+                            cellvolt_diff=eval(format(cellvolt_diff,'.0f'))
+                            df_res.loc[len(df_res)]=[self.bmstime[i], self.sn, cellsoc_diff, cellvolt_diff, cellmin_num, cellmax_num, str(cellvolt_rank)]
+                    elif i>=len(self.df_bms)-3:
+                        standingtime2=0
+                        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.005:
+                            cellvolt_sort=np.argsort(cellvolt_now)
+                            cellvolt_rank=list(np.argsort(cellvolt_sort)+1)
+                            cellmin_num=list(cellvolt_now).index(cellvolt_min)+1
+                            cellmax_num=list(cellvolt_now).index(cellvolt_max)+1
+                            cellsoc_min=np.interp(cellvolt_min,self.param.LookTab_OCV,self.param.LookTab_SOC)
+                            cellsoc_max=np.interp(cellvolt_max,self.param.LookTab_OCV,self.param.LookTab_SOC)
+                            cellvolt_diff=(cellvolt_max-cellvolt_min)*1000
+                            cellsoc_diff=cellsoc_max-cellsoc_min
+                            cellsoc_diff=eval(format(cellsoc_diff,'.1f'))
+                            cellvolt_diff=eval(format(cellvolt_diff,'.0f'))
+                            df_res.loc[len(df_res)]=[self.bmstime[i], self.sn, cellsoc_diff, cellvolt_diff, cellmin_num, cellmax_num, str(cellvolt_rank)]
+                        break
+                    else:
+                        continue
+                else:
+                    continue
+            else:
+                standingtime2=0
+                continue
+        
+        #更新RAM的standingtime
+        df_ram_last3.loc[0]=[self.sn,self.bmstime[len(self.bmstime)-1],standingtime,standingtime1,standingtime2]
+
+        if df_res.empty:    #返回计算结果
+            return pd.DataFrame(), df_ram_last3
+        else:
+            return df_res, df_ram_last3
+
+    #磷酸铁锂电池一致性计算.........................................................................................................................
+    def _lfp_uniform(self):
+        column_name=['time','sn','cellsoc_diff','cellvolt_diff','cellmin_num','cellmax_num','cellvolt_rank']
+        df_res=pd.DataFrame(columns=column_name)
+        df_ram_lfp1=pd.DataFrame(columns=self.df_bms.columns.tolist())
+        chrg_start=[]
+        chrg_end=[]
+        charging=0
+
+        df_ram_last3=self.df_last3
+        if df_ram_last3.empty:
+            standingtime=0
+            standingtime1=0
+            standingtime2=0
+        else:
+            standingtime=df_ram_last3.loc[0,'standingtime']
+            standingtime1=df_ram_last3.loc[0,'standingtime1']
+            standingtime2=df_ram_last3.loc[0,'standingtime2']
+            if abs(self.packcrnt[0])<0.01 and standingtime2>1:
+                standingtime2=standingtime2+(self.bmstime[0]-df_ram_last3.loc[0,'time3']).total_seconds()
+            else:
+                pass
+
+        for i in range(2,len(self.df_bms)-2):
+
+            #静置电压法计算电芯一致性
+            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()
+                standingtime2=standingtime2+delttime
+                self._celltemp_weight(i)     #获取不同温度对应的静置时间
+
+                if standingtime2>self.StandardStandingTime:      #静置时间满足要求
+                    if abs(self.packcrnt[i+2]) >= 0.1:     
+                        standingtime2=0  
+                        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 2 < cellvolt_max < self.param.OcvInflexionBelow-0.002 and 2<cellvolt_min<4.5 and deltvolt<0.005: 
+                            cellvolt_sort=np.argsort(cellvolt_now)
+                            cellvolt_rank=list(np.argsort(cellvolt_sort)+1)               
+                            cellmin_num=list(cellvolt_now).index(cellvolt_min)+1
+                            cellmax_num=list(cellvolt_now).index(cellvolt_max)+1
+                            cellsoc_min=np.interp(cellvolt_min,self.param.LookTab_OCV,self.param.LookTab_SOC)
+                            cellsoc_max=np.interp(cellvolt_max,self.param.LookTab_OCV,self.param.LookTab_SOC)
+                            cellvolt_diff=(cellvolt_max-cellvolt_min)*1000
+                            cellsoc_diff=cellsoc_max-cellsoc_min
+                            cellsoc_diff=eval(format(cellsoc_diff,'.1f'))
+                            cellvolt_diff=eval(format(cellvolt_diff,'.0f'))
+                            df_res.loc[len(df_res)]=[self.bmstime[i], self.sn, cellsoc_diff, cellvolt_diff, cellmin_num, cellmax_num, str(cellvolt_rank)]
+                        elif 2<cellvolt_max<4.5 and 2<cellvolt_min<4.5 and deltvolt<0.005: 
+                            cellvolt_sort=np.argsort(cellvolt_now)
+                            cellvolt_rank=list(np.argsort(cellvolt_sort)+1)
+                            if not df_res.empty:
+                                df_res.loc[len(df_res)]=df_res.loc[len(df_res)-1]
+                                df_res.loc[len(df_res)-1,'cellvolt_rank']=str(cellvolt_rank)
+                                df_res.loc[len(df_res)-1,'time']=self.bmstime[i]
+                            elif not self.df_uniform.empty:
+                                df_res.loc[len(df_res)]=self.df_uniform.iloc[-1]
+                                df_res.loc[len(df_res)-1,'cellvolt_rank']=str(cellvolt_rank)
+                                df_res.loc[len(df_res)-1,'time']=self.bmstime[i]
+                            else:
+                                pass
+                    elif standingtime2>3600*6:
+                        standingtime2=0
+                        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 2 < cellvolt_max < self.param.OcvInflexionBelow-0.002 and 2<cellvolt_min<4.5 and deltvolt<0.005: 
+                            cellvolt_sort=np.argsort(cellvolt_now)
+                            cellvolt_rank=list(np.argsort(cellvolt_sort)+1)
+                            cellmin_num=list(cellvolt_now).index(cellvolt_min)+1
+                            cellmax_num=list(cellvolt_now).index(cellvolt_max)+1
+                            cellsoc_min=np.interp(cellvolt_min,self.param.LookTab_OCV,self.param.LookTab_SOC)
+                            cellsoc_max=np.interp(cellvolt_max,self.param.LookTab_OCV,self.param.LookTab_SOC)
+                            cellvolt_diff=(cellvolt_max-cellvolt_min)*1000
+                            cellsoc_diff=cellsoc_max-cellsoc_min
+                            cellsoc_diff=eval(format(cellsoc_diff,'.1f'))
+                            cellvolt_diff=eval(format(cellvolt_diff,'.0f'))
+                            df_res.loc[len(df_res)]=[self.bmstime[i], self.sn, cellsoc_diff, cellvolt_diff, cellmin_num, cellmax_num, str(cellvolt_rank)]
+                        elif 2<cellvolt_max<4.5 and 2<cellvolt_min<4.5 and deltvolt<0.005: 
+                            cellvolt_sort=np.argsort(cellvolt_now)
+                            cellvolt_rank=list(np.argsort(cellvolt_sort)+1)
+                            if not df_res.empty:
+                                df_res.loc[len(df_res)]=df_res.loc[len(df_res)-1]
+                                df_res.loc[len(df_res)-1,'cellvolt_rank']=str(cellvolt_rank)
+                                df_res.loc[len(df_res)-1,'time']=self.bmstime[i]
+                            elif not self.df_uniform.empty:
+                                df_res.loc[len(df_res)]=self.df_uniform.iloc[-1]
+                                df_res.loc[len(df_res)-1,'cellvolt_rank']=str(cellvolt_rank)
+                                df_res.loc[len(df_res)-1,'time']=self.bmstime[i]
+                            else:
+                                pass
+                    
+                    elif i>=len(self.df_bms)-3:
+                        standingtime2=0
+                        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 2 < cellvolt_max < self.param.OcvInflexionBelow-0.002 and 2<cellvolt_min<4.5 and deltvolt<0.003:
+                            cellvolt_sort=np.argsort(cellvolt_now)
+                            cellvolt_rank=list(np.argsort(cellvolt_sort)+1) 
+                            cellmin_num=list(cellvolt_now).index(cellvolt_min)+1
+                            cellmax_num=list(cellvolt_now).index(cellvolt_max)+1
+                            cellsoc_min=np.interp(cellvolt_min,self.param.LookTab_OCV,self.param.LookTab_SOC)
+                            cellsoc_max=np.interp(cellvolt_max,self.param.LookTab_OCV,self.param.LookTab_SOC)
+                            cellvolt_diff=(cellvolt_max-cellvolt_min)*1000
+                            cellsoc_diff=cellsoc_max-cellsoc_min
+                            cellsoc_diff=eval(format(cellsoc_diff,'.1f'))
+                            cellvolt_diff=eval(format(cellvolt_diff,'.0f'))
+                            df_res.loc[len(df_res)]=[self.bmstime[i], self.sn, cellsoc_diff, cellvolt_diff, cellmin_num, cellmax_num, str(cellvolt_rank)]
+                        elif 2<cellvolt_max<4.5 and 2<cellvolt_min<4.5 and deltvolt<0.005: 
+                            cellvolt_sort=np.argsort(cellvolt_now)
+                            cellvolt_rank=list(np.argsort(cellvolt_sort)+1)
+                            if not df_res.empty:
+                                df_res.loc[len(df_res)]=df_res.loc[len(df_res)-1]
+                                df_res.loc[len(df_res)-1,'cellvolt_rank']=str(cellvolt_rank)
+                                df_res.loc[len(df_res)-1,'time']=self.bmstime[i]
+                            elif not self.df_uniform.empty:
+                                df_res.loc[len(df_res)]=self.df_uniform.iloc[-1]
+                                df_res.loc[len(df_res)-1,'cellvolt_rank']=str(cellvolt_rank)
+                                df_res.loc[len(df_res)-1,'time']=self.bmstime[i]
+                            else:
+                                pass
+                    else:
+                        pass
+                else:
+                    pass
+            else:
+                standingtime2=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:     #充电开始
+                    charging=1
+                    if len(chrg_start)>len(chrg_end):
+                        chrg_start[-1]=i
+                    else:
+                        chrg_start.append(i)
+                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])
+                    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)
+                    if max(cellvolt_now)>self.param.CellFullChrgVolt:   #电压>满充电压
+                        chrg_end.append(i+1)
+                        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):
+                        if self.bms_soc[i]>90:
+                            chrg_end.append(i)
+                        else:
+                            chrg_start.remove(chrg_start[-1])
+                            continue
+                    else:
+                        continue
+                elif i==len(self.packcrnt)-3 and self.packcrnt[i+1]<-1 and self.packcrnt[i+2]<-1:
+                    charging=0
+                    if len(chrg_start)>len(chrg_end) and self.bms_soc[i]>90:   #soc>90
+                        chrg_end.append(i)
+                        continue
+                    else:
+                        df_ram_lfp1=self.df_bms.iloc[chrg_start[-1]:]
+                        df_ram_lfp1['sn']=self.sn
+                        chrg_start.remove(chrg_start[-1])
+                        continue
+                else:
+                    continue   
+
+        if chrg_end:    #DVDQ方法计算soc差
+            peaksoc_list=[]
+            for i in range(len(chrg_end)):
+                peaksoc_list = []
+                self._celltemp_weight(chrg_start[i])
+                if min(self.celltemp)>10:
+                    for j in range(1, self.param.CellVoltNums + 1):
+                        cellvolt = self._singlevolt_get(i,j,2)  #取单体电压j的所有电压值
+                        cellvolt = list(cellvolt[chrg_start[i]:chrg_end[i]])
+                        time = list(self.bmstime[chrg_start[i]:chrg_end[i]])
+                        packcrnt = list(self.packcrnt[chrg_start[i]:chrg_end[i]])
+                        soc = list(self.bms_soc[chrg_start[i]:chrg_end[i]])
+                        peaksoc = self._dvdq_peak(time, soc, cellvolt, packcrnt)
+                        if peaksoc>1:
+                            peaksoc_list.append(peaksoc)    #计算到达峰值点的累计Soc
+                        else:
+                            pass
+                    if len(peaksoc_list)>self.param.CellVoltNums/2:
+                        peaksoc_max=max(peaksoc_list)
+                        peaksoc_min=min(peaksoc_list)
+                        peaksoc_maxnum=peaksoc_list.index(peaksoc_min)+1
+                        peaksoc_minnum=peaksoc_list.index(peaksoc_max)+1
+                        cellsoc_diff=peaksoc_max-peaksoc_min
+                        cellsoc_diff=eval(format(cellsoc_diff,'.1f'))
+                        if not df_res.empty:
+                            cellvolt_rank=df_res.loc[-1]['cellvolt_rank']
+                            df_res.loc[len(df_res)]=[self.bmstime[chrg_start[i]], self.sn, cellsoc_diff, 0, peaksoc_minnum, peaksoc_maxnum, cellvolt_rank]
+                        elif not self.df_uniform.empty:
+                            cellvolt_rank=self.df_uniform.iloc[-1]['cellvolt_rank']
+                            df_res.loc[len(df_res)]=[self.bmstime[chrg_start[i]], self.sn, cellsoc_diff, 0, peaksoc_minnum, peaksoc_maxnum, cellvolt_rank]
+                        else:
+                            pass
+                    else:
+                        pass
+                else:
+                    pass
+
+        #更新RAM的standingtime
+        df_ram_last3.loc[0]=[self.sn,self.bmstime[len(self.bmstime)-1],standingtime,standingtime1,standingtime2]
+        
+        if df_res.empty:
+            return pd.DataFrame(), df_ram_last3, df_ram_lfp1
+        else:
+            df_res.sort_values(by='time', ascending=True, inplace=True)
+            return df_res, df_ram_last3, df_ram_lfp1

+ 94 - 23
LIB/MIDDLE/CellStateEstimation/BatSafetyWarning/V1_0_1/CBMSSafetyWarning.py

@@ -1,17 +1,20 @@
 import pandas as pd
 import numpy as np
 import datetime
+import time
+from matplotlib import pyplot as plt
 from LIB.MIDDLE.CellStateEstimation.Common.V1_0_1 import BatParam
 
 class SafetyWarning:
-    def __init__(self,sn,celltype,df_short,df_liplated,df_uniform):  #参数初始化
+    def __init__(self,sn,celltype,df_short,df_liplated,df_uniform,OutLineVol_Rate):  #参数初始化
 
         self.sn=sn
         self.celltype=celltype
         self.param=BatParam.BatParam(celltype)
         self.df_short=df_short
         self.df_liplated=df_liplated
-        self.df_uniform=df_uniform
+        self.df_uniform=df_uniform.dropna(axis=0,how='any')
+        self.OutLineVol_Rate=OutLineVol_Rate
     
     def diag(self):
         if self.celltype<=50:
@@ -27,21 +30,49 @@ class SafetyWarning:
 
         df_res=pd.DataFrame(columns=['start_time', 'end_time', 'product_id', 'code', 'level', 'info','advice'])
 
-        time=datetime.datetime.now()
+        time_now=datetime.datetime.now()
         time_sp='0000-00-00 00:00:00'
 
-        #参数初始化
+        #参数初始化.......................................
         shortfault=0
         liplatedfault=0
         uniformfault=0
+        volt_rate=[]
+
+        if not self.df_short.empty:
+            short_current=self.df_short['short_current']
+            short_current=short_current.str.replace("[", '')
+            short_current=short_current.str.replace("]", '')
+        
+        if not self.OutLineVol_Rate.empty:
+            volt_column = ['单体电压'+str(i) for i in range(1,self.param.CellVoltNums+1)]
+            self.OutLineVol_Rate['VolChng_Uni']=self.OutLineVol_Rate['VolChng_Uni'].str.replace("[","")
+            self.OutLineVol_Rate['VolChng_Uni']=self.OutLineVol_Rate['VolChng_Uni'].str.replace("]","")
+            self.OutLineVol_Rate['VolOl_Uni']=self.OutLineVol_Rate['VolOl_Uni'].str.replace("[","")
+            self.OutLineVol_Rate['VolOl_Uni']=self.OutLineVol_Rate['VolOl_Uni'].str.replace("]","")
+            VoltChangeOutLine_Rate=self.OutLineVol_Rate['VolChng_Uni'].str.split(',',expand=True)
+            Volt_3Sigma=self.OutLineVol_Rate['VolOl_Uni'].str.split(',',expand=True)
+            VoltChangeOutLine_Rate.columns=volt_column
+            Volt_3Sigma.columns=volt_column
+            VoltChangeOutLine_Rate['time']=self.OutLineVol_Rate['time']
+            time0=time.mktime(VoltChangeOutLine_Rate.loc[0,'time'].timetuple())
+            for i in range(0,len(VoltChangeOutLine_Rate)):
+                VoltChangeOutLine_Rate.loc[i,'time']=(time.mktime(VoltChangeOutLine_Rate.loc[i,'time'].timetuple())-time0)/60000
+        
+        if not self.df_liplated.empty:
+            liplated=self.df_liplated['liplated_amount']
+            liplated=liplated.str.replace("[", '')
+            liplated=liplated.str.replace("]", '')
+        
+        if not self.df_uniform.empty:
+            cellvolt_rank=self.df_uniform['cellvolt_rank']
+            cellvolt_rank=cellvolt_rank.str.replace("[", '')
+            cellvolt_rank=cellvolt_rank.str.replace("]", '')
 
         for i in range(self.param.CellVoltNums):
 
             #漏电流故障判断...........................................................................
             if not self.df_short.empty:
-                short_current=self.df_short['short_current']
-                short_current=short_current.str.replace("[", '')
-                short_current=short_current.str.replace("]", '')
                 self.df_short['cellshort'+str(i+1)]=short_current.map(lambda x:eval(x.split(',')[i]))
 
                 cellshort=self.df_short['cellshort'+str(i+1)]
@@ -53,37 +84,77 @@ class SafetyWarning:
                 else:
                     shortfault=0
             
-            #析锂故障判断...............................................................................
-            if not self.df_liplated.empty:
-                liplated=self.df_liplated['liplated_amount']
-                liplated=liplated.str.replace("[", '')
-                liplated=liplated.str.replace("]", '')
-                self.df_liplated['liplated_amount'+str(i+1)]=liplated.map(lambda x:eval(x.split(',')[i]))
-
-                if max(self.df_liplated['liplated_amount'+str(i+1)])>30:
-                    liplatedfault=1
+            #电压变化率及电压离群度.................................................................................
+            if not self.OutLineVol_Rate.empty and VoltChangeOutLine_Rate.iloc[-1]['time']*60000>24*3600 and len(VoltChangeOutLine_Rate)>10:
+                VoltChangeOutLine_Rate[volt_column[i]]=VoltChangeOutLine_Rate[volt_column[i]].map(lambda x:eval(x))
+                volt3sigma=np.array(Volt_3Sigma[volt_column[i]].map(lambda x:eval(x)))
+                volt3sigma_sum=np.sum(volt3sigma<-3)
+                a1,b1=np.polyfit(VoltChangeOutLine_Rate['time'].tolist(),VoltChangeOutLine_Rate[volt_column[i]].tolist(),1)
+                y=a1*VoltChangeOutLine_Rate['time']+b1
+                volt_rate.append(a1)
+                # plt.plot(VoltChangeOutLine_Rate['time'],y,label=volt_column[i])
+                # plt.scatter( VoltChangeOutLine_Rate['time'],VoltChangeOutLine_Rate[volt_column[i]],marker='o')
+                # plt.legend(loc='best')
+                # plt.rcParams['font.sans-serif']=['SimHei'] #用来正常显示中文标签
+                # plt.rcParams['axes.unicode_minus']=False #用来正常显示负号  
+                # plt.show()
+                
+                if a1<self.param.TrwVoltRate:
+                    voltratefault=1
+                else:
+                    voltratefault=0
+                if volt3sigma_sum>len(self.OutLineVol_Rate)/2:
+                    voltsigmafault=1
                 else:
-                    liplatedfault=0
+                    voltsigmafault=0
             else:
-                liplatedfault=0
+                voltratefault=0
+                voltsigmafault=0
+            
+            #析锂故障判断...............................................................................
+            # if not self.df_liplated.empty:
+            #     self.df_liplated['liplated_amount'+str(i+1)]=liplated.map(lambda x:eval(x.split(',')[i]))
+
+            #     if max(self.df_liplated['liplated_amount'+str(i+1)])>30:
+            #         liplatedfault=1
+            #     else:
+            #         liplatedfault=0
+            # else:
+            #     liplatedfault=0
 
             #电芯SOC排名判断.............................................................................
             if not self.df_uniform.empty:
-                if (i+1) in self.df_uniform['cellmin_num'].tolist():
+                self.df_uniform['cellvolt_rank'+str(i+1)]=cellvolt_rank.map(lambda x:eval(x.split(',')[i]))
+                
+                if max(self.df_uniform['cellvolt_rank'+str(i+1)])<5:
                     uniformfault=1
                 else:
                     uniformfault=0
             else:
                 uniformfault=0
             
-            #电池热安全预警
-            if shortfault==1 and (liplatedfault==1 or uniformfault==1):
+            #电池热安全预警...............................................................................
+            if (shortfault==1 or voltratefault==1)and (voltsigmafault==1 or uniformfault==1):
+                faultcode=110
+                faultlv=4
+                faultinfo='电芯{}发生热失控安全预警'.format(i+1)
+                faultadvice='1联系用户远离电池,立刻召回电池'
+                df_res.loc[0]=[time_now, time_sp, self.sn, faultcode, faultlv, faultinfo, faultadvice]
+            elif voltratefault==1:
                 faultcode=110
                 faultlv=4
                 faultinfo='电芯{}发生热失控安全预警'.format(i+1)
-                faultadvice='联系用户远离电池,立刻召回电池'
-                df_res.loc[0]=[time, time_sp, self.sn, faultcode, faultlv, faultinfo, faultadvice]
+                faultadvice='2联系用户远离电池,立刻召回电池'
+                df_res.loc[0]=[time_now, time_sp, self.sn, faultcode, faultlv, faultinfo, faultadvice]
+            elif shortfault==1:
+                faultcode=110
+                faultlv=4
+                faultinfo='电芯{}发生热失控安全预警'.format(i+1)
+                faultadvice='3联系用户远离电池,立刻召回电池'
+                df_res.loc[0]=[time_now, time_sp, self.sn, faultcode, faultlv, faultinfo, faultadvice]
             else:
                 pass
+        
+        # print(volt_rate)
             
         return df_res

BIN
LIB/MIDDLE/CellStateEstimation/BatSafetyWarning/V1_0_1/Uniform表单.xlsx


+ 233 - 0
LIB/MIDDLE/CellStateEstimation/BatSafetyWarning/V1_0_1/VoltStray.py

@@ -0,0 +1,233 @@
+import pandas as pd
+import numpy as np
+from pandas.core.frame import DataFrame
+from LIB.MIDDLE.CellStateEstimation.Common.V1_0_1 import BatParam
+import pandas as pd
+# 计算充电过程
+def preprocess(df):
+    # 滤除前后电压存在一增一减的情况(采样异常)
+    pass
+
+# 计算电压的偏离度    
+def cal_volt_uniform(dfin, volt_column, window=10, step=5, threshold=3):
+    
+    df = dfin.copy()
+    # time_list = dfin['time'].tolist()
+
+    # # 电压滤波
+    # df_volt = df[volt_column] 
+    # df_volt_rolling = df_volt.rolling(window).mean()[window-1::step]  # 滑动平均值
+    # time_list = time_list[window-1::step]
+    df_volt_rolling = df[volt_column] 
+
+    # 电压偏离度
+    df_volt_rolling_sum=df_volt_rolling.sum(1)-df_volt_rolling.max(1)
+    df_volt_rolling_sum=df_volt_rolling_sum-df_volt_rolling.min(1)
+    mean = df_volt_rolling_sum/(len(volt_column)-2)
+    df_volt_rolling_norm = df_volt_rolling.sub(mean, axis=0)#.div(std,axis=0)
+    df_volt_rolling_norm = df_volt_rolling_norm.reset_index(drop=True)#和均值的距离
+    
+    
+    mean1=df_volt_rolling.mean(axis=1)
+    std = df_volt_rolling.std(axis=1)
+    std = std.replace(0,0.000001)
+    df_volt_rolling = df_volt_rolling.sub(mean1, axis=0).div(std,axis=0)
+    df_volt_rolling = df_volt_rolling.reset_index(drop=True)#分布
+
+    return df_volt_rolling_norm,df_volt_rolling
+
+
+# # 计算电压变化量的偏离度    
+# def cal_voltdiff_uniform(dfin, volt_column, window=10, step=5, window2=10, step2=5,threshold=3):
+    
+#     df = dfin.copy()
+#     time_list = dfin['time'].tolist()
+
+#     # 电压滤波
+#     df_volt = df[volt_column] 
+#     df_volt_rolling = df_volt.rolling(window).mean()[window-1::step]  # 滑动平均值
+#     time_list = time_list[window-1::step] 
+
+#     # 计算电压变化量的绝对值(# 计算前后的差值的绝对值,  时间列-1)
+#     df_volt_diff = abs(df_volt_rolling.diff()[1:])
+#     df_volt_diff = df_volt_diff.reset_index(drop=True)
+#     time_list = time_list[1:]
+
+#     # 压差归一化(偏离度)
+#     # mean = df_volt_diff.mean(axis=1)
+#     # std = df_volt_diff.std(axis=1)
+#     # df_voltdiff_norm = df_volt_diff.sub(mean, axis=0).div(std,axis=0)
+#     df_voltdiff_norm = df_volt_diff.copy()
+
+#     # 压差偏离度滑动平均滤波
+#     df_voltdiff_rolling = df_voltdiff_norm.rolling(window2).mean()[window2-1::step2]  # 滑动平均值
+#     time_list = time_list[window2-1::step2]
+#     df_voltdiff_rolling_sum=df_voltdiff_rolling.sum(1)-df_voltdiff_rolling.max(1)
+#     df_voltdiff_rolling_sum=df_voltdiff_rolling_sum-df_voltdiff_rolling.min(1)
+#     mean = df_voltdiff_rolling_sum/(len(volt_column)-2)
+#     std = df_voltdiff_rolling.std(axis=1)
+#     # mean = [np.array(sorted(x)[1:-1]).mean() for x in df_voltdiff_rolling.values]
+#     # std = [np.array(sorted(x)[1:-1]).std() for x in df_voltdiff_rolling.values]
+#     df_voltdiff_rolling_norm = df_voltdiff_rolling.sub(mean, axis=0)#.div(std,axis=0)
+#     df_voltdiff_rolling_norm = df_voltdiff_rolling_norm.reset_index(drop=True)
+#     return df_voltdiff_rolling_norm, time_list
+def main(sn,df_bms,celltype):
+    param=BatParam.BatParam(celltype)
+    volt_column = ['单体电压'+str(i) for i in range(1,param.CellVoltNums+1)]
+    columns=['时间戳']+volt_column
+    df_bms=df_bms[df_bms['SOC[%]']>15]
+    df_ori = df_bms[columns]
+    df_ori.rename(columns = {'时间戳':'time'}, inplace=True)
+    df_ori['time']=pd.to_datetime(df_ori['time'], format='%Y-%m-%d %H:%M:%S')
+    df = df_ori.drop_duplicates(subset=['time']) # 删除时间相同的数据
+    df= df.set_index('time')
+    df=df[(df[volt_column]>2000) & (df[volt_column]<4500)]
+    time_list=df.index.tolist()
+    df = df.reset_index(drop=True)
+
+    window = 50
+    step = 10
+    window2 = 5
+    step2 = 3
+    OutLineVol=DataFrame(columns=['time','sn','VolOl_Uni','VolChng_Uni'])
+    #df_result_1,time_list_1 = cal_voltdiff_uniform(df,volt_column, window=window, step=step, window2=window2, step2=step2)
+    VolChng_Uni,VolOl_Uni= cal_volt_uniform(df,volt_column, window=window, step=step)
+    
+    if len(VolChng_Uni)>10 and len(VolOl_Uni)>10:
+        #df_result_1['time'] = time_list_1
+        VolChng_Uni['time'] = time_list
+        # VolChng_Uni['time']=pd.to_datetime(VolChng_Uni['time'], format='%Y-%m-%d %H:%M:%S')
+        # df_result_1['time'].apply(lambda x: x.datetime.strptime('%Y-%m-%d %H:%M:%S'))
+        VolChng_Uni= VolChng_Uni.set_index('time')
+        VolChng_Uni[volt_column]=pd.DataFrame(VolChng_Uni[volt_column],dtype=np.float)
+        VolChng_Uni=VolChng_Uni.resample('H').mean()
+        VolChng_Uni=VolChng_Uni.dropna(how='any')#改
+        VolChng_Uni_result=VolChng_Uni.values.tolist()#改
+        
+        VolOl_Uni['time'] = time_list
+        VolOl_Uni= VolOl_Uni.set_index('time')
+        VolOl_Uni.index = pd.to_datetime(VolOl_Uni.index)
+        VolOl_Uni[volt_column]=pd.DataFrame(VolOl_Uni[volt_column],dtype=np.float)
+        VolOl_Uni=VolOl_Uni.resample('H').mean()
+        VolOl_Uni=VolOl_Uni.dropna(how='any')#改
+        VolOl_Uni_result=VolOl_Uni.values.tolist()#改
+        #df_result_VoltDif_Uni=str(df_result_VoltDif_Uni.tolist())
+        
+        
+        for i in range(0,len(VolChng_Uni)):
+            OutLineVol.loc[i,'VolOl_Uni']=str(list(np.around(VolOl_Uni_result[i],decimals=2)))
+            OutLineVol.loc[i,'VolChng_Uni']=str(list(np.around(VolChng_Uni_result[i],decimals=2)))
+        OutLineVol=OutLineVol[~OutLineVol['VolOl_Uni'].str.contains('nan')]
+        OutLineVol=OutLineVol[~OutLineVol['VolChng_Uni'].str.contains('nan')]
+        OutLineVol=OutLineVol.applymap((lambda x:''.join(x.split()) if type(x) is str else x)) 
+        OutLineVol=OutLineVol.reset_index(drop=True) 
+        OutLineVol['time']= VolOl_Uni.index
+        OutLineVol['sn']=sn
+    return(OutLineVol)
+    # this_alarm = {}
+    # df_alarm = df_voltdiff_rolling_norm[abs(df_voltdiff_rolling_norm)>threshold].dropna(how='all')
+    # for index in df_alarm.index:
+    #     df_temp = df_alarm.loc[index, :].dropna(how='all', axis=0)
+    #     this_alarm.update({df_cell_volt.loc[index+1, 'date']:[str(df_temp.keys().tolist()), str([round(x, 2) for x in df_temp.values.tolist()])]})
+    # df_alarm1 = pd.DataFrame(this_alarm)
+
+
+    # return pd.DataFrame(df_alarm1.values.T, index=df_alarm1.columns, columns=df_alarm1.index), pd.DataFrame(df_alarm2.values.T, index=df_alarm2.columns, columns=df_alarm2.index)
+
+    # # 孤立森林算法
+    # def iso_froest(df):
+
+    #     #1. 生成训练数据
+    #     rng = np.random.RandomState(42)
+    #     X = 0.3 * rng.randn(100, 2) #生成100 行,2 列
+    #     X_train = np.r_[X + 2, X - 2]
+    #     print(X_train)
+    #     # 产生一些异常数据
+    #     X_outliers = rng.uniform(low=-4, high=4, size=(20, 2))
+    #     iForest= IsolationForest(contamination=0.1)
+    #     iForest = iForest.fit(X_train)
+    #     #预测
+    #     pred = iForest.predict(X_outliers)
+    #     print(pred)
+    #     # [-1 1 -1 -1 -1 -1 -1 1 -
+
+# # 计算相关系数
+# def cal_coff(df):
+#     cc_mean = np.mean(df, axis=0)  #axis=0,表示按列求均值 ——— 即第一维
+#     cc_std = np.std(df, axis=0)
+#     cc_zscore = (df-cc_mean)/cc_std   #标准化
+#     cc_zscore = cc_zscore.dropna(axis=0, how='any')
+
+#     cc_zscore_corr = cc_zscore.corr(method='spearman')
+    
+#     result = []
+#     for i in range(len(cc_zscore_corr)):
+#         v = abs(np.array((sorted(cc_zscore_corr.iloc[i]))[2:-1])).mean() # 去掉1 和两个最小值后求均值
+#         result.append(v)
+#     return cc_zscore_corr, result
+
+
+# def instorage(sn, df_voltdiff_result, df_volt_result):
+    
+#     df_all_result = pd.DataFrame(columns=['sn', 'time', 'cellnum', 'value', 'type'])
+    
+#     value_list = []
+#     cellnum_list = []
+#     time_list = []
+#     type_list = []
+#     df_result = df_voltdiff_result.copy().drop(columns='time')
+#     time_list_1 = df_voltdiff_result['time']
+#     df_result = df_result[(df_result>3) | (df_result<-3)].dropna(axis=0, how='all').dropna(axis=1, how='all')
+#     for column in df_result.columns:
+#         df = df_result[[column]].dropna(axis=0, how='all')
+#         value_list.extend(df[column].tolist())
+#         cellnum_list.extend([column]*len(df))
+#         time_list.extend([time_list_1[x] for x in df.index])
+#     length_1 = len(value_list)
+    
+
+
+#     df_result = df_volt_result.copy().drop(columns='time')
+#     time_list_2 = df_volt_result['time']
+#     df_result = df_result[(df_result>3) | (df_result<-3)].dropna(axis=0, how='all').dropna(axis=1, how='all')
+#     for column in df_result.columns:
+#         df = df_result[[column]].dropna(axis=0, how='all')
+#         value_list.extend(df[column].tolist())
+#         cellnum_list.extend([column]*len(df))
+#         time_list.extend([time_list_2[x] for x in df.index])
+
+#     length_2 = len(value_list) - length_1
+#     type_list.extend(['电压变化量离群'] * length_1)
+#     type_list.extend(['电压离群'] * length_2)
+#     df_all_result['sn'] = [sn] * len(value_list)
+#     df_all_result['cellnum'] = cellnum_list
+#     df_all_result['value'] = value_list
+#     df_all_result['time'] = time_list
+#     df_all_result['type'] = type_list
+#     return df_all_result
+
+# # 报警.如果在某个窗口内,有超过ratio个的点,偏离度超过threshold, 则报警
+# def alarm(dfin, volt_column, alarm_window=10, alarm_ratio=0.8, alarm_threshold=2.5):
+
+    
+#     time_list = dfin['time'].tolist()
+#     df_result = dfin[volt_column].copy()
+#     alarm_result = pd.DataFrame(columns=['type', 'num', 'alarm_time'])      
+#     df_result_1 = df_result.copy()
+#     df_result_1[df_result_1<alarm_threshold] = 0
+#     df_result_1[df_result_1>alarm_threshold] = 1    
+#     df_result_1 = df_result_1.rolling(alarm_window).sum()
+#     for column in volt_column:
+#         if len(df_result_1[df_result_1[column]>alarm_window * alarm_ratio])>0:
+#             alarm_result = alarm_result.append({'type':'1', 'num':column, 'alarm_time':time_list[df_result_1[df_result_1[column]>alarm_window * alarm_ratio].index[0]]}, ignore_index=True)
+
+#     # time_list = time_list[window-1::step] 
+
+#     df_result_2 = df_result.copy()
+#     df_result_2[df_result_2>-alarm_threshold] = 0
+#     df_result_2[df_result_2<-alarm_threshold] = 1
+#     df_result_2 = df_result_2.rolling(alarm_window).sum()
+#     for column in volt_column:
+#         if len(df_result_2[df_result_2[column]>alarm_window * alarm_ratio])>0:
+#             alarm_result = alarm_result.append({'type':'2', 'num':column, 'alarm_time':time_list[df_result_2[df_result_2[column]>alarm_window * alarm_ratio].index[0]]}, ignore_index=True)
+#     return alarm_result

BIN
LIB/MIDDLE/CellStateEstimation/BatSafetyWarning/V1_0_1/电压变化率离群度表单.xlsx


+ 60 - 21
LIB/MIDDLE/CellStateEstimation/BatSafetyWarning/main.py

@@ -2,10 +2,12 @@ import pandas as pd
 import pymysql
 from LIB.BACKEND import DBManager, Log
 from apscheduler.schedulers.blocking import BlockingScheduler
-import time, datetime
+import datetime
 from LIB.MIDDLE.CellStateEstimation.Common.V1_0_1 import DBDownload
 from LIB.MIDDLE.CellStateEstimation.Common.V1_0_1 import log
 import CBMSBatInterShort
+import CBMSBatUniform
+import VoltStray
 import CBMSSafetyWarning
 
 #电池热安全预警核心算法函数......................................................................................................................
@@ -16,16 +18,25 @@ def saftywarning_cal():
     global df_warning_ram2
     global df_warning_ram3
     global df_lfp_ram
-    global now_time
+    global df_lfp_ram1
 
+    pd.set_option('display.width', 300) # 设置字符显示宽度
+    pd.set_option('display.max_rows', None) # 设置显示最大行
+    pd.set_option('display.max_columns', None) # 设置显示最大列,None为显示所有列
+    
+    # start=time.time()
     now_time=datetime.datetime.now()
     start_time=now_time-datetime.timedelta(hours=12)
     start_time1=now_time-datetime.timedelta(days=7)
     start_time2=now_time-datetime.timedelta(days=90)
+    start_time3=now_time-datetime.timedelta(days=3)
     start_time=start_time.strftime('%Y-%m-%d %H:%M:%S')
     start_time1=start_time1.strftime('%Y-%m-%d %H:%M:%S')
+    start_time2=start_time2.strftime('%Y-%m-%d %H:%M:%S')
+    start_time3=start_time3.strftime('%Y-%m-%d %H:%M:%S')
     end_time=now_time.strftime('%Y-%m-%d %H:%M:%S')
 
+
     #数据库配置
     host='rm-bp10j10qy42bzy0q77o.mysql.rds.aliyuncs.com'
     port=3306
@@ -65,19 +76,20 @@ def saftywarning_cal():
         dbManager = DBManager.DBManager()
         df_data = dbManager.get_data(sn=sn, start_time=start_time, end_time=end_time, data_groups=['bms'])
         df_bms = df_data['bms']
-        # tim=start_time.replace(':','_')
+        # print(df_bms)
         # df_bms.to_csv(r'D:\Platform\platform_python\data_analyze_platform\USER\spf\01qixiang\98Download\\'+sn+'_BMS_'+tim+'.csv',encoding='GB18030')
 
         #读取结果数据库数据........................................................................................................................................................
         db='qx_cas'
         mode=1
-        tablename='cellstateestimation_soh'
         DBRead=DBDownload.DBDownload(host, port, db, user, password,mode)
         with DBRead as DBRead:
-            df_soh=DBRead.getdata('time_st,sn,soh,cellsoh', tablename=tablename, sn=sn, timename='time_sp', st=start_time, sp=end_time)
-
+            df_soh=DBRead.getdata('time_st,sn,soh,cellsoh', tablename='cellstateestimation_soh', sn=sn, timename='time_sp', st=start_time, sp=end_time)
+            df_uniform=DBRead.getdata('time,sn,cellsoc_diff,cellvolt_diff,cellmin_num,cellmax_num', tablename='cellstateestimation_uniform_socvoltdiff', sn=sn, timename='time', st=start_time1, sp=end_time)
+            df_uniform.loc[0,'cellvolt_rank']=str([1]*20)
+            df_voltsigma=pd.DataFrame()
         if not df_bms.empty:
-            #电池内短路计算................................................................................................................................................................
+            #ram处理...............................................................................................................
             df_warning_ram_sn=df_warning_ram[df_warning_ram['sn']==sn]
             df_warning_ram_sn1=df_warning_ram1[df_warning_ram1['sn']==sn]
             df_warning_ram_sn2=df_warning_ram2[df_warning_ram2['sn']==sn]
@@ -86,21 +98,42 @@ def saftywarning_cal():
             df_warning_ram_sn1.reset_index(inplace=True,drop=True)     #重置索引
             df_warning_ram_sn2.reset_index(inplace=True,drop=True)     #重置索引
             df_warning_ram_sn3.reset_index(inplace=True,drop=True)     #重置索引
-            if celltype>50:
-                df_lfp_ram=df_lfp_ram.reindex(columns=df_bms.columns.tolist()+['sn'])
+            if celltype>50 and (not df_lfp_ram.empty):
                 df_lfp_ram_sn=df_lfp_ram[df_lfp_ram['sn']==sn]
                 df_lfp_ram_sn.reset_index(inplace=True,drop=True)     #重置索引
             else:
                 df_lfp_ram_sn=pd.DataFrame()
+                df_lfp_ram=pd.DataFrame(columns=df_bms.columns.tolist()+['sn'])
+            if celltype>50 and (not df_lfp_ram1.empty):
+                df_lfp_ram_sn1=df_lfp_ram1[df_lfp_ram1['sn']==sn]
+                df_lfp_ram_sn1.reset_index(inplace=True,drop=True)     #重置索引
+            else:
+                df_lfp_ram_sn1=pd.DataFrame()
+                df_lfp_ram1=pd.DataFrame(columns=df_bms.columns.tolist()+['sn'])
 
+            #内短路计算..................................................................................................................................................
             BatShort=CBMSBatInterShort.BatInterShort(sn,celltype,df_bms,df_soh,df_warning_ram_sn,df_warning_ram_sn1,df_warning_ram_sn2,df_warning_ram_sn3,df_lfp_ram_sn)
             df_short_res, df_ram_res, df_ram_res1, df_ram_res2, df_ram_res3, df_ram_res4=BatShort.intershort() 
-
             if not df_short_res.empty:
                 with open(r'D:\Platform\platform_python\data_analyze_platform\USER\spf\01qixiang\07BatSafetyWarning\内短路.txt','a') as file:
                     file.write(str(df_short_res)+'\n')
-
-            #内短路ram处理.........................................................
+            
+            #静置电压排名..................................................................................................................................................
+            BatUniform=CBMSBatUniform.BatUniform(sn,celltype,df_bms,df_uniform,df_ram_res3,df_lfp_ram_sn1)
+            df_rank_res, df_ram_res3, df_ram_res5=BatUniform.batuniform()
+            if not df_rank_res.empty:
+                df_uniform=df_rank_res
+                with open(r'D:\Platform\platform_python\data_analyze_platform\USER\spf\01qixiang\07BatSafetyWarning\电压排名.txt','a') as file:
+                    file.write(str(df_rank_res)+'\n')
+            
+            #电压离群.....................................................................................................................................................
+            OutLineVol=VoltStray.main(sn,df_bms,celltype)
+            if not OutLineVol.empty:
+                df_voltsigma=OutLineVol
+                with open(r'D:\Platform\platform_python\data_analyze_platform\USER\spf\01qixiang\07BatSafetyWarning\电压离群.txt','a') as file:
+                    file.write(str(OutLineVol)+'\n')
+
+            #ram处理................................................................................................................
             df_warning_ram=df_warning_ram.drop(df_warning_ram[df_warning_ram.sn==sn].index)
             df_warning_ram1=df_warning_ram1.drop(df_warning_ram1[df_warning_ram1.sn==sn].index)
             df_warning_ram2=df_warning_ram2.drop(df_warning_ram2[df_warning_ram2.sn==sn].index)
@@ -114,6 +147,8 @@ def saftywarning_cal():
             if celltype>50:
                 df_lfp_ram=df_lfp_ram.drop(df_lfp_ram[df_lfp_ram.sn==sn].index)
                 df_lfp_ram=pd.concat([df_lfp_ram,df_ram_res4],ignore_index=True)
+                df_lfp_ram1=df_lfp_ram1.drop(df_lfp_ram1[df_lfp_ram1.sn==sn].index)
+                df_lfp_ram1=pd.concat([df_lfp_ram1,df_ram_res5],ignore_index=True)
             
 
         #电池热安全预警..............................................................................................................................................................
@@ -123,30 +158,32 @@ def saftywarning_cal():
         tablename1='cellstateestimation_intershort'
         tablename2='mechanism_liplated'   #析锂表单
         tablename3='cellstateestimation_uniform_socvoltdiff'
+        tablename4=''   #电压离群表单
         DBRead=DBDownload.DBDownload(host, port, db, user, password,mode)
         with DBRead as DBRead:
             df_short=DBRead.getdata('time_sp,sn,short_current', tablename=tablename1, sn=sn, timename='time_sp', st=start_time1, sp=end_time)
             df_liplated=DBRead.getdata('time,sn,liplated,liplated_amount', tablename=tablename2, sn=sn, timename='time', st=start_time2, sp=end_time)
-            df_uniform=DBRead.getdata('time,sn,cellsoc_diff,cellmin_num', tablename=tablename3, sn=sn, timename='time', st=start_time1, sp=end_time)
-        
+            df_uniform=DBRead.getdata('time,sn,cellsoc_diff,cellvolt_diff,cellmin_num,cellmax_num,cellvolt_rank', tablename=tablename3, sn=sn, timename='time', st=start_time3, sp=end_time)
+            df_voltsigma=DBRead.getdata('time,sn,VolOl_Uni,VolChng_Uni', tablename=tablename4, sn=sn, timename='time', st=start_time3, sp=end_time)
+
         #获取sn的故障RAM
         df_fault_ram_sn=df_fault_ram[df_fault_ram['product_id']==sn]
         
         #热安全预警
         if df_fault_ram_sn.empty:
-            BatWarning=CBMSSafetyWarning.SafetyWarning(sn,celltype,df_short,df_liplated,df_uniform)
+            BatWarning=CBMSSafetyWarning.SafetyWarning(sn,celltype,df_short,df_liplated,df_uniform,df_voltsigma)
             df_warning_res=BatWarning.diag()
             #当前热失控故障写入数据库
             if not df_warning_res.empty:
-                with open(r'D:\Platform\platform_python\data_analyze_platform\USER\spf\01qixiang\07BatSafetyWarning\热失控预警.txt','a') as file:
+                with open(r'D:\Platform\platform_python\data_analyze_platform\USER\spf\01qixiang\07BatSafetyWarning\热失控预警.txt','a',encoding="utf-8") as file:
                     file.write(str(tuple(df_warning_res.iloc[-1]))+'\n')
         
         else:
             fault_time=datetime.datetime.strptime(df_fault_ram_sn.iloc[-1]['start_time'], '%Y-%m-%d %H:%M:%S')
-            if (now_time-fault_time).total_seconds()>24*3600:   #df_warning_end历史故障筛选并更改数据库故障结束时间
+            if (now_time-fault_time).total_seconds()>3*24*3600:   #df_warning_end历史故障筛选并更改数据库故障结束时间
                 df_fault_ram_sn['end_time']=end_time
                 df_fault_ram_sn['Batpos']=1
-                with open(r'D:\Platform\platform_python\data_analyze_platform\USER\spf\01qixiang\07BatSafetyWarning\热失控预警.txt','a') as file:
+                with open(r'D:\Platform\platform_python\data_analyze_platform\USER\spf\01qixiang\07BatSafetyWarning\热失控预警.txt','a',encoding="utf-8") as file:
                     file.write(str(tuple(df_warning_res.iloc[-1]))+'\n')
 
 
@@ -166,8 +203,8 @@ if __name__ == "__main__":
     SNnums_L7255=SNdata_L7255['SN号'].tolist()
     SNnums_C7255=SNdata_C7255['SN号'].tolist()
     SNnums_U7255=SNdata_U7255['SN号'].tolist()
-    SNnums=SNnums_L7255 + SNnums_C7255 + SNnums_U7255, SNnums_6040 + SNnums_4840 + SNnums_6060
-    SNnums=['MGMCLN750N215N155']
+    SNnums=SNnums_L7255 + SNnums_C7255 + SNnums_U7255 + SNnums_6040 + SNnums_4840 + SNnums_6060
+    SNnums=['MGMCLN750N215I091']
     
     mylog=log.Mylog('log_warning.txt','error')
     mylog.logcfg()
@@ -176,8 +213,10 @@ if __name__ == "__main__":
     df_warning_ram=pd.DataFrame(columns=['sn','time','deltsoc','cellsoc'])
     df_warning_ram1=pd.DataFrame(columns=['sn','time1','deltsoc1'])
     df_warning_ram2=pd.DataFrame(columns=['sn','time2','deltAs2'])
-    df_warning_ram3=pd.DataFrame(columns=['sn','time3','standingtime','standingtime1'])
+    df_warning_ram3=pd.DataFrame(columns=['sn','time3','standingtime','standingtime1','standingtime2'])
     df_lfp_ram=pd.DataFrame()
+    df_lfp_ram1=pd.DataFrame()
+
 
     #定时任务.......................................................................................................................................................................
     scheduler = BlockingScheduler()

+ 1 - 0
LIB/MIDDLE/CellStateEstimation/Common/V1_0_1/BatParam.py

@@ -16,6 +16,7 @@ class BatParam:
         self.TrwCellVoltFall=1
         self.TrwCellVoltLow=1.5
         self.TrwPackVoltFall=1.5
+        self.TrwVoltRate=-6
 
         self.SocJump=10
         self.SocClamp=0.1

+ 6 - 6
LIB/MIDDLE/SaftyCenter/DataDiag_Static/CBMSBatDiag.py

@@ -227,7 +227,7 @@ class BatDiag:
                     else:
                         pass
                 else:
-                    if (cellvoltmax0-cellvoltmin0)<self.param.CellVoltDiffLv1-0.05 and (cellvoltmax1-cellvoltmin1)>self.param.CellVoltDiffLv1-0.05: #二级欠压恢复
+                    if (cellvoltmax0-cellvoltmin0)<self.param.CellVoltDiffLv1-0.05 and (cellvoltmax1-cellvoltmin1)<self.param.CellVoltDiffLv1-0.05: #二级欠压恢复
                         time=self.bmstime[i]
                         self.df_diag_ram.loc[self.df_diag_ram[self.df_diag_ram['code']==16].index, 'end_time'] = time
                     else:
@@ -273,9 +273,9 @@ class BatDiag:
             
             #电流过流诊断.......................................................................................................................
             step=(self.bmstime[i]-self.bmstime[i-1]).total_seconds()
-            if step<120 and self.packcrnt[i]>self.param.PackDisOc:
+            if step<120 and self.packcrnt[i]>self.param.PackDisOc and self.packcrnt[i-1]>self.param.PackDisOc:
                 as_dis=as_dis+(self.packcrnt[i]-self.param.PackDisOc)*step    #ah累计
-            elif step<120 and self.packcrnt[i]<self.param.PackChgOc:
+            elif step<120 and self.packcrnt[i]<self.param.PackChgOc and self.packcrnt[i-1]<self.param.PackChgOc:
                 as_chg=as_chg+(self.param.PackDisOc-self.packcrnt[i])*step    #ah累计
             else:
                 as_dis=0
@@ -292,7 +292,7 @@ class BatDiag:
                 else:
                     pass
             else:
-                if self.packcrnt[i-1]<self.param.PackDisOc-10 and self.packcrnt[i]<self.param.PackDisOc-10:
+                if self.packcrnt[i]<self.param.PackDisOc-10:
                     time=self.bmstime[i]
                     self.df_diag_ram.loc[self.df_diag_ram[self.df_diag_ram['code']==22].index, 'end_time'] = time
                 else:
@@ -309,9 +309,9 @@ class BatDiag:
                 else:
                     pass
             else:
-                if self.packcrnt[i-1]>self.param.PackChgOc+10 and self.packcrnt[i]>self.param.PackChgOc+10:
+                if self.packcrnt[i]>self.param.PackChgOc+10:
                     time=self.bmstime[i]
-                    self.df_diag_ram.loc[self.df_diag_ram[self.df_diag_ram['code']==22].index, 'end_time'] = time
+                    self.df_diag_ram.loc[self.df_diag_ram[self.df_diag_ram['code']==21].index, 'end_time'] = time
                 else:
                     pass