APS入门7-数字化车间智能排产调度实战

关注我的公众号YueTan进行交流探讨
欢迎关注我的APS仓库:https://github.com/yuetan1988/python-lekin

APS系列入门

终于来到了实战环节

准备数据

  • job [来自订单]

    • 名称,需要的数量,交付日期
    • 每个job对应的工艺路线itinerary
  • 工艺路线 [来自BOM]

    • 每一步的工序
    • 每个工序的时常
  • 工序

    • 每道工序需要的机器,机器的时常/节拍
    • 每个工序的用料/单位。损耗率 【计算RPM,开始不必考虑?】
  • 机器

    • 机器之间的替换关系,或者以机器组的形式包含哪几台机器
    • 可以是生产单位,也就是产线

job
在这里插入图片描述

工厂建模

数学建模

参数

实体
i i i: 表示机器
j j j: 表示工件

决策变量

目标

全部工序最小完成时间

保证交付,最少违约

约束

基础
一道工序只能在一个机器上加工

一个机器只能加工一种工序

关键
按工艺路线进行加工

解决方案

在工厂建模和数学建模之后,再加上我们在APS入门6-LEKIN学习与复现中已经完成的Lekin 引擎,可以构建完整的解决方案了。

无限产能

  • 直接根据需求日期与单班产能倒推

常见三步

  • 根据需求日期倒排,
  • 如果最早开始日期超过了当前日期,或前面有空位了,进行正排拉动
  • 得到的正排日期进行承诺

Here is a Python code example for the Job Shop Scheduling Problem (JSSP) using backward scheduling:

import heapq

jobs = [(5,3,2), (3,6,5), (6,4,10), (5,8,12), (7,2,4)]
# Each tuple represents a job with (processing_time, release_time, due_date)

num_machines = 3

def backward_scheduling(jobs):
  
  schedule = [[] for _ in range(num_machines)]
  
  jobs.sort(key=lambda x: x[2]) # Sort jobs by decreasing due date
  
  for job in jobs:
    processing_time, release_time, due_date = job
    
    assigned = False
    for m in range(num_machines-1, -1, -1): # Check machines backwards
      if len(schedule[m]) == 0:
        schedule[m].append((processing_time, release_time, due_date))
        assigned = True
        break
        
      else:
        last_job = schedule[m][-1]
        if release_time >= last_job[1]:
          schedule[m].append((processing_time, release_time, due_date))  
          assigned = True
          break
      
    if not assigned:
      return None # No feasible schedule
  
  return schedule
  
schedule = backward_scheduling(jobs)

if schedule is not None:
  print(schedule)
else:
  print("No feasible schedule found!")

2

When assigning a job, iterate through the compatible machines instead of all machines:

for job in jobs:
    processing_time, release_time, due_date, compatible_machines = job
    
    assigned = False  
    for m in compatible_machines:
      # check if job can be scheduled on machine m
      ...

When checking for overlap, only consider jobs already assigned to that specific machine:

last_job = schedule[m][-1] 
if release_time >= last_job[1]:
  # schedule job

This allows jobs to be processed on any of their compatible machines. The backward scheduling logic remains the same otherwise.

The full code would look something like:

# jobs with compatible machines
jobs = [((5,3,2), [0,1]), ...] 

def backward_scheduling(jobs):

  schedule = [[] for _ in range(num_machines)]  

  for job in jobs:
    processing_time, release_time, due_date, compatible_machines = job
    
    assigned = False
    for m in compatible_machines:
      if len(schedule[m]) == 0:
        schedule[m].append(job)
        assigned = True
        break
        
      else:
        last_job = schedule[m][-1]  
        if release_time >= last_job[1]:
           schedule[m].append(job)
           assigned = True
           break
           
    if not assigned:
      return None
  
  return schedule

3

for each job j:
  
  earliest_start = 0
  
  for each operation op in job j:
  
    find earliest compatible machine m that can schedule op after earliest_start 
    schedule op on machine m
    update earliest_start = op.end_time
    
  if any op cannot be scheduled:
    return None // infeasible
    
return schedule

Good question! To handle the case where a single operation can be processed on multiple compatible machines, we just need to modify the machine selection logic in the pseudocode above.

Instead of just selecting the earliest feasible machine, we can:

Find all compatible machines where the operation can be scheduled after the earliest start time.
Evaluate a heuristic to select the best machine from these options.
Some example heuristics:

Choose the machine with the minimum load or length of operations already assigned. This balances load across machines.
Choose the machine that minimizes the start time of the operation. This prioritizes reducing operation start times.
Choose randomly among the feasible options.
The modified pseudocode would be:

