原创博客,转载请注明出处,谢谢!

代码下载地址:https://github.com/XiuzeZhou/CALCE

1. 需求

近几年,随着智能手机和电动汽车的广泛应用,锂电池的话题越来越多,研究也越来越丰富,总之一句话:新能源很热。于是,趁最近一个空段,我也加入了新能源的研究大军。

花了几个月时间,看了百来篇与锂电池相关的论文。大部分有关锂电池寿命预测的论文用到两个数据集:NASA 和 CALCE。NASA 是美国宇航局 NASA 埃姆斯研究中心提供的锂电池老化实验数据,CALCE 是马里兰大学高级生命周期工程中心的电池循环测试数据集。

本文为 CALCE 锂电池数据集分析,NASA 锂电池数据集请看: https://snailwish.com/395/

2. CALCE 数据集

I. 数据集

原始下载地址:https://calce.umd.edu/data#CS2

(我的程序中已经包括下载好的数据集:CS2_35,CS2_36,CS2_37,CS2_38)

II. 测试内容

温度:测试温度 1 度

充电:以的恒定电流(CC)模式进行充电,直到电池电压达到 4.2V,然后以恒定电压(CV)模式充电,直到充电电流降至 20mA。

放电:以恒定电流(CC)模式进行放电,直到电池电压降到 2.7V。

终止条件:当电池达到寿命终止(End Of Life, EOF)标准——额定容量下降到它的30%,即电池的额定容量从 1.1Ahr 到 0.77Ahr。

III. 数据介绍

提取数据之前,我们需要先了解这个数据集的结构和内容。首先,我们打开其中一个数据集,查看一下数据。

数据集有 17 列:Data_Point,Test_Time(s),Date_Time,Step_Time(s),Step_Index,Cycle_Index,Current(A),Voltage(V),Charge_Capacity(Ah),Discharge_Capacity(Ah),Charge_Energy(Wh),Discharge_Energy(Wh),dV/dt(V/s),Internal_Resistance(Ohm),Is_FC_Data,AC_Impedance(Ohm),ACI_Phase_Angle(Deg)

3. 数据处理

I. 文件读取

读取 xlsx 文件,然后将对应的数据另存为 python 的数据格式。

# 加载数据
def load_data(Battary_list, dir_path):
    Battery = {}
    for name in Battary_list:
        print('Load Dataset ' + name + ' ...')
        path = glob.glob(dir_path + name + '/*.xlsx')
        dates = []
        for p in path:
            df = pd.read_excel(p, sheetname=1)
            print('Load ' + str(p) + ' ...')
            dates.append(df['Date_Time'][0])
        idx = np.argsort(dates)
        path_sorted = np.array(path)[idx]

        count = 0
        discharge_capacities = []
        health_indicator = []
        internal_resistance = []
        CCCT = []
        CVCT = []
        for p in path_sorted:
            df = pd.read_excel(p,sheetname=1)
            print('Load ' + str(p) + ' ...')
            cycles = list(set(df['Cycle_Index']))
            for c in cycles:
                df_lim = df[df['Cycle_Index'] == c]
                #Charging
                df_c = df_lim[(df_lim['Step_Index'] == 2)|(df_lim['Step_Index'] == 4)]
                c_v = df_c['Voltage(V)']
                c_c = df_c['Current(A)']
                c_t = df_c['Test_Time(s)']
                #CC or CV
                df_cc = df_lim[df_lim['Step_Index'] == 2]
                df_cv = df_lim[df_lim['Step_Index'] == 4]
                CCCT.append(np.max(df_cc['Test_Time(s)'])-np.min(df_cc['Test_Time(s)']))
                CVCT.append(np.max(df_cv['Test_Time(s)'])-np.min(df_cv['Test_Time(s)']))

                #Discharging
                df_d = df_lim[df_lim['Step_Index'] == 7]
                d_v = df_d['Voltage(V)']
                d_c = df_d['Current(A)']
                d_t = df_d['Test_Time(s)']
                d_im = df_d['Internal_Resistance(Ohm)']

                if(len(list(d_c)) != 0):
                    time_diff = np.diff(list(d_t))
                    d_c = np.array(list(d_c))[1:]
                    discharge_capacity = time_diff*d_c/3600 # Q = A*h
                    discharge_capacity = [np.sum(discharge_capacity[:n]) 
                                          for n in range(discharge_capacity.shape[0])]
                    discharge_capacities.append(-1*discharge_capacity[-1])

                    dec = np.abs(np.array(d_v) - 3.8)[1:]
                    start = np.array(discharge_capacity)[np.argmin(dec)]
                    dec = np.abs(np.array(d_v) - 3.4)[1:]
                    end = np.array(discharge_capacity)[np.argmin(dec)]
                    health_indicator.append(-1 * (end - start))

                    internal_resistance.append(np.mean(np.array(d_im)))
                    count += 1

        discharge_capacities = np.array(discharge_capacities)
        health_indicator = np.array(health_indicator)
        internal_resistance = np.array(internal_resistance)
        CCCT = np.array(CCCT)
        CVCT = np.array(CVCT)

        idx = drop_outlier(discharge_capacities, count, 40)
        df_result = pd.DataFrame({'cycle':np.linspace(1,idx.shape[0],idx.shape[0]),
                                  'capacity':discharge_capacities[idx],
                                  'SoH':health_indicator[idx],
                                  'resistance':internal_resistance[idx],
                                  'CCCT':CCCT[idx],
                                  'CVCT':CVCT[idx]})
        Battery[name] = df_result
    return Battery

