
简介一份围绕铁路整车货运流程优化的研究资源面向物流管理专业人员、铁路运输从业者及物流优化研究者。内容针对发送作业流程冗长、职能部门繁多、到达作业服务不规范等现实问题基于Petri网完成发送与到达流程建模使用Flexsim仿真定位瓶颈环节并提出简化流程、提升效率、降低成本的具体优化方案同时通过二次仿真验证效果。资源包内为单个PDF文档体积约871KB文档内附有Petri网建模的Python代码及详细解释涵盖库所、变迁、弧和标记等核心类实现以及流程模型构建与可视化方法同时展示Flexsim仿真分析思路和Python辅助数据处理技术便于复现研究并迁移到其他流程优化场景。已有87人学习适合希望深入掌握铁路货运流程优化方法并借鉴仿真建模技术的专业人士。1. 整车货运流程优化为什么非用 Petri 网和 Flexsim 仿真不可铁路货运站的整车作业优化最让人头疼的不是方案不够多而是不知道瓶颈到底在哪。日办理辆数卡在某个数上不去到发线经常排满调车机在站内来回跑却还是让车列等待装卸机械反而有时闲着。光画流程图看不出问题靠经验拍板加设备、加线路往往是刚把 A 点的堵塞疏通B 点立刻变成新瓶颈钱花了效果却不明显。这个标题拆开就是一套标准打法先用 Petri 网把整车货运流程建造成可计算的逻辑模型从结构上找出死锁和资源瓶颈再用 Flexsim 把模型放进动态时钟里跑仿真用吞吐量、利用率和等待时间验证改进方案。两个工具一个管逻辑、一个管动态配合起来才能回答「瓶颈在哪、改了到底有没有效」。这套组合适合场站运营、物流系统仿真和流程规划方向的从业者照着复现也适合用来给方案做定量论证。2. 用 Petri 网把整车货运流程变成可分析模型库所、变迁与托肯的真实映射2.1 为什么不用流程图Petri 网对资源竞争的三个优势流程图最大的问题是单一线程。画「列车到达→占用到发线→调车机取送→货位装卸→取走车辆」逻辑上没错但表达不了「到发线只有 3 条、调车机只有 1 台、货位只有 2 个」这种资源竞争。两个车列同时申请同一条到发线流程图里画不出排队和互斥调车机既要送车到货位又要取走卸完的车流程图里表达不了这种共享资源的抢占。这也是为什么现场人员画的作业流程图看起来很顺但一按图核对能力总是对不上的原因。Petri 网用五要素把流程变成形式化模型库所Place表示状态或资源变迁Transition表示动作或事件弧Arc连接库所和变迁权值Weight表示一次动作消耗或产生多少资源初始标识M0表示系统初始状态。托肯Token在库所里的分布就是系统当前状态。相比流程图Petri 网有三个直接对住铁路货运场景的优势。第一并发是显式的两个互不冲突的变迁可以同时使能比如两列车在不同货位同时卸车模型里能直接表达。第二资源守恒看得见库所容量和托肯数量直接对应到发线条数、货位个数和设备台数不会画着画着就把资源画没了。第三模型可做数学分析关联矩阵、不变量、可达图都能算不用等仿真跑完就知道结构上有没有死锁。2.2 整车货运作业的映射规则一个卸车场景的完整建模常见做法是把「到发线空闲」「车列待取送」「调车机空闲」「车辆在货位」「装卸机械空闲」这类状态建模成库所把「列车到达」「取送作业」「开始装卸」「装卸完成取走」建模成变迁。以「一列车到达、分解、卸车、取走」的完整流程为例最小模型可以定义成下面的样子。库所和变迁的对应含义用表列清楚后续写 Python 分析代码和搭 Flexsim 模型都要拿这张表当锚点。元素类型含义初始托肯P1 到发线空闲库所空闲到发线数量3P2 车列待取送库所停在到发线等待调车作业的车列0P3 调车机空闲库所调车机可用状态1P4 车辆在货位库所已送达货位待装卸的车辆0P5 装卸机械空闲库所装卸机械可用状态2P6 卸车完成待取库所完成卸车等待取走的车辆0T1 列车到达占用到发线变迁列车到达并占用一条到发线–T2 调车机取送变迁将车列从到发线送到货位–T3 开始装卸变迁车辆在货位上进行卸车作业–T4 卸车完成取走变迁卸完车辆被取走释放货位和装卸机械–初始标识 M0 (3, 0, 1, 0, 2, 0)。T1 使能条件是 P1 有托肯且外部有到达请求T2 使能条件是 P2 有托肯且 P3 有托肯T3 使能条件是 P4 有托肯且 P5 有托肯T4 使能条件是 P6 有托肯。T2 发生后P2 减 1、P3 减 1调车机被占用、P4 加 1、P1 加 1到发线释放这套增减关系就是下一章关联矩阵的来源。这里有个值得强调的细节到发线的释放时机。常见做法是车列被调车机取走的瞬间就释放到发线而不是等货位卸完再释放。很多人第一次建模时把「到发线腾空」和「货位作业」强绑在一起结果死锁率高得离谱跟现场完全对不上。实际生产中车列取走后到发线立刻能接下一列车这是货运站提高日办理辆数的核心逻辑映射时务必体现。2.3 死锁和活性静态就能看出的瓶颈Petri 网的实用价值在于不用跑仿真就能预判死锁。以这个模型为例如果 T1 连续发生 3 次P1 的 3 个托肯被占用再来的列车进不了到发线如果此时 P2 有车列等待、P3 调车机被占、P4 货位全满系统就卡死了——没有任何变迁可以发生。这种状态在 Petri 网里叫死锁标识。判断死锁的标准方法是画可达图枚举所有从 M0 能到达的标识找出没有后继状态的标识节点。但状态数会随托肯数量指数增长现场模型几十个库所、上百个托肯枚举根本走不完。工程上更常用的是不变量分析找一组库所的加权和在任何可达状态下都守恒。比如「P1 P2 P4 P6 3」如果成立说明车列要么在到发线、要么等待取送、要么在货位、要么卸完待取系统里同时在线的车列总数被限制为 3。货位容量只有 2剩下 1 必须堵在到发线——这个守恒式本身就是瓶颈证据。下一章的代码会把计算落下来。3. 用 Python 算出 Petri 网结构瓶颈关联矩阵、不变量与死锁检测代码3.1 构建关联矩阵从形式化定义到代码的样板关联矩阵把库所和变迁之间的弧权值写成矩阵行对应库所列对应变迁。正数表示变迁发生后该库所净增加的托肯数负数表示净减少。拿上一章的最小模型代码可以这样构建。import numpy as np # 库所顺序: P1到发线空闲, P2车列待取送, P3调车机空闲, P4车辆在货位, P5装卸机械空闲, P6卸车完成待取 # 变迁顺序: T1到达占用, T2调车取送, T3开始装卸, T4卸完取走 places [P1_到发线空闲, P2_车列待取送, P3_调车机空闲, P4_车辆在货位, P5_装卸机械空闲, P6_卸车完成待取] transitions [T1_到达占用, T2_调车取送, T3_开始装卸, T4_卸完取走] # 关联矩阵 C: 行库所, 列变迁; 负数为消耗, 正数为产生 C np.array([ [-1, 1, 0, 0], # P1: T1消耗1个空闲到发线, T2释放1个 [ 1, -1, 0, 0], # P2: T1产生1个待取送车列, T2消耗1个 [ 0, -1, 1, 0], # P3: T2占用调车机, T3释放 [ 0, 1, -1, 0], # P4: T2送入1个车辆到货位, T3消耗 [ 0, 0, -1, 1], # P5: T3占用装卸机械, T4释放 [ 0, 0, 1, -1], # P6: T3产生卸完待取, T4取走 ], dtypeint) M0 np.array([3, 0, 1, 0, 2, 0]) # 初始标识 print(关联矩阵形状:, C.shape) print(库所-变迁对应关系:) for i, p in enumerate(places): row .join(f{v:3d} for v in C[i]) print(f {p:20s} [{row}])这段代码的关键是符号约定。Petri 网教材里有两种记法一种用「后集减前集」直接算净变化一种用消耗矩阵减产生矩阵。这里用的是净变化矩阵C[i][j] 0 表示变迁 T_j 发生后库所 P_i 净增加 n 个托肯C[i][j] 0 表示净减少。后续不变量计算都建立在这个矩阵上符号一旦搞反算出来的 S-不变量含义完全颠倒。需要强调的第二点是命名库所和变迁直接带业务含义别用 P1、T1 裸名模型一大人就会晕对照现场资源写注释时特别容易出错。3.2 T-不变量与 S-不变量哪条作业链是必须循环的不变量是 Petri 网结构分析的体检报告。T-不变量 x 满足 C x 0 的非负整数向量表示一组变迁按 x 规定的次数各发生一遍后系统整体状态回到原点——也就是一个完整作业循环。S-不变量 y 满足 yᵀ C 0表示一组库所的加权托肯总数在运行中守恒。from sympy import Matrix, Fraction C_sym Matrix(C) # T-不变量: C x 0 - 求右零空间 t_invariants C_sym.nullspace() print(T-不变量(作业循环):) for i, vec in enumerate(t_invariants): vec_int [Fraction(v).limit_denominator() for v in vec] print(f x{i1} {vec_int}) # S-不变量: y.T C 0 - 转置后求右零空间 s_invariants C_sym.T.nullspace() print(\nS-不变量(守恒资源组):) for i, vec in enumerate(s_invariants): vec_int [Fraction(v).limit_denominator() for v in vec] print(f y{i1} {vec_int}) # 用第一个S-不变量验证守恒量 if s_invariants: y np.array(s_invariants[0], dtypeint) conserved int(y M0) print(f\n守恒量 y.M0 {conserved})这个模型里 T-不变量会算出一个完整循环 (1,1,1,1)即「到达→取送→装卸→取走」是一个闭环作业链。S-不变量中最有价值的一条对应「P1 P2 P4 P6」守恒守恒量等于 3——这就是系统在途车列上限。货位容量只有 2那剩下 1 列必然要在到发线等待日办理辆数的天花板上限被算死了不是仿真跑出来的是结构上决定的。参数说明sympy 的 nullspace 返回有理数基向量用 limit_denominator 归一成整数向量方便解释。模型规模大、矩阵稀疏时可以先对 C 做有理数域行化简再求零空间千万别直接对浮点数求数值误差会把不变量淹没。实际项目里我一般把所有 S-不变量列出来逐个对照现场资源写注释这堆注释本身就能当方案汇报材料用。3.3 死锁检测有限状态枚举的代码与其边界结构不变量看不出「某个具体标识下是否卡死」要看具体状态必须做可达标识枚举。做法是维护一个已访问标识集合反复触发所有使能变迁生成新标识直到没有新状态为止。from collections import deque def enabled_transitions(marking, C): 返回当前标识下所有使能的变迁索引列表 enabled [] for j in range(C.shape[1]): col C[:, j] # 变迁使能要求: 所有消耗弧上的库所托肯数不小于消耗量 can_fire True for i in range(C.shape[0]): if col[i] 0 and marking[i] -col[i]: can_fire False break if can_fire: enabled.append(j) return enabled def fire(marking, C, j): 触发变迁 j, 返回新标识 return marking C[:, j] def reachable_deadlocks(M0, C, max_states100000): visited {tuple(M0)} queue deque([M0]) deadlocks [] state_count 0 while queue and state_count max_states: m queue.popleft() state_count 1 en enabled_transitions(m, C) if not en: deadlocks.append(m) continue for j in en: m2 fire(m, C, j) if tuple(m2) not in visited: visited.add(tuple(m2)) queue.append(m2) return deadlocks, state_count deadlocks, cnt reachable_deadlocks(M0, C) print(f遍历状态数: {cnt}) print(f死锁标识数: {len(deadlocks)}) for d in deadlocks[:10]: print( 死锁标识:, dict(zip(places, d)))对最小模型运行结果通常不会出现死锁因为资源配比还算合理。把货位容量从 2 改成 1或者调车机数量从 1 改成 0物理上不成立但模型上可行死锁立刻出现。典型的死锁标识是「P21, P4容量上限, P30」有车列在到发线等待取送但调车机被占、货位已满——调车机送完上一列还没回来新到的车列进不了货位。这是整车货运作业里最典型的死锁结构现场表现就是「到发线堵死、调车机来回空跑、货位闲不下来」。这段代码的边界必须讲清楚它只适合托肯总数很小的模型。状态数随托肯数指数增长现场模型几十个库所、上百个托肯时max_states 很快撞上限。工程上的正确用法是先用枚举法在小规模模型上验证映射逻辑没错再退回不变量分析做全规模结构判断。第 4 章的 Flexsim 仿真本质上是绕开状态爆炸用随机采样估计系统在长时间运行下的行为特征。4. 把 Petri 网模型翻译成 Flexsim 仿真整车货运作业最小模型的搭建与参数4.1 映射表库所、变迁、托肯在 Flexsim 里各是什么Flexsim 的建模思维是「实体沿连接流动」Petri 网思维是「托肯因变迁在库所间转移」。翻译的关键是把五要素一一对应有限容量库所对应 Queue 实体Capacity 参数直接填库所容量变迁的延时对应 Processor 实体或消息延迟托肯对应临时实体 item资源类库所对应任务执行器或资源占用状态弧的权值通过 Batch/Unbatch 表达。Petri 网元素Flexsim 元素关键参数有限容量库所到发线、货位QueueCapacity 设成库所容量超出容量时的策略选 Stop变迁到达、取送、装卸Processor / 消息延迟Process Time 设成作业时长分布托肯车列、车辆临时实体 item按车列或单车粒度决定一个 item 代表多少辆资源调车机、装卸机械任务执行器 / 资源 Queue数量等于资源数量用忙闲状态统计利用率弧的权值一列车 N 辆车Batch / Unbatch车列到货位后拆成 N 辆逐个装卸映射时的常见做法是把「车列」作为 item 粒度。到发线就是一条 Capacity3 的 Queue调车机用任务执行器执行「从到发线取走 item → 送到货位 Queue」的任务序列货位 Queue 容量 2item 进入后拆成单车再用装卸机械资源逐辆处理。这样模型里 item 数量少仿真速度快又不丢车辆级细节。4.2 最小模型的 FlexScript 骨架事件驱动与资源占用Flexsim 建模通常先拖实体、连端口再在实体触发器里写 FlexScript。下面给出一套最小骨架对应上一章 Petri 网模型3 条到发线、1 台调车机、2 个货位、2 台装卸机械。这段示例代码讲解「到发线释放」和「调车机占用」两件事怎么用脚本表达。// 模型实体命名约定: // Source_列车到达 - Queue_到发线(Capacity3) - Processor_调车取送 - Queue_货位(Capacity2) - Processor_装卸 - Sink_离站 // 调车机资源先用全局变量模拟, 场景复杂后换成任务执行器 int tug_free 1; // 1调车机空闲, 0占用, 放在模型全局变量区 // Source_列车到达的到达间隔: // return 60; // 按运行图固定间隔, 不要用指数分布 // Queue_到发线的 OnEntry: // 车列占用到发线库所, 触发调车请求 // if (tug_free 1 content(Queue_货位) 2) 则把item发往Processor_调车取送 // Processor_调车取送的 Process Time: // return triangular(20, 30, 45, 1); // 取送作业, 均值30分钟 // Processor_调车取送的 OnFinish: // tug_free 1; // 调车机释放 // 同时触发 Queue_到发线的下一车列进入取送流程 // Queue_货位的 OnEntry: // 车列到达货位, 拆成单车, 逐辆送Processor_装卸这段骨架的意图不是直接跑通而是把 Petri 网模型里的库所增减翻译成 Flexsim 的触发器。最容易忽略的是调车机释放时机Petri 网模型里 T2 发生后到发线释放、调车机占用T3 完成后调车机释放Flexsim 里对应 Processor_调车取送的 OnFinish 置 tug_free1同时保证 Queue_到发线只在 OnFinish 的触发里送下一列车这样才能和 Petri 网的使能条件完全对齐。参数说明用全局变量模拟单一调车机只适合最小模型场景里调车机跨多个区域、任务有优先级时要换成 Dispatcher TaskExecuter 的任务序列体系否则多个作业抢一个资源时逻辑会写乱。Process Time 用 triangular(20,30,45) 起步是常见的均值和上下限明确比直接用 exponential 方差小得多。4.3 关键参数设定表先定容量再定分布最后做标定仿真参数分三类到达参数、作业时长参数、容量参数。第一次搭模型按下面表格设定跑通后再用现场统计标定。参数建议取值理由列车到达间隔按运行图固定间隔 60~120 分钟铁路是按图行车不是随机到达调车取送时长三角分布 triangular(20, 30, 45) 分钟均值 30波动体现在上下限单车装卸时长按货物品类分triangular(10, 15, 25) 分钟整车装卸波动大于调车作业到发线容量现场实际条数示例 3必须与 Petri 网库所容量严格一致货位容量现场实际货位数示例 2与 S-不变量算出的上限对照预热时间至少 5 个仿真日跑掉空场阶段统计从稳态开始有一条规定动作容量参数必须和 Petri 网模型完全一致否则 Petri 网算出的守恒量和死锁预判在仿真里对不上后续所有瓶颈分析失去锚点。作业时长分布则可以在仿真里自由调整因为仿真要回答的核心问题就是「如果取送快 20%瓶颈会不会转移」。固定间隔到达的写法很简单Source 的 Interarrival Time 填常量或者用 Schedule 表按运行图逐列给出到达时刻。5. 避坑Petri 网模型与 Flexsim 仿真对不上的 5 个常见问题5.1 库所容量改了容量忘了同步模型对不上的头号翻车点现象Petri 网分析日办理辆数上限是 3 列Flexsim 仿真跑出来却是 5 列怎么调时长参数都对不上开始怀疑不变量算错了。原因Flexsim 里某个 Queue 的 Capacity 设大了。Petri 网把到发线容量定为 3但 Flexsim 里 Queue 默认容量上千忘了改或者货位 Queue 里因为 Batch/Unbatch 逻辑不对实际 item 数超过 Petri 网托肯数导致「Petri 网没死锁、仿真也没死锁」但吞吐量虚高。这类问题最难查因为它不报错只让结果悄悄偏离。解决建一张「Petri 网库所 ↔ Flexsim Queue 容量」对照表贴在模型说明里每次改容量两侧同步改。经验做法是在 Queue 的 OnEntry 里加一句容量断言超出就输出红色日志让模型自己报警。这个习惯能省掉大量对数据的时间。5.2 变迁使能条件写成全局变量判断并发被压成了串行现象仿真结果比现场吞吐量低 30%调车机利用率显示 95%到发线也有大量空闲时间两个指标自相矛盾现场人员一眼就质疑模型。原因把「到发线是否有空闲」「调车机是否空闲」写成了全局标志位在多个触发器里互相读写。Flexsim 触发器是事件驱动同一时刻多个 item 可能并行触发全局变量一改后触发的逻辑读到的是新值Petri 网的并发使能关系丢失系统被压成了串行。解决并发状态放进实体自身属性管理别用全局变量。到发线还剩多少容量直接读 Queue 的当前库存算调车机忙闲状态用任务执行器的状态统计字段。Petri 网使能条件是「每个输入库所有足够托肯」翻译成 Flexsim 就是「每个输入实体有可用容量」。在 OnEntry 和 OnExit 里做容量检查而不是维护标志位。5.3 到达间隔用了指数分布仿真方差爆炸结论全变噪声现象同一套方案跑 5 次日办理辆数从 3 到 8 剧烈波动置信区间宽到没法用。换随机种子结论就反转方案 A 有时比 B 好、有时比 B 差无法决策。原因铁路货运站是按图行车车列到达时刻是运行图排定的不是随机泊松到达。指数分布方差极大短间隔和长间隔交替出现到发线时而拥堵、时而空转平均指标根本没有统计意义。这种情况最容易被误读成「仿真发散」——模型数值没问题是输入分布选错了这属于仿真参数里的玄学问题越调随机种子越乱。解决Source 的 Interarrival Time 填固定常量或者用 Arrival Schedule 按运行图输入真实到发时刻。实在要加扰动用三角分布或正态分布把标准差控制在均值的 20% 以内别让随机性主导系统行为。5.4 调车机跨区域作业被建成固定资源利用率虚高 20 个点现象仿真报告里调车机利用率 95%现场统计只有 70%一线人员直接说模型不可信项目推进卡壳。原因调车机在真实现场要在到发线、货位、编组区之间移动移动时间占作业周期很大比重。建模图省事把调车机建成一个固定的 Processor只算了取送作业时间忽略走行时间或者把一台调车机绑在某一条线路上没建跨区域移动。解决把调车机建成 Dispatcher 多个 TaskExecuterTravel Time 按区段距离设定或者退一步在 Processor 的 Process Time 里把平均走行时间折算进去。折算系数取法用现场一个班的作业日志统计「调车机从接到指令到移动到位」的平均耗时加到处理时间上。这是最稳妥的折中做法精度足够支撑方案对比。5.5 预热时间设太短统计窗口混入空场数据现象仿真刚开始的几天系统还没进满车列平均队列长度偏低、完成时间偏高。拿前 3 天和后 7 天数据对比差异明显但没人注意到统计窗口混了空场。原因Flexsim 默认从模型开始运行就记录统计初始状态是空场。空场阶段没有排队所有指标都好得离谱混进统计窗口会系统性低估瓶颈。解决模型先置空跑预热期预热期内清空统计再从零开始记录。Flexsim 里可以在 Reset 后跑固定时长然后清空 Statistics 再继续。预热时长怎么定观察到发线累计吞吐量曲线等斜率稳定后再清统计。常见做法是跑至少 5 个仿真日或者等累计吞吐量曲线两次经过同样的峰值形态。6. 瓶颈识别与效率提升方案验证吞吐量、利用率与三套方案的对比方法6.1 三个指标怎么读队列长度、利用率、平均完成时间瓶颈识别不靠肉眼盯堆积靠三个指标对比到发线平均队列长度排队越长越接近瓶颈、调车机利用率持续高利用说明调车是卡点、单车平均完成时间整条链路的综合结果。每次改动一个资源后看哪个指标先顶到天花板那个位置就是当前瓶颈。6.2 三套方案的对比实验单因子逐个试常见做法是给方案编码A 加一条到发线B 改调车取送优先级为「先进先发」C 增加一台调车机。用 Flexsim 的 Experimenter 跑批量实验每组跑 20 次取均值对比日办理辆数和平均完成时间。判定原则小改大收益直接上大改小收益要算投资回报。方案 C 看似直接加资源但调车机之间的走行避让往往让收益打折仿真的价值就在这里——用数据压住经验拍板。做这类项目我最大的教训是别把模型越建越复杂。Petri 网画得再漂亮、Flexsim 仿真跑得再精细都不算本事只有把瓶颈说清楚、把方案验证到位才算数。模型的价值是推动决策不是证明自己会用工具。我现在拿到一个货运流程优化需求会先花一周时间把 Petri 网结构和现场数据对齐再花三天搭仿真做对比而不是一上来就拖实体。希望这套「Petri 网定结构、Flexsim 验动态」的组合拳能帮到你少踩几个我踩过的坑。本文还有配套的精品资源点击获取