for each operation op in job j:

  find all compatible machines M where op can be scheduled after earliest_start
  
  m = apply_machine_heuristic(M) // select best machine
  
  schedule op on machine m
  update earliest_start
for each operation op in job j:

  find all compatible and feasible machines M for op  
  
  allocate op processing time across M 
  // e.g. 60 mins across 2 machines, schedule 30 mins on each
  
  for each machine m in M:
  
    find available time slot on m for portion of op
    schedule op portion on m 
  
  update earliest_start for next op
import heapq

jobs = [
  [(op1, [0,1]), (op2, [1,2]), (op3, [2])],
  [(op1, [0,3]), (op2, [1,2]), (op3, [1])] 
]

num_machines = 3

def schedule_op(op, compatible_machines, schedule):

  portion = op[0] / len(compatible_machines)
  
  for m in compatible_machines:
  
    if len(schedule[m]) == 0:
      schedule[m].append((portion, op[1], op[2]))
      
    else:
      last_op = schedule[m][-1]
      if op[1] >= last_op[2]:
        schedule[m].append((portion, op[1], last_op[2] + portion)) 
        
  op[1] = max(s[-1][2] for s in schedule)
  
def schedule_jobs(jobs):

  schedule = [[] for _ in range(num_machines)]
  
  for job in jobs:
    earliest_start = 0
    for op in job:
      compatible_machines = op[1]  
      schedule_op(op, compatible_machines, schedule)
      earliest_start = op[1]
      
  return schedule

schedule = schedule_jobs(jobs)
print(schedule)
def schedule_op(op, compatible_machines, machine_slots):

  portion = op[0] / len(compatible_machines)  

  for m in compatible_machines:
    
    for slot in machine_slots[m]:
      if portion <= slot[1] - slot[0]:
        op_start = slot[0]
        op_end = op_start + portion 
        machine_slots[m].remove(slot)
        machine_slots[m].append((slot[0], op_start))
        machine_slots[m].append((op_end, slot[1]))
        break
        
  # Set op start and end times

Here is a Python code example that schedules a set of jobs with multiple operations per job. It handles:

Operations with multiple compatible machines
Splitting operations across compatible machines
Pre-occupied slots on machines
Updating time slots after scheduling


jobs = [
  [(op1, 60mins, [0,1]), (op2, 30mins, [1,2])],
  [(op1, 45mins, [0,3]), (op2, 20mins, [1,2])]
]

machine_slots = [
  [(0, 100), (200, 300)], # Machine 0
  [(0,50)], # Machine 1 
  [] # Machine 2
]

def schedule_jobs(jobs, machine_slots):

  for job in jobs:
    
    for op in job:
        
      portion = op[1] / len(op[2]) # Avg portion per machine
    
      for m in op[2]: 
      
        for slot in machine_slots[m]:
        
          if slot is occupied: 
            continue
            
          if portion <= slot[1] - slot[0]:

            op_start = slot[0]
            op_end = op_start + portion

            # Update slots
            machine_slots[m].remove(slot)
            machine_slots[m].append((slot[0], op_start))
            machine_slots[m].append((op_end, slot[1]))
            
            # Schedule portion
            schedule[m].append((portion, op_start, op_end))
            
            break
            
  return schedule
          
schedule = schedule_jobs(jobs, machine_slots)

综合起来

jobs = [
  [ (op1, 60mins, [0,1]), 
    (op2, 30mins, [1,2]),
    demand_date = 100
  ],
  [ (op1, 45mins, [0,3]),
    (op2, 20mins, [1,2]), 
    demand_date = 75  
  ]
]

machine_priority = {0: 1, 1: 2, 2: 3} 

def schedule_jobs(jobs):

  sort_by_demand_date(jobs)
  
  for job in jobs:
  
    total_time = 0
    
    for op in job:
    
      try_machines = op[2]
      machine = None
      min_time = infinity
      
      for m in try_machines:
      
        projected_time = get_completion_time(op, m)
        
        if projected_time < min_time:
          machine = m 
          min_time = projected_time
          
      schedule_op(op, machine)  
      total_time += op[1] 
      
    job_completion[job] = total_time
  
def get_completion_time(op, machine):

  projected_start = find_slot(op, machine)
  
  priority_factor = machine_priority[machine] 
  
  projected_end = projected_start + (op[1] * priority_factor)

  return projected_end 

schedule = schedule_jobs(jobs)
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

YueTann

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

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

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

打赏作者

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

抵扣说明:

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

余额充值