☰
电梯调度问题的数学建模与Python优化仿真
2026/10/9 20:04:01 网站建设 项目流程

简介:面向数学建模竞赛、运筹优化课程设计及高层建筑电梯系统分析,doc文档完整呈现电梯调度问题的建模与求解过程。包内仅1个文件,压缩后约144KB,内容覆盖从问题重述到模型评价的完整链条:先建立以乘客等待时间、电梯总运送时间、停靠次数和行进总时间为指标的评价体系,再结合层次分析法(AHP)确定指标权重,并通过无量纲化与满意度函数对不同方案进行综合评分。针对高峰时段,文档设计了中间不停靠的理想化模型与分段楼层分配模型,后者利用MATLAB遍历搜索确定最优分区,同时引入概率论计算各层停靠概率,能明显减少停靠次数与运行时间。全文包含具体假设、公式推导、数据表格和求解思路,已有1064人学习下载,适合需要撰写数学建模论文或复现调度算法的读者参考。

1. 电梯调度问题里藏着的数学建模:先到先得为什么不是最优解

一座 20 层写字楼,4 部电梯,早高峰所有楼层都在按按钮。先到先得策略看起来公平,结果就是电梯频繁往返 1 层和顶层,每层都停,中央大厅排队越来越长。这是典型的资源调度问题:电梯是稀缺资源,乘客请求随机到达,要让所有人平均等待时间尽量短。与其靠“高峰模式”这种模糊经验,不如把它建模成一个优化问题:定义决策变量、目标函数和约束,再让算法算出调度方案。这正是数学建模电梯的调度问题要解决的事,也是高校竞赛题和实际楼宇群控系统的共同起点。这篇文章从数学模型讲起,逐步落到可运行的 Python 代码和仿真避坑,适合参赛学生、系统设计工程师,以及想用数据方法优化建筑运维的从业者。

2. 把电梯调度翻译成数学模型:从物理过程到目标函数

2.1 决策变量与状态变量:一部电梯到底要决定什么

电梯调度问题的输入很明确:每个乘客按下按钮的时刻、出发楼层、目标楼层,加上电梯自身的位置和运行方向。系统要决定的不是“电梯朝哪走”,而是“在某个时刻,由哪一部电梯去响应这个召唤;如果路过多层,要不要顺路停靠;如果多部电梯同时可用,优先派谁”。

这里要区分两层模型。第一层是静态分区模型:把楼层集合分给不同电梯,适用客流方向集中的场景,早高峰写字楼是典型。第二层是动态调度模型:每来一个事件都要重新决策,适用商场、医院这类随机性强的场景。竞赛题目如果只给了高峰客流表,通常静态分区模型就足够;如果强调实时召唤,就要做动态模型。

无论哪一层,都要先记住电梯的物理约束。电梯不是传送门,一次运行时间由这么几部分构成:层间运行时间(含加减速)、开关门时间、乘客进出时间、容量限制。常见做法是给这些参数一个参考表,建模时按场景赋值。

参数典型参考值对模型的影响
层间运行时间(含加减速)3~5 秒/层决定运行周期下限
开关门时间1.5~2.5 秒/次停靠次数越多越不可忽略
单个乘客进出时间0.5~1 秒/人高峰期满载时显著放大服务时间
额定容量10~20 人决定电梯是否会排队溢出

我一般会在建模前把这张表贴在论文模型假设一节里,后面所有公式都从这张表出发。这一步看着琐碎,实际决定了你的模型是“能落地”还是“纯理论”。

2.2 目标函数:平均等待时间 vs 最长等待时间

电梯调度常见目标有三个:平均等待时间(AWT)、平均乘梯时间(AJT)、最长等待时间(MWT)。工程上还常加一个能耗目标,但竞赛论文里最核心的还是等待时间。

假设一个调度方案服务 N 个乘客,第 i 个乘客从按下按钮到电梯开门等待了 W_i 秒,那么:

平均等待时间 AWT = (1/N) Σ W_i

最长等待时间 MWT = max_i W_i

如果只优化 AWT,算法很可能为了整体数据漂亮牺牲少数人,导致某个乘客等 5 分钟。反过来只优化 MWT,又会让平均等待时间变长。所以常见做法是把它们线性加权:

minimize f = α·AWT + β·MWT + γ·Energy

α、β、γ 就是权重系数。比赛论文里可以给三组不同权重做灵敏度分析,说明你选的权重为什么合理;工程里则应该按物业方的真实诉求来定,比如“宁可平均多等 10 秒,也要保证没人等超过 90 秒”。

