基于多层编码遗传算法的车间调度系统

问题描述与算法设计

车间调度问题(Job Shop Scheduling Problem, JSSP)是制造业的核心优化问题,目标是在满足工艺约束条件下最小化最大完工时间(makespan)。多层编码遗传算法通过分层表示调度解的多个维度,有效解决了传统编码方法难以同时处理工序顺序、机器分配和时间安排的难题。

编码结构设计

三层编码方案:

  1. 工序层:表示所有工序的执行顺序
  2. 机器层:表示每道工序分配的机器
  3. 时间层:表示工序的开始时间
染色体结构示例:
工序层: [J1_op1, J2_op1, J1_op2, J3_op1, J2_op2, ...]
机器层: [M3, M1, M2, M3, M2, ...]
时间层: [0, 12, 5, 18, 25, ...] (相对时间)

算法流程

MATLAB实现代码

classdef MultiLayerGA < handle
    properties
        pop_size = 50;       % 种群大小
        max_gen = 200;       % 最大迭代次数
        pc = 0.85;           % 交叉概率
        pm = 0.15;           % 变异概率
        elite_rate = 0.1;    % 精英保留比例
        jobs;                % 工件信息 {工件ID: {工序: [机器, 时间]}}
        num_jobs;            % 工件数量
        num_machines;        % 机器数量
        ops_per_job;         % 每个工件的工序数
        total_ops;           % 总工序数
        population;          % 种群
        best_fitness_history % 适应度历史
    end
    
    methods
        function obj = MultiLayerGA(jobs_data)
            % 初始化问题数据
            obj.jobs = jobs_data;
            job_ids = fieldnames(obj.jobs);
            obj.num_jobs = length(job_ids);
            obj.ops_per_job = zeros(1, obj.num_jobs);
            
            % 提取机器数量和工序信息
            all_machines = [];
            for i = 1:obj.num_jobs
                job = job_ids{i};
                ops = fieldnames(obj.jobs.(job));
                obj.ops_per_job(i) = length(ops);
                for j = 1:length(ops)
                    machine = obj.jobs.(job).(ops{j})(1);
                    all_machines = [all_machines, machine];
                end
            end
            obj.num_machines = max(all_machines);
            obj.total_ops = sum(obj.ops_per_job);
            
            % 初始化种群
            obj.initializePopulation();
            obj.best_fitness_history = zeros(1, obj.max_gen);
        end
        
        function initializePopulation(obj)
            % 创建初始种群
            obj.population = struct('operation_seq', {}, 'machine_assign', {}, 'start_times', {});
            
            for p = 1:obj.pop_size
                % 工序层:随机排列所有工序
                op_seq = [];
                for job_id = 1:obj.num_jobs
                    op_seq = [op_seq, repmat(job_id, 1, obj.ops_per_job(job_id))];
                end
                op_seq = op_seq(randperm(obj.total_ops));
                
                % 机器层:为每道工序随机选择可用机器
                machine_assign = zeros(1, obj.total_ops);
                op_idx = 1;
                for job_id = 1:obj.num_jobs
                    job_name = ['J' num2str(job_id)];
                    ops = fieldnames(obj.jobs.(job_name));
                    for op_id = 1:length(ops)
                        available_machines = obj.jobs.(job_name).(ops{op_id})(1); % 假设只有一个可选机器
                        machine_assign(op_idx) = available_machines;
                        op_idx = op_idx + 1;
                    end
                end
                
                % 时间层:初始化为零
                start_times = zeros(1, obj.total_ops);
                
                obj.population(p).operation_seq = op_seq;
                obj.population(p).machine_assign = machine_assign;
                obj.population(p).start_times = start_times;
            end
        end
        
        function [makespan, schedule] = decodeChromosome(obj, chrom)
            % 解码染色体为可行调度
            num_ops = obj.total_ops;
            job_progress = zeros(1, obj.num_jobs); % 每个工件的当前工序
            machine_free_time = zeros(1, obj.num_machines); % 每台机器的空闲时间
            job_free_time = zeros(1, obj.num_jobs); % 每个工件的空闲时间
            op_start = zeros(1, num_ops); % 每道工序的开始时间
            op_end = zeros(1, num_ops);   % 每道工序的结束时间
            
            % 创建工序映射表
            op_counter = 1;
            job_op_map = cell(1, obj.num_jobs);
            for job_id = 1:obj.num_jobs
                job_op_map{job_id} = op_counter:(op_counter+obj.ops_per_job(job_id)-1);
                op_counter = op_counter + obj.ops_per_job(job_id);
            end
            
            % 按工序序列顺序处理
            for seq_idx = 1:length(chrom.operation_seq)
                job_id = chrom.operation_seq(seq_idx);
                op_index = job_progress(job_id) + 1; % 当前工序索引
                job_progress(job_id) = op_index;
                
                % 获取工序信息
                job_name = ['J' num2str(job_id)];
                op_name = ['op' num2str(op_index)];
                machine_id = chrom.machine_assign(job_op_map{job_id}(op_index));
                proc_time = obj.jobs.(job_name).(op_name)(2);
                
                % 计算开始时间
                earliest_start = max(job_free_time(job_id), machine_free_time(machine_id));
                op_start(job_op_map{job_id}(op_index)) = earliest_start;
                op_end(job_op_map{job_id}(op_index)) = earliest_start + proc_time;
                
                % 更新机器和工件的空闲时间
                machine_free_time(machine_id) = earliest_start + proc_time;
                job_free_time(job_id) = earliest_start + proc_time;
            end
            
            % 计算最大完工时间
            makespan = max(op_end);
            
            % 构建调度表
            schedule = struct();
            for job_id = 1:obj.num_jobs
                job_name = ['J' num2str(job_id)];
                ops = fieldnames(obj.jobs.(job_name));
                for op_id = 1:length(ops)
                    op_idx = job_op_map{job_id}(op_id);
                    schedule.(job_name).(ops{op_id}) = [
                        op_start(op_idx), op_end(op_idx), chrom.machine_assign(op_idx)
                    ];
                end
            end
        end
        
        function fitness = evaluateFitness(obj, chrom)
            % 评估适应度 (最小化makespan)
            [makespan, ~] = obj.decodeChromosome(chrom);
            fitness = 1 / makespan; % 最大化适应度
        end
        
        function runOptimization(obj)
            % 主优化循环
            for gen = 1:obj.max_gen
                % 评估当前种群
                fitness = zeros(1, obj.pop_size);
                for p = 1:obj.pop_size
                    fitness(p) = obj.evaluateFitness(obj.population(p));
                end
                
                % 记录最佳适应度
                [best_fit, best_idx] = max(fitness);
                obj.best_fitness_history(gen) = best_fit;
                best_chrom = obj.population(best_idx);
                
                % 精英保留
                elite_size = round(obj.elite_rate * obj.pop_size);
                [~, elite_idx] = sort(fitness, 'descend');
                new_population = obj.population(elite_idx(1:elite_size));
                
                % 选择操作 (锦标赛选择)
                while length(new_population) < obj.pop_size
                    candidates = randperm(obj.pop_size, 2);
                    [~, winner] = max(fitness(candidates));
                    selected = obj.population(candidates(winner));
                    
                    % 交叉操作
                    if rand < obj.pc
                        % 选择另一个父代
                        candidates2 = randperm(obj.pop_size, 2);
                        [~, winner2] = max(fitness(candidates2));
                        selected2 = obj.population(candidates2(winner2));
                        
                        offspring = obj.crossover(selected, selected2);
                    else
                        offspring = selected;
                    end
                    
                    % 变异操作
                    if rand < obj.pm
                        offspring = obj.mutate(offspring);
                    end
                    
                    % 修复非法解
                    offspring = obj.repairSolution(offspring);
                    
                    new_population(end+1) = offspring;
                end
                
                obj.population = new_population;
                
                % 显示进度
                if mod(gen, 10) == 0
                    fprintf('Generation %d: Best Makespan = %.2f\n', gen, 1/best_fit);
                end
            end
            
            % 返回最佳解
            [best_fit, best_idx] = max(fitness);
            best_solution = obj.population(best_idx);
            [best_makespan, best_schedule] = obj.decodeChromosome(best_solution);
            fprintf('\nOptimization Complete!\nBest Makespan: %.2f\n', best_makespan);
            
            % 可视化结果
            obj.visualizeResults(best_schedule, best_makespan);
        end
        
        function offspring = crossover(obj, parent1, parent2)
            % 三层交叉操作
            offspring = struct('operation_seq', [], 'machine_assign', [], 'start_times', []);
            
            % 工序层交叉 (POX交叉)
            jobs = 1:obj.num_jobs;
            subset = randperm(obj.num_jobs, ceil(obj.num_jobs/2));
            other_subset = setdiff(jobs, subset);
            
            % 初始化子代工序序列
            op_seq = zeros(1, obj.total_ops);
            
            % 复制parent1中subset的工序
            positions = false(1, obj.total_ops);
            for i = 1:length(parent1.operation_seq)
                job_id = parent1.operation_seq(i);
                if ismember(job_id, subset)
                    op_seq(i) = job_id;
                    positions(i) = true;
                end
            end
            
            % 填充parent2中other_subset的工序
            ptr = 1;
            for i = 1:length(parent2.operation_seq)
                job_id = parent2.operation_seq(i);
                if ismember(job_id, other_subset)
                    while positions(ptr)
                        ptr = ptr + 1;
                    end
                    op_seq(ptr) = job_id;
                    positions(ptr) = true;
                end
            end
            
            offspring.operation_seq = op_seq;
            
            % 机器层交叉 (均匀交叉)
            machine_assign = zeros(1, obj.total_ops);
            for i = 1:obj.total_ops
                if rand < 0.5
                    machine_assign(i) = parent1.machine_assign(i);
                else
                    machine_assign(i) = parent2.machine_assign(i);
                end
            end
            offspring.machine_assign = machine_assign;
            
            % 时间层初始化为零
            offspring.start_times = zeros(1, obj.total_ops);
        end
        
        function mutated = mutate(obj, chrom)
            % 三层变异操作
            mutated = chrom;
            
            % 工序层变异 (交换两个工序)
            if rand < 0.5
                idx1 = randi(obj.total_ops);
                idx2 = randi(obj.total_ops);
                temp = mutated.operation_seq(idx1);
                mutated.operation_seq(idx1) = mutated.operation_seq(idx2);
                mutated.operation_seq(idx2) = temp;
            end
            
            % 机器层变异 (改变一台机器的分配)
            if rand < 0.5
                idx = randi(obj.total_ops);
                job_id = mutated.operation_seq(idx);
                op_index = sum(mutated.operation_seq(1:idx) == job_id); % 当前工序索引
                
                job_name = ['J' num2str(job_id)];
                op_name = ['op' num2str(op_index)];
                available_machines = obj.jobs.(job_name).(op_name)(1); % 假设只有一个可选机器
                % 在实际应用中,这里可以添加机器选择逻辑
            end
            
            % 时间层变异 (扰动开始时间)
            if rand < 0.3
                idx = randi(obj.total_ops);
                mutation_amount = randi([-5, 5]);
                mutated.start_times(idx) = max(0, mutated.start_times(idx) + mutation_amount);
            end
        end
        
        function repaired = repairSolution(obj, chrom)
            % 修复非法解
            repaired = chrom;
            
            % 确保工序序列包含所有工件的正确次数
            job_counts = histcounts(repaired.operation_seq, 1:(obj.num_jobs+1));
            for job_id = 1:obj.num_jobs
                diff = obj.ops_per_job(job_id) - job_counts(job_id);
                if diff > 0
                    % 添加缺失的工序
                    for k = 1:diff
                        pos = randi(obj.total_ops);
                        repaired.operation_seq = [repaired.operation_seq(1:pos-1), job_id, repaired.operation_seq(pos:end)];
                    end
                elseif diff < 0
                    % 移除多余的工序
                    indices = find(repaired.operation_seq == job_id);
                    remove_indices = indices(randperm(length(indices), -diff));
                    repaired.operation_seq(remove_indices) = [];
                    % 需要重新填充到原始长度,这里简化处理
                    repaired.operation_seq = [repaired.operation_seq, repmat(job_id, 1, -diff)];
                    repaired.operation_seq = repaired.operation_seq(randperm(obj.total_ops));
                end
            end
        end
        
        function visualizeResults(obj, schedule, makespan)
            % 可视化调度结果
            figure('Name', '车间调度甘特图', 'Position', [100, 100, 1200, 600]);
            
            % 颜色映射
            colors = lines(obj.num_jobs);
            yticks = [];
            yticklabels = {};
            
            % 创建机器时间线
            machines = 1:obj.num_machines;
            hold on;
            for machine_id = 1:obj.num_machines
                y_pos = machine_id * 10;
                plot([0, makespan], [y_pos, y_pos], 'k-', 'LineWidth', 1);
                
                % 查找在该机器上加工的工序
                for job_id = 1:obj.num_jobs
                    job_name = ['J' num2str(job_id)];
                    ops = fieldnames(schedule.(job_name));
                    for op_id = 1:length(ops)
                        op_info = schedule.(job_name).(ops{op_id});
                        if op_info(3) == machine_id
                            start_time = op_info(1);
                            end_time = op_info(2);
                            duration = end_time - start_time;
                            
                            % 绘制工序条
                            rectangle('Position', [start_time, y_pos-3, duration, 6], ...
                                      'FaceColor', colors(job_id,:), 'EdgeColor', 'k');
                            
                            % 添加文本标签
                            text(start_time + duration/2, y_pos, [job_name '-' ops{op_id}], ...
                                 'HorizontalAlignment', 'center', 'FontSize', 8);
                        end
                    end
                end
                
                yticks(end+1) = y_pos;
                yticklabels{end+1} = ['M' num2str(machine_id)];
            end
            
            % 设置图表属性
            xlabel('时间');
            ylabel('机器');
            title(sprintf('车间调度甘特图 (Makespan = %.2f)', makespan));
            set(gca, 'YTick', yticks, 'YTickLabel', yticklabels);
            grid on;
            hold off;
            
            % 绘制适应度进化曲线
            figure('Name', '优化过程');
            plot(1:obj.max_gen, 1./obj.best_fitness_history, 'LineWidth', 2);
            xlabel('迭代次数');
            ylabel('最大完工时间');
            title('适应度进化曲线');
            grid on;
        end
    end
