简介:这份资源面向工业工程、物流管理及智能制造方向的研究生与企业流程改进人员,围绕W公司合肥工厂原料仓储物流场景,提供基于Petri网的入厂物流与厂内物流流程建模及优化方法。包内为1个PDF文件,约977KB,完整呈现论文的建模思路、关联矩阵计算过程与仿真验证结果,并附有可运行的Matlab复现代码及逐段中文解释,便于对照学习Petri网结构定义、关联矩阵分析、S不变量与T不变量求解以及离散事件仿真流程。读者可借此掌握从流程调研、模型构建、有界性与活性验证,到关联矩阵重组算法识别瓶颈与冗余环节,再到优化前后变迁数量与单箱货物平均处理耗时对比的完整闭环方法。目前已有42人学习,适合需要将Petri网理论落地到仓储物流降本增效场景、并希望获得可复现代码参考的技术人员。
1. 从一张画满圆圈和箭头的图说起:这套 Petri 网仓储优化资料到底能干什么
如果你在物流工程或生产运作管理方向做过课题,大概率见过这样的场景:导师甩过来一句“把 W 公司原料仓储流程优化一下”,你打开 Visio 画了张流程图,交上去被批“太静态,看不出瓶颈在哪”。问题就出在——流程图只能描述“谁接谁”,描述不了“资源被占用了多久、哪里会堵、堵了之后系统还能不能跑”。Petri 网恰好补上这一刀:它把流程里的每个环节变成“库所(place)”,把每个动作变成“变迁(transition)”,用托肯(token)的流动模拟物料和资源的流转,于是“堵不堵、堵多久、加一个人能不能缓解”就变成了可以算的东西。
这份资料围绕 W 公司的入厂物流与厂内原料仓储业务,用 Petri 网建模 + 关联矩阵分析 + Matlab 仿真,把一条原本靠经验拍脑袋的流程,拆成了可量化、可复现的模型。它适合三类人:正在做物流/工业工程课程设计的学生、需要给仓储流程做量化论证的从业者、以及想找一个完整 Petri 网实战案例(而不是只有定义)的建模初学者。下面我按“模型怎么建 → 矩阵怎么算 → 仿真怎么跑 → 坑在哪”的顺序,把这份资料拆开讲透。
2. Petri 网建模:把 W 公司原料仓储流程翻译成库所与变迁
2.1 为什么选 Petri 网而不是流程图或排队论
先说选型理由,这决定了后面所有工作的边界。流程图是静态的,画完只能看结构,无法回答“同时到两车原料时先处理谁”;排队论擅长算等待时间,但要求到达和服务服从特定分布,而仓储流程里的“叉车调度、质检返工、库位占用”往往是离散、并发、带冲突的,硬套排队论模型会失真。Petri 网的优势在于它天生描述并发与冲突:两个变迁共享一个输入库所时,谁先触发取决于托肯分布,这正好对应“一台叉车被两个任务争抢”的现实。
常见做法是先用“库所-变迁”把流程骨架搭出来,再逐步细化。W 公司的原料仓储主流程大致是:原料到厂 → 卸货 → 质检 → 合格入库/不合格退货 → 上架存储 → 按工单出库 → 配送至产线。每一步的“状态”是库所,“动作”是变迁。这里有个容易翻车的地方:很多人一上来就把所有细节都塞进去,结果库所几十个,关联矩阵大到没法看。我的经验是先建“粗模型”验证逻辑通不通,再针对瓶颈环节做“精化”,也就是把某个变迁拆成子网。
2.2 从业务流程到 Petri 网元素的映射规则
把业务语言翻译成 Petri 网语言,需要一套稳定的映射规则,否则不同人建出来的模型没法对比。我一般按下面这张表来对应:
| 业务对象 | Petri 网元素 | 说明 |
|---|---|---|
| 原料、托盘、工单 | 托肯 token | 流动的“东西”,数量代表资源数 |
| 流程状态/缓冲区 | 库所 place | 圆圈,如“待质检区”“合格品区” |
| 处理动作 | 变迁 transition | 方框,如“卸货”“质检” |
| 资源约束 | 库所容量/弧权 | 限制同时处理的数量 |
| 流程走向 | 有向弧 arc | 连接库所与变迁 |
按这个规则,W 公司入厂物流的核心子网可以这样描述:库所 P1 表示“待卸原料”,变迁 T1 表示“卸货”,P2 表示“待质检”,T2 表示“质检”,P3 表示“合格品暂存”,T3 表示“上架”,P4 表示“库位占用”。不合格品则从 T2 分出一条弧到 P5“退货区”。这套映射的好处是,任何一个业务改动(比如增加一个质检工位)都能直接反映为库所或变迁的增减,而不是重画整张图。
2.3 用 Matlab 搭出可运行的模型骨架
理论讲完必须落到能跑的东西上。这份资料用 Matlab 实现 Petri 网,核心是关联矩阵和状态演化。下面是一段可直接抄的骨架代码,先定义库所数、变迁数,再构造输入输出矩阵:
% Petri网骨架:W公司原料仓储入厂流程 % 库所:P1待卸 P2待检 P3合格暂存 P4库位 P5退货 % 变迁:T1卸货 T2质检 T3上架 T4退货处理 nP = 5; nT = 4; % 输入矩阵 I:I(i,j)=1 表示库所i是变迁j的输入 I = zeros(nP, nT); I(1,1) = 1; % P1 -> T1 I(2,2) = 1; % P2 -> T2 I(3,3) = 1; % P3 -> T3 I(5,4) = 1; % P5 -> T4 % 输出矩阵 O:O(i,j)=1 表示库所i是变迁j的输出 O = zeros(nP, nT); O(2,1) = 1; % T1 -> P2 O(3,2) = 1; % T2 -> P3 (合格) O(5,2) = 1; % T2 -> P5 (不合格) O(4,3) = 1; % T3 -> P4 O(1,4) = 1; % T4 -> P1 (退货后重新到厂,简化处理) % 关联矩阵 C = O - I C = O - I; disp('关联矩阵 C ='); disp(C);这段代码的逻辑说明:输入矩阵 I 描述“哪个库所的托肯被消耗”,输出矩阵 O 描述“哪个库所获得托肯”,两者相减得到关联矩阵 C,它是后续分析的核心。参数说明:nP 和 nT 必须和实际流程一致,I 和 O 中每个 1 的位置都对应一条有向弧,写错一个位置整个模型就偏了。运行后你会看到一个 5×4 的矩阵,正数表示该变迁产生托肯,负数表示消耗托肯。这一步跑通,说明模型骨架立住了,接下来才能谈“它会不会死锁、有没有冲突”。
3. 关联矩阵分析:从矩阵里读出死锁、冲突与瓶颈
3.1 关联矩阵到底能看出什么
很多人建完模型就急着跑仿真,其实关联矩阵本身就能暴露大量问题。C 矩阵的每一列对应一个变迁,列中 -1 表示消耗、+1 表示产生。如果某一列全是非负(没有 -1),说明这个变迁不需要任何输入就能触发,现实中就是“凭空产生物料”,模型错了。如果某一行在所有列中都没有 +1,说明这个库所永远得不到托肯,是个死库所。这些检查不需要跑仿真,看矩阵就能发现。
更关键的是,C 矩阵的秩和零空间能帮我们找“守恒关系”。比如原料总量在“待卸+待检+合格+库位”之间流转,理论上总量守恒,这对应 C 的某个线性组合为零。如果算出来不守恒,说明建模时漏了某个环节或者多加了弧。W 公司案例里,质检环节有合格和不合格两条输出,守恒关系就要分两条链分别验证,这也是这份资料比一般示例细致的地方。
3.2 用矩阵判断冲突与并发
冲突和并发是 Petri 网分析的核心。判断方法很直接:如果两个变迁共享同一个输入库所,且该库所托肯数不足以同时满足两者,就存在冲突。在 W 公司场景里,“质检合格上架”和“质检不合格退货”共享质检后的托肯,但一个托肯只能走一条路,这就是典型的冲突结构。并发则是两个变迁各自有独立输入,可以同时触发,比如“卸货”和“另一批原料的质检”在资源充足时可以并行。
下面这段代码用来检测冲突结构,思路是遍历所有变迁对,看它们是否有共同输入库所:
% 冲突检测:找出共享输入库所的变迁对 conflicts = []; for j1 = 1:nT for j2 = j1+1:nT shared = find(I(:,j1) & I(:,j2)); % 共同输入库所 if ~isempty(shared) conflicts = [conflicts; j1, j2, shared']; fprintf('变迁T%d与T%d存在冲突,共享库所:%s\n', ... j1, j2, num2str(shared')); end end end if isempty(conflicts) disp('未检测到冲突结构'); end逻辑说明:I(:,j1) & I(:,j2)做逐元素逻辑与,非零位置就是共同输入库所。参数说明:这段代码假设弧权为 1,如果实际有权重,需要改成比较权重之和与库所托肯数。跑出来如果发现冲突对很多,说明流程里资源争抢严重,这正是优化的切入点——要么增加资源(加叉车、加质检工位),要么调整流程顺序减少争抢。
3.3 从矩阵到瓶颈定位的完整链路
光检测冲突还不够,要定位瓶颈得结合托肯数和变迁触发频率。W 公司案例的做法是:先给初始托肯分布(比如待卸区 3 个托肯代表 3 车原料),然后模拟触发,统计每个变迁被触发的次数和每个库所的托肯积压量。积压最多的库所就是瓶颈所在。常见做法是用 Matlab 写一个简单的触发循环,每次随机选一个可触发的变迁执行,跑几千步后看统计。
这里有个血泪经验:初始托肯分布不能随便设。如果待卸区只给 1 个托肯,整个系统可能一直串行执行,看不出并发瓶颈;给太多又可能瞬间淹没下游。合理的做法是参考 W 公司实际日均到货量,折算成托肯数。资料里给的是按班次到货 3~5 车来设,这个量级下冲突和积压都能显现出来,又不至于让模型跑飞。
4. Matlab 仿真:让托肯流动起来,看效率提升到底有多少
4.1 仿真主循环怎么写
仿真部分是把前面建好的模型“跑起来”。核心是一个 while 循环:每轮找出所有可触发的变迁(输入库所托肯数足够),随机或按优先级选一个执行,更新托肯分布,记录数据。下面是可以直接用的主循环框架:
% 托肯演化仿真主循环 M = [3;0;0;0;0]; % 初始托肯:3车待卸,其余为0 maxSteps = 5000; history = zeros(nP, maxSteps); triggerCount = zeros(1, nT); for step = 1:maxSteps history(:,step) = M; enabled = []; % 当前可触发的变迁 for j = 1:nT if all(M >= I(:,j)) % 输入库所托肯足够 enabled = [enabled, j]; end end if isempty(enabled) break; % 无变迁可触发,系统停滞 end j = enabled(randi(length(enabled))); % 随机选一个触发 M = M + C(:,j); % 更新托肯分布 triggerCount(j) = triggerCount(j) + 1; end fprintf('各变迁触发次数:T1=%d T2=%d T3=%d T4=%d\n', triggerCount);逻辑说明:M >= I(:,j)判断变迁 j 的所有输入库所是否都有足够托肯,这是 Petri 网触发规则的标准实现。M = M + C(:,j)用关联矩阵一次性完成消耗和产生,比手动加减更不容易出错。参数说明:maxSteps 控制仿真长度,太小统计不稳,太浪费机时,5000 步对这个小模型足够;初始托肯 M 要根据实际到货量调整。跑完后 triggerCount 告诉你哪个变迁最忙,history 可以画成托肯变化曲线。
4.2 效率提升怎么量化
仿真跑完,要回答“优化后到底提升了多少”。W 公司案例用的是两个指标:一是单位时间吞吐量(总触发次数/仿真步数),二是平均在制品数量(各库所托肯数的时间平均)。优化前,质检环节是单工位,冲突严重,在制品积压多;优化方案是增加一个质检变迁(相当于加一个质检员),模型里表现为 T2 拆成 T2a 和 T2b 并行。改完后重跑仿真,吞吐量提升、积压在制品下降,这就是量化论证。
这里要注意:仿真结果是随机的,单次跑不能下结论。常见做法是跑 20 次取平均,或者把随机种子固定下来做对比。资料里用的是固定种子 + 多次平均,这样优化前后的差异才可信。如果你只跑一次就写“效率提升 30%”,答辩时很容易被问住。
4.3 把仿真结果画成能放进报告的图
数据出来还得会表达。Matlab 画托肯变化曲线很直接,下面这段代码把 history 画成堆叠面积图,一眼能看出哪个库所积压:
% 绘制托肯分布变化图 figure; area(history'); xlabel('仿真步数'); ylabel('托肯数'); legend('P1待卸','P2待检','P3合格暂存','P4库位','P5退货'); title('W公司原料仓储流程托肯演化'); grid on;逻辑说明:history'转置是因为 area 函数按列画堆叠,转置后每列是一个库所的时间序列。参数说明:legend 的标签要和库所定义一一对应,别写错。这张图放进报告里,比一堆数字直观得多,评审一眼就能看到“待检区”那条带子最宽,瓶颈在哪不言自明。
5. 避坑与排查:建模和仿真里最容易翻车的五件事
5.1 现象:仿真跑几步就停了,提示无变迁可触发
原因:多半是某个库所初始托肯为 0,而它又是下游变迁的唯一输入,导致整条链断掉。比如 P2 待检区初始为 0,T2 质检永远触发不了。解决:检查初始托肯分布,确保每条流程链的起点都有托肯;或者确认是否有“凭空产生”的遗漏弧。我一般会在仿真前先打印一遍 I 和 O 矩阵,肉眼过一遍每个变迁的输入输出是否合理。
5.2 现象:关联矩阵算出来某列全为正,变迁不需要输入
原因:输出矩阵 O 写多了,或者输入矩阵 I 漏写了。常见于复制粘贴时改错下标。解决:逐个变迁核对业务逻辑——卸货必须有待卸原料,质检必须有待检品,没有输入的变迁在现实中就是“无中生有”。把 I 和 O 并排打印出来对照,比盯着代码找快得多。
5.3 现象:优化后吞吐量反而下降
原因:加了一个并行变迁,但共享资源没增加,冲突转移到了别处。比如加了质检工位,但上架叉车没加,积压从待检区挪到了合格暂存区。解决:优化要看全链路,不能只盯一个环节。用仿真跑优化前后各库所的托肯曲线,看积压是不是真的消失了,还是只是换了个地方。这也是 Petri 网仿真的价值——它能把“按下葫芦浮起瓢”可视化。
5.4 现象:仿真结果每次都不一样,没法对比
原因:用了随机触发且没固定种子,或者仿真步数太少统计不稳。解决:在代码开头加rng(42)固定随机种子,对比实验用同一个种子;仿真步数至少几千步,让统计量收敛。如果要做严谨对比,跑 20 次不同种子取均值,报告里写“均值±标准差”,比单次结果可信得多。
5.5 现象:模型库所太多,关联矩阵大到看不懂
原因:一开始就追求细节,把每个工位、每个缓冲区都建成独立库所。解决:分层建模。先建粗模型验证主流程逻辑,再对瓶颈环节单独精化。W 公司案例里,入厂物流和厂内仓储是分开建子网再拼接的,这样每个子网规模可控,出了问题也容易定位。记住一句话:模型是拿来算的,不是拿来炫的,能说明问题的最小模型就是最好的模型。
6. 进阶技巧:用不变量验证模型正确性,以及一个我常犯的错
前面讲的都是“怎么跑”,这一章讲“怎么确认跑得对”。Petri 网有个很强的工具叫不变量分析:P-不变量(库所不变量)能告诉你哪些库所的托肯总数守恒,T-不变量(变迁不变量)能告诉你哪些变迁必须成组触发才能回到初始状态。对于 W 公司这种物料守恒的系统,P-不变量是天然的校验器——原料总量在“待卸+待检+合格+库位+退货”之间应该守恒(退货相当于退出主链),如果算出来的不变量不满足这个关系,模型一定有错。
计算 P-不变量就是求关联矩阵 C 的零空间(左零空间),即解方程 ( y^T C = 0 )。Matlab 里用null(C', 'r')可以求有理基。下面这段代码演示怎么求并解读:
% 求P-不变量(左零空间) Pinv = null(C', 'r'); disp('P-不变量基:'); disp(Pinv); % 验证:不变量与初始托肯的点积应守恒 for k = 1:size(Pinv,2) val = Pinv(:,k)' * M; fprintf('第%d个不变量值:%d\n', k, val); end逻辑说明:null(C','r')求的是 C 转置的零空间,结果每一列是一个 P-不变量。参数说明:'r'表示求有理数基,避免浮点误差。解读时,如果某个不变量的非零分量正好对应“待卸、待检、合格、库位”四个库所且系数都为 1,说明这四个库所的托肯总数守恒,符合物料守恒预期。如果算出来的不变量系数出现负数或分数,说明模型里有弧的方向或权重写反了。
T-不变量则用来检查“有没有循环触发导致系统空转”。求法是解 ( C x = 0 ),Matlab 里null(C, 'r')。如果存在全正整数的 T-不变量,说明系统可以回到初始状态,是个良性循环;如果只有全零解,说明流程是单向的,跑完就停。W 公司案例里,退货后重新到厂那条弧(T4 → P1)会形成一个 T-不变量,这代表退货返厂循环,现实中要确认这个循环是否合理——如果退货直接退出系统,这条弧就不该存在。
说个我自己的教训。刚开始做这类建模时,我总觉得不变量分析是“理论花架子”,跑通仿真就行了。结果有一次答辩,评委问“你怎么证明你的模型没有死锁”,我答不上来,因为仿真没停不代表没有潜在死锁,只是初始托肯恰好绕过了。从那以后,我每次建完 Petri 网模型,都强制走一遍“关联矩阵 → P-不变量 → T-不变量 → 仿真”四步,前两步花不了十分钟,但能挡掉大部分逻辑错误。这份 W 公司的资料之所以值得细看,就是因为它把这四步都走全了,而不是只贴一段仿真代码了事。希望这套拆解能帮到你,少走我当年走过的弯路。
本文还有配套的精品资源,点击获取