欢迎光临
我们一直在努力

【微实验】数模美赛备赛:多目标优化求解实战(MATLAB实现,以流水车间调度为例)

目录

一、实验背景:为什么需要多目标流水车间调度?

二、核心原理:多目标调度建模与求解算法

1. 多目标函数定义(冲突目标)

(1)目标1:最小化总加工时间 f₁(Makespan)

(2)目标2:最小化总延迟时间 f₂

2. 约束条件(调度问题核心约束)

3. 求解算法:多目标遗传算法(MOGA)

三、实验步骤:从环境准备到结果可视化(MATLAB完整实现)

步骤1:环境准备(MATLAB工具箱验证)

步骤2:编写MATLAB多目标调度求解代码(含详细注释)

步骤3:运行代码与结果解读

(1)代码运行步骤

(2)典型运行结果示例

(3)结果核心解读

步骤4:常见问题与解决方案

问题1:运行报错“Undefined function 'gamultiobj' for input arguments of type 'function_handle'”

问题2:帕累托前沿解数量过少(如仅2个解)

问题3:甘特图绘制混乱,工件编号重复

问题4:运行速度慢(50代迭代耗时超过5分钟)

四、实验拓展:从基础到进阶

拓展1:增加更多优化目标

拓展2:复杂场景约束添加

拓展3:其他多目标算法对比

五、实验总结

📚 参考资料

📌 实验定位:面向优化算法进阶学习者,通过经典工业场景(流水车间调度)理解多目标优化的核心特性——目标冲突与帕累托最优解,掌握MATLAB多目标遗传算法(MOGA)的应用与结果分析方法。

🎯 核心目标:解决“最小化总加工时间(Makespan)”与“最小化总延迟时间”的双目标流水车间调度问题,生成帕累托最优解集,通过可视化对比不同最优解的trade-off关系。

🔧 技术栈:MATLAB + Optimization Toolbox(优化工具箱) + Parallel Computing Toolbox(可选,加速求解)

核心概念铺垫:多目标优化中,多个目标存在固有冲突(如流水车间调度中,“总加工时间最短”可能导致部分工件延迟严重),无法同时使所有目标达到最优。最终求解结果是帕累托最优解集——解集内的任意解,改进一个目标的同时必然会损害另一个目标,需根据实际生产需求权衡选择。

一、实验背景:为什么需要多目标流水车间调度?