还可以用排队论做理论近似。把一台电梯看成服务台,乘客到达看成随机过程,对单电梯模型,用 M/G/1 排队公式粗略估计平均等待时间:

ρ = λ·E[S]

W ≈ λ·E[S²] / (2(1-ρ)) + E[S]/2

其中 λ 是每秒到达率,E[S] 是平均服务时间,E[S²] 是服务时间的二阶矩。这个公式不要求电梯系统完全满足排队论假设,但能用来快速判断系统是否过载:一旦 ρ 接近 1,等待时间会急剧上升。这个结论在数学建模论文里非常有用,可以直接支撑“当前电梯数量不足”的判断。

2.3 约束条件与模型假设:写参赛论文前先把这些说清楚

模型再好,约束条件写不全是丢分的主要原因。电梯问题里必须明确的约束至少有四条:

第一,容量约束。电梯内人数不超过额定容量,高峰期乘客可能在楼层滞留。第二,方向约束。电梯通常只响应同方向的召唤,反向召唤要等下一趟。第三,停靠约束。电梯不会在没有召唤的楼层停靠,除非该层有人在轿厢内按下目标键。第四,运动学约束。电梯不能瞬间启动和停止,加减速时间要折进层间运行时间。

再就是模型假设。数学建模最忌讳隐藏假设,凡是简化都要写出来。我常见做法是:假设乘客到达服从泊松过程;早高峰目标楼层集中在首层;忽略满载不能停靠的情况;假设所有电梯参数一致;假设建筑内部无紧急事件。这些假设在正文里一一列出来,后面仿真再逐步放宽。

如果目标楼层五花八门,静态模型还会失效,这时就要补一个乘客目的地分布矩阵 D,D[i][j] 表示从 i 层到 j 层的乘客比例。有了这个矩阵,动态调度模型才能算真实的顺路效果。矩阵数据可以从电梯控制器日志里统计,没有日志就用经验分布:底层住户多去高层,高层住户多去首层。

3. 调度策略怎么选:先到先得、分区调度和动态规划的边界

3.1 先到先得为什么“看起来公平,做起来低效”

先到先得是新手最容易写进模型的对策:所有召唤按时间排序,电梯依次响应。从排队论看它公平,但从电梯物理运动看,它会带来两个问题。第一,电梯频繁跨层。低层召唤和高层召唤交叠出现时,电梯要在整个楼层区间来回跑,运行效率极低。第二,新召唤会打断既定路线,长距离请求一直得不到服务,乘客在高层越等越久。

现实电梯内置的 SCAN 策略要好得多:电梯沿一个方向运行,只响应同方向召唤,到顶后再反向。SCAN 避免了电梯频繁转向,减少了平均等待时间,也是很多电梯控制器的默认逻辑。但 SCAN 在高峰期的吞吐量仍然不够,因为它没有从全局角度协调多部电梯,每部电梯都独立扫描,可能同时停在同一批楼层。

我在竞赛里看到的常见错误,是把 FCFS 或 SCAN 直接当成“优化好的方案”,没有和更优策略做对比。正确做法是把它们当成基线,后面任何算法只要能赢过这两条,才有说服力。

3.2 静态分区调度:高峰场景下最简单有效的策略之一

静态分区很简单:把楼层分成几个区间,每部电梯只负责其中一个区间。上班早高峰,所有人从首层或地下车库上行,目标楼层分布在各个办公层;这时电梯的停靠次数越多,周期越长,排队越严重。分区后,每部电梯只停靠自己负责的楼层,停靠次数立刻降下来,运行周期也随之缩短。

分区不是把楼层平均切一刀就完事。楼层越高,往返运行时间越长,所以低层电梯可以多负责一些楼层,高层电梯少负责几层。更准确的做法是按“每部电梯的运行周期和到达率乘积”来均衡:

一部电梯一次往返周期 T = 2·(最高层-1)·t_run + 停靠次数·t_door + 上下客时间

理论上,最优分区要让每部电梯的 T 与它的到达率 λ 的乘积尽量接近,因为这样所有电梯的利用率一致,系统不会出现一台过载、一台空闲。这里的 λ 不是写死的,而是排队论里那个到达率。

