
简介面向数学建模和电梯调度算法研究的MATLAB电梯群控仿真代码包适合参赛学生、自动化专业学习者及优化控制方向开发者。代码包围绕电梯群控中的派梯策略、行程时间计算、乘客等待评估等核心模块展开提供完整可运行的程序框架。压缩包共23个文件含14个m脚本与9张运行结果示例图m文件包含主程序main.m及多个自定义函数分别实现乘客生成、电梯调度、等待时间统计、行程时间预测等功能jpg图片便于直观核对仿真效果。包体仅111KB轻量易部署。目前已有333人学习下载可作为电梯群控仿真的快速入门参考。通过主函数即可串联各子函数完整展示从乘客到达、派梯决策到运行统计的仿真链路各模块独立封装便于按需修改或二次开发也可作为数学建模优化问题的对照实现。代码基于MATLAB 2019b编写若遇版本差异可参照提示调整运行。1. 基于Matlab的电梯群控仿真从“就近响应”失效说起电梯群控仿真最大的反直觉点在于“就近响应”并不保证整体等待时间最短。当多部电梯同时冲向大厅里同一个呼梯按钮后面的乘客没有被服务前面却堵在门厅这种相互干扰只有在仿真里把乘客生成、轿厢加减速和开门时间放在同一个时钟下才能暴露出来。基于Matlab做电梯群控仿真在数学建模任务中通常以调度算法为核心但真正决定结果可用的是电梯状态、呼梯事件和统计口径是否一致。下面给出的事件驱动建模方案从状态定义到调度算法只保留必要细节配上可直接执行的Matlab代码适合用来复现常见的群控策略也方便往后替换成自己的优化算法。2. 电梯群控仿真的对象建模与评价指标定义2.1 电梯、楼层与乘客请求的最小状态集合我先把结论放在前面做电梯群控仿真第一步不是写调度算法而是固定状态变量。如果状态太少电梯没有加减速约束调度算法算出来的“最优”轿厢在真实电梯里根本追不上如果状态多到每一段导轨都建模仿真耗时会让后续几十组对照实验变得不可接受。竞赛场景下我一般只保留三层状态电梯状态、楼层呼梯状态、轿厢内请求状态。对象状态变量含义典型取值范围电梯floor当前所在楼层1 ~ N电梯dir当前运行方向-1 下行0 静止1 上行电梯targets已登记的目标楼层数组空数组或正整数数组电梯load轿厢当前载客数0 ~ capacity电梯capacity额定载客量8 ~ 21乘客startFloor呼梯起始楼层1 ~ N乘客destFloor目的楼层1 ~ N乘客arriveTime呼梯产生时刻仿真时间秒乘客boardTime乘客登上电梯的时刻仿真时间秒在Matlab里我习惯用struct数组保存电梯状态用两个逻辑数组upCall(1:N)和downCall(1:N)保存楼层外侧呼梯。轿厢内请求不额外开数组直接追加到对应电梯的targets里。这样写的好处是调度函数只用读取一个结构体不用同时传五六个散落的变量。2.2 群控质量评价指标AWT、ATT、LWP与启停次数评价群控调度策略时观察单个乘客的等待时间没有意义必须用一批乘客的统计量。我常用的四个指标是AWT平均等待时间mean(boardTime - arriveTime)表示乘客从按下呼梯按钮到进入轿厢的平均用时ATT平均乘梯时间mean(reachTime - boardTime)表示乘客进入轿厢后到到达目的楼层的平均用时LWP长时间等待率等待时间超过60秒的乘客占比高峰时段这个指标比AWT更说明问题启停次数电梯一次从运动到停止的过程记为一次启停用来代表能耗和机械损耗。这四个指标在Matlab中对应一个乘客记录数组passengerLog。每次乘客登梯和到达目的楼层时追加一条记录最后统一计算。这个数组不参与调度循环只在仿真结束时做统计能避免在群控算法里到处插入统计代码。2.3 随机客流生成与分布参数设置写字楼乘客到达一般视为泊松过程相邻两个乘客到达的时间间隔服从指数分布。在Matlab中可以直接用exprnd生成也可以用-meanInterval * log(rand)。我一般把乘客起始楼层和目的楼层按工作场景设置成不同分布早高峰以1楼为起点目的楼层偏向高层午休时段则双向均匀。lambdaPerMin 20; % 每分钟平均到达人数 meanInterval 60 / lambdaPerMin; % 平均到达间隔单位秒 nextArrival exprnd(meanInterval); % 指数分布随机数 arriveFloor randi(N_floor); % 起始楼层 destFloor randi(N_floor); % 目的楼层 while destFloor arriveFloor destFloor randi(N_floor); % 保证起点和终点不同 end upCall(arriveFloor) upCall(arriveFloor) 1; % 记录上行呼梯这段代码的meanInterval是核心参数。将lambdaPerMin调大高峰期乘客密度增大调度策略的差异会更容易暴露调小时仿真跑出的曲线更平缓适合先验证逻辑正确性。实际比赛中我会把早高峰到达率先设为每分钟30人做压力测试再用每分钟5人做常规工况对比两种工况下使用的电梯数量应不同否则策略优劣没有实际参考价值。3. 用Matlab事件循环搭建电梯群控仿真主框架3.1 时间步长与电梯状态机的选择电梯群控是一个典型的离散事件系统事件分为乘客到达、电梯到站、开关门完成三类。严格的事件驱动仿真需要维护一个事件堆每次取最小事件时间推进。在Matlab里我更喜欢用固定步长扫描步长取0.1秒。理由很直接开关门需要2到3秒楼层间运行通常不少于5秒0.1秒的精度已经足够刻画这些物理约束而且代码调试时不需要处理复杂的事件列表。电梯状态机可以简化为三种状态静止等待、向目标楼层运行、在目标楼层开关门。用dir字段表达方向用targets数组表达未来停靠顺序用remainTime表达距当前目标楼层还剩余的时间。这种设计在Matlab里最紧凑也方便后面接入不同的调度策略。初始化电梯状态时我会写成下面这样dt 0.1; % 仿真步长单位秒 simTime 3600; % 仿真时长单位秒 numElev 6; % 电梯数量 numFloor 20; % 楼层数量 floorHeight 4; % 层间距单位米 velocity 2; % 额定速度单位米/秒 accel 1.5; % 加速度单位米/秒^2 doorTime 2.5; % 开关门总时间单位秒 capacity 13; % 额定载客人数 elFloor ones(1, numElev); % 所有电梯初始在1楼 elDir zeros(1, numElev); % 初始静止 elTargets cell(1, numElev); % 初始无目标楼层 elRemain zeros(1, numElev); % 剩余到站时间 elLoad zeros(1, numElev); % 初始载客量为0elTargets使用cell数组是为了让每部电梯拥有独立长度的目标队列。如果改用普通矩阵则需要固定最大长度并频繁补零逻辑上会多出许多无效分支。3.2 乘客到达事件与电梯到站事件的处理逻辑主循环每次推进0.1秒先检查是否有乘客到达事件再推进所有电梯的位置。乘客到达时直接写入呼梯数组并交给调度函数分配。电梯推进时只需要判断elRemain是否小于等于当前步长如果成立就说明电梯在本次扫描内到达目标楼层。t 0; nextArrival exprnd(meanInterval); % 第一个乘客到达时间 while t simTime while nextArrival t dt % 覆盖步长内到达的乘客 callFloor randi(numFloor); destFloor randi(numFloor); while destFloor callFloor destFloor randi(numFloor); end if destFloor callFloor upCall(callFloor) upCall(callFloor) 1; else downCall(callFloor) downCall(callFloor) 1; end assignElevator(callFloor, destFloor, ... elFloor, elDir, elTargets, elLoad); nextArrival nextArrival exprnd(meanInterval); end for k 1:numElev if elRemain(k) 0 if elRemain(k) dt arriveAtFloor(k); % 到站处理函数 updateNextTarget(k); % 设置下一目标楼层 end elRemain(k) elRemain(k) - dt; else elDir(k) 0; % 没有目标则保持静止 end end t t dt; end这里while nextArrival t dt是关键。由于dt是0.1秒一个步长内可能发生多次乘客到达必须用while而不是if否则在高峰时刻每分钟30人的参数下会丢失事件。nextArrival每次到达后继续加上新的指数随机数形成泊松到达序列。3.3 群控仿真初始化参数表与典型取值参数名含义低层楼高层写字楼numFloor楼层数1032numElev电梯数28capacity额定载客量816velocity额定速度(m/s)1.52.5accel加速度(m/s^2)1.21.5doorTime开关门时间(s)32.2lambdaPerMin每分钟到达人数530这批参数直接决定了调度的“难度”。电梯台数少而楼层多时任何策略都难跑出低等待时间反之电梯数量充足时普通集选控制和复杂优化算法的差距会缩小。我在做实验前会先跑一组不调度、只让电梯顺序扫描的仿真作为基线后续策略若不能优于这个基线说明参数设置或策略代码存在问题。4. 常见电梯群控调度策略的Matlab实现与参数调优4.1 集选控制方向优先与顺路捎带的代价函数集选控制是最常见的传统策略核心规则是电梯沿当前方向运行只响应运行方向前方的呼梯当到达当前方向的端点后再反向。放在群控仿真里问题的关键变成“新呼梯应该指派给哪部电梯”。我不会直接判断哪部电梯离呼梯楼层最远或最近而是用一个代价函数估算每部电梯响应呼梯所需时间。function cost answerCost(elFloor, elDir, callFloor, callDir, numFloor) if elDir 0 cost abs(elFloor - callFloor); elseif elDir callDir if elDir 1 callFloor elFloor cost callFloor - elFloor; elseif elDir -1 callFloor elFloor cost elFloor - callFloor; else cost 2 * numFloor - abs(callFloor - elFloor); end else cost 2 * numFloor - abs(callFloor - elFloor); end end这个代价函数把电梯看成只能沿当前方向移动若要回头则先跑到当前方向端点再折返到呼梯楼层。2 * numFloor是折返的惩罚项数值上等于电梯跑完两个全程所需经过的楼层数。随后在assignElevator中遍历所有电梯取cost最小的那部。这部分还有两个细节容易被忽略。一是轿厢内请求targets需要保持有序否则电梯到站后不知道该往哪边拐二是在targets为空而只有外部新呼梯时电梯方向应直接设为呼梯方向。我通常在assignElevator里对targets使用insert排序把新目标按与当前方向一致性的优先级插入。4.2 动态分区控制减少长距离空驶的另一种策略集选控制在乘客分布均衡时表现不错但在早高峰所有乘客都集中在1楼时外部呼梯会让所有电梯扑向大厅造成严重的群聚效应。一种简单的做法是动态分区把楼层切成若干连续区间每部电梯只响应自己区间内的呼梯。动态分区的参数是区间边界。我一般设置成每隔60秒根据各电梯载客量重新划分一次区间载客少的电梯负责更多楼层载客多的电梯缩小服务范围。划分代码如下function zoneBoundary reallocateZone(elLoad, numFloor, numElev) [~, idx] sort(elLoad); % 按载荷升序排列电梯 zoneBoundary zeros(1, numElev 1); zoneBoundary(1) 1; zoneBoundary(end) numFloor; for k 2:numElev zoneBoundary(k) round(numFloor * k / numElev); end % 按电梯索引恢复原始顺序并返回每个电梯的区间边界 end这个简化版本把楼层均匀切分然后根据负载做旋转。真实项目中会把乘客到达率放进去但我建议先用均匀切分跑通流程再加入预测客流。否则仿真结果出现异常时很难判断是分区逻辑错误还是客流预测偏差。动态分区对早高峰有效对午休双向客流反而可能增加呼梯到电梯的距离。因此我会在仿真里同时记录两种策略的AWT通过对比曲线决定当前工况下是否启用分区。4.3 参数调优电梯数量、楼层数与客流强度的交互影响群控仿真的参数调优不只是调调度系数更要调物理参数。电梯数量增加时AWT会明显下降但超过某个阈值后收益趋缓楼层数增加会直接拉长每台电梯的运行周期客流强度越高长时间等待率上升越快。三者的交互关系在仿真里表现为“电梯平均完成一个乘客行程的时间”与“乘客到达间隔”的比值。我会先用固定客流做电梯数量的敏感性分析把电梯数从2慢慢增加到10记录AWT和LWP画出曲线后找到拐点。然后固定电梯数改变lambdaPerMin观察策略差异。这种“单变量扫描”能帮助识别代码中的隐藏缺陷比如电梯总是空驶到楼层而没有带上乘客这类问题只有在某个参数区间才会显性出现。5. 群控仿真输出的统计检验与可视化验证5.1 电梯运行轨迹图检查空驶与扎堆电梯群控仿真不是跑完并记录AWT就结束还要检查每部电梯是否出现不合理的空驶或连续扎堆。我常用一张楼层-时间图来观察横轴是仿真时间纵轴是某部电梯的楼层。stairs命令最适合画这种阶跃轨迹因为电梯楼层变化是离散的。subplot(2, 1, 1); stairs(timePoints, floorHistory(:, 1), LineWidth, 1.2); hold on; stairs(timePoints, floorHistory(:, 2), LineWidth, 1.2); xlabel(时间 (s)); ylabel(楼层); legend(电梯1, 电梯2);绘图时只选两到三部电梯否则轨迹混在一起看不出规律。如果两条阶梯线经常重合说明策略倾向于把多部电梯派到同一层此时应回看代价函数中“顺路捎带”的权重是否过低。如果一个电梯长时间保持静止但外部呼梯仍然存在说明分配函数没有把呼梯派给空闲电梯。5.2 等待时间直方图与箱线图比较调度算法差异等待时间分布通常是长尾的只看平均值容易掩盖极端情况。我建议把同一个工况下的两组策略等待时间做成箱线图并排显示这样中位数、四分位距和离群点一目了然。figure; boxplot([waitingTimeA, waitingTimeB], {集选控制, 动态分区}); ylabel(等待时间 (s));注意boxplot输入向量长度不同时需要用NaN补齐较短的一组。更稳妥的做法是使用groups形式的输入即把两组合并为一个列向量和一个分组标签向量。写入代码时我体会最深的是箱线图中的长尾点往往代表LWP超标而LWP是群控仿真中比AWT更敏感的指标。5.3 使用配对t检验做统计显著性验证多组随机客流下的AWT有随机波动直接比较均值会得出误导性结论。我的习惯是做20组不同随机种子的仿真记录两组策略在相同种子下的AWT序列然后用配对t检验判断差异是否显著。[h, p] ttest(awtStrategyA, awtStrategyB); fprintf(p %.4f, h %d\n, p, h);ttest的原假设是两组均值相等h1表示拒绝原假设即两组存在显著差异。这里必须保持同一随机种子否则两组仿真面对的乘客到达序列不同配对检验的前提就不成立。随机种子变化时AWT序列整体平移但只要配对关系不变检验结论仍然可信。6. 用极端用例与手算对照做最后的群控仿真验证群控仿真最容易出现“看着运行正常实际逻辑错误”的情况因此我会在正式实验前用边界用例做验证。最简单的是单电梯模型把numElev改为1关闭群控调度只让电梯按先到先服务顺序运动。由于系统退化为单电梯排队模型AWT可以用手算方式估计检查仿真输出是否落在理论范围内。另一个有效的边界用例是全体乘客从1楼前往顶层。此时电梯在早高峰的负载达到最大值所有电梯都应向1楼运动同一时刻可能有多部电梯在1楼排队。如果调度函数把呼梯分配给了已经满载的电梯elLoad就会超过capacity。为此我在仿真循环里显式加入容量判断只有elLoad(k) capacity的电梯才允许接收新目标若所有电梯都满载则该呼梯留在外部队列中等待。这个逻辑缺失会让AWT偏小因为乘客被错误地塞进并不存在的空闲空间。还有一个针对事件风暴的验证技巧把simTime设为10秒lambdaPerMin设为300制造极高密度的乘客到达。如果主循环内出现某个数组越界那么大概率发生在呼梯分配分支如果运行期间出现NaN楼层则需要检查电梯到站时targets是否被清空。经过这三类边界验证后仿真平台才算达到可以比较调度策略的状态后续调参增益也就有了可信前提。本文还有配套的精品资源点击获取