十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

MATLAB公交线路仿真:从发车表到车辆运行模型

MATLAB公交线路仿真:从发车表到车辆运行模型 简介面向交通仿真与城市公交优化的MATLAB建模资源适合具备一定编程基础的交通专业学生、科研人员及调度策略开发者使用。压缩包内仅包含一个M脚本整包约两KB却完整实现了公交线路仿真中的车辆行驶与乘客上下车两大模型车辆模型通过微分方程刻画车速、加速度、道路阻力和交通信号约束乘客模型基于离散事件仿真用泊松分布模拟到站客流以均匀或指数分布描述上下车时间。脚本整合了状态机、事件调度与主循环可直接修改参数并运行便于观察不同道路条件和发车方案下的系统表现。已有256人学习下载。借助这份源码读者能快速搭建仿真原型计算平均候车时间、车辆占用率等关键指标并测试调整发车间隔、增减线路等策略为公交系统评估与改进提供量化支撑。1. 公交线路仿真从发车表到车辆运行模型公交线路仿真的核心不是把线路画出来而是用一组可调参数描述“车在什么时刻到哪一站、停多久、能上多少人”然后让这批车在电脑里跑上几个小时产出一组能指导排班和调度的指标。直接拿 Excel 拉公式也能算平均间隔但一旦涉及随机波动乘客到达、站间延误、上下客时间、车辆互相影响以及满载带来的过站就必须上仿真。MATLAB 做这件事的便利在于它自带的随机数、统计函数和画图工具能让你把模型从脚本快速推向实验。这个 bus_line_model 适合三种场景运营方案对比高峰期加车 vs 区间车、线路参数标定确定合理发车间隔、以及算法验证比如动态调度策略。它不追求渲染场景也不需要接入实时数据只要你和业务方认可用平均值和分布来描述现实即可。下面从模型边界开始讲。2. 建立公交线路模型的三个核心要素站点、车辆与乘客到达2.1 站点与线路拓扑的数据结构做公交线路仿真第一步不是写代码而是决定用数据模型表示一条线。常见做法是定义两个数组站点列表stops和区段行驶时间tripTime。站点列表用一维坐标或序号表示顺序区段时间数组表示“从第 k 站开到第 k1 站”的期望用时。线路类型分为两种往返型车到终点后原路返回和环型所有站点首尾相接。在 MATLAB 里环型实现起来更省事因为车辆永远不会“下线”只需用mod(index, N)处理下一站索引。我一般用struct而不是多个分散变量来保存线路数据这样往函数里传参方便。下面是一个最小站点表的结构字段类型说明s.iddouble站点编号1 到 Ns.namestring站名仅调试用s.demanddouble单位小时乘客到达数人/小时s.alightProbdouble到达本站的下车概率占车上人数比例tripTime(k)double从站点 k 到下一站的基础行驶时间秒在实际项目中tripTime不应是固定值而应从历史 GPS 轨迹里取每个区段的均值和标准差然后用随机数生成每一次实际通行时长。s.demand也要按高峰/平峰拆成多个时段值仿真时根据当前时间选用对应参数。这样建出来的模型才具备对真实线路的还原度而不是一个“会动的图”。2.2 车辆运行时间与 dwell time 的随机建模车辆从 A 站驶向 B 站的时间受红绿灯、拥堵和上下客影响。一般把站间行驶时间建模为正态分布并做截断actualTime max(mu sigma * randn, 0.5*mu)。不建议用均匀分布因为现实中极端延误虽然少但会造成车辆串车后车紧跟前车这是公交仿真里最需要捕捉的现象。方差的设定直接决定串车发生频率方差越大越容易发生后车追上前车导致前车更挤、后车更空。停站时间dwell time与上下车人数强相关最常用的线性模型是dwell c0 c1 * board c2 * alight其中c0是基础开门关窗时间约 2 秒c1是每位上车乘客平均耗时约 0.8 秒c2是每位下车乘客平均耗时约 0.6 秒。实际中乘车人数多时上下车会并行因此有人会改用max(board, alight)而不是两者相加。在 MATLAB 里计算时建议对dwell向上取整到整数秒同时设置最小值为 5 秒避免因乘客数少导致停靠时间变成负数。2.3 乘客到达率与上下客流量生成乘客到达的经典假设是泊松过程即单位时间到达人数服从泊松分布poissrnd(lambda * dt / 3600)。其中lambda是每小时平均到达率dt是仿真时间步长秒。这个公式对 dt 较小时近似成立但如果dt过大比如 60 秒一次累积生成的人数偏多会掩盖乘客到达的随机波动因此 dt 不要超过 5 秒。下车乘客建模有两种方式一种是用 OD 矩阵出发站×到达站的比例另一种是简化为“每站按概率下车”。OD 矩阵精度高但标定成本高我通常先用下车概率做预研后续有刷卡数据再升级。下车人数生成用二项分布binornd(onboard, alightProb(s))。这里要注意alightProb需要满足整条线路各站概率之和小于等于 1实际会略小于 1因为部分乘客坐满全程。3. MATLAB 实现最小可运行 bus_line_model事件驱动还是时间步进3.1 时间步进法的主循环框架公交仿真有两种流派事件驱动和时间步进。事件驱动只在车辆到站、发车等离散时间点更新状态效率高时间步进在每个固定步长如 1 秒检查所有对象状态逻辑简单、容易加规则。对于小时级的线路仿真时间步进法的计算量完全可以接受所以我推荐先用时间步进跑通再根据需求决定是否重构。主循环框架如下仿真时间从 0 到 T 秒每一步依次处理“新乘客到达 → 车辆运行状态转移 → 乘客上下车”。下面是 MATLAB 代码的最小实现骨架注意这个版本环型线路车辆从站点 1 发车后按 1→2→…→N→1 循环行驶。% bus_line_sim.m 最小可运行的公交线路仿真时间步进法 clearvars; clc; rng(2025); % 固定随机种子保证可复现 % ----- 线路与车辆参数 ----- N 10; % 站点数 M 5; % 车辆数 H 300; % 发车间隔秒 T 3 * 3600; % 总仿真时长秒 dt 1; % 时间步长秒 capacity 80; % 单车容量人 baseDwell 2; % 基础停靠时间秒 dwellPerPerson 0.8; % 上下一位乘客耗时秒 % 区段基础行驶时间tripTime(s) 表示从站点 s 到下一站 tripTimeMu 80 * ones(1, N); tripTimeSigma 15 * ones(1, N); lambda [20 80 120 50 30 10 15 25 60 5]; % 各站乘客到达率人/小时 alightProb [0 .1 .2 .1 .05 .1 .15 .1 .05 .15]; % 各站下车概率 % ----- 更新数据结构 ----- waiting zeros(1, N); % 各站候车人数 events []; % 记录事件: [时间, 车辆, 站点, 上车数, 下车数, 载客数] % 初始化车辆结构数组 for i 1:M veh(i).id i; veh(i).state idle; % idle: 尚未发车; running: 行驶; dwell: 停靠 veh(i).atStop 1; % 当前所在站点或最近经过的站点 veh(i).tRemain 0; % 运行剩余时间秒 veh(i).dwellRemain 0; % 停靠剩余时间秒 veh(i).onboard 0; % 载客数 veh(i).departTime (i-1) * H; % 预定发车时间 end % ----- 主循环 ----- for t 0:dt:T % 1. 更新乘客到达泊松过程 waiting waiting poissrnd(lambda * dt / 3600); % 2. 更新每辆车状态 for i 1:M v veh(i); if strcmp(v.state, idle) % 等待发车 if t v.departTime % 在起始站完成上下客后发车 [v, waiting, events] handleStop(v, 1, ... alightProb(1), capacity, waiting, events, t, baseDwell, dwellPerPerson); v.state running; v.tRemain tripTimeMu(1) tripTimeSigma(1) * randn; v.atStop 1; end elseif strcmp(v.state, running) % 行驶中 v.tRemain v.tRemain - dt; if v.tRemain 0 % 到达下一站 nextStop mod(v.atStop, N) 1; [v, waiting, events] handleStop(v, nextStop, ... alightProb(nextStop), capacity, waiting, events, t, baseDwell, dwellPerPerson); v.state dwell; v.dwellRemain max(5, ceil(baseDwell dwellPerPerson * ... (events(end,4) events(end,5)))); % 按上下车人数延长时间 end elseif strcmp(v.state, dwell) % 停靠中 v.dwellRemain v.dwellRemain - dt; if v.dwellRemain 0 v.state running; v.tRemain tripTimeMu(v.atStop) tripTimeSigma(v.atStop) * randn; end end veh(i) v; end end代码里的handleStop是一个子函数负责在车辆到达站点时执行下车、上车、容量限制和事件记录。由于篇幅这里不展开全部行但其逻辑固定先下车再按剩余空间决定上车人数超出的乘客留在候车区。主循环中的events是 n×6 矩阵每行保存[时间, 车辆id, 站点, 上车数, 下车数, 载客数]后续所有指标都从它提取。3.2 车辆位置、站间行驶与停站逻辑上面的主循环里车辆状态机只有三个状态但有一个关键规则必须写对车辆在running状态减到 0 后必须先执行handleStop里的上下客再切换到dwell而不是先 dwell 后上下客。实际运营中上下客发生在停稳那一刻随后才计时停靠。我见过不少仿真把顺序写反导致乘客会在车辆已经离站后才上车结果指标全乱。另外起点站的发车逻辑要单独处理。上例中所有车辆初始状态是idle到点后在起点站执行一次上下客然后直接出发相当于把“起始站停靠”压缩到发车瞬间。如果起始站也有候车乘客这里就自然计入。注意tripTimeMu(N)表示从站点 N 到站点 1 的时间因此环型线路的行驶时间数组长度必须是 N而不是 N-1这是一个容易数组越界的地方。容量限制的实现按以下顺序先alight min(onboard, binornd(onboard, alightProb))后board min(waiting, capacity - (onboard - alight))。这里waiting是当前站的候车人数binornd的用法是每次抽样确定多少人下车。如果车辆满载board为 0后续乘客只能等下一班队列逐渐积累。这个积累过程正是串车现象在指标上显现的原因。3.3 输出指标准点率、满载率与平均候车时间仿真跑完后events矩阵包含了每个站每次车辆到达事件。从它可以计算三个核心运营指标含义如下表指标计算方式用途车头时距均值对同一站点按时间排序计算相邻到达时刻差判断发车稳定性准点率车头时距落在[0.8H, 1.2H]区间内的比例衡量服务可靠性平均候车时间若乘客到达均匀则等于mean(车头时距)/2服务质量核心指标满载率所有离站事件中载客数/容量的平均值评估运力配置在 MATLAB 中提取这些指标很简单% 假设 events 已在主循环中生成 % 计算每站车头时距 stations unique(events(:,3)); headwaySum 0; for s stations idx (events(:,3) s); times sort(events(idx,1)); hw diff(times); hw hw(hw 0); % 去掉无意义零间隔 headwaySum headwaySum mean(hw); end meanHeadway headwaySum / length(stations); meanWait meanHeadway / 2; fprintf(平均候车时间: %.1f 秒\n, meanWait);注意这里用mean(hw)/2是建立在乘客到达率恒定且车辆按时通过的前提下如果仿真中高峰时段有变化更好的做法是直接模拟乘客个体到达时间。但对于初步方案对比这个近似已经够用。满载率可以直接取events(:,6)对capacity求平均。4. 参数标定与仿真实验设计发车间隔和客流密度怎么设4.1 关键参数表与敏感度分析仿真模型的可信度取决于参数标定。下表列出 bus_line_model 中最关键的输入参数、建议标定来源和常见取值范围参数符号含义标定方法常见范围tripTimeMu区段平均行驶时间从车载 GPS 轨迹提取按时段求均值30~180 秒tripTimeSigma区段行驶时间标准差同一数据源求标准差5~30 秒lambda站点到达率刷卡/OD 数据或人工调查5~150 人/小时alightProb本站下车概率刷卡数据下车人数/车上人数0.02~0.3capacity车辆最大载客数车型参数70~120dwellPerPerson单人上下车耗时视频观察或实测0.5~1.5 秒敏感度分析的作用是找出哪些参数对指标影响最大。最常见做法是固定其他参数让H发车间隔从 180 秒扫到 600 秒观察平均候车时间和满载率的变化。MATLAB 用 for 循环批量执行即可headwayList 180:60:600; result zeros(length(headwayList), 2); for k 1:length(headwayList) H headwayList(k); % 重新运行仿真函数 busLineSim(H, ...)返回指标 [meanWait, loadFactor] busLineSim(H, ...); result(k, :) [meanWait, loadFactor]; end plot(headwayList, result(:,1), -o); xlabel(发车间隔 (秒)); ylabel(平均候车时间 (秒));注意运行仿真函数前要调用rng保持同一随机种子否则两次仿真的随机波动会掩盖参数变化的影响。对于更正式的敏感度分析可以用 MATLAB 的set扰动法即每次只改一个参数为 ±20%记录指标的变化百分比。4.2 用 MATLAB 批处理跑多组场景实际项目中很少只跑一条曲线更多是同时比较“现状方案”“高峰期加 2 辆车”“缩短发车间隔到 240 秒”“区间车调度”等场景。这时需要把仿真主体封装成独立函数接收参数结构体并返回指标。封装的好处是场景参数可以用表格组织批量执行时一行代码搞定。例如设计一个参数表scenarios场景名MH区间车区间base5300无shortH5240无addBus6270无zone5300站点 3~7批量脚本可以用for遍历表格将结果写入输出矩阵。这里要注意如果某个场景新增了“区间车”逻辑需要在仿真主循环里额外判断车辆是否到达区间终点。一般我建议将调度规则也作为参数传入这样完全没有必要为每个场景复制一份仿真函数。批处理的一个常见坑是内存增长。events会随时间增长跑 100 次仿真可能攒下几十万行后续分析变慢。解决办法是每次仿真结束后立即计算指标只保存指标值不保留原始事件矩阵除非需要详细回溯。4.3 结果可视化与对比时空图与候车时间分布公交仿真最有说服力的输出不是数字而是时空图。时空图纵轴为站点编号横轴为时间每辆车的轨迹是一条斜线斜线水平段表示车辆停站。从图上可以直观看到串车两条斜线几乎平行或相交说明后车追上了前车。绘制时空图的代码片段figure; hold on; for i 1:M % 从 events 中提取车辆 i 到站时刻和站点 idx (events(:,2) i); plot(events(idx,1)/60, events(idx,3), .-); end xlabel(时间 (分钟)); ylabel(站点编号); grid on;除了时空图候车时间分布可以用直方图画出。由于events里没有个体乘客可以用车头时距的一半作为近似样本也可以用histogram(events(idx,1))展示到站间隔分布。如果模型支持个体乘客级仿真则可以绘制真实的候车时间分布直方图用于对比不同调度方案的公平性而不仅看平均值。5. 进阶加速仿真与验证模型的三个技巧5.1 用 vectorization 替代 for 循环加速时间步进法的瓶颈在于每步遍历所有车辆。当车辆数和仿真时长变大时例如 20 辆车跑 24 小时dt1的循环会执行 86400 次内层再循环车辆总迭代次数几百万MATLAB 会比较吃力。一个简单的加速技巧是把“更新乘客到达”向量化已经在上例中实现。车辆循环目前很难完全向量化因为车辆之间存在先后顺序导致的依赖但可以用parfor并行化“不同 H 值的场景”而不是并行化单次仿真。另一种做法是把dt适当增大到 2 秒前提是停站时间和行驶时间都取整到秒。这样总迭代次数减半误差小于 0.5%。如果你需要极快的实验速度可以考虑把仿真函数转成 MEX 文件但收益有限通常场景并行已经足够。5.2 与 Simulink 联合仿真的边界有人会问为什么不用 Simulink 里的 SimEvents 做离散事件仿真答案是 SimEvents 的学习成本和配置成本更高适合复杂的排队网络或资源竞争模型。公交线路如果只是定时发班、单线循环用脚本仿真更透明。当模型升级到多线路换乘、车辆充电调度、实时控制策略时SimEvents 的优势才开始体现。我的建议是先用纯 MATLAB 把逻辑验证清楚再考虑是否迁移到 Simulink。如果你需要与 Simulink 联合可以在 Simulink 里调用 MATLAB Function 模块将核心状态更新函数嵌入。注意仿真时间同步问题Simulink 的固定步长求解器与我们的dt需要一致否则会出现状态更新不匹配。同时 MATLAB Function 模块里的代码不支持随机数流rng直接控制需要在函数外传入随机种子或者用coder.extrinsic(randn)声明为外部调用。5.3 用随机种子做可复现性验证仿真里的随机性来自randn、poissrnd和binornd它们都受全局随机流影响。如果不在仿真前固定种子每次运行结果都会不同这会导致实验对比失效。正确做法是给仿真函数增加一个seed输入参数在函数开头调用rng(seed)。批处理时每个场景可以固定同一个种子也可以给不同种子运行多次后取平均后者用于评估随机波动。推荐的可复现性验证套路是对同一组参数分别运行 1 次和 20 次比较指标均值的差异。若 20 次平均后的结果与单次相差 5% 以内说明单次仿真已足够代表系统若超过 10%说明随机性过大需要加长仿真时间或多次取平均。这个验证只花几秒钟但能避免你拿着一次偶然的仿真结果去调整运营方案。本文还有配套的精品资源点击获取
返回列表