end

应用示例

问题定义

% 定义车间调度问题
jobs_data.J1.op1 = [1, 3];  % [机器, 加工时间]
jobs_data.J1.op2 = [2, 2];
jobs_data.J1.op3 = [3, 2];

jobs_data.J2.op1 = [1, 2];
jobs_data.J2.op2 = [3, 1];
jobs_data.J2.op3 = [2, 3];

jobs_data.J3.op1 = [2, 2];
jobs_data.J3.op2 = [1, 4];
jobs_data.J3.op3 = [3, 1];

% 创建并运行优化器
optimizer = MultiLayerGA(jobs_data);
optimizer.runOptimization();

结果分析

运行上述代码将生成:

  1. 车间调度甘特图:直观展示每台机器上的工序安排
  2. 适应度进化曲线:显示优化过程中最大完工时间的改进

算法优势与创新点

  1. 多层编码机制 分离工序顺序、机器分配和时间安排三个决策维度 避免单层编码的约束冲突问题 提高搜索空间的探索效率
  2. 混合交叉策略 工序层:采用POX交叉保持工序顺序约束 机器层:使用均匀交叉探索机器分配方案 时间层:基于启发式规则初始化
  3. 智能修复机制 自动检测并修复非法解 保持工序序列的完整性 确保机器分配的可行性
  4. 可视化分析 甘特图直观展示调度方案 进化曲线监控优化过程 支持多维度性能分析

