【回归预测-ELM预测】基于粒子群算法PSO优化极限学习机预测附matlab代码

 1 内容介绍

风电功率预测为电网规划提供重要的依据,研究风电功率预测方法对确保电网在安全稳定运行下接纳更多的风电具有重要的意义.针对极限学习机(ELM)回归模型预测结果受输入参数影响的问题,现将粒子群优化算法(PSO)应用于ELM中,提出了一种基于粒子群优化极限学习机的风功率预测方法.该方法首先将数值天气预报信息(NWP)数据进行数据预处理,并构建出训练样本集,随后建立ELM模型,利用粒子群算法优化ELM中的输入权值和阈值,从而建立起基于NWP和PSO-ELM风功率预测模型.对华东地区3个不同装机容量的风场NWP数据进行实验.结果表明:该方法的预测精度高且稳定性能好,能够为风电场功率预测以及风电并网安全可靠性提供科学有效的参考依据.

风的随机性和波动性导致了风电功率的不平 稳性,风电场的接入影响电网的稳定运行环境[1]。 对风电场的输出功率进行精准预测,有助于电网人 员根据风电场输出功率的变化调整发电计划,减少 电网的备用容量以节约能源的消耗。因此,有效的 风电功率预测方法可保障电网的稳定经济运行。 目前,国内外研究人员做了大量的工 作,研 究 方法主要包括物理方法、统计方法以及神经网络方 法[2]。其中物理方法要求风机相关物理信息,建模 复杂且 精 度 不 稳 定。统 计 方 法 模 型 简 单、数 据 单 一,但预测的精度受时间的限制,预测时间越长精 度越低,通常应用于超短期预测。神经网络方法泛 化能力强,能够处理回归问题,适用于风功率预测。 极 限 学 习 机 (Extreme Learning Machine, ELM)是一类基于前馈神经网络的算法[3]。ELM 因结构简单、学习效率高,被用以解决回归、聚类、 分类等问题。ELM 算法的优化问题被很多学者研 究,王浩等[4]利用遗传算法优化极限学习机,并将 风电系统参数模糊化,从而提高预测模型的精度。 龙浩[5]提出了加权极限学习机方法,旨在解决样本 数据不均衡的问题。王文锦等[6]利用蜂群算 法 优 化 ELM,旨在提高模型的稳定性 能。王 宏 刚 等[7] 将蜻蜓算法(DragonflyAlgorithm,DA)分布式应 用于 ELM,优化初始化输入权重和阈值的影响,有 效提高了电能质量扰动识别率。 风电功率的预测主要基于当日的数值天气预 报信 息 (Numerical WeatherPrediction,NWP)。 NWP包括风速、风向、温度、湿度以及气压[8]。实 测 NWP数据在同一风功率下存在奇异值以及波 动的问题。关于风功率 NWP数据的预处理问题, 符杨等[9]针对 NWP数据不准确、爬坡事件频发等 原因将 NWP 数据分类,并对功率波动进 行 预 测。 杨茂等[10]利用经验模态分解对风功率数据进行分 解去噪重构,一定程度上减少了噪点对预测结果的 影响。杨家然等[11]利用模糊聚类的方法将原始信 号进行分类,采用不同的模型组合预测。 本 文 将 粒 子 群 优 化 算 法 (Particle Swarm Optimization,PSO)和 ELM 结 合,欲 提 高 传 统 ELM 预测模型的精度和稳定性能,从而 为 风 电 功 率预测技术提供新的方法。

基本理论

1.1 ELM 算法

ELM 因学习效率高被普遍应用于风电功率预测、变压器故障诊断、风机故障诊断等方面。该 算法任意赋值输入层权重和阈值,训练过程中无需改变模型参数,仅设定隐层神经元个数,便可通过最小二乘法获得输出层权重。