静态分区只适用客流方向集中的场景。商场、医院、写字楼午休这种乘客目标楼层分散的场合,分区反而会让电梯空跑,因为低区乘客要去高区时,低区电梯把他送完还得跑一趟高区,跨越多个分区。所以选策略之前,先看目标楼层分布矩阵 D 是否稀疏。

3.3 动态规划:精确但状态爆炸,什么时候不要硬上

动态规划可以给出电梯调度的最优解,思路是把每部电梯的状态表示成五元组:当前位置、运行方向、轿厢内人数、轿厢内目标集合、楼外召唤集合。状态转移就是电梯在下一瞬间要么移动一层、要么开门、要么关门出发。这样建模之后,可以用 MDP(马尔可夫决策过程)求解最小期望等待时间。

问题是状态空间膨胀极快。20 层、4 部电梯,每部电梯的位置有 20 个可能,方向 3 个,召唤集合是 2^40 级别,四个维度的笛卡尔积是一个天文数字。即使离线算出策略表,在线查询时的实时性也顶不住。所以我在实际项目里很少把动态规划作为主算法,最多拿来验证 2 部电梯、6~8 层这种小规模场景,确认某个启发式算法离最优解有多远。

竞赛论文里更常见路线是:静态分区或启发式算法作为主模型,动态规划作为小规模特例验证。这样既证明了你有精确建模能力,又避免了状态爆炸写不出来。

既然精确方法不可行,启发式算法就成了主流选择。遗传算法擅长搜索“调度策略的参数”,比如分区边界、目标函数权重、是否允许跨区支援;粒子群算法适合连续参数;模拟退火适合在已有策略上做局部微调。它们都不保证最优,但对于电梯调度这种“找到够用且可解释的方案”的实际需求,性价比最高。

4. 用 Python 求解双电梯静态分区:枚举代码与遗传算法参数

4.1 双电梯分区的最小可运行枚举代码

下面这段代码解决一个简化问题:10 层办公楼,2 部电梯,每分钟各层产生 4 个上行请求,求楼层分配方案,让两部电梯的估算平均等待时间尽量小。先说明,这不是精确仿真,而是一个静态近似模型,目的是把“分区优化”这件事变成可运行代码;真正要落地时,把 evaluate 函数换成离散事件仿真即可。

from itertools import product # ---------- 环境参数 ---------- N_FLOORS = 10 # 建模楼层数,第 1 层为大厅 CAPACITY = 10 # 单台电梯额定容量(人) T_RUN = 3 # 电梯运行一个层站的平均时间(秒),含加减速 T_DOOR = 2 # 开关门一次时间(秒) T_IO = 1 # 单个乘客进出电梯平均时间(秒) # ARRIVAL[i] 表示第 i 层每分钟产生的上行请求数,下标 0 不用 ARRIVAL = [0] + [4] * (N_FLOORS - 1) def evaluate(floors): """计算某电梯负责给定楼层时的估算平均等待时间(秒)""" if not floors: return float('inf') # 没有分配楼层,视为不可行 highest = max(floors) # 电梯最高要到达的楼层 stops = len(floors) # 粗略认为每个负责楼层都停一次 period = 2 * (highest - 1) * T_RUN + stops * T_DOOR rate = sum(ARRIVAL[f] for f in floors) # 该电梯负责楼层的总请求率 arrivals_per_cycle = rate * period / 60.0 # 一个往返周期内新到请求数 # 如果一周期内请求数超过容量,超出部分要等待下一周期 if arrivals_per_cycle > CAPACITY: extra_cycles = (arrivals_per_cycle - CAPACITY) / CAPACITY waiting = period * (0.5 + extra_cycles) else: waiting = period * 0.5 return waiting best_score = float('inf') best_floors_a = None best_floors_b = None # 楼层 2..N_FLOORS 各自分配给电梯 A 或电梯 B,共有 2^(N_FLOORS-1) 种方案 for chromo in product([0, 1], repeat=N_FLOORS - 1): floors_a = [i + 2 for i, bit in enumerate(chromo) if bit == 0] floors_b = [i + 2 for i, bit in enumerate(chromo) if bit == 1] score = max(evaluate(floors_a), evaluate(floors_b)) # 取较大值,避免牺牲某一部电梯 if score < best_score: best_score = score best_floors_a, best_floors_b = floors_a, floors_b print("电梯 A 负责楼层:", best_floors_a) print("电梯 B 负责楼层:", best_floors_b) print("估算平均等待时间: %.1f 秒" % best_score)