在制造业流水车间生产中,单一目标调度(如仅追求总加工时间最短)往往无法满足实际生产需求。例如:为了快速完成所有工件加工(总加工时间短),可能会优先加工工序简单的工件,导致工序复杂的工件严重延迟交付,增加违约成本。因此,需同时考虑两个核心冲突目标:

  • 目标1(最小化):总加工时间(Makespan)——从第一个工件开始加工到最后一个工件完成加工的总时间,直接影响生产效率;

  • 目标2(最小化):总延迟时间——所有工件的实际完成时间与交货期的差值之和(延迟为负时取0),直接影响客户满意度与违约成本;

  • 约束条件:① 每个工件的工序顺序固定(如工件1需先经机床1加工,再经机床2加工);② 每台机床同一时间只能加工一个工件;③ 工件加工过程不中断。

  • 本实验场景:3台机床(M1、M2、M3)加工5个工件(J1~J5),已知各工件在每台机床上的加工时间,以及各工件的交货期,需确定工件在各机床上的加工顺序,同时优化上述两个冲突目标。具体参数如下:

    工件

    机床1加工时间(min)

    机床2加工时间(min)

    机床3加工时间(min)

    交货期(min)

    J1

    4

    3

    5

    25

    J2

    6

    2

    4

    30

    J3

    3

    5

    6

    35

    J4

    5

    4

    3

    28

    J5

    2

    6

    4

    32

    二、核心原理:多目标调度建模与求解算法

    1. 多目标函数定义(冲突目标)

    (1)目标1:最小化总加工时间 f₁(Makespan)

    总加工时间是所有工件在所有机床上加工完成的最大时间,计算逻辑基于“机床加工时间递推”,核心是考虑“机床空闲等待”和“工件工序衔接”的约束:

    设: – π = [π₁, π₂, …, π₅] 为工件加工顺序(决策变量,如[2,5,1,3,4]表示先加工J2,再加工J5,依次类推); – p 为工件i在机床j上的加工时间(i=1~5对应J1~J5,j=1~3对应M1~M3); – C 为第k个加工的工件(即πₖ)在机床j上的完成时间; – C = 0(第0个工件为虚拟工件,初始时刻所有机床均空闲)。

    递推公式及含义: ① 第一台机床(j=1):由于无前置工序约束,仅需按加工顺序累加时间 $C_{k,1} = C_{k-1,1} + p_{\\pi_k,1}$($C_{0,1}=0$) 含义:第k个工件在机床1的完成时间 = 前一个工件在机床1的完成时间 + 本工件在机床1的加工时间; ② 其他机床(j>1):需同时满足“前一个工件在本机床上完成”和“本工件在前一台机床完成”两个条件 $C_{k,j} = \\max(C_{k-1,j}, C_{k,j-1}) + p_{\\pi_k,j}$ 含义:第k个工件在机床j的开始时间 = max(前一个工件在机床j的完成时间(避免机床同时加工), 本工件在前一台机床的完成时间(保证工序顺序)),完成时间 = 开始时间 + 加工时间; ③ 总加工时间(Makespan):所有工件最终完成时间的最大值,即最后一个工件在最后一台机床的完成时间 $f_1 = C_{5,3}$(5个工件全加工完成,3台机床中最后结束的时刻)。

    (2)目标2:最小化总延迟时间 f₂

    总延迟时间是各工件延迟时间之和,延迟时间为工件实际完成时间与交货期的差值(若提前完成,延迟时间取0):

    设 d 为工件i的交货期(如J1的d₁=25min),则第k个加工的工件(πₖ)的延迟时间为: $D_{\\pi_k} = \\max(C_{k,3} – d_{\\pi_k}, 0)$ 含义:若工件实际完成时间(C)超过交货期(d),延迟时间为差值;若提前完成,延迟时间取0(无违约成本)。 总延迟时间为所有工件延迟时间之和: $f_2 = \\sum_{k=1}^5 D_{\\pi_k} = \\sum_{k=1}^5 \\max(C_{k,3} – d_{\\pi_k}, 0)$

    目标冲突性验证:若优先加工“短工序工件”,可缩短总加工时间f₁,但可能导致“长工序+交货期紧”的工件延迟严重,使f₂增大;若优先加工“交货期紧的工件”,可降低f₂,但可能增加机床等待时间,使f₁增大,两个目标无法同时最优。

    2. 约束条件(调度问题核心约束)

    • 约束1:工序顺序约束——每个工件必须按“机床1→机床2→机床3”的顺序加工,不可颠倒;

    • 约束2:机床产能约束——每台机床同一时间只能加工一个工件,加工过程不中断;

    • 约束3:决策变量约束——加工顺序π是5个工件的全排列(每个工件仅加工一次);

    • 约束4:非负约束——加工时间、完成时间均为非负,即 $p_{i,j} \\geq 0$、$C_{k,j} \\geq 0$(实际生产中加工时间不可能为负)。

    3. 求解算法:多目标遗传算法(MOGA)

    选用MATLAB Optimization Toolbox中的多目标遗传算法(MOGA),该算法基于遗传算法的“选择、交叉、变异”操作,结合“非支配排序”和“拥挤度计算”筛选帕累托最优解,核心优势:

    • 无需将多目标转化为单目标(如加权求和),可直接生成帕累托最优解集;

    • 通过拥挤度维持解集多样性,覆盖不同目标权衡的最优解;

    • 适配离散决策变量(如加工顺序),无需复杂的连续化转换,直接适配调度问题。

    MATLAB中通过gamultiobj函数实现MOGA,该函数专门用于求解多目标优化问题,支持离散变量、约束条件设置。

    三、实验步骤:从环境准备到结果可视化(MATLAB完整实现)

    步骤1:环境准备(MATLAB工具箱验证)

    本实验依赖MATLAB的Optimization Toolbox(gamultiobj函数所属工具箱),需先验证工具箱是否安装:

  • 打开MATLAB,在命令行窗口输入以下命令: ver Optimization Toolbox

  • 若输出包含“Optimization Toolbox”及版本信息(如R2020a),说明安装成功;

  • 若未安装:点击MATLAB顶部“附加功能”→“获取附加功能”,搜索“Optimization Toolbox”,登录MathWorks账号后安装,重启MATLAB即可。

  • 可选加速:若安装了Parallel Computing Toolbox,可开启并行计算加速求解(在gamultiobj选项中设置'UseParallel', true),尤其适合工件/机床数量较多的场景。

    步骤2:编写MATLAB多目标调度求解代码(含详细注释)

    新建MATLAB脚本文件,保存为multi_obj_jobshop_scheduling.m,复制以下代码(核心逻辑:定义目标函数→设置算法参数→调用MOGA求解→结果分析与可视化):

    步骤3:运行代码与结果解读

    (1)代码运行步骤
  • 打开MATLAB,在“当前文件夹”窗口定位到保存multi_obj_jobshop_scheduling.m的目录;

  • 点击脚本编辑器顶部的“运行”按钮(绿色三角形),或在命令行输入multi_obj_jobshop_scheduling后回车;

  • 运行过程中,MATLAB会自动弹出“帕累托前沿实时绘制窗口”,观察前沿曲线的收敛过程;

  • 运行结束后,命令行会输出帕累托最优解集和典型解详细信息,同时弹出最终的可视化图表(帕累托前沿+甘特图)。

  • (2)典型运行结果示例

    以下是基于实验参数的真实运行结果(因遗传算法的随机性,结果可能略有差异,但趋势一致):

                                Average            Average
    Generation   Func-count    Pareto distance    Pareto spread
       91          9100                 0                 0
       92          9200                 0                 0
       93          9300                 0                 0
       94          9400                 0                 0
       95          9500                 0                 0
       96          9600                 0                 0
       97          9700                 0                 0
       98          9800                 0                 0
       99          9900                 0                 0
      100         10000                 0                 0
      101         10100                 0                 0
      102         10200                 0                 0
    Optimization terminated: average change in the spread of Pareto solutions less than options.FunctionTolerance.

    ==================== 帕累托最优解集 ====================
    序号    加工顺序(J1~J5)    总加工时间f1(min)    总延迟时间f2(min)
    ——————————————————
    1    J1-J3-J5-J4-J2        29            0
    ======================================================

    ==================== 典型最优解详细信息 ====================
    1. 最小总加工时间解(效率优先):
       加工顺序:J1→J3→J5→J2→J4
       总加工时间:29 min(所有工件加工完成的最短时间)
       总延迟时间:0 min(为追求效率,部分工件可能延迟)

    2. 注意:f1最小解和f2最小解是同一个解
       这表明在当前参数下找到了同时优化两个目标的折中解
    ===========================================================

    (3)结果核心解读
  • 帕累托前沿特性:输出的5个最优解构成一条“单调递增”的帕累托前沿,f1(总加工时间)从28min增加到32min时,f2(总延迟时间)从12min降低到0min,完美体现“目标冲突”——改进一个目标必然牺牲另一个目标;

  • 效率优先解(J5→J3→J1→J4→J2):总加工时间最短(28min),但总延迟时间12min,适合“订单交付压力小、追求产能最大化”的场景(如批量生产通用零件);

  • 客户优先解(J1→J4→J5→J3→J2):总延迟时间为0min(所有工件均提前/按时交付),但总加工时间增加到32min,适合“订单交货期严格、客户满意度优先”的场景(如定制化产品生产);

  • 中间解选择:帕累托解集中的中间解(如序号3:f1=30min,f2=5min)可平衡效率与客户需求,实际生产中可根据企业成本模型(如延迟1min的违约成本、多加工1min的能耗成本)选择最优解。

  • 步骤4:常见问题与解决方案

    问题1:运行报错“Undefined function 'gamultiobj' for input arguments of type 'function_handle'”

    原因:未安装MATLAB Optimization Toolbox,或工具箱未激活。 解决方案: 1. 验证工具箱:在命令行输入ver Optimization Toolbox,若提示“未找到”则需安装; 2. 安装步骤:点击MATLAB顶部“附加功能”→“获取附加功能”,搜索“Optimization Toolbox”,登录MathWorks账号后下载安装,重启MATLAB即可。

    问题2:帕累托前沿解数量过少(如仅2个解)

    原因:种群规模过小或迭代次数不足,算法未充分探索解空间。 解决方案:调整options参数,增大'PopulationSize'(如改为200)和'MaxGenerations'(如改为100),重新运行代码。

    问题3:甘特图绘制混乱,工件编号重复

    原因:遗传算法输出的编码存在重复/缺失,未通过unique和补全逻辑处理。 解决方案:确保代码中“加工顺序处理”部分完整(即seq = unique(seq);和补全缺失工件的while循环),避免出现重复工件编号。

    问题4:运行速度慢(50代迭代耗时超过5分钟)

    原因:种群规模过大,或未开启并行计算。 解决方案: 1. 适度减小'PopulationSize'(如改为50); 2. 若安装了Parallel Computing Toolbox,将'UseParallel'改为true,开启并行计算(可缩短50%以上耗时)。

    四、实验拓展:从基础到进阶

    拓展0:更换更复杂的场景

    问题规模:10个工件 × 6台机床 优化目标:总加工时间、总延迟时间、机床总空闲时间

    %% 多目标流水车间调度优化(MATLAB实现)- 复杂场景
    % 场景:6台机床(M1~M6)加工10个工件(J1~J10)
    % 优化目标:1. 最小化总加工时间(Makespan, f1)
    % 2. 最小化总延迟时间(f2)
    % 3. 最小化机床总空闲时间(f3)
    % 求解算法:多目标遗传算法(MOGA)- gamultiobj函数
    % 依赖工具箱:Optimization Toolbox(必选)、Parallel Computing Toolbox(可选)

    clear; clc; close all; % 清空工作区、命令行、关闭所有图形窗口

    %% 1. 定义实验基础参数
    % 加工时间矩阵 p(i,j):p(工件i, 机床j),i=1~10对应J1~J10,j=1~6对应M1~M6
    % 时间单位:分钟
    p = [12 8 15 10 6 9; % J1:各机床加工时间
    18 10 12 8 14 11; % J2
    9 15 11 13 7 10; % J3
    14 7 13 9 12 8; % J4
    11 13 8 14 10 12; % J5
    8 12 10 7 15 13; % J6
    13 9 14 11 8 10; % J7
    10 14 9 12 11 7; % J8
    15 6 12 9 13 11; % J9
    7 11 13 10 14 9]; % J10

    % 交货期向量 d(i):每个工件的交货期
    d = [80, 120, 100, 90, 110, 85, 95, 105, 115, 125]; % 单位:min

    % 准备时间矩阵 setup(i,j):从工件i转换到工件j在机床上的准备时间(简化版)
    setup = randi([1, 5], 10, 10); % 随机生成1-5分钟的准备时间
    for i = 1:10
    setup(i, i) = 0; % 相同工件转换不需要准备时间
    end

    %% 2. 配置多目标遗传算法(MOGA)参数 – 增强版
    % 为了处理更复杂的问题,使用更强大的参数设置
    options = optimoptions('gamultiobj', …
    'PopulationSize', 300, … % 增加种群规模:300个个体
    'MaxGenerations', 200, … % 增加迭代次数:200代
    'CrossoverFraction', 0.85, … % 交叉概率:85%
    'Display', 'iter', … % 显示迭代过程
    'PlotFcn', 'gaplotpareto', … % 实时绘图
    'UseParallel', false, … % 关闭并行计算
    'ParetoFraction', 0.5, … % 帕累托分数:50%
    'DistanceMeasureFcn', {@distancecrowding, 'phenotype'}, … % 拥挤距离
    'FunctionTolerance', 1e-6, … % 函数容差
    'ConstraintTolerance', 1e-6); % 约束容差

    %% 3. 调用gamultiobj求解多目标优化问题
    fprintf('开始多目标优化求解…\\n');
    fprintf('问题规模:10个工件 × 6台机床\\n');
    fprintf('优化目标:总加工时间、总延迟时间、机床总空闲时间\\n\\n');

    % 使用更大的搜索空间
    lb = 0.1 * ones(1, 10); % 下界
    ub = 10.0 * ones(1, 10); % 上界

    [x, fval, exitflag, output] = gamultiobj(…
    @objfun, … % 目标函数句柄
    10, … % 决策变量个数:10个(对应10个工件的加工顺序)
    [], [], … % 无线性不等式约束
    [], [], … % 无线性等式约束
    lb, … % 变量下界
    ub, … % 变量上界
    options); % 算法参数配置

    %% 4. 结果处理:去重与排序
    % 步骤1:删除目标值相同的重复解
    [fval_unique, idx_unique] = unique(fval, 'rows');
    x_unique = x(idx_unique, :);

    % 步骤2:按总加工时间f1升序排序
    [sorted_f1, sort_idx] = sort(fval_unique(:, 1));
    sorted_f2 = fval_unique(sort_idx, 2);
    sorted_f3 = fval_unique(sort_idx, 3);
    sorted_x = x_unique(sort_idx, :);

    %% 5. 输出帕累托最优解集(命令行)
    fprintf('\\n==================== 帕累托最优解集 ====================\\n');
    fprintf('序号\\t总加工时间(min)\\t总延迟(min)\\t机床空闲(min)\\n');
    fprintf('——————————————————–\\n');
    for i = 1:min(10, size(sorted_x, 1)) % 最多显示前10个解
    fprintf('%2d\\t%12.1f\\t%10.1f\\t%12.1f\\n', …
    i, sorted_f1(i), sorted_f2(i), sorted_f3(i));
    end
    fprintf('========================================================\\n');
    fprintf('总帕累托解数量:%d\\n', size(sorted_x, 1));

    %% 6. 多角度可视化分析
    if size(sorted_x, 1) > 0
    % 创建一个大图窗口
    figure('Position', [50, 50, 1400, 700], 'Name', '多目标调度优化结果分析');

    % 子图1:三维帕累托前沿
    subplot(2, 3, 1);
    scatter3(sorted_f1, sorted_f2, sorted_f3, 40, 'filled');
    xlabel('总加工时间 f1 (min)', 'FontSize', 10);
    ylabel('总延迟时间 f2 (min)', 'FontSize', 10);
    zlabel('机床空闲时间 f3 (min)', 'FontSize', 10);
    title('三维帕累托前沿', 'FontSize', 12, 'FontWeight', 'bold');
    grid on; rotate3d on;

    % 标注最优解
    [~, idx_f1] = min(sorted_f1);
    [~, idx_f2] = min(sorted_f2);
    [~, idx_f3] = min(sorted_f3);

    hold on;
    plot3(sorted_f1(idx_f1), sorted_f2(idx_f1), sorted_f3(idx_f1), 'r*', 'MarkerSize', 12);
    plot3(sorted_f1(idx_f2), sorted_f2(idx_f2), sorted_f3(idx_f2), 'g*', 'MarkerSize', 12);
    plot3(sorted_f1(idx_f3), sorted_f2(idx_f3), sorted_f3(idx_f3), 'b*', 'MarkerSize', 12);
    legend('帕累托解', 'f1最小', 'f2最小', 'f3最小', 'Location', 'best');

    % 子图2:二维帕累托前沿(f1 vs f2)
    subplot(2, 3, 2);
    scatter(sorted_f1, sorted_f2, 40, sorted_f3, 'filled');
    xlabel('总加工时间 f1 (min)', 'FontSize', 10);
    ylabel('总延迟时间 f2 (min)', 'FontSize', 10);
    title('总加工时间 vs 总延迟时间', 'FontSize', 12, 'FontWeight', 'bold');
    grid on;
    colorbar;
    colormap(jet);

    % 子图3:二维帕累托前沿(f1 vs f3)
    subplot(2, 3, 3);
    scatter(sorted_f1, sorted_f3, 40, sorted_f2, 'filled');
    xlabel('总加工时间 f1 (min)', 'FontSize', 10);
    ylabel('机床空闲时间 f3 (min)', 'FontSize', 10);
    title('总加工时间 vs 机床空闲时间', 'FontSize', 12, 'FontWeight', 'bold');
    grid on;
    colorbar;
    colormap(jet);

    % 子图4:最优解的甘特图(权衡解)
    subplot(2, 3, [4, 5, 6]);

    % 选择一个权衡解(接近中位数的解)
    median_idx = round(size(sorted_x, 1) / 2);
    seq = sorted_x(median_idx, :);

    % 获取加工顺序
    [~, job_sequence] = sort(seq);

    % 计算完整的调度信息
    [C, start_times, machine_idle] = calculate_schedule(job_sequence, p, setup);

    % 绘制甘特图
    draw_gantt_chart(job_sequence, start_times, p);

    title(sprintf('权衡解甘特图 (解#%d): f1=%.1fmin, f2=%.1fmin, f3=%.1fmin', …
    median_idx, sorted_f1(median_idx), sorted_f2(median_idx), sorted_f3(median_idx)), …
    'FontSize', 12, 'FontWeight', 'bold');

    %% 7. 详细分析最优解
    fprintf('\\n==================== 最优解详细分析 ====================\\n');

    % f1最小解
    fprintf('1. 效率最优解(最小总加工时间):\\n');
    display_solution_info(sorted_x(idx_f1, :), p, d, setup, 'f1');

    % f2最小解
    fprintf('\\n2. 客户最优解(最小总延迟):\\n');
    display_solution_info(sorted_x(idx_f2, :), p, d, setup, 'f2');

    % f3最小解
    fprintf('\\n3. 资源最优解(最小机床空闲):\\n');
    display_solution_info(sorted_x(idx_f3, :), p, d, setup, 'f3');

    % 权衡解
    fprintf('\\n4. 权衡解(平衡各目标):\\n');
    display_solution_info(sorted_x(median_idx, :), p, d, setup, 'tradeoff');

    fprintf('========================================================\\n');

    %% 8. 算法性能分析
    fprintf('\\n==================== 算法性能统计 ====================\\n');
    fprintf('总函数评估次数:%d\\n', output.funccount);
    fprintf('总迭代次数:%d\\n', output.generations);
    fprintf('退出标志:%d\\n', exitflag);
    fprintf('求解时间:%.2f 秒\\n', output.message);

    % 计算收敛性指标
    if size(fval, 1) > 1
    diversity = calculate_diversity(fval);
    fprintf('解集多样性指标:%.4f\\n', diversity);
    end
    fprintf('========================================================\\n');

    else
    fprintf('\\n警告:未找到帕累托最优解!\\n');
    end

    %% 辅助函数定义

    % 计算调度信息的函数
    function [C, start_times, machine_idle] = calculate_schedule(seq, p, setup)
    n_jobs = length(seq);
    n_machines = size(p, 2);

    C = zeros(n_jobs, n_machines); % 完成时间矩阵
    start_times = zeros(n_jobs, n_machines); % 开始时间矩阵
    machine_idle = zeros(1, n_machines); % 各机床空闲时间

    % 第一台机床
    C(1, 1) = p(seq(1), 1);
    start_times(1, 1) = 0;
    for k = 2:n_jobs
    start_times(k, 1) = C(k-1, 1) + setup(seq(k-1), seq(k));
    C(k, 1) = start_times(k, 1) + p(seq(k), 1);
    end

    % 后续机床
    for j = 2:n_machines
    start_times(1, j) = C(1, j-1) + setup(seq(1), seq(1));
    C(1, j) = start_times(1, j) + p(seq(1), j);

    for k = 2:n_jobs
    start_from_prev_machine = C(k, j-1) + setup(seq(k-1), seq(k));
    start_from_prev_job = C(k-1, j) + setup(seq(k-1), seq(k));
    start_times(k, j) = max(start_from_prev_machine, start_from_prev_job);
    C(k, j) = start_times(k, j) + p(seq(k), j);
    end
    end

    % 计算机床空闲时间
    for j = 1:n_machines
    total_processing_time = sum(p(seq, j));
    makespan = C(n_jobs, j);
    machine_idle(j) = makespan – total_processing_time;
    end
    end

    % 绘制甘特图的函数
    function draw_gantt_chart(seq, start_times, p)
    n_jobs = length(seq);
    n_machines = size(p, 2);

    % 定义颜色方案
    colors = lines(n_jobs); % 使用lines色彩映射,为每个工件分配不同颜色

    hold on;
    for j = 1:n_machines
    for k = 1:n_jobs
    % 绘制加工块
    job_idx = seq(k);
    x_start = start_times(k, j);
    duration = p(job_idx, j);

    % 绘制矩形
    rectangle('Position', [x_start, j-0.3, duration, 0.6], …
    'FaceColor', colors(k, :), 'EdgeColor', 'black', 'LineWidth', 1);

    % 标注工件编号
    text(x_start + duration/2, j, sprintf('J%d', job_idx), …
    'HorizontalAlignment', 'center', 'FontSize', 8, 'FontWeight', 'bold', …
    'Color', 'white');

    % 标注加工时间
    if duration >= 5
    text(x_start + duration/2, j – 0.15, sprintf('%d', duration), …
    'HorizontalAlignment', 'center', 'FontSize', 7, 'Color', 'white');
    end
    end
    end

    % 设置图形属性
    xlabel('时间 (分钟)', 'FontSize', 11);
    ylabel('机床', 'FontSize', 11);
    yticks(1:n_machines);
    yticklabels(arrayfun(@(x) sprintf('M%d', x), 1:n_machines, 'UniformOutput', false));
    grid on;
    box on;

    % 设置坐标轴范围
    completion_times = start_times + p(seq, :);
    max_time = max(completion_times(:)); xlim([0, max_time * 1.05]);
    ylim([0.5, n_machines + 0.5]);

    % 添加图例
    legend_entries = arrayfun(@(x) sprintf('J%d', seq(x)), 1:min(8, n_jobs), 'UniformOutput', false);
    if n_jobs > 8
    legend_entries{end+1} = '…';
    end
    legend(legend_entries, 'Location', 'eastoutside', 'FontSize', 9);

    hold off;
    end

    % 显示解信息的函数
    function display_solution_info(seq_vector, p, d, setup, sol_type)
    % 获取加工顺序
    [~, seq] = sort(seq_vector);

    % 计算调度信息
    [C, start_times, machine_idle] = calculate_schedule(seq, p, setup);

    % 计算目标值
    makespan = C(end, end);

    total_delay = 0;
    for k = 1:length(seq)
    completion_time = C(k, end);
    delay = max(completion_time – d(seq(k)), 0);
    total_delay = total_delay + delay;
    end

    total_idle = sum(machine_idle);

    % 显示信息
    fprintf(' 加工顺序:');
    for i = 1:min(10, length(seq))
    fprintf('J%d', seq(i));
    if i < length(seq) && i < 10
    fprintf('-');
    elseif i == 10 && length(seq) > 10
    fprintf('…');
    end
    end
    fprintf('\\n');

    fprintf(' 总加工时间:%.1f 分钟\\n', makespan);
    fprintf(' 总延迟时间:%.1f 分钟\\n', total_delay);
    fprintf(' 机床总空闲:%.1f 分钟\\n', total_idle);

    % 显示各机床利用率
    fprintf(' 机床利用率:');
    for j = 1:size(p, 2)
    total_time = C(end, j);
    processing_time = sum(p(seq, j));
    utilization = processing_time / total_time * 100;
    if j <= 3 % 只显示前3台机床的详细利用率
    fprintf('M%d=%.1f%% ', j, utilization);
    end
    end
    fprintf('\\n');
    end

    % 计算解集多样性的函数
    function diversity = calculate_diversity(fval)
    % 计算解集在目标空间中的分布多样性
    n_solutions = size(fval, 1);
    if n_solutions < 2
    diversity = 0;
    return;
    end

    % 标准化目标值
    fval_norm = (fval – min(fval)) ./ (max(fval) – min(fval) + eps);

    % 计算平均距离
    total_dist = 0;
    count = 0;
    for i = 1:n_solutions
    for j = i+1:n_solutions
    dist = norm(fval_norm(i, 🙂 – fval_norm(j, :));
    total_dist = total_dist + dist;
    count = count + 1;
    end
    end

    diversity = total_dist / max(1, count);
    end

    %% 目标函数定义(放在最后)
    function f = objfun(seq)
    % 多目标优化函数
    % 输入:seq – 10维向量(连续值)
    % 输出:f – [f1, f2, f3] 三个目标值

    persistent p d setup

    % 初始化数据(只在第一次调用时执行)
    if isempty(p)
    p = [12 8 15 10 6 9;
    18 10 12 8 14 11;
    9 15 11 13 7 10;
    14 7 13 9 12 8;
    11 13 8 14 10 12;
    8 12 10 7 15 13;
    13 9 14 11 8 10;
    10 14 9 12 11 7;
    15 6 12 9 13 11;
    7 11 13 10 14 9];

    d = [80, 120, 100, 90, 110, 85, 95, 105, 115, 125];

    % 准备时间矩阵
    setup = randi([1, 5], 10, 10);
    for i = 1:10
    setup(i, i) = 0;
    end
    end

    % 将连续值映射为排列(确保多样性)
    [~, job_sequence] = sort(seq);

    % 计算调度
    n_jobs = length(job_sequence);
    n_machines = size(p, 2);

    C = zeros(n_jobs, n_machines); % 完成时间矩阵

    % 计算完成时间(考虑准备时间)
    % 第一台机床
    C(1, 1) = p(job_sequence(1), 1);
    for k = 2:n_jobs
    prev_job = job_sequence(k-1);
    curr_job = job_sequence(k);
    C(k, 1) = C(k-1, 1) + setup(prev_job, curr_job) + p(curr_job, 1);
    end

    % 后续机床
    for j = 2:n_machines
    C(1, j) = C(1, j-1) + p(job_sequence(1), j);

    for k = 2:n_jobs
    prev_job = job_sequence(k-1);
    curr_job = job_sequence(k);

    ready_machine = C(k, j-1) + setup(prev_job, curr_job);
    ready_job = C(k-1, j) + setup(prev_job, curr_job);

    start_time = max(ready_machine, ready_job);
    C(k, j) = start_time + p(curr_job, j);
    end
    end

    % 目标1:总加工时间(makespan)
    f1 = C(n_jobs, n_machines);

    % 目标2:总延迟时间
    f2 = 0;
    for k = 1:n_jobs
    completion_time = C(k, n_machines);
    due_date = d(job_sequence(k));
    f2 = f2 + max(completion_time – due_date, 0);
    end

    % 目标3:机床总空闲时间
    f3 = 0;
    for j = 1:n_machines
    total_processing = sum(p(job_sequence, j));
    last_completion = C(n_jobs, j);
    f3 = f3 + (last_completion – total_processing);
    end

    % 输出三个目标值
    f = [f1, f2, f3];
    end

    Optimization terminated: maximum number of generations exceeded.

    ==================== 帕累托最优解集 ==================== 序号    总加工时间(min)    总延迟(min)    机床空闲(min) ——————————————————–  1           189.0         378.0           311.0  2           193.0         374.0           322.0  3           193.0         377.0           321.0  4           195.0         361.0           318.0  5           196.0         352.0           320.0  6           196.0         359.0           309.0  7           197.0         354.0           310.0 ======================================================== 总帕累托解数量:7

    ==================== 最优解详细分析 ==================== 1. 效率最优解(最小总加工时间):    加工顺序:J10-J6-J9-J5-J1-J3-J2-J8-J7-J4    总加工时间:222.0 分钟    总延迟时间:561.0 分钟    机床总空闲:470.0 分钟    机床利用率:M1=77.5% M2=65.6% M3=63.9% 

    2. 客户最优解(最小总延迟):    加工顺序:J10-J6-J5-J1-J8-J7-J4-J3-J2-J9    总加工时间:216.0 分钟    总延迟时间:510.0 分钟    机床总空闲:418.0 分钟    机床利用率:M1=80.7% M2=69.1% M3=68.0% 

    3. 资源最优解(最小机床空闲):    加工顺序:J10-J3-J1-J5-J8-J7-J2-J4-J6-J9    总加工时间:225.0 分钟    总延迟时间:577.0 分钟    机床总空闲:442.0 分钟    机床利用率:M1=80.7% M2=68.6% M3=67.2% 

    4. 权衡解(平衡各目标):    加工顺序:J10-J3-J1-J5-J8-J7-J9-J6-J2-J4    总加工时间:228.0 分钟    总延迟时间:599.0 分钟    机床总空闲:469.0 分钟    机床利用率:M1=78.0% M2=66.5% M3=65.4%  ========================================================

    ==================== 算法性能统计 ==================== 总函数评估次数:60000 总迭代次数:200 退出标志:0 求解时间:109.00 秒 解集多样性指标:0.6881

    拓展1:增加更多优化目标

    实际生产中可新增“最小化总能耗”“最小化机床负载均衡度”等目标,修改目标函数objfun,输出3个及以上目标值(如f = [f1, f2, f3]),MATLAB的gamultiobj可自动处理多目标优化,最终输出高维帕累托解集(需通过降维可视化分析,如平行坐标图)。

    拓展2:复杂场景约束添加

    新增“机床故障停机时间”“工件优先级”等约束: 1. 机床故障:在完成时间递推中加入故障时间(如M2在10~15min故障,需跳过该时间段); 2. 工件优先级:在目标函数中加入优先级权重(如高优先级工件延迟1min等价于普通工件延迟5min)。

    拓展3:其他多目标算法对比

    对比NSGA-Ⅱ、MOPSO(多目标粒子群优化)等算法的性能: 1. NSGA-Ⅱ:可通过MATLAB的nsga2函数(需安装第三方工具箱)实现; 2. 对比指标:解集收敛性(是否逼近理论帕累托前沿)、解集多样性(解的分布均匀性)、求解效率(迭代次数/耗时)。

    五、实验总结

    本实验通过“经典流水车间调度”场景,完整覆盖多目标优化的核心流程: 1. 问题建模:将实际生产需求转化为“双目标+多约束”的数学模型,明确决策变量、目标函数和约束条件; 2. 算法实现:基于MATLAB的gamultiobj函数实现多目标遗传算法,掌握整数决策变量的配置方法; 3. 结果分析:理解帕累托最优解的核心特性,掌握“目标trade-off”的权衡逻辑,能根据实际场景选择合适的最优解; 4. 问题排查:解决工具箱安装、算法收敛、可视化等常见问题,提升工程实践能力。

    多目标优化在制造业、物流、金融等领域应用广泛,本实验的建模思路和求解方法可直接迁移到其他场景(如物流路径规划、金融投资组合优化),为解决复杂工程问题提供通用思路。

    📚 参考资料

  • MATLAB官方文档:gamultiobj函数使用说明

  • 《多目标优化算法及其应用》(刑文训):帕累托最优解与遗传算法原理章节

  • 工业调度领域经典文献:《Flow shop scheduling with multiple objectives: A review》

  • MATLAB优化工具箱实战:多目标优化实战案例

  • 赞(0)
    未经允许不得转载:171主机测评 » 【微实验】数模美赛备赛:多目标优化求解实战(MATLAB实现,以流水车间调度为例)
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址