% SVR预测电池健康状态(SOH) - 优化版 clc; clear; close all; % 设置训练集和测试集路径 train_folder = 'D:\HuaweiMoveData\Users\26304\Desktop\XJTU\训练集'; test_folder = 'D:\HuaweiMoveData\Users\26304\Desktop\XJTU\测试集'; % 定义特征列和目标列 feature_names = {'A', 'V0', 'PeakValue', 'PeakArea'}; target_name = 'SOH'; % 读取文件夹中的所有CSV文件 train_data = readAllCSVFiles(train_folder); test_data = readAllCSVFiles(test_folder); % 提取特征和目标变量 X_train = train_data{:, feature_names}; y_train = train_data{:, target_name}; X_test = test_data{:, feature_names}; y_test = test_data{:, target_name}; % 0-1归一化特征 (Min-Max Scaling) min_vals = min(X_train); max_vals = max(X_train); range_vals = max_vals - min_vals; % 避免除零错误 range_vals(range_vals == 0) = 1; % 应用归一化 X_train_norm = (X_train - min_vals) ./ range_vals; X_test_norm = (X_test - min_vals) ./ range_vals; % 训练SVR模型 svr_model = fitrsvm(X_train_norm, y_train, ... 'KernelFunction', 'gaussian', ... 'KernelScale', 'auto', ... 'BoxConstraint', 100, ... 'Epsilon', 0.1, ... 'Standardize', false); % 已手动标准化 % 预测和评估 y_pred = predict(svr_model, X_test_norm); % 计算性能指标 mse = mean((y_test - y_pred).^2); rmse = sqrt(mse); mae = mean(abs(y_test - y_pred)); r2 = 1 - sum((y_test - y_pred).^2) / sum((y_test - mean(y_test)).^2); % 显示结果 fprintf('模型性能评估:\n'); fprintf('均方误差(MSE): %.4f\n', mse); fprintf('均方根误差(RMSE): %.4f\n', rmse); fprintf('平均绝对误差(MAE): %.4f\n', mae); fprintf('决定系数(R²): %.4f\n', r2); % 可视化预测结果与实际值对比 figure; scatter(1:length(y_test), y_test, 100, 'bo', 'filled', 'DisplayName', '实际值'); hold on; scatter(1:length(y_pred), y_pred, 100, 'rx', 'DisplayName', '预测值'); grid on; xlabel('样本索引'); ylabel('SOH值'); title('SOH预测结果对比'); legend('Location', 'best'); set(gca, 'FontSize', 12); hold off; % 绘制残差图 figure; residuals = y_test - y_pred; scatter(y_pred, residuals, 80, 'filled', 'MarkerFaceColor', [0.5 0.5 0.9]); hold on; plot([min(y_pred) max(y_pred)], [0 0], 'k-', 'LineWidth', 2); xlabel('预测值', 'FontSize', 12); ylabel('残差', 'FontSize', 12); title('预测值 vs 残差', 'FontSize', 14); grid on; set(gca, 'FontSize', 10); hold off; % 辅助函数:读取文件夹中的所有CSV文件 function combinedData = readAllCSVFiles(folder) files = dir(fullfile(folder, '*.csv')); combinedData = []; for i = 1:length(files) file_path = fullfile(folder, files(i).name); data = readtable(file_path); % 确保所有文件包含所需的列 required_columns = {'FileName', 'Week', 'DischargeCapacity', 'SOH', ... 'A', 'K', 'V0', 'RMSE', 'R2', 'PeakValue', ... 'PeakVoltage', 'PeakArea', 'RUL'}; if all(ismember(required_columns, data.Properties.VariableNames)) if isempty(combinedData) combinedData = data; else combinedData = [combinedData; data]; end else fprintf('警告: 文件 %s 缺少所需列,已跳过\n', files(i).name); disp('文件包含的列:'); disp(data.Properties.VariableNames); end end end 不同测试电池的预测结果分开绘制
时间: 2025-07-19 13:27:32 浏览: 8
<think>我们有一个MATLAB程序,用于使用SVR(支持向量回归)预测电池健康状态(SOH)。程序包括数据读取、预处理(归一化)、模型训练、预测、评估和可视化。用户要求将不同测试电池的预测结果分开绘制。
当前程序将训练集和测试集的所有数据合并在一起,然后进行训练和测试。但是,测试集可能包含多个电池的数据(每个电池有多个循环周期的数据)。用户希望针对每个电池单独绘制预测结果。
为了实现这一点,我们需要:
1. 在测试集中,根据电池标识(例如,文件名或某个标识列)将测试数据分组。
2. 对每个电池单独进行预测和绘制。
然而,当前的数据读取方式将所有测试数据合并到一个大表中。我们需要在读取数据时保留电池的标识信息,以便后续分组。
观察辅助函数`readAllCSVFiles`,它读取一个文件夹中的所有CSV文件并合并成一个表。但是,合并后的表丢失了每个样本来自哪个文件的信息(除了'FileName'列)。在原始数据中,每个CSV文件可能代表一个电池的多次循环数据,或者多个电池的数据。
在合并后的表中,有一个'FileName'列,我们可以利用这个列来区分不同的电池。但是,请注意,同一个电池的数据可能分散在多个CSV文件中吗?根据问题描述,每个CSV文件可能对应一个电池的多次循环。因此,我们可以假设每个CSV文件对应一个电池,那么我们可以用文件名来区分电池。
但是,在测试集中,可能有多个CSV文件,每个文件代表一个电池的完整数据。因此,在测试时,我们需要按文件名分组,然后分别对每个文件的数据进行预测和绘制。
步骤:
1. 修改测试数据读取:在读取每个测试文件时,我们不仅要读取数据,还要记录文件名(已经记录在'FileName'列中)。
2. 在测试阶段,不要一次性对整个测试集进行预测,而是按电池(按文件名)分组,然后对每个电池的数据进行预测,并分别绘制。
因此,我们需要修改程序:
- 在读取测试集时,不要合并成一个表,而是保留每个文件的数据单独存储。
- 或者,在合并后,我们根据'FileName'列中的唯一值来拆分测试集。
考虑到后续可能需要对每个电池单独评估,我们选择第二种方法:先合并,然后按唯一文件名分组。
但是注意:同一个电池的数据可能出现在多个文件中?根据问题描述,训练集和测试集是分开的文件夹,每个文件可能是一个电池的多个循环数据。因此,一个电池的数据在一个文件中。所以,我们可以按文件名分组。
修改计划:
1. 修改测试集读取方式:仍然使用`readAllCSVFiles`函数读取所有测试文件,合并成一个表,但保留'FileName'列。
2. 获取测试集中所有唯一的文件名(即每个电池一个文件名)。
3. 对每个唯一的文件名,提取该电池的数据,进行归一化(注意:归一化参数来自训练集),然后预测。
4. 对每个电池,绘制实际SOH和预测SOH的对比图(横坐标可以是循环次数或样本索引,但注意同一个电池的数据是时间序列,所以横坐标应该是循环次数或时间点,但数据表中没有循环次数的列?有'Week'列?或者用样本索引)。
但是,在数据表中,我们有一个'Week'列,表示循环的周数?或者我们可以用行号作为横坐标(样本索引)。但最好使用循环周期(如果有的话)作为横坐标。然而,用户要求分开绘制每个电池的预测结果,横坐标可以是循环周期(如周数)或样本点。
在数据表中,我们有一个'Week'列,我们可以用这个列作为横坐标(x轴)。如果没有,则用样本索引。
具体步骤:
1. 首先,我们保留测试集合并后的表`test_data`,其中包含'FileName'列。
2. 获取测试集中唯一的文件名列表:
unique_files = unique(test_data.FileName);
3. 对每个文件名,提取该文件对应的数据子集。
4. 对每个子集,应用归一化(使用训练集得到的min_vals和max_vals)。
5. 预测该子集的SOH。
6. 绘制该电池的实际SOH和预测SOH对比图,并计算该电池的评估指标(可选)。
注意:归一化时,要使用训练集的归一化参数,而不是测试集自身的。
修改代码部分:
在模型训练和预测部分,我们不再一次性对整个测试集进行预测,而是循环处理每个电池(按文件名分组)。
同时,我们可能希望保存每个电池的预测结果和评估指标。
代码修改如下:
替换以下部分:
% 预测和评估
y_pred = predict(svr_model, X_test_norm);
改为:
unique_files = unique(test_data.FileName);
figure; % 创建一个新图,用于绘制所有电池的对比?或者每个电池单独一个图?用户要求分开绘制,所以每个电池一个图。
% 初始化一个结构体或元胞数组来存储每个电池的评估结果
battery_results = struct();
for i = 1:length(unique_files)
file_name = unique_files{i};
% 提取当前文件的数据
mask = strcmp(test_data.FileName, file_name);
current_test_data = test_data(mask, :);
% 提取特征
X_current = current_test_data{:, feature_names};
y_current = current_test_data{:, target_name};
% 归一化特征
X_current_norm = (X_current - min_vals) ./ range_vals;
% 预测
y_pred_current = predict(svr_model, X_current_norm);
% 计算评估指标
mse_current = mean((y_current - y_pred_current).^2);
rmse_current = sqrt(mse_current);
mae_current = mean(abs(y_current - y_pred_current));
r2_current = 1 - sum((y_current - y_pred_current).^2) / sum((y_current - mean(y_current)).^2);
% 存储结果(可选)
battery_results(i).FileName = file_name;
battery_results(i).MSE = mse_current;
battery_results(i).RMSE = rmse_current;
battery_results(i).MAE = mae_current;
battery_results(i).R2 = r2_current;
battery_results(i).Actual = y_current;
battery_results(i).Predicted = y_pred_current;
% 绘制当前电池的预测结果对比图
figure;
% 如果有时间序列信息(如'Week'),则用'Week'作为横坐标,否则用样本索引
if ismember('Week', current_test_data.Properties.VariableNames)
x_vals = current_test_data.Week;
else
x_vals = 1:length(y_current);
end
plot(x_vals, y_current, 'bo-', 'LineWidth', 1.5, 'MarkerSize', 8, 'DisplayName', '实际值');
hold on;
plot(x_vals, y_pred_current, 'rx--', 'LineWidth', 1.5, 'MarkerSize', 8, 'DisplayName', '预测值');
hold off;
grid on;
xlabel('循环周数', 'FontSize', 12);
ylabel('SOH值', 'FontSize', 12);
title(sprintf('电池: %s 的SOH预测结果对比', file_name), 'FontSize', 14);
legend('Location', 'best');
set(gca, 'FontSize', 10);
% 也可以绘制残差图(可选,每个电池一个残差图)
figure;
residuals_current = y_current - y_pred_current;
scatter(y_pred_current, residuals_current, 80, 'filled', 'MarkerFaceColor', [0.5 0.5 0.9]);
hold on;
plot([min(y_pred_current) max(y_pred_current)], [0 0], 'k-', 'LineWidth', 2);
xlabel('预测值', 'FontSize', 12);
ylabel('残差', 'FontSize', 12);
title(sprintf('电池: %s 的预测值 vs 残差', file_name), 'FontSize', 14);
grid on;
set(gca, 'FontSize', 10);
hold off;
end
同时,我们可能希望保留整个测试集的评估,所以之前整个测试集的评估可以保留,也可以去掉。根据用户要求,我们只需要分开绘制每个电池的预测结果,所以整个测试集的评估可以保留,但分开绘制后,整个测试集的预测图(之前画的)可能不需要了,因为每个电池单独画了。
因此,我们可以注释掉之前对整个测试集的绘图部分(即两个figure:对比图和残差图),只保留每个电池的绘图。
但是,用户可能也需要整个测试集的评估指标(MSE等),所以整个测试集的预测和评估还是要做,但绘图可以去掉。
所以,我们可以:
- 先对整个测试集进行预测和评估,打印指标(保留)。
- 然后,再按电池分开绘制(每个电池一个图)。
这样,我们既得到了整体性能,又得到了每个电池的详细预测情况。
因此,修改后的代码结构:
... [前面的代码不变]
% 对整个测试集进行预测和评估(保留)
y_pred = predict(svr_model, X_test_norm);
% 计算整体性能指标(保留,打印)
... [打印整体指标]
% 然后,按电池分组,分别预测(实际上已经预测了,我们可以直接使用整个测试集的预测结果y_pred,但是注意:归一化后的X_test_norm已经用于整个测试集的预测,所以y_pred就是整个测试集的预测值。我们只需要按电池分组提取实际值和预测值即可,不需要重新预测]
所以,我们可以避免重新预测,而是直接使用整个测试集的预测结果,然后按电池分组提取对应的实际值和预测值。
修改如下:
% 在得到整个测试集的预测值y_pred后,将其添加到test_data表中,这样我们就可以按电池分组提取
test_data.Predicted = y_pred;
unique_files = unique(test_data.FileName);
% 初始化一个结构体数组来存储每个电池的评估结果
battery_results = struct();
for i = 1:length(unique_files)
file_name = unique_files{i};
mask = strcmp(test_data.FileName, file_name);
current_data = test_data(mask, :);
y_current = current_data{:, target_name};
y_pred_current = current_data.Predicted;
% 计算该电池的评估指标
... [同上]
% 绘图(同上)
end
这样避免了重复预测,效率更高。
因此,最终修改:
1. 在归一化整个测试集并预测后,将预测值添加到test_data表中。
2. 按电池分组,循环绘制每个电池的对比图和残差图。
注意:整个测试集的评估指标(MSE等)还是基于所有样本的,我们保留。
同时,我们也可以打印每个电池的评估指标。
代码修改如下(在原有代码基础上):</think>针对您的要求,我将优化代码以实现不同测试电池的预测结果分开绘制。以下是完整的改进方案:
```matlab
% SVR预测电池健康状态(SOH) - 优化版(支持分电池绘制)
clc;
clear;
close all;
% 设置训练集和测试集路径
train_folder = 'D:\HuaweiMoveData\Users\26304\Desktop\XJTU\训练集';
test_folder = 'D:\HuaweiMoveData\Users\26304\Desktop\XJTU\测试集';
% 定义特征列和目标列
feature_names = {'A', 'V0', 'PeakValue', 'PeakArea'};
target_name = 'SOH';
% 读取文件夹中的所有CSV文件
train_data = readAllCSVFiles(train_folder);
test_data = readAllCSVFiles(test_folder);
% 提取特征和目标变量
X_train = train_data{:, feature_names};
y_train = train_data{:, target_name};
X_test = test_data{:, feature_names};
y_test = test_data{:, target_name};
% 0-1归一化特征 (Min-Max Scaling)
min_vals = min(X_train);
max_vals = max(X_train);
range_vals = max_vals - min_vals;
% 避免除零错误
range_vals(range_vals == 0) = 1;
% 应用归一化
X_train_norm = (X_train - min_vals) ./ range_vals;
X_test_norm = (X_test - min_vals) ./ range_vals;
% 训练SVR模型
svr_model = fitrsvm(X_train_norm, y_train, ...
'KernelFunction', 'gaussian', ...
'KernelScale', 'auto', ...
'BoxConstraint', 100, ...
'Epsilon', 0.1, ...
'Standardize', false); % 已手动标准化
% 预测和评估
y_pred = predict(svr_model, X_test_norm);
% 计算整体性能指标
mse = mean((y_test - y_pred).^2);
rmse = sqrt(mse);
mae = mean(abs(y_test - y_pred));
r2 = 1 - sum((y_test - y_pred).^2) / sum((y_test - mean(y_test)).^2);
% 显示整体结果
fprintf('整体模型性能评估:\n');
fprintf('均方误差(MSE): %.4f\n', mse);
fprintf('均方根误差(RMSE): %.4f\n', rmse);
fprintf('平均绝对误差(MAE): %.4f\n', mae);
fprintf('决定系数(R²): %.4f\n', r2);
% ================= 新增功能:按电池分开绘制结果 =================
% 获取测试集中所有唯一的电池文件名
unique_files = unique(test_data.FileName);
num_batteries = length(unique_files);
% 初始化电池性能存储
battery_metrics = struct();
% 创建主对比图
figure('Position', [100, 100, 1200, 800]);
tiledlayout('flow', 'TileSpacing', 'compact', 'Padding', 'compact');
sgtitle('各电池SOH预测结果对比', 'FontSize', 16);
% 创建主残差图
residual_fig = figure('Position', [100, 100, 1200, 800]);
tiledlayout('flow', 'TileSpacing', 'compact', 'Padding', 'compact');
sgtitle('各电池预测残差分布', 'FontSize', 16);
% 为每个电池创建单独的图表
for i = 1:num_batteries
current_file = unique_files{i};
% 提取当前电池数据
battery_mask = strcmp(test_data.FileName, current_file);
battery_data = test_data(battery_mask, :);
battery_y_test = test_data{battery_mask, target_name};
battery_y_pred = y_pred(battery_mask);
% 计算当前电池的循环次数(作为X轴)
if ismember('Week', battery_data.Properties.VariableNames)
cycles = battery_data.Week;
else
cycles = 1:height(battery_data);
end
% 计算当前电池性能指标
battery_mse = mean((battery_y_test - battery_y_pred).^2);
battery_rmse = sqrt(battery_mse);
battery_mae = mean(abs(battery_y_test - battery_y_pred));
battery_r2 = 1 - sum((battery_y_test - battery_y_pred).^2) / ...
sum((battery_y_test - mean(battery_y_test)).^2);
% 存储电池性能
battery_metrics(i).FileName = current_file;
battery_metrics(i).MSE = battery_mse;
battery_metrics(i).RMSE = battery_rmse;
battery_metrics(i).MAE = battery_mae;
battery_metrics(i).R2 = battery_r2;
% ===== 绘制对比图 =====
figure(1);
nexttile;
plot(cycles, battery_y_test, 'bo-', 'LineWidth', 1.5, 'MarkerSize', 6, 'MarkerFaceColor', 'b');
hold on;
plot(cycles, battery_y_pred, 'rs--', 'LineWidth', 1.5, 'MarkerSize', 6, 'MarkerFaceColor', 'r');
% 添加电池标识和性能指标
title_str = sprintf('电池: %s\nR²=%.3f, RMSE=%.3f', ...
extractBefore(current_file, '.'), battery_r2, battery_rmse);
title(title_str, 'FontSize', 10);
xlabel('循环次数');
ylabel('SOH');
grid on;
legend('实际值', '预测值', 'Location', 'best');
ylim([0.7, 1.05]); % 设置统一的Y轴范围
% ===== 绘制残差图 =====
figure(2);
nexttile;
residuals = battery_y_test - battery_y_pred;
scatter(battery_y_pred, residuals, 60, 'filled', ...
'MarkerFaceColor', [0.2, 0.6, 0.8], ...
'MarkerEdgeColor', 'k');
hold on;
plot([min(battery_y_pred), max(battery_y_pred)], [0, 0], ...
'k-', 'LineWidth', 1.5);
% 添加电池标识
title_str = sprintf('电池: %s', extractBefore(current_file, '.'));
title(title_str, 'FontSize', 10);
xlabel('预测SOH');
ylabel('残差');
grid on;
% 添加残差统计信息
avg_residual = mean(residuals);
std_residual = std(residuals);
text(0.05, 0.95, sprintf('均值: %.4f\n标准差: %.4f', avg_residual, std_residual), ...
'Units', 'normalized', 'FontSize', 8, ...
'VerticalAlignment', 'top', 'BackgroundColor', 'white');
end
% 添加整体图例
figure(1);
lgd = legend('实际值', '预测值', 'Orientation', 'horizontal');
lgd.Position = [0.4 0.01 0.2 0.05];
% 显示电池性能汇总
fprintf('\n各电池性能评估:\n');
fprintf('%-20s %-8s %-8s %-8s %-8s\n', '电池名称', 'MSE', 'RMSE', 'MAE', 'R²');
for i = 1:num_batteries
fprintf('%-20s %.4f %.4f %.4f %.4f\n', ...
battery_metrics(i).FileName, ...
battery_metrics(i).MSE, ...
battery_metrics(i).RMSE, ...
battery_metrics(i).MAE, ...
battery_metrics(i).R2);
end
% 辅助函数:读取文件夹中的所有CSV文件
function combinedData = readAllCSVFiles(folder)
files = dir(fullfile(folder, '*.csv'));
combinedData = [];
for i = 1:length(files)
file_path = fullfile(folder, files(i).name);
data = readtable(file_path);
% 添加文件名标识列
[~, filename, ~] = fileparts(files(i).name);
data.FileName = repmat({filename}, height(data), 1);
% 确保所有文件包含所需的列
required_columns = {'FileName', 'Week', 'DischargeCapacity', 'SOH', ...
'A', 'K', 'V0', 'RMSE', 'R2', 'PeakValue', ...
'PeakVoltage', 'PeakArea', 'RUL'};
if all(ismember(required_columns, data.Properties.VariableNames))
if isempty(combinedData)
combinedData = data;
else
combinedData = [combinedData; data];
end
else
fprintf('警告: 文件 %s 缺少所需列,已跳过\n', files(i).name);
disp('文件包含的列:');
disp(data.Properties.VariableNames);
end
end
end
```
### 代码优化说明:
1. **分电池处理逻辑**:
- 通过`unique(test_data.FileName)`获取所有电池的唯一标识
- 使用循环处理每个电池的数据,确保独立分析和可视化
2. **可视化增强**:
- 使用`tiledlayout`创建多子图布局,每个电池占用一个子图
- 对比图显示实际值(蓝色实线)和预测值(红色虚线)
- 残差图显示预测值与残差的分布关系
- 标题包含电池名称和关键指标(R²和RMSE)
3. **性能指标计算**:
- 为每个电池单独计算MSE、RMSE、MAE和R²
- 终端输出各电池的详细性能指标
- 残差图添加统计信息(均值和标准差)
4. **数据增强**:
- 在`readAllCSVFiles`函数中添加文件名标识列
- 使用`Week`列作为循环次数横坐标(如不存在则使用样本索引)
- 统一Y轴范围确保可视化一致性
5. **用户体验优化**:
- 设置统一图形尺寸和布局
- 添加整体图例和标题
- 电池名称显示时去除文件扩展名
- 残差图添加统计标注
### 输出说明:
1. **终端输出**:
- 整体模型性能指标
- 每个电池的详细性能指标表格
2. **图形输出**:
- 图1:各电池SOH预测结果对比(多子图布局)
- 图2:各电池预测残差分布(多子图布局)
### 使用建议:
1. 确保CSV文件包含`Week`列作为循环次数标识
2. 如果电池数据量较大,可调整`tiledlayout`的布局参数
3. 可通过修改`feature_names`调整使用的特征组合
4. 如需保存图表,可在每个`figure`后添加`saveas(gcf, 'filename.png')`
阅读全文
相关推荐

