代码逻辑不复杂:用 product 生成所有楼层分配组合,对每台电梯调用 evaluate 估算等待时间,取两台电梯里更大的值作为该方案的评分。用最大值而不是平均值,是为了保证公平性,避免算法把某一部电梯分到不可能完成的任务。

参数说明:T_RUN、T_DOOR、T_IO、CAPACITY、ARRIVAL 都是直接可调的。把 ARRIVAL 改成[0, 8, 6, 5, ...],就能模拟不同楼层的早高峰差异;把 N_FLOORS 改成 15,枚举次数会从 512 变成 16384,代码仍然能跑。evaluate 里的 period 计算有意省略了 T_IO,因为 T_IO 与乘客数量耦合,会形成循环依赖;想加回 T_IO,可以等仿真阶段再做。

4.2 从枚举到启发式:为什么楼层多了要改用遗传算法

上面枚举法能跑通,是因为 2 部电梯、10 层楼,分配方案只有 512 种。一旦电梯数变成 4、楼层数变成 30,分配方案数是 4^29,约 2.9 万亿种,枚举直接报废。这时常见做法是遗传算法。

提示:枚举法只在问题规模很小时可用;如果电梯数量或楼层数变大,先把本节代码换成遗传算法框架再跑。

遗传算法做静态分区优化时,染色体可以继续沿用上面的二进制编码:染色体每一位代表一个楼层,取值 0 表示电梯 A,取值 1 表示电梯 B。适应度函数就是上面的 evaluate 组合逻辑。每迭代一代,算法通过选择、交叉、变异产生新的楼层分配方案,最终收敛到一个“够用”的解。

但要注意,遗传算法是黑匣子,不保证全局最优。我在项目里通常先跑一遍枚举或随机撒点,了解解的大致水平,再决定要不要用遗传算法。如果问题规模只有 2 部电梯 10 层楼,直接枚举更稳,写进论文也更清晰:评审能看懂每一个结果是怎么来的。启发式算法的意义是处理“枚举跑不动”的场景,而不是显得技术高级。

4.3 遗传算法的三个必调参数:种群大小、交叉率、变异率

用遗传算法时,参数调不好,结果比随机分配还差。我一般只先调三个参数:

参数常用范围设得太大设得太小
种群大小20~100每代计算慢容易早熟,陷入局部最优
交叉率0.6~0.9好基因被拆散新方案产生太慢
变异率0.01~0.1搜索接近随机乱猜难以跳出局部最优

调参建议:先固定随机种子,确保算法可复现,再让种群大小从 50 开始,交叉率 0.8,变异率 0.05。看收敛曲线:如果前 10 代就已经收敛,说明多样性不足,把变异率往上调;如果到第 100 代还在大幅波动,说明收敛性差,把交叉率或种群大小往上调。

竞赛论文里做灵敏度分析时,把这三个参数各取三个水平,做三组对比,表格放进去就足够。真正工程落地时,还要把适应度函数从静态近似换成仿真器的多次随机结果,因为单次仿真的方差很大,算法会把噪声当成优化信号。

5. 仿真验证与避坑:客流分布、时间参数、评价指标

5.1 用离散事件仿真验证调度策略:至少要把客流和时间参数做对

静态模型能给出理论上的最优分区,但真实电梯系统是动态的:乘客随机到达、电梯之间有抢占、满载不停靠。用离散事件仿真验证,是让模型从论文走向落地的关键一步。

仿真器最低配置要包含四件事。第一,事件队列:乘客召唤事件、电梯到站事件、电梯开门和关门事件。第二,乘客生成器:根据到达率和时间片生成乘客,每位乘客有出发楼层和目标楼层。第三,电梯逻辑:实现你要比较的调度策略,SCAN 或分区策略都写进去。第四,统计模块:记录每位乘客的开始等待时间和结束等待时间,最后算 AWT、MWT 和 P95。

我给仿真器的建议参数如下:

项目建议取值说明
仿真时长3 个高峰时段前 10 分钟作为预热期,不计入统计
随机种子5~10 个每个种子跑一次,取平均值
客流时间片每 5 分钟一个 λ(t)高峰与平峰分开
目标楼层分布从历史日志统计没有日志就用经验矩阵

这里特别提醒:仿真里电梯的加减速时间和开关门时间,不要再折成一个简单常数“每层 3 秒”,否则仿真的乐观偏差会非常大。更稳的做法是把层间运行时间拆成匀速段和加减速段,再和开关门、乘客进出叠加。