1.2 PSO-ELM 预测模型 如前文所述,ELM 的 初 始 输 入 权 值 和 阈 值 是 随机确 定 的,其 训 练 的 效 果 会 受 初 始 值 影 响。因 此,采用 PSO 优化 ELM 的输入权重和阈值,可避 免盲目 性 训 练 ELM 模 型。PSO 算 法 优 化 ELM 的步骤中,先 初 始 化 PSO 参 数,包括粒子群的规 模、空间维度、惯性参数w、学习因子c1 和c2、迭代 次数和最 大 速 度vmax等。PSO-ELM 预 测 模 型 是 将每个粒子对应的输入权值和阈值代入 ELM 预 测模型中,将 ELM 学习样本输出与实际输出的均 方误差(MeanSquaredError,MSE)作为 PSO 的 适应度。将粒子的当前适应度与最优适应度做对 比,若比最优适应度小,说明当前输入权值和阈值 所建立的 ELM 模型进行预测产生的均方误差较 小,则将当前适应度更新为最优适应度,将当前位 置更新为Pb,否则保持最优适应度不变。同 理 比 较适应度和全局适应 度,更 新 Gb。当 迭 代 次 数 达 到最大值或适应度达到设定值时停止算法。PSO 优化得到的最优输入权值 W 和 阈 值b 后,代 入 ELM 模型中进行预测。具体步骤如图1所示。

2 仿真代码

%% 极限学习机在回归拟合问题中的应用研究
%% 导入数据
clear all;clc
load data
% 随机生成训练集、测试集
k = randperm(size(input,1));
% 训练集——1900个样本
P_train=input(k(1:1900),:)';
T_train=output(k(1:1900));
% 测试集——100个样本
P_test=input(k(1901:2000),:)';
T_test=output(k(1901:2000));
%% 归一化


% 训练集
[Pn_train,inputps] = mapminmax(P_train,-1,1);
Pn_test = mapminmax('apply',P_test,inputps);
% 测试集
[Tn_train,outputps] = mapminmax(T_train,-1,1);
Tn_test = mapminmax('apply',T_test,outputps);
tic
%% 节点个数
inputnum=2;  
hiddennum=20;         
outputnum=1;
%% 参数初始化
%粒子群算法中的两个参数
c1 = 1.49445;
c2 = 1.49445;
maxgen=100;       % 进化次数  
sizepop=30;       % 种群规模
Vmax=10^(2);      % 最大速度
Vmin=-Vmax;       % 最小速度
function Y = elmpredict(P,IW,B,LW)
Q = size(P,2);
BiasMatrix = repmat(B,1,Q);
tempH = IW * P + BiasMatrix;
H = 1 ./ (1 + exp(-tempH));
% 计算预测输出