工业应用场景

  1. 半导体制造:晶圆加工的多设备调度
  2. 汽车装配:混流生产线平衡优化
  3. 航空航天:复杂零部件加工排程
  4. 物流中心:包裹分拣与配送调度
  5. 3D打印农场:多打印机任务分配

优化

并行计算加速

parfor p = 1:obj.pop_size
    fitness(p) = obj.evaluateFitness(obj.population(p));
end

自适应参数调整

% 随迭代次数动态调整参数
current_pc = obj.pc * (1 - gen/obj.max_gen);
current_pm = obj.pm * (1 + gen/obj.max_gen);

局部搜索增强

% 在最优解附近进行邻域搜索
improved = true;
while improved
    improved = obj.localSearch(best_solution);
end

GPU加速解码

% 使用GPU加速调度解码
gpuJobs = gpuArray(obj.jobs);
[~, schedule] = obj.decodeChromosome_gpu(chrom, gpuJobs);

参考代码 基于多层编码遗传算法的车间调度算法 www.youwenfan.com/contentcnm/82813.html

扩展

  1. 动态调度:处理实时到达的紧急订单
  2. 多目标优化:同时优化交货期、能耗和设备利用率
  3. 鲁棒调度:应对加工时间不确定性的鲁棒优化
  4. 分布式调度:多工厂协同生产计划
  5. 数字孪生集成:与物理车间实时数据交互
posted @ 2025-12-01 17:43  kang_ms  阅读(0)  评论(0)    收藏  举报