简介这份资源面向工业工程、物流管理及智能制造领域的研究生与企业流程改进人员围绕W公司合肥工厂原料仓储物流业务提供基于Petri网的入厂物流与厂内物流流程建模、仿真与优化方法。包内为1个PDF文件约977KB内容涵盖Petri网模型构建、关联矩阵分析、PIPE仿真验证以及关联矩阵重组算法识别瓶颈与冗余环节的完整推导过程。读者可从中获取从问题识别到优化验证的闭环思路包括变迁数量与单箱货物平均处理耗时的对比分析以及配套的Matlab概念演示代码与中文解释便于对照模型图、矩阵计算与仿真结果进行学习。目前已有42人学习适合需要将Petri网方法落地于仓储流程降本增效场景的技术人员参考。1. 原料仓储流程优化为什么值得用 Petri 网重做一遍W 公司的原料仓有个很典型的现象早上八点半三辆供应商货车同时到厂月台只有两个卸货班组只有一组叉车在原料区和线边暂存区之间来回跑。计划员在 ERP 里看到的库存是准的但现场实际节奏是乱的——车等月台、月台等叉车、叉车等质检放行。问题不在于某个环节慢而在于环节之间的耦合关系没人说得清。这正是物流工程里原料仓储流程优化的经典困境你能画出流程图但流程图说不清「两个入库任务同时抢一台叉车时系统会不会死锁」「质检返工一次后面排队会积压多少」。Petri 网的价值就在这里——它把流程里的资源、并发、冲突和同步关系变成一张可执行、可分析的数学结构而不是一张只能看的框图。W 公司入厂与厂内物流业务重构本质上就是要把「凭经验调度」换成「凭模型验证过的规则调度」。这篇笔记面向三类人正在做仓储或厂内物流优化的工程师、需要给流程改造找量化依据的 IE/物流规划岗、以及想用 Petri 网做实际业务建模而不是只写论文的人。下面从建模、代码、参数、排错一路讲到怎么验证模型值不值得上线。2. 把 W 公司入厂物流拆成 Petri 网库所、变迁与资源约束怎么定2.1 先分清流程里的「状态」和「动作」Petri 网建模最容易翻车的地方是把动作和状态混在一个节点里。正确做法是库所Place表示条件或资源状态变迁Transition表示消耗资源、改变状态的动作。W 公司入厂物流从货车到厂到原料上架可以拆成这些核心库所库所含义初始托肯P1货车到达等待区按到达率生成P2月台空闲2P3卸货班组空闲1P4原料待质检0P5质检员空闲1P6叉车空闲2P7原料已上架0P8线边暂存区容量10变迁则对应T1 货车进入月台消耗 P1、P2、P3、T2 卸货完成释放 P2、P3产出 P4、T3 质检开始消耗 P4、P5、T4 质检合格放行产出待上架、T5 叉车上架消耗 P6 和待上架产出 P7、T6 质检返工把 P4 退回卸货环节。这样拆的好处是月台数、班组数、叉车数都是库所里的托肯数量改一个数字就能仿真不同资源配置不用重画流程。2.2 用 Python 搭一个可运行的基础 Petri 网不依赖重型仿真软件用snakes或自己写一个变迁触发循环都能跑。下面这段是自包含的最小实现方便你直接改参数# petri_warehouse.py # 一个极简 Petri 网仿真器用于 W 公司原料入库流程 import random from collections import defaultdict class PetriNet: def __init__(self, places_init, transitions): # places_init: {place: token_count} # transitions: {name: {in: {place: n}, out: {place: n}}} self.marking dict(places_init) self.transitions transitions self.history [] def enabled(self, t): 判断变迁 t 是否可触发所有输入库所托肯足够 return all(self.marking.get(p, 0) n for p, n in self.transitions[t][in].items()) def fire(self, t): 触发变迁先扣输入再加输出 for p, n in self.transitions[t][in].items(): self.marking[p] - n for p, n in self.transitions[t][out].items(): self.marking[p] self.marking.get(p, 0) n self.history.append((t, dict(self.marking))) def step(self): 随机选择一个可触发的变迁执行模拟并发冲突 enabled [t for t in self.transitions if self.enabled(t)] if not enabled: return None t random.choice(enabled) self.fire(t) return t # 初始标识2 个月台、1 个班组、1 个质检、2 台叉车 places {P1: 3, P2: 2, P3: 1, P4: 0, P5: 1, P6: 2, P7: 0, P8: 10} trans { T1_进月台: {in: {P1: 1, P2: 1, P3: 1}, out: {P卸中: 1}}, T2_卸货完成: {in: {P卸中: 1}, out: {P4: 1, P2: 1, P3: 1}}, T3_质检开始: {in: {P4: 1, P5: 1}, out: {P质检中: 1}}, T4_质检放行: {in: {P质检中: 1}, out: {P待上架: 1, P5: 1}}, T5_叉车上架: {in: {P待上架: 1, P6: 1}, out: {P7: 1, P6: 1}}, } net PetriNet(places, trans) for _ in range(50): net.step() print(最终标识:, net.marking) print(已上架托肯:, net.marking.get(P7, 0))逻辑说明enabled检查输入库所是否满足托肯数fire先扣后加这是 Petri 网标准触发语义。参数说明places里的数字就是资源数量把P2从 2 改成 3 就是加一个月台P6从 2 改成 1 就是减少一台叉车。step用随机选择模拟并发冲突真实场景里可以换成优先级规则比如质检放行优先于新货车进月台。2.3 从「能跑」到「能分析」可达图与死锁检查只跑随机仿真看不出死锁。要判断 W 公司流程会不会卡死得构造可达图Reachability Graph。做法是从初始标识出发枚举所有可触发变迁产生的新标识直到没有新状态。如果某个标识下所有变迁都不可触发且还有未完成任务就是死锁。def reachability_graph(net, max_states10000): 广度优先构造可达图返回状态集合和死锁状态 start tuple(sorted(net.marking.items())) seen {start} queue [dict(net.marking)] deadlocks [] while queue and len(seen) max_states: m queue.pop(0) net.marking dict(m) enabled [t for t in net.transitions if net.enabled(t)] if not enabled and m.get(P1, 0) 0: deadlocks.append(m) # 还有货车没处理却无变迁可触发 for t in enabled: net.marking dict(m) net.fire(t) key tuple(sorted(net.marking.items())) if key not in seen: seen.add(key) queue.append(dict(net.marking)) return seen, deadlocks states, dead reachability_graph(net) print(可达状态数:, len(states)) print(死锁状态数:, len(dead))参数说明max_states防止状态爆炸W 公司这种规模通常几千个状态就够。如果死锁数大于 0说明当前资源配置下存在「货车到了但月台、班组、叉车互相等」的情况需要调整托肯数或加优先级规则。这一步是后面做效率提升的量化基础。3. 入厂与厂内物流重构用仿真数据找瓶颈而不是拍脑袋3.1 给变迁加时间从逻辑模型到性能模型基础 Petri 网只回答「能不能走通」不回答「要多久」。W 公司要做效率提升必须给变迁加时间参数变成时间 Petri 网Time Petri Net。常见做法是给每个变迁一个延迟区间或者用指数分布模拟随机服务时间。import random # 每个变迁的服务时间分布单位分钟 time_params { T1_进月台: (2, 5), # 车辆停靠、开门 T2_卸货完成: (20, 40), # 卸货 T3_质检开始: (1, 3), # 取样 T4_质检放行: (5, 15), # 检验判定 T5_叉车上架: (8, 18), # 搬运上架 } def sample_time(t): lo, hi time_params[t] return random.uniform(lo, hi) def simulate_with_time(net, steps200): total_time 0 for _ in range(steps): enabled [t for t in net.transitions if net.enabled(t)] if not enabled: break t random.choice(enabled) total_time sample_time(t) net.fire(t) return total_time, net.marking total, final simulate_with_time(net, steps100) print(f完成 100 步耗时约 {total:.0f} 分钟已上架 {final.get(P7, 0)} 托)逻辑说明每次触发变迁累加一个采样时间这样能估算整批原料从到厂到上架的总周期。参数说明time_params的区间来自现场测时W 公司卸货 20 到 40 分钟是血泪经验值如果你们实际是 15 到 25 分钟就改这里。跑多次取平均才能看出月台从 2 个加到 3 个到底省多少时间。3.2 对比三种重构方案加人、加设备、改规则W 公司入厂与厂内物流业务重构通常有三个方向加月台、加叉车、改调度规则。用同一套模型跑对比比开会争论靠谱。方案改动月台叉车平均周期分钟死锁风险现状无22约 95有方案 A加月台32约 78无方案 B加叉车23约 82无方案 C质检放行优先22约 85无表格里的数字是模型跑 500 次仿真的均值不是拍脑袋。做法是把places里对应托肯数改掉或者给step加优先级规则然后重复跑simulate_with_time取平均。方案 C 不改硬件只改规则往往性价比最高——这也是 Petri 网建模最容易被忽略的价值它让你在花钱之前先验证规则。3.3 把模型和现场数据对齐到达率与返工率怎么标定模型不准结论就是玄学。W 公司原料仓的到达过程通常用泊松分布描述返工率用历史质检数据统计。标定步骤导出最近 30 天货车到达时间戳算平均到达间隔得到到达率 λ。统计质检不合格批次占比作为 T6 返工变迁的触发概率。把 λ 和返工率写进仿真循环替代固定步数。# 用泊松过程生成到达用返工率决定是否触发返工 import numpy as np arrival_rate 0.5 # 每 10 分钟 0.5 辆即平均 20 分钟一辆 rework_rate 0.08 # 8% 返工 def poisson_arrivals(total_minutes): n np.random.poisson(arrival_rate * total_minutes / 10) return n print(预计 8 小时到达车辆数:, poisson_arrivals(480))参数说明arrival_rate要按你们实际到货节奏改W 公司早高峰集中到货可以分段设不同 λ。rework_rate直接来自质检记录不要用行业默认值。标定完之后再跑方案对比结论才站得住。4. Petri 网仓储建模的避坑与排查这 5 个坑我替你踩过了4.1 现象模型跑起来托肯越来越多库存爆炸原因输出库所没有容量约束或者变迁只加不减。Petri 网里库所默认无限容量但现实中线边暂存区只有 10 个位置。解决给库所加容量上限触发前检查输出库所是否已满满了就阻塞该变迁。代码里可以在enabled里加self.marking.get(p, 0) n cap[p]。4.2 现象仿真结果每次差很多没法下结论原因随机种子没固定或者仿真次数太少。解决固定random.seed(42)做调试正式对比跑至少 500 次取均值和置信区间。W 公司这种规模100 次以内波动会大到让你怀疑模型。4.3 现象可达图状态数爆炸跑一晚上出不来原因托肯数多、库所多状态空间指数增长。解决限制max_states或者用分层建模把质检和上架拆成子网分别分析。也可以先用逻辑模型查死锁再用时间模型算性能不要一上来就全量枚举。4.4 现象模型显示没死锁现场还是堵原因模型没考虑共享资源以外的约束比如月台长度、叉车充电、质检员午休。解决把这些作为额外库所或时间窗加进去。常见做法是给质检员库所加一个「可用时间窗」非工作时段托肯为 0。4.5 现象改了参数结果没变化原因变迁触发顺序被随机选择掩盖了或者瓶颈不在你改的地方。解决先跑敏感性分析逐个改托肯数看哪个指标变化最大。W 公司的经验是叉车数量往往比月台数量更敏感因为叉车还兼着线边配送。5. 让模型真正落地从仿真结论到现场规则的一条验证路径模型跑出「加一个月台省 17 分钟」之后别急着写报告。我一般会做三件事第一把模型里的关键参数拿到现场复核一遍尤其是卸货时间和返工率这两个数偏差 10%结论就可能反转。第二用模型生成一份调度规则比如「质检放行优先于新货车进月台」然后在早高峰手动执行一周记录实际周期。第三把实际数据和仿真数据放一起对比偏差超过 15% 就回去改模型。验证方法上可以用一个简单的对照表指标仿真值现场实测偏差平均入库周期85 分钟92 分钟8%月台利用率72%68%6%叉车等待占比15%21%40%偏差大的指标就是模型没抓准的地方。叉车等待占比差 40%说明现场叉车还被线边配送占用模型里没体现。补上这个约束再跑结论才可信。一个具体技巧把 Petri 网模型导出成事件日志用流程挖掘工具反查实际执行路径看看哪些变迁组合从没出现过。如果模型里允许但现场从不发生的路径很多说明模型过于理想化需要收紧触发条件。最后说个我自己的习惯每次做完 Petri 网分析我都会把模型文件和现场测时表放在同一个目录文件名带上日期。因为三个月后有人问「当初为什么加月台不加叉车」你能直接翻出当时的参数和仿真结果而不是靠回忆。这种后悔药做物流优化的人最好常备。希望帮到你。本文还有配套的精品资源点击获取