% 随机生成训练集、测试集
k = randperm(size(input,1));
% 训练集——1900个样本
P_train=input(k(1:1900),:)';
T_train=output(k(1:1900));
% 测试集——100个样本
P_test=input(k(1901:2000),:)';
T_test=output(k(1901:2000));
%% 归一化
% 训练集
[Pn_train,inputps] = mapminmax(P_train,-1,1);
Pn_test = mapminmax('apply',P_test,inputps);
% 测试集
[Tn_train,outputps] = mapminmax(T_train,-1,1);
Tn_test = mapminmax('apply',T_test,outputps);
Y = (H' * LW)';
disp(zbest)
x=zbest;%% 优化结束后种群中的最好个体
IW=x(1:inputnum*hiddennum); 
B=x(inputnum*hiddennum+1:inputnum*hiddennum+hiddennum); 
%赋给网络权值和阈值
IW=reshape(IW,hiddennum,inputnum);
B=reshape(B,hiddennum,1);
%% ELM创建/训练
LW = elmtrain(IW,B,Pn_train,Tn_train,'sig');
% 随机生成训练集、测试集
k = randperm(size(input,1));
% 训练集——1900个样本
P_train=input(k(1:1900),:)';
T_train=output(k(1:1900));
% 测试集——100个样本
P_test=input(k(1901:2000),:)';
T_test=output(k(1901:2000));
%% 归一化
% 训练集

% 随机生成训练集、测试集
k = randperm(size(input,1));
% 训练集——1900个样本
P_train=input(k(1:1900),:)';
T_train=output(k(1:1900));
% 测试集——100个样本
P_test=input(k(1901:2000),:)';
T_test=output(k(1901:2000));
%% 归一化


% 训练集
[Pn_train,inputps] = mapminmax(P_train,-1,1);
Pn_test = mapminmax('apply',P_test,inputps);
% 测试集
[Tn_train,outputps] = mapminmax(T_train,-1,1);
Tn_test = mapminmax('apply',T_test,outputps);
tic
%% 节点个数
inputnum=2;  
hiddennum=20;         
outputnum=1;
%% 参数初始化
%粒子群算法中的两个参数
c1 = 1.49445;
c2 = 1.49445;
maxgen=100;       % 进化次数  
sizepop=30;       % 种群规模
Vmax=10^(2);      % 最大速度
Vmin=-Vmax;       % 最小速度
function Y = elmpredict(P,IW,B,LW)
Q = size(P,2);
BiasMatrix = repmat(B,1,Q);
tempH = IW * P + BiasMatrix;
H = 1 ./ (1 + exp(-tempH));
% 计算预测输出

% 随机生成训练集、测试集
k = randperm(size(input,1));
% 训练集——1900个样本
P_train=input(k(1:1900),:)';
T_train=output(k(1:1900));
% 测试集——100个样本
P_test=input(k(1901:2000),:)';
T_test=output(k(1901:2000));
%% 归一化
% 训练集


[Pn_train,inputps] = mapminmax(P_train,-1,1);
Pn_test = mapminmax('apply',P_test,inputps);
% 测试集
[Tn_train,outputps] = mapminmax(T_train,-1,1);
Tn_test = mapminmax('apply',T_test,outputps);
%% 结果对比
result = [T_test' T_sim'];
% 均方误差
E = mse(T_sim - T_test)
% 决定系数
N = length(T_test);
R2 = (N*sum(T_sim.*T_test)-sum(T_sim)*sum(T_test))^2/((N*sum((T_sim).^2)-(sum(T_sim))^2)*(N*sum((T_test).^2)-(sum(T_test))^2))
%% 绘图
figure
plot(1:length(T_test),T_test,'r*')
hold on
plot(1:length(T_sim),T_sim,'b:o')
xlabel('测试集样本编号')
ylabel('测试集输出')
title('ELM测试集输出')
legend('期望输出','预测输出')

figure
plot(1:length(T_test),T_test-T_sim,'r-*')
xlabel('测试集样本编号')
ylabel('绝对误差')
title('ELM测试集预测误差')

function Y = elmpredict(P,IW,B,LW)
Q = size(P,2);
BiasMatrix = repmat(B,1,Q);
tempH = IW * P + BiasMatrix;
H = 1 ./ (1 + exp(-tempH));
% 计算预测输出
Y = (H' * LW)';

% 随机生成训练集、测试集
k = randperm(size(input,1));
% 训练集——1900个样本
P_train=input(k(1:1900),:)';
T_train=output(k(1:1900));
% 测试集——100个样本
P_test=input(k(1901:2000),:)';
T_test=output(k(1901:2000));
%% 归一化


% 训练集
[Pn_train,inputps] = mapminmax(P_train,-1,1);
Pn_test = mapminmax('apply',P_test,inputps);
% 测试集
[Tn_train,outputps] = mapminmax(T_train,-1,1);
Tn_test = mapminmax('apply',T_test,outputps);
tic
%% 节点个数
inputnum=2;  
hiddennum=20;         
outputnum=1;
%% 参数初始化
%粒子群算法中的两个参数
c1 = 1.49445;
c2 = 1.49445;
maxgen=100;       % 进化次数  
sizepop=30;       % 种群规模
Vmax=10^(2);      % 最大速度
Vmin=-Vmax;       % 最小速度
function Y = elmpredict(P,IW,B,LW)
Q = size(P,2);
BiasMatrix = repmat(B,1,Q);
tempH = IW * P + BiasMatrix;
H = 1 ./ (1 + exp(-tempH));
% 计算预测输出

%% SOTracking - Single Object Tracking
% data format
% *data format:* 1-X, 2-Y, 3-Z, 4-RANGE, 5-AZIMUTH, 6-ELEVATION, 7-DOPPLER, 
% 8-POWER, 9-POWER_VALUE, 10-TIMESTAMP_MS

%% env init
clear, clc, close
addpath(genpath('./utils'));

%% param
% path and data
result_dir = './result/';
data_dir = './data/mmWave_radar_data/';
data_item = 'SOT/';


start_frame = 1;
end_frame = 10000;
traj_dim = 2; % 2d/3d trajectory 

% denoise
param_denoise.dpl_thr = 0.15;
param_denoise.loc_thr = [-40, 40, 0, 40, -5, 40];

% cluster
epsilon = 5;
MinPts = 20;
obj_count = 1;

% Kalman filter
motion_type = 'ConstantVelocity'; % 'ConstantVelocity' | 'ConstantAcceleration'
param_kf = getDefaultKFParameters(motion_type);
% param.initialEstimateError  = 1E5 * ones(1, 2);
% param.motionNoise           = [25, 10];
param.measurementNoise      = 100;    

% show
% axis_range = [-5, 5, 0, 20, -2, 5];
axis_range = [-10, 10, 0, 20, -1, 5];
show_delay = 0.0;

%% denoise, cluster, KF_tracking
% ---- file info ----
datas = dir([data_dir data_item '*.txt']);
data_names = {datas.name};
data_num = length(data_names);
end_frame = min(data_num, end_frame);
if start_frame>end_frame
    error("start frame over range")
end

% ---- init ----
KF = []; % KF handle
det_loc = []; % detected location
meas_traj = NaN(start_frame-1,traj_dim); % trajectory points
kf_traj = NaN(start_frame-1,traj_dim);   % KF corrected trajectory points
bounding_box = NaN(start_frame-1,traj_dim*2); % bounding box
isDetected = false; % detected flag

figure;

for k = start_frame:end_frame
    % ---- load data
    frame=importdata([data_dir data_item data_names{k}]);
    
    % ---- denoise ----
    frame_clean = point_cloud_denoise(frame, param_denoise);
    disp(['effective points num: ' num2str(size(frame_clean,1))])
    
    %  [ToDo] TBD
    if size(frame_clean, 1) < 4
        isDetected = false;
    end

    idx = DBSCAN(frame_clean(:,[1,2]),epsilon,MinPts); % DBSCAN Cluster
    
    % delete noise points cluster(idx==0)
    frame_clean(idx==0,:) = []; 
    idx(idx==0,:) = [];

%     [idx,C] = kmeans(frame_doppler(:,[1,2]),2); % K-Means Cluster

    [idx, Dg] = cluster_idx_arranege(frame_clean(:,[1,2]), idx);
    disp(['cluster count:' num2str(numel(unique(idx)))])

    if isempty(idx)
        isDetected = false;
    else
        isDetected = true;
    end
    
    if isDetected
        frame_obj = frame_clean(idx<=obj_count,:);

        subplot(121)
        gscatter3(frame_obj(:,1),frame_obj(:,2),frame_obj(:,3),idx,[],[],10,'on')
        axis(axis_range)
        
        % calc bounding box
        rect_min = min(frame_obj(:,1:3),[],1);
        rect_max = max(frame_obj(:,1:3),[],1);
        rect_size = rect_max - rect_min;
        rect_center = calcCentroid(frame_obj(:,1:3));
        det_loc = rect_center(1:traj_dim); 

        % show bounding box
        plotBoundingbox(rect_min, rect_size, [0 0 1], 'obj1', k, axis_range)
    
    else
        det_loc = NaN(1,traj_dim);
    end
    
    % Kalman Filter
    [kf_loc, KF, states] = KF_step(det_loc, KF, param_kf);
    if isempty(kf_loc)
        kf_loc = NaN(1,traj_dim);
    end
    
    meas_traj(k,:) = det_loc;
    kf_traj(k,:) = kf_loc;
    
    
    % show trajectory
    subplot(122)
%     cmpTraj(meas_traj, kf_traj, 'plot', 'xlim', axis_range(1:2), 'ylim', axis_range(3:4));
    plotTraj(kf_traj, k, axis_range)

    
    figtitle(data_item(1:end-1),'color','blue','linewidth',4,'fontsize',15);
    drawnow
    pause(show_delay)
    
end

%% save data
data_save_dir = [result_dir data_item 'ResData/'];
if ~exist(data_save_dir,'dir')
    mkdir(data_save_dir)
end
save([data_save_dir 'traj.mat'], 'meas_traj', 'kf_traj')
disp(['result data saved to: ' data_save_dir])


%% -------------------------------------------------------
%% sub functions
% get KF default parameters
function param = getDefaultKFParameters(motion_type)
    if nargin<1
        motion_type = 'ConstantVelocity';
    end
    
    param.motionModel = motion_type;
    param.initialLocation       = 'Same as first detection';
    
    if strcmp(motion_type, 'ConstantAcceleration')
      param.initialEstimateError  = 1E5 * ones(1, 3);
      param.motionNoise           = [25, 10, 1];
      param.measurementNoise      = 25;
    elseif strcmp(motion_type, 'ConstantVelocity')
      param.initialEstimateError  = 1E5 * ones(1, 2);
      param.motionNoise           = [25, 10];
      param.measurementNoise      = 25;        
    else
        error(['No assigned motion type - ' motion_type])
    end
end

function plotBoundingbox(rect_p, rect_size, clr, lgd, frame_idx, axis_range)
    plotcube(rect_size, rect_p, 0.1, clr,lgd)
    title(['Frame #' num2str(frame_idx) ' - 3D detection']);
    xlabel('X'), ylabel('Y'), zlabel('Z');
    axis(axis_range);
    view(3);
    grid on
end

function plotTraj(traj, frame_idx, axis_range)
    if size(traj, 2)==2
        plot(traj(:,1),traj(:,2),'r-o','MarkerSize',4,'LineWidth',1.5)
    elseif size(traj, 2)==3
        plot3(traj(:,1),traj(:,2),traj(:,3),'r-o','MarkerSize',4,'LineWidth',1.5)
    end
    
    title(['Frame #' num2str(frame_idx) ' - XY trajectory']);
    xlabel('X'), ylabel('Y'), zlabel('Z');
    legend('KF Traj.')
    axis(axis_range);  
    view(2);
    grid on
end

%% MOTracking - Multiple Object Tracking
% data format
% *data format:* 1-X, 2-Y, 3-Z, 4-RANGE, 5-AZIMUTH, 6-ELEVATION, 7-DOPPLER, 
% 8-POWER, 9-POWER_VALUE, 10-TIMESTAMP_MS

%% env init
clear, clc, close
addpath(genpath('./utils'));

%% param
% path and data
result_dir = './result/';
data_dir = './data/mmWave_radar_data/';
data_item = 'MOT/';

start_frame = 1;
end_frame = 10000;
traj_dim = 2; % 2d/3d trajectory 

% denoise
param_denoise.dpl_thr = 0.1;

% detection
param_det.minObjPoints = 30;
param_det.DBSCAN_epsilon = 0.3;
param_det.DBSCAN_MinPts = 30;
% param_det.max_obj_count = 2;

% Kalman filter
motion_type = 'ConstantVelocity'; % 'ConstantVelocity' | 'ConstantAcceleration'
param_kf = getDefaultKFParameters(motion_type);
param.initialEstimateError  = [200 50];
param.motionNoise           = [100 25];
param.measurementNoise      = 600;

% show
axis_range = [-5, 5, 0, 20, -2, 5];
% axis_range = [-20, 20, 0, 20, -10, 10];

%% denoise, cluster, KF_tracking
% ---- file info ----
datas = dir([data_dir data_item '*.txt']);
data_names = {datas.name};
data_num = length(data_names);
end_frame = min(data_num, end_frame);
if start_frame>end_frame
    error("start frame over range")
end

% ---- init ----
KF = []; % KF handle
tracks = initializeTracks(); % object tracks
nextId = 1; % next track id
meas_traj = NaN(start_frame-1,traj_dim); % trajectory points
% isDetected = false; % detected flag

figure;

for k = start_frame:end_frame
    % ---- load data
    frame = importdata([data_dir data_item data_names{k}]);
    
    % ---- denoise ----
    frame_clean = point_cloud_denoise(frame, param_denoise);
    disp(['clean points num: ' num2str(size(frame_clean,1))])
    
    % ---- detect ----
    [centroids, bboxes, obj_frame, obj_idx, obj_features] = getDetections(frame_clean,param_det);
    disp(['cluster count:' num2str(size(centroids,1))])
    
    % ---- track ---- 
    % predict new locations of last location (for cost calculation)
    tracks = predictNewLocationsOfTracks(tracks);

    % determine assignment of detection to tracks
    [assignments, unassignedTracks, unassignedDetections] = ...
    detectionToTrackAssignment(tracks, centroids, obj_frame, obj_idx, obj_features);

    % undate assigned tracks
    tracks = updateAssignedTracks(tracks, assignments, centroids, bboxes);
    
    % update unassigned tracks;
    tracks = updateUnassignedTracks(tracks, unassignedTracks);
    
    % update track states
    tracks = updateTrackStates(tracks);
    
    % create new tracks(tracks);
    [tracks,nextId] = createNewTracks(tracks, unassignedDetections, ...
        centroids, bboxes, obj_features, param_kf, nextId, k);

    % display track results
    showTrackingResults(obj_frame, obj_idx, tracks, k, axis_range, data_item(1:end-1))


    end


%% save data
data_save_dir = [result_dir data_item 'ResData/'];
if ~exist(data_save_dir,'dir')
    mkdir(data_save_dir)
end
save([data_save_dir 'track.mat'], 'meas_traj', 'tracks')
disp(['result data saved to: ' data_save_dir])


%% -------------------------------------------------------
%% sub functions
% get KF default parameters
function param = getDefaultKFParameters(motion_type)
    if nargin<1
        motion_type = 'ConstantVelocity';
    end
    
    param.motionModel = motion_type;
    param.initialLocation       = 'Same as first detection';
    
    if strcmp(motion_type, 'ConstantAcceleration')
      param.initialEstimateError  = 1E5 * ones(1, 3);
      param.motionNoise           = [25, 10, 1];
      param.measurementNoise      = 25;
    elseif strcmp(motion_type, 'ConstantVelocity')
      param.initialEstimateError  = 1E5 * ones(1, 2);
      param.motionNoise           = [25, 10];
      param.measurementNoise      = 25;        
    else
        error(['No assigned motion type - ' motion_type])
    end
end

% show tracking results
function showTrackingResults(obj_frame, obj_idx, tracks, frame_idx, axis_range, fig_name)
    % init
    % default bbox color
    clr = [1 0 0;
           0 1 0;
           0 0 1;
           0 1 1;
           1 0 1;
           1 1 0];
    show_delay = 0.0;
    % show 3d condition
    minVisibleCount = 5;   % minimal consecutive appearing frame count
    maxInvisibleCount = 5; % maximal consecutive disappearing frame count
    
    % get normal tracks & effect tracks
    normal_track_ind = ...
            [tracks(:).totalVisibleCount] > minVisibleCount &...
            [tracks(:).consecutiveInvisibleCount] < maxInvisibleCount &...
            strcmp([tracks(:).state],"normal"); 
        
    normalTracks = tracks(normal_track_ind);

    effect_track_ind = ...
            [tracks(:).totalVisibleCount] > minVisibleCount &...
            ~strcmp([tracks(:).state],"noise"); 
    effectTracks = tracks(effect_track_ind);
    
    clf(gcf) % clear figure before new display
    if ~ isempty(effectTracks) && ~isempty(obj_idx)
        % show 3d
        subplot(121)        
        % scatter3(obj_frame(:,1),obj_frame(:,2),obj_frame(:,3),10,'filled')
        gscatter3(obj_frame(:,1),obj_frame(:,2),obj_frame(:,3),obj_idx,0.3*ones(10,3),[],10,'off')

        for m = 1:length(normalTracks)
            plotBoundingbox(normalTracks(m).bbox(1:3), normalTracks(m).bbox(4:6), clr(normalTracks(m).id,:), ['obj' num2str(normalTracks(m).id)], axis_range)
        end
        legend
         % view(2) % 2D view for debug

        % show 2D
        subplot(122)
        hold on
        for m = 1:length(effectTracks)
            plotTraj(effectTracks(m).traj_rec(:,1:2), axis_range, ['obj' num2str(effectTracks(m).id)], clr(effectTracks(m).id,:))
        end
        hold off
        legend
        
    end
    
    % fig info
    figtitle([fig_name ' - Frame #' num2str(frame_idx)],'color','blue','linewidth',4,'fontsize',15);
    drawnow
    pause(show_delay)

end

3 运行结果

4 参考文献

[1]赵睿智, and 丁云飞. "基于粒子群优化极限学习机的风功率预测." 上海电机学院学报 22.4(2019):6.

[2]郭城, 刘新忠, and 苗宇. "基于粒子群优化极限学习机的轧制力预测." 冶金自动化 45.S01(2021):4.

博主简介:擅长智能优化算法、神经网络预测、信号处理、元胞自动机、图像处理、路径规划、无人机等多种领域的Matlab仿真,相关matlab代码问题可私信交流。

部分理论引用网络文献,若有侵权联系博主删除。

  • 2
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

matlab科研助手

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值