II. 数据预处理

数据分析前,需要去除数据中一些异常点。

# 去除异常点
def drop_outlier(array,count,bins):
    index = []
    range_ = np.arange(1,count,bins)
    for i in range_[:-1]:
        array_lim = array[i:i+bins]
        sigma = np.std(array_lim)
        mean = np.mean(array_lim)
        th_max,th_min = mean + sigma*2, mean - sigma*2
        idx = np.where((array_lim < th_max) & (array_lim > th_min))
        idx = idx[0] + i
        index.extend(list(idx))
    return np.array(index)

II. 数据分析

做锂电池分析,最重要的三个参数是:容量,电流和电压。接下来,我们分别对提取它们的数据,并查看它们的变化趋势。

放电容量 v.s. 放电周期

锂电池寿命预测,最重要的是对放电时的电池容量衰减曲线进行建模和分析。

# 放电容量 v.s. 放电周期
fig, ax = plt.subplots(1, figsize=(12, 8))
color_list = ['b:', 'g--', 'r-.', 'c:']
for name,color in zip(Battary_list, color_list):
    battery = Battery[name]
    ax.plot(battery['cycle'], battery['capacity'], color, label='Battery_'+name)

ax.set(xlabel='Discharge cycles', ylabel='Capacity (Ah)', 
title='Capacity degradation at ambient temperature of 1°C')
plt.legend()

放电容量和内阻变化

随着电池的充放电次数增多,电池的内阻逐渐增大。这也是电池老化的重要原因。下面我们看一下内阻随充放电次数增加的变化情况。

各项指标 v.s. 充放电周期

除了电池容量和内阻变化,恒定电流充电时间(CCCT)和恒定电压充电时间(CVCT)也是电池健康状态(SOH)评估的重要指标。

更多内容

1. NASA 锂电池数据集,基于 Python 的锂电池寿命预测: https://snailwish.com/395/

2. NASA 锂电池数据集,基于 python 的 MLP 锂电池寿命预测: https://snailwish.com/427/

3. 马里兰大学锂电池数据集 CALCE,MLP 锂电池寿命预测: https://snailwish.com/464/

4. NASA 和 CALCE 锂电池数据集,基于 Pytorch 的 RNN、LSTM、GRU 寿命预测: https://snailwish.com/497/

5. 基于 Pytorch 的 Transformer 锂电池寿命预测: https://snailwish.com/555/

6. 锂电池研究之七——基于 Pytorch 的高斯函数拟合时间序列数据: https://snailwish.com/576/