5.2 避坑记录 1:把均匀到达率套到高峰场景,结论直接失效

现象:仿真跑出来的平均等待时间只有 30 秒,和实际体验相差很大,现场乘客普遍反映要等两三分钟。

原因:模型里把到达率设成了常数 λ,早高峰 20 分钟内实际到达率是平峰期的 3~5 倍,均匀假设让电梯始终处于轻载状态。

解决:把到达率写成随时间变化的函数 λ(t),按 5 分钟或 10 分钟一个时间片切分。例如 8:00~8:05 到达率 40 人/分钟,8:05~8:10 到达率 60 人/分钟,这样仿真器才能真正压测电梯系统。

5.3 避坑记录 2:忽略电梯加减速与开关门时间,模型过于乐观

现象:模型算出的最优方案在仿真里表现很差,电梯实际运行时间比理论周期长了将近一半。

原因:静态模型把层间运行时间当成常数,没有考虑电梯从静止加速、到达减速和开门等待。楼层越高、停靠越多,这个误差被放大越严重。

解决:在模型和仿真正式计算前,把一次运行时间拆成这样:启动加速段 + 匀速段 + 制动减速段 + 开关门时间 + 乘客进出时间。即使没有实测数据,也要用电梯厂商参数把数量级填对,否则后面对比不同分区方案都是在比谁错得更小。

5.4 避坑记录 3:只用平均等待时间评价,忘记最长等待时间

现象:优化后的方案平均等待时间从 45 秒降到了 35 秒,方案看起来很好,但上线后投诉反而变多。

原因:平均值被大部分短等待乘客拉低了,少数长尾乘客可能等了 3 分钟。平均等待时间掩盖了极端情况,而乘客对“等太久”的记忆远比“平均还行”深刻。

解决:评价指标至少同时看 AWT、MWT 和 P95。P95 指排在第 95 百分位的等待时间,它比平均值更能反映大多数人的真实体验。论文里把这三项指标列在同一张表,方案优劣一对比就很清楚。

5.5 避坑记录 4:把静态分区结论直接套到动态呼叫场景

现象:静态分区优化出的楼层分配,放到实时召唤仿真里跑,出现空闲电梯在高区空跑、低区排队的情况。

原因:静态分区假设乘客目标楼层高度集中,每部电梯只在负责区间内做往返;动态场景里目标楼层分散,跨越分区的乘客大量存在,电梯不得不越区服务。

解决:先看目标楼层分布矩阵 D,如果 D 的非对角线项占比超过 30%,静态分区就要谨慎使用。更通用的做法是把分区边界也作为动态决策的一部分,每隔一段时间按实时客流重算一次边界,而不是一个方案用一整天。

6. 进阶方向:把静态规划升级成动态策略,用仿真反馈调参

静态分区是迈出第一步的稳妥解,但真实写字楼的客流一天内至少有三波变化:早高峰上行、午休双向、晚高峰下行。更进阶的做法是把分区边界从固定值变成随时间变化的函数:8:00~9:00 用早高峰最优分区,17:00~18:00 把分区反转成“高层负责低区”的镜像方案。这样调度策略就从“一个静态方案”升级成“按时间片切换的规则表”。

再进一步,可以去掉分区这个中间变量,直接用遗传算法去搜索调度器内部的权重参数。比如给电梯控制器设计一个评分函数:当新召唤到来时,对每部电梯计算“响应该召唤的综合代价”,代价 = 距离 + 方向惩罚 + 顺路收益 + 满载惩罚。这里的方向惩罚和顺路收益就是几个连续权重,用遗传算法跑几百轮仿真就能搜出来。这也是很多商用群控电梯的做法,只是它们用的是实时强化学习,比赛和原型项目用遗传算法足够。

验证进阶方案时,我习惯在同一份历史客流数据上做三组对比:基线 SCAN、静态分区、动态周期分区。三组都跑同样的随机种子,统计 AWT、MWT、P95 和能耗,看哪一组的提升最显著。只有动态方案在指标上稳定优于静态方案,才值得投入。

我调电梯调度参数时踩过一个坑:变异率设得偏低,遗传算法卡在局部最优,搜出来的权重甚至不如原始 SCAN。后来把适应度函数从单次仿真改成多个随机种子的平均结果,才把链路走通。做电梯调度,第一步不是写高级算法,而是把时间参数和客流分布做对;基线跑稳了,算法优化才有意义。希望帮到你。

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

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询