简介:本资源是一份面向制造业信息化从业者、ERP实施顾问及工业软件开发者的生产排程算法技术文档,聚焦解决传统ERP系统在详细作业计划自动生成方面的核心瓶颈。文档深入剖析主生产计划向精细化作业计划转化的逻辑路径,系统阐述需求拉动与物料供需平衡两大设计原则,并附有可研读的Pascal风格排程算法实现代码,涵盖节点建模、投料计算、产能约束处理及订单优先级调度等关键环节。资源为单文件Word文档(.docx),大小仅42KB,内容精炼但信息密度高,含基础数据设置、需求平台、预排程与正式排程四大模块说明及实际代码片段。目前已有849人学习下载,适合希望理解高级排程逻辑、开展算法二次开发或优化企业生产计划体系的技术人员深度研读。
1. 这不是排产Excel表,而是一套可落地的制造资源约束型排程算法
很多制造业IT同事拿到“生产排程算法”几个字,第一反应是:又来画甘特图?又来调Solver参数?但这份文档里嵌着的 Pascal 实现,本质是一套带物料供需闭环反馈的递归式BOM展开引擎——它不依赖调度器时间片划分,也不靠遗传算法迭代寻优,而是用确定性逻辑,在主生产计划(MPS)输入后,同步完成工序作业量分配、半成品需求推导、原始物料缺口计算三重耦合决策。它解决的不是“哪天开工”,而是“为什么这个工单必须今天投料、而隔壁线要等三天”。典型适用场景是多级装配体+小批量混线+安全库存动态浮动的离散制造现场,比如汽车零部件厂应对主机厂日频次变更订单,或医疗器械企业处理CE认证批次追溯带来的工序拆分约束。如果你的ERP还在用MRPⅡ的“净需求=毛需求−现有库存”线性公式,这套算法暴露的正是其底层缺陷:它把“车间当前在制品WIP占用”“供应商采购周期内已承诺未到货量”“上道工序返工率导致的额外补投比例”全部编码进BestNumber()函数的四层SQL查询链中,让排程结果天然携带执行可行性校验。
2. 算法核心机制:基于节点类型的状态机驱动BOM分解
2.1 四类节点定义与状态流转逻辑
算法将整个产品结构抽象为四种节点类型,每种节点承载不同维度的约束信息:
| 节点类型 | 编号 | 核心职责 | 关键字段作用 |
|---|---|---|---|
| 流程节点 | 0 | 表示一个完整工艺流程(如“发动机总装”) | FlowId关联工序表,ProRate为全程投料系数 |
| 配方节点 | 1 | 表示替代性工艺路径(如“焊接/胶粘二选一”) | IngredientId指向配方库,Resolved=false触发分支处理 |
| 半成品节点 | 2 | 表示中间产出物(如“缸体毛坯”) | stepid绑定具体工序,Number为实际作业量 |
| 原始物料节点 | 3 | 表示采购件或外协件(如“曲轴”) | proneed直接生成采购申请,proneed0用于缺口预警 |
注意:节点状态
Resolved并非布尔开关,而是决策锁止标志。当Resolved=true时,该节点所有下游计算被冻结,避免同一物料在多条装配路径中重复计数。这解决了传统MRP中“同一螺丝在发动机和变速箱BOM中各算一次”的经典错误。
2.2ResolveMaterial1()主过程的三层递归结构
主过程采用深度优先遍历策略,通过三次嵌套循环实现跨层级耦合计算:
// 第一层:初始化顶层流程节点 nodes1[0].nodetype := 0; nodes1[0].Number := pronumber; // 主生产计划数量 nodes1[0].Resolved := true; // 第二层:反向遍历当前流程所有工序(从后向前) form2.sysdataset.commandtext := 'SELECT * FROM WORKSTEP WHERE FLOWID='''+flowid+''' AND STEPID<='''+stepid+''' ORDER BY STEPID DESC'; while not form2.sysdataset.eof do begin // 计算本工序实际作业量:调用BestNumber()进行供需平衡 tempnumber := BestNumber( trim(form2.sysdataset.fieldbyname('MaterialId').asstring), tempnumber * prorate ); // 生成对应半成品节点 nodes1[k].nodetype := 2; nodes1[k].Number := tempnumber; // 实际作业量 nodes1[k].proneed := tempnumber * prorate; // 实际投料量 // 处理工序末端:判断是否需跳转至新流程或配方 if trim(form2.sysdataset.fieldbyname('StepId').asstring) = firststepid then begin if nodes1[k].stepid = '00' then // 创建配方节点(分支处理) else // 查询FLOWS表寻找对接流程(串接处理) end; end; // 第三层:循环处理未解析节点(递归展开子流程) j := 0; while j < nodecount1 do begin if not nodes1[j].Resolved then begin case nodes1[j].nodetype of 0: // 递归调用ResolveMaterial1处理子流程 1: // 加载配方并重置prorate系数 3: // 直接标记Resolved并终止 end; end; inc(j); end;关键参数说明:
pronumber:主生产计划输入量,是整个计算树的根值prorate:单工序投料比例(如“缸盖加工工序投料系数0.98”,含损耗)tempprorate:全程投料系数(从顶层物料到当前节点的累计乘积)tempnumber:动态传递的作业量,随工序深度衰减或放大
该结构强制要求每个节点必须明确回答三个问题:① 我的上游是谁?② 我的下游有哪些?③ 我的物料缺口由谁补足?这比单纯调用MRP.Calculate()方法多出两层业务语义校验。
2.3BestNumber()函数的四步供需平衡决策链
此函数是算法区别于传统MRP的核心,它用SQL查询链构建实时库存快照:
function TTaskForm.BestNumber(materialId: string; ProNumber: real): real; var oldneed, oknumber, leftnumber, addnumber: real; begin // Step1:获取该物料历史总需求(含已确认工单+预测需求) form2.syscommand.commandtext := 'SELECT MINNUMBER+PRONEED+PLANNUMBER FROM MATERIAL WHERE MATERIALID='''+MaterialId+''''; oldneed := form2.syscommand.execute.Fields[0].Value; // Step2:获取该物料基础保有量(安全库存MINNUMBER + 已计划采购PLANNUMBER) form2.syscommand.commandtext := 'SELECT MINNUMBER+PLANNUMBER FROM MATERIAL WHERE MATERIALID='''+MaterialId+''''; oldneed1 := form2.syscommand.execute.Fields[0].Value; // Step3:计算当前可用量(仓库库存+在途采购+车间在制 - 已占用需求) form2.syscommand.commandtext := 'SELECT SAVEDNUMBER+WORKSHOPNUMBER+BUYPLANNUMBER+PROPLANNUMBER FROM MATERIAL WHERE MATERIALID='''+MaterialId+''''; oknumber := form2.syscommand.execute.Fields[0].Value; // Step4:动态缺口计算(考虑车间在制WIP占用) leftnumber := oknumber - oldneed - x; // x为本次分解中其他路径对该物料的需求 if leftnumber < 0 then begin if oknumber < oldneed1 then addnumber := oldneed1 - oknumber // 强制补足安全库存 else addnumber := 0; leftnumber := 0; end; // 最终决策:理论需求量 > 可用量 → 新增作业;否则为0 if leftnumber >= pronumber then result := 0 else result := pronumber - leftnumber; result := result + addnumber; // 加入安全库存补足量 end;逻辑穿透点:
SAVEDNUMBER:物理库存(含质检待判区)WORKSHOPNUMBER:车间在制(已领料未完工)BUYPLANNUMBER:采购在途(含供应商已承诺未发货)PROPLANNUMBER:生产在途(已排产未入库)
这种设计使算法能自动识别:“某型号轴承库存显示500件,但其中320件已被发动机线领用且未完工,实际可用仅180件”,从而避免因库存虚高导致的排产过载。
3. 生产排程系统四模块与算法的工程化集成
3.1 基础数据设置:将业务规则转化为节点属性
算法有效性高度依赖基础数据的颗粒度控制。以“换线时间”为例,传统ERP将其设为产线级常量(如“A线换线耗时30分钟”),而本系统要求在WORKSTEP表中为每个工序配置:
| 字段名 | 示例值 | 算法作用 |
|---|---|---|
ChangeoverTime | 1800(秒) | 在预排程阶段参与产能计算:可用工时 = 总工时 − Σ(换线时间×切换次数) |
MinLotSize | 50 | BestNumber()中强制向上取整:ceil(pronumber / MinLotSize) × MinLotSize |
ScrapRate | 0.02 | 动态修正prorate:实际投料系数 = 理论系数 / (1−ScrapRate) |
提示:若
MinLotSize未配置,算法会退化为单件流模式,导致小批量订单在产线间频繁切换,此时ChangeoverTime权重急剧上升,系统自动触发“合并同类订单”建议。
3.2 需求平台:主生产计划的动态注入接口
需求平台并非简单接收销售订单,而是构建三层过滤器:
- 时间窗过滤:只加载
需求时间 ∈ [T−7天, T+90天]的订单,避免远期预测干扰短期排程 - 优先级映射:将CRM系统中的
OrderPriority字段转换为算法内部PriorityWeight(如VIP客户=1.5,常规客户=1.0) - 成组规则引擎:根据
MaterialGroup字段自动聚合相似BOM结构订单(如所有“ECU控制器”订单合并计算共用PCB板需求)
该设计使ResolveMaterial1()的pronumber输入不再是静态数值,而是带权重的动态集合。例如某日收到3个订单:A(VIP,数量200)、B(常规,数量150)、C(VIP,数量80),算法会先按权重排序,再以pronumber=200×1.5+150×1.0+80×1.5=570作为顶层输入,确保高优先级订单的物料保障。
3.3 预排程与排程:双阶段计算的边界条件
预排程(Pre-Scheduling)和正式排程(Scheduling)采用不同约束集:
| 阶段 | 约束条件 | 算法行为 | 输出目标 |
|---|---|---|---|
| 预排程 | ① 订单优先级 ② 交期倒排 ③ 产线能力上限 ④ 换线时间 | 调用ResolveMaterial1()生成初步作业量,但不锁定工序时间 | 得到各产线负荷分布热力图,识别瓶颈线 |
| 正式排程 | ① 预排程结果 ② 工序顺排规则 ③ 已确认工单不可移动 | 对预排程结果做时间轴压缩,调用BestNumber()重新校验物料缺口 | 生成精确到小时的工单下达清单 |
关键差异:预排程阶段BestNumber()查询WORKSHOPNUMBER时仅统计已完工未入库的在制品,而正式排程阶段则包含所有已领料工序的在制量。这导致同一物料在两个阶段的leftnumber值可能相差30%以上,迫使系统在正式排程前强制触发物料齐套检查。
3.4 排程工作台:算法结果的可视化干预通道
工作台不是简单展示甘特图,而是提供五维干预入口:
- 订单优先级调整:拖拽订单至更高优先级槽位,触发
ResolveMaterial1()重计算,但仅重算该订单路径节点 - 产线重分配:将某工单从A线拖至B线,系统自动:
- 查询B线
ChangeoverTime并更新产能模型 - 重新执行
BestNumber()验证B线对应物料库存 - 若缺口超阈值(>15%),弹出“建议采购加急”对话框
- 查询B线
- 计划订单重排:选择某订单树(如“整车→底盘→悬挂系统”),系统递归调用
ResolveMaterial1(),但保留已确认子节点的Resolved=true状态,仅重算未确认分支 - 能力负荷查询:输入时间范围,返回SQL:
SELECT line_id, SUM(duration) as used_hours, (SELECT capacity FROM line_capacity WHERE line_id=t.line_id) as total_hours, ROUND(SUM(duration)/(SELECT capacity FROM line_capacity WHERE line_id=t.line_id)*100,1) as util_rate FROM task_schedule t WHERE start_time BETWEEN '2023-10-01' AND '2023-10-07' GROUP BY line_id HAVING util_rate > 95 - 跟单部件同步:点击“总装部装联动”,系统自动:
- 提取总装订单BOM中所有部装件
- 批量调用
ResolveMaterial1()生成部装计划 - 比对总装与部装交期,若偏差>24小时,标红预警
这种设计使算法从“黑箱计算”变为“可干预决策中枢”,工程师可随时切入任意节点修正参数,而非等待整轮重排。
4. 算法落地的关键参数调优与异常诊断
4.1 三类典型异常的SQL级定位方法
当排程结果出现偏差时,应绕过应用层日志,直查数据库状态:
| 异常现象 | 定位SQL | 参数含义 |
|---|---|---|
| 物料需求虚高 | SELECT MATERIALID, SAVEDNUMBER, WORKSHOPNUMBER, BUYPLANNUMBER, PROPLANNUMBER, MINNUMBER FROM MATERIAL WHERE MATERIALID='XXX' | 检查WORKSHOPNUMBER是否包含已报废在制品(应清零) |
| 产线负荷突降 | SELECT STEPID, COUNT(*) as task_count, SUM(duration) as total_duration FROM TASK_SCHEDULE WHERE LINE_ID='A01' AND START_TIME>'2023-10-01' GROUP BY STEPID ORDER BY total_duration DESC | 发现某工序任务量骤减,需核查该工序ChangeoverTime是否被误设为0 |
| 订单交期漂移 | SELECT ORDER_ID, PRIORITY_WEIGHT, DEMAND_DATE, CALCULATED_START, CALCULATED_END FROM SCHEDULE_RESULT WHERE ORDER_ID='ORD-2023-XXXX' | 比较DEMAND_DATE与CALCULATED_END,若差值>3天,检查PriorityWeight是否被CRM系统覆盖 |
提示:所有诊断SQL必须在
SCHEDULE_RESULT表添加复合索引:CREATE INDEX idx_order_time ON SCHEDULE_RESULT(ORDER_ID, CALCULATED_END);否则百万级订单查询将超时。
4.2BestNumber()函数的四个调优杠杆
该函数输出直接受以下参数影响,需按现场实绩校准:
| 参数 | 默认值 | 调优依据 | 效果 |
|---|---|---|---|
MINNUMBER | 0 | 基于ABC分类:A类物料安全库存=月用量×1.2 | 过高导致虚假缺口,过低引发停线 |
ScrapRate | 0.01 | 按工序采集3个月不良率,取P95分位值 | 铸造工序通常设0.03~0.05,机加工设0.005~0.015 |
BuyPlanLeadTime | 30天 | 与采购部确认供应商平均交期,加权计算(铜材=15天,芯片=60天) | 影响BUYPLANNUMBER的时效性 |
WorkshopNumber更新频率 | 实时 | 必须与MES系统对接,每道工序报工后立即更新 | 延迟更新将导致在制量漏计 |
实操技巧:在测试环境部署影子表MATERIAL_SHADOW,每日凌晨用生产数据全量同步,然后运行BestNumber()对比MATERIAL与MATERIAL_SHADOW的输出差异。若差异率>5%,说明基础数据存在系统性偏差。
4.3 预排程阶段的产能模型校准表
预排程的准确性取决于产能模型精度,需建立产线能力校准表:
| 产线ID | 理论节拍(秒) | 实际OEE | 有效工时(小时/班) | 换线时间(秒/次) | 日最大切换次数 | 当前算法产能(件/班) |
|---|---|---|---|---|---|---|
| A01 | 45 | 82% | 7.3 | 1800 | 8 | floor(7.3×3600÷45) − 8×1800÷3600 = 584 |
| B02 | 60 | 76% | 6.8 | 3600 | 4 | floor(6.8×3600÷60) − 4×3600÷3600 = 404 |
验证方法:选取连续5个工作日,对比算法输出产能与实际产出。若偏差持续>10%,需检查MES报工数据是否漏传(如夜班报工延迟至次日早8点),此时应在WORKSTEP表中增加ShiftOffset字段补偿。
4.4 算法与ERP系统的数据契约规范
为避免算法结果被ERP覆盖,必须约定六项数据契约:
- 主键约束:算法生成的
SCHEDULE_ID必须为UUID格式,ERP不得修改 - 状态锁:
SCHEDULE_STATUS字段仅允许Draft/Confirmed/Released三种值,算法只写Draft,ERP审批后改为Confirmed - 时间戳保护:
CALCULATED_AT字段由算法写入,ERP禁止更新 - 物料版本:算法读取
BOM_VERSION字段,若ERP升级BOM必须同步更新此字段 - 工单隔离:算法生成的
TASK_NO前缀为ALG-,ERP生成的为ERP-,两者互不覆盖 - 异常标记:当
BestNumber()检测到缺口>20%时,写入EXCEPTION_FLAG='MATERIAL_SHORTAGE',ERP必须拦截该工单下达
这些契约通过数据库触发器强制实施,例如:
CREATE TRIGGER trg_schedule_protect ON SCHEDULE_RESULT AFTER UPDATE AS BEGIN IF UPDATE(CALCULATED_AT) OR UPDATE(SCHEDULE_ID) BEGIN RAISERROR('Algorithm fields are read-only', 16, 1) ROLLBACK TRANSACTION END END这种硬性约束确保算法输出不被业务操作污染,使排程结果真正成为生产执行的唯一可信源。
本文还有配套的精品资源,点击获取