深度学习 | 基于 LSTM 模型的多电池健康状态对比及预测
Hi,大家好,我是半亩花海。在电池管理系统(BMS)中,电池的健康状态(State of Health, SOH)是评估电池剩余寿命的重要指标,而准确预测电池的健康状态可以帮助实现电池的高效管理,延长电池的使用寿命并预防电池故障。本项目的目标是使用长短期记忆(LSTM)神经网络对多个电池进行健康状态对比及预测,并使用收集到的电池循环数据来训练模型并评估其性能。
目录
一、数据概述
二、数据可视化
1. 绘制容量与循环次数的关系
2. 绘制健康状态与循环次数的关系
3. 多电池的健康状态对比
三、数据预处理
四、LSTM模型构建
五、模型训练
六、模型评估
七、保存预测结果
一、数据概述
数据来自八个电池单元(B05, B07, B18, B33, B34, B46, B47, B48),每个电池包含多个充放电周期的数据。如下所示,数据存储在CSV文件中。经过初步数据处理之后,每个数据文件提取了包含以下字段的关键信息:
- cycle:循环次数
- capacity:容量
- SOH:健康状态(State of Health)
以下是数据的读取和清洗过程。
import os
import pandas as pd
# 获取数据集目录
directory_main = os.listdir('D:\Soochow University\Battery Research\ML_Course_SOH\case_one\datasets')
directory_edited = [file for file in directory_main if file != '.ipynb_checkpoints'] # 排除.ipynb_checkpoints目录
print(directory_main)
print(directory_edited)
print("The numbers of datasets:", len(directory_main))
print("The numbers of datasets:", len(directory_edited))
os.listdir()
用于列出指定路径下的所有文件,返回一个文件名列表。
# 定义电池编号
num = ['B05', 'B07', 'B18', 'B33', 'B34', 'B46', 'B47', 'B48']
# 循环读取每个电池的数据文件
for i in range(len(directory_edited)):
path = os.path.join('D:\Soochow University\Battery Research\ML_Course_SOH\case_one\datasets/', num[i] + '_discharge_soh.csv')
csv = pd.read_csv(path)
df = pd.DataFrame(csv)
vec = df[['cycle', 'capacity', 'SOH']]
globals()['data_{}'.format(num[i])] = vec
data = pd.read_csv('D:\Soochow University\Battery Research\ML_Course_SOH\case_one\datasets/B05_discharge_soh.csv')
df = pd.DataFrame(data)
df
- 通过
pandas.read_csv()
读取每个电池单元的CSV文件,并提取其包含的循环次数、容量、SOH数据。 - 使用
globals()
动态创建每个电池的数据框(data_B05
、data_B07
等),便于后续的处理。
data_B05
以其中一个电池为例,展示其 'cycle', 'capacity', 'SOH' 部分的数据,如上所示。
二、数据可视化
我们通过可视化来分析电池数据的特点。以下图表展示了电池的容量、健康状态(SOH)与循环次数之间的关系,即电池退化曲线、健康状态分析曲线。
1. 绘制容量与循环次数的关系
import seaborn as sns
import matplotlib.pyplot as plt
# 绘制每个电池的容量随循环次数变化的散点图
for i in range(len(directory_edited)):
dff = globals()['data_{}'.format(num[i])]
sns.set_style("darkgrid")
plt.figure(figsize=(12, 8))
plt.scatter(dff['cycle'], dff['capacity'])
plt.ylabel('Capacity', fontsize=15)
plt.xlabel('cycle', fontsize=15)
plt.title('Discharge_' + num[i], fontsize=15)
plt.show()
- 使用
seaborn.set_style()
设置图表背景样式。 - 使用
matplotlib.pyplot.scatter()
绘制每个电池的容量随循环次数的变化。 plt.show()
显示每张图表。
2. 绘制健康状态与循环次数的关系
# 绘制每个电池的SOH随循环次数变化的散点图
for i in range(len(directory_edited)):
dff = globals()['data_{}'.format(num[i])]
sns.set_style("darkgrid")
plt.figure(figsize=(12, 8))
plt.scatter(dff['cycle'], dff['SOH'])
plt.ylabel('SoH', fontsize=15)
plt.xlabel('cycle', fontsize=15)
plt.title('Discharge_' + num[i], fontsize=15)
plt.show()
- 通过同样的方式,我们可以查看电池的健康状态(SOH)与循环次数之间的关系。
- 每个电池的健康状态随循环次数的变化趋势会有所不同。
3. 多电池的健康状态对比
- 为了比较不同电池的健康状态,我们将多个电池的SOH变化绘制在同一张图上。
- 通过
plt.legend()
添加图例,便于区分不同电池。
# 比较不同电池的SOH变化
sns.set_style("darkgrid")
plt.figure(figsize=(12, 8))
plt.scatter(data_B05['cycle'], data_B05['SOH'], label='B05')
plt.scatter(data_B07['cycle'], data_B07['SOH'], label='B07')
plt.scatter(data_B18['cycle'], data_B18['SOH'], label='B18')
plt.legend(prop={'size': 16})
plt.ylabel('SoH', fontsize=15)
plt.xlabel('Discharge cycle', fontsize=15)
plt.title('SoH of group A', fontsize=15)
plt.show()
sns.set_style("darkgrid")
plt.figure(figsize=(12, 8))
plt.scatter(data_B33['cycle'], data_B33['SOH'],label='B33')
plt.scatter(data_B34['cycle'], data_B34['SOH'],label='B34')
plt.legend(prop={'size': 16})
plt.ylabel('SoH', fontsize = 15)
plt.xlabel('Discharge cycle', fontsize = 15)
plt.title('SoH of group B', fontsize = 15)
plt.show()
sns.set_style("darkgrid")
plt.figure(figsize=(12, 8))
plt.scatter(data_B46['cycle'], data_B46['SOH'],label='B46')
plt.scatter(data_B47['cycle'], data_B47['SOH'],label='B47')
plt.scatter(data_B48['cycle'], data_B48['SOH'],label='B48')
plt.legend(prop={'size': 16})
plt.ylabel('SoH', fontsize = 15)
plt.xlabel('Discharge cycle', fontsize = 15)
plt.title('SoH of group C', fontsize = 15)
plt.show()
三、数据预处理
LSTM模型需要以时间序列的形式输入数据,因此我们需要将电池健康状态的数据转化为适合LSTM的格式。以下是模型构建之前的数据集创建部分。
create_dataset()
:该函数将原始的时间序列数据切分成多个子序列,每个子序列的长度由look_back
决定。每个子序列的目标值是该子序列之后的一个时间步的健康状态(SoH)。- 数据集被划分为50%的训练集和50%的测试集。
import numpy as np
# 划分训练集和测试集
dataset = data_B48["SOH"]
cycle = data_B48['cycle']
dataset = np.array(dataset)
dataset = dataset.reshape((len(dataset), 1))
dataset.shape
train_size = int(len(dataset) * 0.5)
test_size = len(dataset) - train_size
train, test = dataset[0:train_size,:], dataset[train_size:len(dataset),:]
print(len(train), len(test))
# 创建时间序列数据集
def create_dataset(dataset, look_back=1):
dataX, dataY = [], []
for i in range(len(dataset) - look_back):
a = dataset[i:(i + look_back), 0]
dataX.append(a)
dataY.append(dataset[i + look_back, 0])
return np.array(dataX), np.array(dataY)
look_back = 10
trainX, trainY = create_dataset(train, look_back)
testX, testY = create_dataset(test, look_back)
trainX
# 重塑数据以适应LSTM输入
trainX = np.reshape(trainX, (trainX.shape[0], 1, trainX.shape[1]))
testX = np.reshape(testX, (testX.shape[0], 1, testX.shape[1]))
print(trainX.shape)
print(testX.shape)
四、LSTM模型构建
LSTM是一个处理时间序列数据的强大工具。我们将使用LSTM来预测电池的健康状态。以下是LSTM模型定义部分。
import torch
import torch.nn as nn
import torch.optim as optim
class MyLSTMModel(nn.Module):
def __init__(self, input_size, hidden_size, output_size):
super(MyLSTMModel, self).__init__()
self.lstm = nn.LSTM(input_size, hidden_size, batch_first=True)
self.fc = nn.Linear(hidden_size, output_size)
def forward(self, x):
lstm_out, _ = self.lstm(x)
output = self.fc(lstm_out[:, -1, :]) # 使用最后一个时间步的输出
return output
LSTM
:LSTM层用于处理输入的时间序列数据,input_size
为每个时间步的特征数量,hidden_size
为LSTM层的隐藏单元数量。Linear
:全连接层,用于将LSTM层的输出映射到目标变量(电池健康状态)的预测值。
五、模型训练
我们使用PyTorch训练LSTM模型,采用Adam优化器和L1损失函数(Mean Absolute Error,MAE)来优化模型。
# 定义训练循环
model = MyLSTMModel(input_size=1, hidden_size=64, output_size=1)
criterion = nn.L1Loss() # L1损失函数(MAE)
optimizer = optim.Adam(model.parameters())
# 转换为PyTorch张量
trainX_tensor = torch.tensor(trainX, dtype=torch.float32)
trainY_tensor = torch.tensor(trainY, dtype=torch.float32)
# 训练模型
num_epochs = 566 # 训练566个epoch
history = {'train_loss': [], 'val_loss': []}
for epoch in range(num_epochs):
model.train()
optimizer.zero_grad()
# 假设trainX_tensor是一个三维形状张量(batch_size,sequence_length,input_size)
output = model(trainX_tensor)
# 假设trainY_tensor是PyTorch张量
loss = criterion(output.squeeze(), trainY_tensor)
loss.backward()
optimizer.step()
history['train_loss'].append(loss.item())
print(f'Epoch [{epoch + 1}/{num_epochs}], Train Loss: {loss.item():.4f}')
optimizer.zero_grad()
:在每个训练周期开始时清除梯度。model.train()
:将模型设置为训练模式。loss.backward()
:反向传播计算梯度。optimizer.step()
:更新模型参数。
plt.plot(history['train_loss'], label='train')
plt.xlabel('Epoch')
plt.ylabel('Loss')
plt.legend()
plt.show()
六、模型评估
训练完成后,我们使用测试集对模型进行评估,并计算其预测的RMSE和MAE。
model.eval()
:将模型设置为评估模式,关闭dropout等操作。mean_absolute_error
和mean_squared_error
:计算模型预测的平均绝对误差(MAE)和均方根误差(RMSE),用来评估模型性能。
from sklearn.metrics import mean_absolute_error, mean_squared_error
import torch
import math
# 假设“model”是你训练的PyTorch模型
# 假设'testX'、'trainX'、'testY'、'trainY'是您的数据
# 将“testX”和“trainX”转换为PyTorch张量
testX_tensor = torch.tensor(testX, dtype=torch.float32)
testY_tensor = torch.tensor(testY, dtype=torch.float32)
# 将模型设置为评估模式
model.eval()
# 使用PyTorch进行预测
with torch.no_grad():
yhat_test = model(testX_tensor)
yhat_test_torch = yhat_test.numpy()
# 假设'testY'和'yhat_test_torch'是numpy数组或PyTorch张量
# 如果'testY'是PyTorch张量,则将其转换为numpy数组
testY = testY.numpy() if torch.is_tensor(testY) else testY
# 计算误差
rmse = math.sqrt(mean_squared_error(testY, yhat_test_torch))
mae = mean_absolute_error(testY, yhat_test_torch)
print(f'Test RMSE: {rmse:.3f}')
print(f'Test MAE: {mae:.3f}')
七、保存预测结果
我们将预测结果保存到Excel文件中,以便后续分析。
- 使用
pandas.DataFrame()
创建一个包含真实值和预测值的表格,然后将其保存为Excel文件。
import pandas as pd
def save_to_excel(origin_y_true, origin_y_pred):
data_dict = {'真实值': origin_y_true, '预测值': origin_y_pred}
df = pd.DataFrame(data_dict)
output_excel_path = "output_predictions.xlsx"
with pd.ExcelWriter(output_excel_path) as writer:
df.to_excel(writer)
save_to_excel(testY, yhat_test_torch)
结论:
1. 本项目基于LSTM模型成功地对电池的健康状态进行了预测,验证了LSTM在时间序列预测中的有效性。
2. 通过对不同电池单元的SOH进行比较,我们发现不同电池有不同的退化模式。
3. 在未来的工作中,可以通过加入更多的特征和优化模型结构进一步提高预测准确性。