news 2026/9/6 18:39:44

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

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
用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待入厂登记车辆队列1010000
P2等待质检批次0100
P3合格待上架托盘0200
P4不合格待处理批次020
P5库位占用数8001200
P6可用月台数33
P7领料看板信号11
P8可用质检员数11

变迁定义:

变迁前置库所后置库所耗时设置说明
T0_车辆到达P1 +1指数分布,均值16分钟外部事件,约3.75辆/小时
T1_入厂登记P1取1,P6取1P2 +1,P6 +1均匀分布,3~7分钟登记完月台即释放
T2_质检合格P2取1,P8取1P3 +1,P8 +1均匀分布,15~25分钟,概率70%合格分支
T3_质检不合格P2取1,P8取1P4 +1,P8 +1均匀分布,15~25分钟,概率30%不合格分支
T4_不合格处理P4取1固定30分钟处理完离开系统
T5_入库上架P3取1P5 +1均匀分布,9~15分钟托盘入库,库位占用加1
T6_生产领料P5取1,P7取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, rcond=0.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_type="exp"), Transition("T1_入厂登记", pre={"P1": 1, "P6": 1}, post={"P2": 1, "P6": 1}, delay_range=(3, 7), delay_type="uniform"), Transition("T2_质检合格", pre={"P2": 1, "P8": 1}, post={"P3": 1, "P8": 1}, delay_range=(15, 25), delay_type="uniform", prob=0.7), Transition("T3_质检不合格", pre={"P2": 1, "P8": 1}, post={"P4": 1, "P8": 1}, delay_range=(15, 25), delay_type="uniform", prob=0.3), Transition("T4_不合格处理", pre={"P4": 1}, post={}, delay_range=(30, 30), delay_type="fixed"), Transition("T5_入库上架", pre={"P3": 1}, post={"P5": 1}, delay_range=(9, 15), delay_type="uniform"), Transition("T6_生产领料", pre={"P5": 1, "P7": 1}, post={"P7": 1}, delay_range=(6, 10), delay_type="uniform"), ] return PNModel(places, transitions)

写代码的时候踩过一个坑:T1的post里必须同时把P6加回来,否则月台被消耗掉就永远不释放了。修改后的模型T2和T3占用P8同时也返还P8,质检员同理。数据上用初始10辆车打底,因为这是早上开工时厂区内真实积压的车辆数。

3.3 跑仿真,看数据

运行480分钟(8小时一班)并每10分钟采样一次。为了结论可复现,固定随机种子:

def run_simulation(model, max_time=480, seed=42): 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, weights=weights, k=1)[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改成2,P6可用月台数从3改成4,P7领料看板信号从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网只是一个工具,真正值钱的是逼着你把每个业务参数问清楚、算明白的这套分析框架。

最后再说句实在话。做这类优化项目,最大的收获往往不在那张模型图本身,而在调研阶段。质检到底多久?合格率到底多少?月台每天高峰到底挤多少台车?很多企业对这些基础数据是说不清的,而答不上来的地方,恰恰就是流程优化的切入点。模型只是把那层模糊的业务描述揭开了,后面的优化动作都是顺理成章的事。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/6 18:37:25

Cap:三步出片的免费开源跨平台录屏工具

Cap&#xff1a;三步出片的免费开源跨平台录屏工具 【免费下载链接】Cap Open source Loom alternative. Beautiful, shareable screen recordings. 项目地址: https://gitcode.com/GitHub_Trending/cap1/Cap 要把一段屏幕操作演示发给同事&#xff0c;很多人的流程是&a…

作者头像 李华
网站建设 2026/9/6 18:35:24

Oracle数据库存储双活配置实战:从概念到落地

简介&#xff1a;Oracle存储双活配置实战指南&#xff0c;为构建跨数据中心高可用架构的DBA和系统架构师提供一套可落地的双活存储解决方案。文档从传统RAC依赖共享存储导致单点故障、ADG仅提供数据级容灾无法实时接管应用的局限性切入&#xff0c;阐明双活存储方案的适用场景。…

作者头像 李华
网站建设 2026/9/6 18:31:21

量子芯片低温控制系统功耗优化:从稀释制冷机到系统协同设计

简介&#xff1a;一份系统讲解量子芯片低温控制与集成制冷平台功耗优化的技术文档&#xff0c;面向量子计算硬件工程师、低温系统设计人员及相关专业研究生。资源为单个PDF文件&#xff0c;压缩包约13.19MB&#xff0c;共464页、51个章节&#xff0c;支持目录跳转与书签大纲定位…

作者头像 李华
网站建设 2026/9/6 18:29:26

ABS钢质船舶规范实战指南:从卷册检索到审图检验

简介&#xff1a;《ABS钢质船舶建造与分类规则&#xff08;内河及近岸水道航行服务&#xff09;》2023年版是一份权威规范文件&#xff0c;面向船厂、船舶设计公司、航运公司及检验人员&#xff0c;适用于内河与沿海水域钢质船舶的设计、建造和运营合规。资源以单个PDF文件形式…

作者头像 李华