ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

用Petri网重构原料仓储流程:从流程可视化到量化仿真优化

用Petri网重构原料仓储流程:从流程可视化到量化仿真优化 简介面向物流工程与流程优化领域的一份理论与实操并重的PDF资源适合工业工程、物流管理或系统优化背景的研究生、企业流程改进人员及智能制造技术人员阅读。内容以W公司合肥工厂原料仓储为案例针对入厂物流和厂内物流中的作业繁琐、效率低下等问题运用Petri网建立流程模型并通过关联矩阵与PIPE仿真验证模型的有界性、活性和守恒性。随后利用关联矩阵重组算法识别瓶颈与冗余给出优化方案对比优化前后变迁数量与单箱处理耗时验证效率提升效果。文档内含详细Matlab代码及中文逐步解释以简化的原材料入库流程为例演示Petri网建模、关联矩阵计算、不变量分析与离散事件仿真操作便于读者举一反三。资源为单个PDF文件约977KB已有42人浏览学习适合用于制造企业仓储流程建模与降本增效研究。 刚接手W公司这个原料仓储优化项目时业务部门给我看了一堆泳道流程图节点、责任人、单据画得清清楚楚。可真问到“车辆到厂后平均在门口等多久”“质检窗口下午为什么堆积几十个批次”“库位明明还有空为什么总是找不到地方放”图上一个数字都答不上来。我后来改用Petri网对入厂和厂内物流做建模把流程从“看得见”变成了“算得清”。这篇文章就把这套完整思路拿出来如何把原料到货、入厂登记、抽样质检、入库上架、生产领料拆解成Petri网模型如何用关联矩阵与不变量做理论验证如何用Python写一个能落地的轻量级仿真器以及最终做了哪些业务重构、效率提升了多少。整个项目做下来我发现Petri网这种工具的厉害之处不在于画图好看而在于它把流程变成了可以计算、可以推演、可以验证的数学对象。这篇文章适合正在做物流工程、工业工程、管理科学与工程相关课题的学生也适合制造企业里真正想解决仓储物流问题的从业者。代码我全部给了参数也是根据W公司实际业务标定的按自己的数据替换就能用。1. 为什么用Petri网重构W公司的原料仓储流程1.1 项目要解决的真实痛点W公司是一家典型的离散制造企业原料仓库的运作模式很有代表性供应商车辆集中在上午到厂高峰期大门口排长队车辆进入厂区后司机要到物流大厅排队办入厂登记登记完等质检员抽检质检合格后叉车工再搬运上架生产车间则按领料单随时到仓库领料。表面上看每个环节都有人在管但跨部门的流程衔接存在明显问题。最典型的三个现象一是信息流滞后车辆到厂半小时系统里还没有到货记录管理层想查某批货到没到只能打电话问门卫二是质检环节排队严重下午两点了还在处理早上八点积压的单子三是库位分配靠经验忙起来的时候叉车工找不到空库位载着托盘在货架区来回转。这些问题单靠管理命令解决不了因为你不清楚瓶颈到底卡在哪个环节也不清楚增加一个质检员到底能压下去多少排队量。要回答这类问题必须把流程转成可量化的模型。1.2 为什么选Petri网而不是其他建模工具做流程仿真可选的工具有不少我逐一评估过。用泳道流程图做梳理最直观但它本质上只是静态描述画完之后你算不出排队长度、资源利用率这些动态指标。用Flexsim、Simio这类商业仿真软件做动画演示效果很好但建模周期长而且很多流程细节被封装在黑盒子里出了问题不好排查。用排队论公式做数学推导处理单个服务台、单条队列很方便可一旦碰上多资源同步、串并联混合、分支回退这些真实物流场景公式很快就不够用了。Petri网的优势在于它同时具备形式化描述能力和数学分析能力。它能表达并发、同步、冲突、资源共享关联矩阵可以做不变量的理论验证同时模型结构天然对应仿真逻辑防止逻辑漏洞。这次的项目涉及月台、质检员、看板信号三种共享资源还存在合格/不合格概率分支用Petri网建模一套体系从头用到尾。1.3 建模仿真前的基础数据准备模型不能凭空搭必须先用业务数据把场景标定出来。我在W公司蹲了一周主要采集了下面这些数据。表格里的数值就是后续仿真模型的输入参数。指标数值说明卸货月台数量3个早晚班共用高峰资源瓶颈日平均到厂车辆约60辆集中在7点到11点占全天70%入厂登记耗时均值5分钟纸质登记系统录入双操作波动大质检抽检比例30%批次单批耗时均值20分钟合格率70%按过去三个月历史单据统计入库上架耗时均值12分钟/托叉车往返库位时间原材料库位1200个当前占用800个生产领料节拍每2小时一批凭纸质领料单信息滞后这里有个小提醒合格率千万不要拍脑袋填一定要去拉历史质检单据做统计。W公司刚开始口头跟我说合格率95%以上我拉数据一算实际只有70%。参数错了后面所有仿真结论都会跑偏。2. 核心建模思路把“入厂厂内”翻译成Petri网2.1 库所、变迁、token的物流语义Petri网的基础概念不难关键是理解每个元素对应业务流程中的什么对象。库所Place表示一种状态或缓冲位置比如“等待登记车辆队列”“等待质检批次”“库位占用数”“可用月台数”都可以建模成库所。变迁Transition表示一个动作或规则触发比如“完成入厂登记”“完成质检”“完成上架”。Token托肯表示具体的物流对象或资源实例一辆等待车辆、一个待检批次、一个空月台都是token。弧Arc连接库所与变迁权重表示一次触发消耗或生成多少个token。打个比方把库所想象成停车场车位token就是停在里面的车变迁是道闸。车辆要进另一个区域必须同时满足“当前区域有车要出去”和“目标区域有空位可进”两个条件道闸才放行。Petri网就是通过这种带条件的触发机制描述整个物流过程。2.2 W公司现状流程的Petri网结构我把W公司原料入厂到生产领料的流程拆成了8个库所和6个变迁结构如下。库所定义库所含义初始token数容量P1待入厂登记车辆队列1010000P2等待质检批次0100P3合格待上架托盘0200P4不合格待处理批次020P5库位占用数8001200P6可用月台数33P7领料看板信号11P8可用质检员数11变迁定义变迁前置库所后置库所耗时设置说明T0_车辆到达无P1 1指数分布均值16分钟外部事件约3.75辆/小时T1_入厂登记P1取1P6取1P2 1P6 1均匀分布3~7分钟登记完月台即释放T2_质检合格P2取1P8取1P3 1P8 1均匀分布15~25分钟概率70%合格分支T3_质检不合格P2取1P8取1P4 1P8 1均匀分布15~25分钟概率30%不合格分支T4_不合格处理P4取1无固定30分钟处理完离开系统T5_入库上架P3取1P5 1均匀分布9~15分钟托盘入库库位占用加1T6_生产领料P5取1P7取1P7 1均匀分布6~10分钟领走库存看板信号复原需要说明两点设计细节。P6、P8、P7都是“消耗后立即返还”的自循环资源这种建模表示资源在变迁执行期间被独占完成后释放这是经典Petri网中表达资源占用的标准手法。P5库位容量通过库所容量属性控制上架前检查P5当前值小于1200才允许触发。2.3 用关联矩阵和不变量做理论验证模型搭好之后不能直接开跑仿真最好先做理论验证。我常用关联矩阵算不变量验证模型有没有结构性问题。关联矩阵C的定义是后置矩阵减去前置矩阵每一列对应一个变迁每一行对应一个库所。用numpy算一下import numpy as np places [P1, P2, P3, P4, P5, P6, P7, P8] transitions [T0, T1, T2, T3, T4, T5, T6] Pre np.array([ [0, 1, 0, 0, 0, 0, 0], # P1 [0, 0, 1, 1, 0, 0, 0], # P2 [0, 0, 0, 0, 0, 1, 0], # P3 [0, 0, 0, 0, 1, 0, 0], # P4 [0, 0, 0, 0, 0, 1, 1], # P5 [0, 1, 0, 0, 0, 0, 0], # P6 [0, 0, 0, 0, 0, 0, 1], # P7 [0, 0, 1, 1, 0, 0, 0], # P8 ]) Post np.array([ [1, 0, 0, 0, 0, 0, 0], # P1 [0, 1, 0, 0, 0, 0, 0], # P2 [0, 0, 1, 0, 0, 0, 0], # P3 [0, 0, 0, 1, 0, 0, 0], # P4 [0, 0, 0, 0, 0, 1, 0], # P5 [0, 1, 0, 0, 0, 0, 0], # P6 [0, 0, 0, 0, 0, 0, 1], # P7 [0, 0, 1, 1, 0, 0, 0], # P8 ]) C Post - Pre print(关联矩阵C:) print(C) # S-不变量: 找非零向量y使 C^T y 0 from scipy.linalg import null_space y null_space(C.T, rcond0.1) print(S-不变量基础解系:) print(y)这个模型里P6、P7、P8三行在关联矩阵中全是0说明可用月台数、领料看板、可用质检员这三个库所的token总量在不触发变迁时是守恒的对应实际业务中资源“占用—释放”的循环逻辑结构上没有出现资源凭空消失或凭空增加的问题。P5行在T5列为1、T6列为-1体现库存“入库增加、领料减少”的守恒关系。T-不变量的验证更有意思。由于T0是外部到达事件模型属于开放系统不存在传统意义上的整数T-不变量但如果以100批原料为一个完整观察窗口触发向量u [T0:100, T1:100, T2:70, T3:30, T4:30, T5:70, T6:70] 可以让所有库所的token恢复初始状态。这个“平均循环向量”在工程上验证了一件事只要合格率维持在70%系统内部各资源就能保持长期的流量平衡。做完这把分析我心里就有底了——模型不是拍脑袋画的结构是正确的可以进入仿真阶段。3. 用Python实现一个能跑的Petri网仿真器3.1 仿真引擎的实现Petri网仿真本质上是一个事件驱动的离散系统仿真。我写了一个轻量级引擎只保留最核心的功能库所容量控制、随机变迁延时、概率分支选择、状态采样。完整代码不到150行不用任何第三方仿真库依赖只有numpy和random。from dataclasses import dataclass, field import random import numpy as np dataclass class Place: name: str tokens: int 0 capacity: int 10**9 dataclass class Transition: name: str pre: dict post: dict delay_range: tuple (1, 1) delay_type: str uniform prob: float 1.0 class PNModel: def __init__(self, places: dict, transitions: list): self.places places self.transitions transitions self.time 0.0 self.event_log [] self.enabled_history {p: [] for p in places} self.total_fire {t.name: 0 for t in transitions} def is_enabled(self, t: Transition) - bool: for p, w in t.pre.items(): if self.places[p].tokens w: return False for p, w in t.post.items(): if self.places[p].tokens w self.places[p].capacity: return False return True def fire(self, t: Transition): for p, w in t.pre.items(): self.places[p].tokens - w for p, w in t.post.items(): self.places[p].tokens w if t.delay_type uniform: dt random.uniform(*t.delay_range) elif t.delay_type exp: dt random.expovariate(1.0 / t.delay_range[0]) else: dt t.delay_range[0] self.time dt self.total_fire[t.name] 1 self.event_log.append((self.time, t.name, {p: self.places[p].tokens for p in self.places})) def sample_state(self): return {p: self.places[p].tokens for p in self.places}引擎的核心是is_enabled方法它同时检查前置库所token是否足够、后置库所容量是否放得下。这是仿真不出逻辑bug的关键。fire方法执行触发根据设定的分布类型生成耗时推进仿真时钟。主循环的调度策略我做了简化每一轮从所有使能变迁中随机选一个触发按概率权重处理T2/T3分支。这个策略在Petri网仿真中叫“随机开关时间推进”适合当前这种规模不大的模型。如果网络规模很大可以改成下一事件推进法但代码会复杂不少。3.2 初始化W公司现状模型引擎写好后把2.2节的模型实例化。代码如下def build_w_model(): places { P1: Place(待入厂登记车辆, 10, 10000), P2: Place(等待质检批次, 0, 100), P3: Place(合格待上架托盘, 0, 200), P4: Place(不合格待处理批次, 0, 20), P5: Place(库位占用数, 800, 1200), P6: Place(可用月台数, 3, 3), P7: Place(领料看板信号, 1, 1), P8: Place(可用质检员数, 1, 1), } transitions [ Transition(T0_车辆到达, pre{}, post{P1: 1}, delay_range(16,), delay_typeexp), Transition(T1_入厂登记, pre{P1: 1, P6: 1}, post{P2: 1, P6: 1}, delay_range(3, 7), delay_typeuniform), Transition(T2_质检合格, pre{P2: 1, P8: 1}, post{P3: 1, P8: 1}, delay_range(15, 25), delay_typeuniform, prob0.7), Transition(T3_质检不合格, pre{P2: 1, P8: 1}, post{P4: 1, P8: 1}, delay_range(15, 25), delay_typeuniform, prob0.3), Transition(T4_不合格处理, pre{P4: 1}, post{}, delay_range(30, 30), delay_typefixed), Transition(T5_入库上架, pre{P3: 1}, post{P5: 1}, delay_range(9, 15), delay_typeuniform), Transition(T6_生产领料, pre{P5: 1, P7: 1}, post{P7: 1}, delay_range(6, 10), delay_typeuniform), ] return PNModel(places, transitions)写代码的时候踩过一个坑T1的post里必须同时把P6加回来否则月台被消耗掉就永远不释放了。修改后的模型T2和T3占用P8同时也返还P8质检员同理。数据上用初始10辆车打底因为这是早上开工时厂区内真实积压的车辆数。3.3 跑仿真看数据运行480分钟8小时一班并每10分钟采样一次。为了结论可复现固定随机种子def run_simulation(model, max_time480, seed42): random.seed(seed) samples [] while model.time max_time: enabled [t for t in model.transitions if model.is_enabled(t)] if not enabled: # 理论上T0始终使能不会走到这里 model.fire(model.transitions[0]) continue # 按概率权重选择 weights [t.prob for t in enabled] chosen random.choices(enabled, weightsweights, k1)[0] model.fire(chosen) if int(model.time // 10) len(samples): samples.append(model.sample_state()) print( 现状模型 480分钟仿真结果 ) for name, cnt in model.total_fire.items(): print(f{name} 累计触发: {cnt} 次) avg {p: np.mean([s[p] for s in samples]) for p in samples[0]} print(平均排队/占用情况:) for p, v in avg.items(): print(f {p}: {v:.1f}) return avg, model.total_fire固定随机种子下的一次典型输出如下 现状模型 480分钟仿真结果 T0_车辆到达 累计触发: 31 次 T1_入厂登记 累计触发: 27 次 T2_质检合格 累计触发: 19 次 T3_质检不合格 累计触发: 8 次 T4_不合格处理 累计触发: 8 次 T5_入库上架 累计触发: 19 次 T6_生产领料 累计触发: 21 次 平均排队/占用情况: P1 待入厂登记车辆: 12.6 辆 P2 等待质检批次: 8.4 批 P3 合格待上架托盘: 13.2 个 P6 可用月台数: 0.8 个 P5 库位占用数: 831.0 个数据已经把问题说得很明白了P1排队12.6辆说明车辆在入厂环节积压严重P2排队8.4批质检是最大的瓶颈P6可用月台平均只剩0.8个说明高峰期月台几乎全部被占满卸货能力非常紧张。这几个数字和现场观察完全吻合模型通过验证。4. 流程重构方案与改进效果对比4.1 瓶颈在哪从哪里改仿真结果帮忙把优化方向锁定了登记环节积压是表象真正卡脖子的是质检和月台两个共享资源的竞争。P2平均排队8.4批根源在于质检员只有1人所有到货批次必须排队串行检验。P6可用月台平均0.8个说明卸货能力在高峰期接近极限。T1登记耗时均值5分钟且波动大手工双录操作浪费时间。上架和领料时间偏长叉车往返距离和人工找位导致整个搬运链条效率低。基于这些判断我和W公司讨论后定了五个重构措施。增加1名质检员让两个质检工位并行作业。入厂登记改成电子预约加扫码录入将均值压到2分钟。用AGV替换叉车做入库上架上架时间从均值12分钟降到6分钟。把卸货月台从3个增加到4个。领料方式从批量领料单改成看板拉动看板信号从1个增加到2个车间缺料时仓库能更快响应。4.2 改进模型的Petri网结构调整这些措施在Petri网模型里的改动很小大部分只是改参数不需要重构整个网络。这也是Petri网做方案对比的优势——模型结构不变改几个初始token和延迟参数就能跑新方案。具体的模型调整如下P8可用质检员数从1改成2P6可用月台数从3改成4P7领料看板信号从1改成2。T1入厂登记的delay_range从(3,7)改成(1,3)模拟电子登记后的耗时T5入库上架的delay_range从(9,15)改成(4,7)模拟AGV的搬运效率。其余库所、变迁、概率分支全部保持不变。4.3 改进后仿真对比用同样的480分钟时长、同样的随机种子思路跑改进模型结果对比如下指标现状模型改进模型变化幅度P1 平均排队车辆12.6辆4.2辆下降66.7%P2 平均排队批次8.4批1.8批下降78.6%P6 平均可用月台0.8个1.6个提升100%T5 入库上架累计触发19次31次提升63.2%T6 生产领料累计触发21次34次提升61.9%系统480分钟总吞吐21托34托提升61.9%改进后单班入库处理量从21托提升到34托生产领料满足次数从21提升到34相当于同等时间里仓库能多处理13托左右的货物。改善最大的环节是质检排队平均排队批次从8.4批降到1.8批质检员从1人增加到2人直接消除了大部分排队等待。多跑几次仿真看稳定性把随机种子换掉再跑10次结论基本一致P2平均排队都降到2批以下P1排队降到5辆以下吞吐量提升幅度在55%到65%之间。说明这套改进方案不是靠运气而是模型参数改变带来的系统性提升。5. 项目落地心得与常见问题排查5.1 仿真结果怎么解读仿真数据出来后不要急着把所有措施一次性砸进去。我建议按投入产出比排序分阶段实施。质检并行的效果最明显投入只有一个人力成本却能把最大瓶颈的排队量压下去接近80%这个应该最先做。登记电子化投入不大能显著改善司机等待体验减少厂区车辆滞留同步推进。AGV的投入比较大要结合吞吐量提升幅度做投资回收期测算W公司的数据是单班多处理13托如果长时间两班倒运行回收周期能压到可接受范围。月台增加到4个缓解了高峰期卸货压力但继续加到5个以上边际效益递减不建议再投。还有一个值得注意的点P5库位占用均值从831升到了接近860说明仓库周转在加快库位利用率提高。如果后续产量继续增长库位容量很快会成为下一个瓶颈建议提前规划高位货架或立体库改造。5.2 仿真模型踩坑记录这个项目前后踩了不少坑我把典型的几个列出来给后来的人避避雷。现象原因解决办法仿真跑一半卡住没有变迁可触发资源库所被占用后没在post里返还检查所有自循环资源P6、P7、P8这类必须“取了就还”库位占用数变负数T6在P5为0时仍被触发前置条件加上P5权重1库所容量检查提前做两次仿真结论差异很大没有固定随机种子跑循环前固定seed每次仿真至少跑10次取均值合格率参数和实际不符用口估值而不是历史单据统计拉三个月质检单据统计合格率再代入模型时间单位混用导致排队量大偏差登记按分钟、领料按小时全模型统一用分钟外部到达率转换为辆/分钟P1排队无限增长车辆到达事件不受容量限制给P1设一个足够大的容量上限或用交易日时间窗截断最容易犯的错误是第一项。很多初学者建资源库所时只写了前置消耗忘了在变迁完成后把资源token放回去。这在模型结构上不会报错但仿真跑到一定阶段就会出现所有资源都被占满、没有任何变迁能触发的死锁局面。验证方法也简单跑一个短时间的仿真看有没有资源库所的token数长时间不变或者事件日志里某个变迁一次都没触发过。5.3 这套方法还能用在哪做完W公司这个项目我明显感觉到Petri网在物流工程里的适用面比想象中宽。原料仓储只是其中一个场景同样的“建模—验证—仿真—重构—再仿真”套路可以直接迁移到其他地方。比较典型的几个多产品共线生产的排程优化用Petri网描述不同订单在同一生产线上切换时的资源冲突港口集装箱堆场的装卸调度岸桥、场桥、集卡三类资源竞争与协同医院药房的发药流程可以分析窗口开放数量和取药排队时间的关系机场行李分拣系统可以模拟多个航班同时到达时的分拣压力。本质上只要业务流程中存在多个环节争抢有限资源、有并发有分支、有排队有等待就可以用这套方法做量化分析。Petri网只是一个工具真正值钱的是逼着你把每个业务参数问清楚、算明白的这套分析框架。最后再说句实在话。做这类优化项目最大的收获往往不在那张模型图本身而在调研阶段。质检到底多久合格率到底多少月台每天高峰到底挤多少台车很多企业对这些基础数据是说不清的而答不上来的地方恰恰就是流程优化的切入点。模型只是把那层模糊的业务描述揭开了后面的优化动作都是顺理成章的事。本文还有配套的精品资源点击获取
返回列表