用MATLAB绘制列车运行图:从时刻表到自动化铺线与冲突检测

发布时间:2026/9/10 12:20:31
用MATLAB绘制列车运行图:从时刻表到自动化铺线与冲突检测 简介基于MATLAB的列车运行图绘制项目面向铁路调度、交通运输专业学生及MATLAB初学者提供一套从数据定义到图形可视化的完整示例。压缩包共7个文件包含3个M脚本、2个Fig界面、1个PNG效果图及1个说明文档大小约1.55MB。其中M脚本分别承担车站前后线路规划、交路划分等功能Fig文件提供GUI交互界面PNG直观显示全天候运行图示例文档则对实现过程进行说明。已有2309人学习下载。通过学习可掌握如何利用MATLAB GUI和绘图函数处理列车时刻、区间运行速度等数据生成全天候列车运行图同时理解项目结构组织和数据可视化思路为后续接入真实数据或扩展调度优化算法打下基础。整个项目代码简洁、注释清晰适合作为课程设计或交通仿真入门参考。1. 为什么用matlab绘制列车运行图而不直接用Excel铁路调度员拿到新版图定时刻后最常用的核对方式是打开Excel铺线看看有没有交叉。但列车运行图的本质是“时间-位移”图纵轴是车站而不是等距数值Excel折线图既做不出那种斜线疏密反映运行速度的效果也做不了批量铺线、自动冲突检查。用matlab写脚本把时刻表变成图只需要维护一张csv几百趟车的铺线、标注、冲突检测都能自动化。这篇博文面向需要处理列车时刻数据的人调度规则设计、信号方案制图、仿真建模前的数据可视化以及想用matlab替代手工制图的一线工程师。核心讲清楚数据如何映射成图坐标、停站折线如何绘制、冲突如何判断。2. 从时刻表到matlab数据模型先把列车运行图的坐标系定下来列车运行图和一般折线图最大的不同在于纵轴。y轴是车站而不是数值且车站之间的真实距离和区间运行时分通常不成比例——直接使用真实里程会让相邻大站的区间很长、小站区间很短画出来的线条要么贴着水平轴要么几乎垂直图面信息密度完全失衡。这个问题的解法是给车站重新分配“显示距离”让纵轴坐标反映运行时分和停站逻辑而不是物理长度。这是整个绘制流程里最容易被忽略却最影响成图质量的一步。2.1 用table组织列车时刻数据绘图前先决定数据载体。常见做法是用MATLAB的table结构来存时刻表而不是散落的cell数组或矩阵table自带变量名和行名后续用T(T.trainIDG101, :)筛选车次、按车站排序、合并追加数据都非常直接。另外如果你的原始数据存在csv里用readtable导入后修一下变量名就可以直接参与运算这也是从Excel转过来的数据最容易对接的方式。% 从时刻表csv导入数据readtable会自动生成表格 T readtable(timetable.csv); % 统一变量名方便后续脚本引用 T.Properties.VariableNames {trainID,station,arrTime,depTime,stopFlag}; % 将HH:MM字符串转成当天分钟数跨夜处理和区间计算都依赖数值型时间 T.arrMin minutes(duration(string(T.arrTime), InputFormat,hh:mm)); T.depMin minutes(duration(string(T.depTime), InputFormat,hh:mm));这个转换是后续所有计算的地基。duration是matlab内置的时间长度类型minutes把它转成纯数值单位是分钟也方便和其他数值直接做加减比较。这里有个小坑InputFormat里的hh必须小写因为大写HH在部分版本里表示12小时制导入“13:05”这类时刻会得到错误结果。如果csv里到发时刻为空比如通过列车不填终到站时刻duration会得到NaT转换结果就成了NaN绘图前需要单独处理。表格里推荐保留这些字段缺一不可变量名类型说明trainIDstring车次用于筛选和标注stationstring车站名绘图的y轴类别arrTimestring到达时刻起点站可为空depTimestring出发时刻终到站可为空stopFlaglogical是否停站决定是否画水平停站线arrMin / depMindouble由arrTime/depTime转换得到的分钟数2.2 车站纵坐标用“显示距离”而非真实里程定义y轴之前要先确认一个原则运行图纵坐标反映的是“车站顺序”和“区间相对运行时长”而不是“里程”。常见的实现方法有两种。第一种是等间距排站即不管区间长短相邻车站之间的距离都设为相同值。这种做法的好处是车站较密时图面整齐缺点是运行速度快慢在图中完全看不出来高速列车和慢车的斜率没有区分度。第二种是按区间运行时分占全线总时分的比例分配显示高度这也是我推荐的做法。2.2.1 按区间运行时分的比例分配坐标公式很简单区间显示距离 该区间运行时分 / 全线纯运行时分 * 总显示高度“区间运行时分”是指列车从A站出发到B站到达的实际行驶时间不含中间停站时间。这样画出来的斜线斜率直接反映旅行速度速度越快线条越陡在y轴方向上跨越快。全线开行的列车等级不同假如把每趟车都拿来算一遍区间时分再取平均图上线条会比较均衡。如果只拿标杆车算则慢车的斜线会偏平也是常见做法取决于你希望强调哪类列车。2.2.2 构造最小可用的坐标映射代码上可以用unique提取车站顺序再给每个区间赋一个权重用cumsum累加得到y坐标% 提取区间列表按图定顺序排序 stationList {A, B, C, D}; % 图定车站顺序 runMinutes [18, 12, 25]; % 区间纯运行时分 A-B, B-C, C-D % 将区间时分归一化并累加得到车站y坐标 runFraction runMinutes / sum(runMinutes); stationY [0, cumsum(runFraction) * 100]; % 总显示高度100个单位 stationY stationY - stationY(end) / 2; % 上下居中视觉更好参数说明stationList是站序数组可以写死也可以从时间表里unique(T.station, stable)按出现顺序自动生成runMinutes对应每一段的区间运行时分从运行图技术资料里读单位是分钟。得到stationY后把它和车站名一起存入containers.Map后续绘制每趟列车时直接按站名查坐标避免每次都用find搜索字符串几千条记录时效率差异明显。注意这里的示例把区间和车次绑定实际应用时一个区间有多趟车运行时分各不相同。我的做法是取所有列车在该区间运行时分的median作为权重这样既不会被某趟晚点数据带偏也不会因个别标杆车太快导致其他车线抖得太厉害。生成映射后最好再画一次纵轴网格检查一下相邻车站间距如果出现某一段特别大或特别小多半是权重里有问题需要回头检查区间时分数据。 ## 3. 用plot绘出列车运行线的最小实现 有数据、有坐标映射就可以画运行线了。这里的关键不是一次plot画一整条线而是理解列车运行线的形状列车在区间内是斜线停站时是一条水平线所以每一趟车在图上是由“斜线段 水平段”交替拼成的折线。如果直接把所有到发点按顺序连起来中间会多出本来不存在的斜线图就错了。下面给出拆点函数和调用方式。 ### 3.1 停站折线的绘图逻辑 核心思路把一条车次的所有到发事件逐个展开成坐标点序列只在“区间运行”段添加斜线只在“停站”段添加水平线。 matlab function [xs, ys] makeTrainLine(train, stationCoord) % train: 某一车次的时刻表table按车站顺序排列 % stationCoord: containers.Map站名-y坐标 n height(train); xs []; ys []; for i 1:n-1 % 区间斜线上一站出发到下一站到达 xs [xs; train.depMin(i); train.arrMin(i1)]; ys [ys; stationCoord(train.station{i}); stationCoord(train.station{i1})]; % 若下一站停站则添加水平停站段 if train.stopFlag(i1) 1 xs [xs; train.arrMin(i1); train.depMin(i1)]; ys [ys; stationCoord(train.station{i1}); stationCoord(train.station{i1})]; end end end这里train.station{i}是cell取字符串因为从csv导入后station列可能是cell数组。xs里的时间都是分钟数matlab的plot可以直接用数值轴不需要转成datetime。逻辑说明循环从第1个站处理到倒数第2个站每段先画区间斜线再看下一站有没有停站如果有停站就补一段水平线。这样输出的xs, ys就能直接传给plot得到一列车的完整运行线且线型连续、不用分段调用。调用方式示例figure; ax axes(NextPlot,add, YDir,reverse); % 遍历所有车次按车次分组绘制 [trainIDs, ~] findgroups(T.TrainID); for i 1:height(T) % 简化示意按车次分组 end % 实际推荐对每个车次调用makeTrainLine后plot hLine plot(ax, xs/60, ys, LineWidth, 1.2);注意YDir设为reverse让列车运行方向从上往下符合国内铁路运行图阅读习惯下行列车线从左上方延伸到右下方上行列车线相反。如果做行别分开的双线图则需要两个子图分别处理。3.2 把时间轴标成真实的HH:MMx轴坐标是分钟数绘图时如果不做处理刻度会显示成“0, 120, 240”之类的数字可读性差。matlab中xticklabels可以接受字符串数组因此只需要把刻度值转换成HH:MM字符串即可。我通常把刻度间隔设为60分钟或30分钟具体取决于铺线密度。% 时间轴刻度从0点到24点每60分钟一个刻度 xticks(0:60:1440); xticklabels(arrayfun((t) sprintf(%02d:%02d, floor(t/60), mod(t,60)), ... 0:60:1440, UniformOutput, false)); xlim([0, 1440]);说明arrayfun把0到1440分钟的数值循环格式化成字符串sprintf中%02d保证分钟数小于10时还是两位显示。这样图面上x轴显示的是“00:00”、“01:00”等可读时刻。xlim直接锁定全天范围避免涨缩。3.3 用颜色与线型区分上下行和列车等级一张图几十上百条折线不区分会画成“一团黑线”。最常见的分法上行一个颜色下行一个颜色再按高普速设定线型或线宽。颜色和线型的映射适合用containers.Map做查表避免写大量if-else。% 定义颜色表上行蓝色、下行红色 directionColor containers.Map({UP,DOWN}, {[0 0.4470 0.7410], [0.85 0.3250 0.0980]}); % 定义线型表高速列车实线普速列车虚线 serviceTypeStyle containers.Map({G,C}, {-, --}); % 绘制时按车次属性取线型和颜色 lineSpec directionColor(UP); plot(ax, xs/60, ys, Color, lineSpec, LineStyle, serviceTypeStyle(G));参数说明颜色值用的是matlab默认的colororder中的蓝和橙印刷时颜色对比度高。containers.Map查找速度比strcmp循环快得多而且代码里可以统一管理。如果某次调整需要交换配色只改Map里的值就行。4. 铺线冲突检查与常见绘制问题处理运行图画出来只是第一步真正要用起来还得解决几个实际问题同一区间两列车是否交会、凌晨跨夜列车的时刻如何展开、图面线条太密怎么抽线、中文字体和导出清晰度怎么保证。这四类问题几乎每次画图都会遇到处理思路通用性很强。4.1 用插值判断两列车是否在区间交会两列车的运行线在同一区间内如果不该交会却出现交点意味着铺线冲突通常表现为同向列车在区间内速度倒挂或追踪间隔不足。几何求交能做但更稳健的工程做法是把时间离散化在同一个时间网格上插值比较纵坐标。原因很简单列车时刻数据的密度远小于区间长度原始采样点之间没有交点并不代表实际运行线不交错插值后比较才能捕捉到细小的冲突。% 取两列车数据示意trainA、trainB是已经展开好的运行线坐标 tGrid (0:1:1440); % 1分钟间隔的时间网格 yA interp1(trainA.xs, trainA.ys, tGrid); yB interp1(trainB.xs, trainB.ys, tGrid); % 找到纵坐标差小于阈值的点 conflictIdx find(abs(yA - yB) 0.5); if ~isempty(conflictIdx) warning(列车A与列车B在%d分钟附近可能存在冲突, conflictIdx(1)); end参数说明interp1默认线性插值正好符合运行线斜连线的假设阈值0.5代表两条运行线在纵轴上间距小于0.5个显示单位这里的显示单位来自2.2.2节stationY中总高100的坐标具体阈值需要根据图面高度调整。如果想更严谨可以把时间网格加密到10秒即(0:1/6:1440)代价是计算量增加。这个检查可以封装成一个函数输入若干车次的xs/ys两两比较输出冲突车次对和时间点。4.2 凌晨跨夜列车的时间轴展开处理列车运行图里总有00:30、01:10这类凌晨发车车次。如果直接把它们和白天车次画在同一张图上凌晨的折线会堆在左侧起点看起来像“凭空出现”其实是因为一个重要的时间约定列车运行图是一个周期循环图00:30发车的列车应该画在前一天的下午或晚上开始铺线而不是从当天0点开始。因此需要把出发时刻大于某个阈值通常是12:00的时刻减掉1440分钟或者反过来说把凌晨的时间加上1440分钟后放到图尾形成跨日连续线。% 调整凌晨发车车次的时间轴 T.depMin(T.depMin 300) T.depMin(T.depMin 300) 1440; T.arrMin(T.arrMin 300) T.arrMin(T.arrMin 300) 1440;说明阈值300代表凌晨5点前的所有时刻都视为跨夜。加上1440分钟后凌晨1点的车次会画在x轴的1440601500分钟位置而xlim此时要扩展到1680即次日4点才能显示完整。绘图时xticklabels依然会显示成“25:00”这种不直观的格式所以还需要对超过1440的刻度做取模显示xticklabels(arrayfun((t) sprintf(%02d:%02d, mod(floor(t/60),24), mod(t,60)), ... 0:120:1680, UniformOutput, false));这样x轴在24:00之后重新显示为00:00、01:00但实际坐标仍然递增跨夜线条不会折回原点。这是运行图绘制最容易出错的地方忘了扩展xlim或者忘了取模都会让跨夜车次看不清始发和终到关系。4.3 图面线条太密时的抽线与分窗口策略当一个区段日均开行列车超过100对全图铺满会比蜘蛛网还密这时候谈配色意义不大。实际绘图前需要先加过滤条件比如只画高速列车、只画下行方向、只画某个时间段窗口。我的习惯是提供三个独立控制参数用逻辑索引筛选后再绘制。% 抽线控制参数方向、列车等级、时间窗口 dirFilter T.Direction UP; typeFilter contains(T.TrainID, G); timeFilter T.depMin 360 T.depMin 1080; % 6:00-18:00 Tdraw T(dirFilter typeFilter timeFilter, :);筛选后再按车次分组绘图速度会快很多。如果只是需要看局部区段的铺线则更推荐对y轴坐标区间做截取把不关心的车站直接删掉而不是缩小坐标范围去“放大”因为删除车站后剩余车站的显示距离需要重新计算否则线条斜率会失真。4.4 中文字体乱码与图片导出模糊的问题matlab默认字体在中文环境里经常出现方框乱码尤其输出为png图片发给别人时看不见站名等于废图。通常在脚本开头设两行全局字体能解决80%的情况set(groot, defaultAxesFontName, Microsoft YaHei); set(groot, defaultTextFontName, Microsoft YaHei);如果系统里没有微软雅黑改成SimHei或Noto Sans CJK SC。这个设置对title、xlabel、text和legend都生效。导出清晰度方面saveas默认分辨率偏低推荐用exportgraphicsexportgraphics(gcf, train_diagram.png, Resolution, 300);该函数在不同版本中表现稳定也比print -r300更好控制边框大小。如果输出的是矢量图exportgraphics直接导出pdf用print -dpdf的兼容性次之。一个隐藏坑exportgraphics在导出包含datacursormode交互对象的图窗时会报错需要先关掉交互再导出。5. 提升matlab列车运行图可读性的三个实用技巧运行图画完不是终点要在讨论会上给人看、让人能快速查车次和时刻还需要加一层交互和辅助信息。下面三个技巧成本低、改动小但实际用起来差距非常大。5.1 给列车运行线挂上DataTipmatlab的plot返回的线条对象支持DataTipTemplate属性鼠标悬停时能显示自定义文本比用text逐个标注车次号码清楚得多也不污染图面。常见做法是绘制完后给每根线的DataTip模板绑定车次和起终站。hLine plot(ax, xs/60, ys, LineWidth, 1.0); % 构造DataTip模板显示车次和车站名称 dTip datatip(hLine); dTip.Interpreter tex; dTip.Label {sprintf(%s, trainID), ... sprintf(A站-B站)};注意datatip必须在line对象存在于图窗之后创建且图中不能开启hold off后的清理操作。显示文本用sprintf拼装内容可以是车次、方向、运行等级等。这样开会演示时鼠标移上去就能看到是哪一趟车不需要肉眼找图例。5.2 在运行图右侧叠加开行密度柱状图列车运行图看多了之后人会自然地关心“某时段到底开了多少趟车”单纯数线并不直观。一个很实用的办法是在主图右侧用一个小子图展示按小时统计的开行对数与运行图共享y轴时间范围。实现方式是用histcounts统计发车时刻落在哪个小时区间。% 统计每个小时的发车数量 edges 0:60:1440; counts histcounts(T.depMin, edges); % 新建子图并翻转y轴与主图的24小时时间轴对齐 axHist axes(Position,[0.88 0.11 0.08 0.8], YDir,reverse); bar(axHist, counts, (edges(1:end-1)30)/60, 50); set(axHist, YTick, []); xlabel(axHist, 对数);histcounts数据是没有问题的唯一要注意的是跨夜车次必须先做过4.2节的时间展开否则凌晨3点的车会被分到当天开头而不是前一天末尾。柱状图放在主图右侧后一眼就能看出早晚高峰集中在哪几个时段。5.3 按需导出不同版本的图片文件日常调试用png评审和存档用矢量图。exportgraphics支持png和pdf其中pdf在文档排版里是矢量不会因为缩放而模糊。如果合作方一定要eps用print导出更稳妥。% 导出为png分辨率300DPI exportgraphics(gcf, train_diagram_png, Resolution, 300); % 导出为pdf矢量 exportgraphics(gcf, train_diagram.pdf, ContentType,vector); % 兼容旧版同事导出eps矢量图 print(gcf, -depsc2, train_diagram.eps);三个导出方式在同一个图窗上执行多次没有任何副作用所以可以写成一个导出按钮回调函数。最后一个细节导出之前把figure的窗口大小固定好用set(gcf,Position,[100 100 1600 900])设置像素尺寸否则不同机器上导出结果比例差异很大——图形坐标是相对值但文字大小会随图窗尺寸缩放。固定尺寸后注释、标题和坐标轴标签的显示比例就会一致。本文还有配套的精品资源点击获取

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询