拓冰建站拓冰建站
首页 / 资讯中心 / 正文

用CTM在MATLAB中快速复现交叉口排队:细胞传输模型原理与配时优化实践

简介MATLAB环境下基于元胞传输模型CTM的交通流模拟项目聚焦交叉口交通管理与控制适用于交通工程研究人员、工程师及相关专业学生。项目提供完整的CTM模型构建、仿真与结果分析代码涵盖细胞划分、交通信号周期与相位设置、车道/路段/交叉口构建等核心模块并通过单元格划分模拟车辆移动、停车与起步行为还附带基于四交叉口的构建与仿真示例能够直观反映不同信号控制策略下的通行效果。包内共45个文件包括30个MATLAB脚本核心仿真与辅助函数、11张说明示意图、3份Markdown文档含README、开发日志与许可证整体仅82KB轻量易用。内容同时涉及数据输入、仿真算法、延误与排队等指标读取以及信号控制优化思路可帮助读者深入理解CTM原理并直接用于交通控制策略的评估与优化。已有455人学习下载适合希望快速上手CTM仿真、开展科研或实际项目验证的用户。1. 用 CTM 在 MATLAB 里复现交叉口排队先别急着上微观模型交叉口瓶颈的本质是绿灯时空放、红灯时排队向上游蔓延这些现象不需要跟车模型也能算准。CTMCell Transmission Model元胞传输模型把道路切成等长细胞用一条流量-密度关系驱动细胞状态更新就能以足够精度模拟排队形成、消散和溢出的全过程。ctm_matlab-master这套 MATLAB 脚本把上述过程封装成了所见即所得的模块ctm_add_link搭路、ctm_add_int建交叉口、ctm_set_phase管信号、ctm_read_total_delay出延误指标。适合信号配时评估、瓶颈识别和交通流理论验证这三类场景。不需要额外工具箱基础 MATLAB 就能跑也不要求你精通微观仿真标定理解三角形基本图即可上手。2. CTM 细胞状态更新从三角形基本图到 ctm_simulation.m 的迭代循环2.1 细胞、连接器与三角形基本图CTM 的内存模型非常朴素一条道路被离散成若干等长细胞每个细胞在时刻 t 保存一个状态量——当前细胞内的车辆数 n(t)。细胞之间靠连接器传递流量连接器能传多少取决于发送细胞能放出多少以及接收细胞能吸收多少。要让这两个“多少”可计算需要先给定一条流量-密度q-k关系。CTM 最常用的是三角形基本图只要三个参数参数含义典型取值说明vf自由流速度 (m/s)16.7 (60 km/h) 或 11.1 (40 km/h)决定了细胞长度的基准kjam阻塞密度 (veh/m)0.1 0.14单车道每米最多能停多少辆车w反向波速 (m/s)4.5 5.6排队向上游蔓延的速度三个参数推导出通行能力c vf * kjam * w / (vf w)单位是 veh/s。这个值就是绿灯饱和流率在离散模型里的上限。参数不匹配会出现通行能力算出来比实际观测低 30% 的情况所以拿到包之后第一件事是验算这条基本图而不是直接跑交叉口。2.2 一个细胞三步更新发送、接收、传递ctm_simulation.m的每次迭代对每个细胞做同样的三件事顺序不能乱发送量发送细胞根据自身车辆数和自由流速度决定最多放出多少车s(t) min(n(t), c * dt)。接收量接收细胞根据剩余空间和反向波速决定最多接收多少车r(t) min((kjam * dx - n(t)) * dx / w, c * dt)。这里kjam * dx是细胞最大容纳车辆数。实际流量q(t) min(s(t), r(t))。把上述逻辑落到这个包的语境里ctm_simulation.m干的事情就是遍历所有连接器按“先算发送、再算接收、最后取小”的顺序更新流量再把流量加到下游细胞、从上游细胞扣掉。关键参数是时间步长dt和细胞长度dx它们必须满足 CFL 条件dx vf * dt否则一个时间步内车辆能跨过整个细胞模型直接发散。% 典型的单细胞更新逻辑示意 dt 2; % 仿真步长单位秒 dx 33.4; % 细胞长度 vf * dt约 33 米 n_cur 12; % 当前细胞车辆数 kjam 0.12; % 阻塞密度 veh/m c 1.8; % 通行能力 veh/s由基本图算出 s min(n_cur, c * dt); % 发送量细胞有多少车可放 r min((kjam * dx - n_cur) * dx / w, c * dt); % 接收量下游还能进多少 q min(s, r); % 实际通过连接器的流量这段代码的意义在于把 CTM 更新规则还原到了最容易排查的形式当排队向后蔓延时r项因为剩余空间变小而受限q会持续小于s表现就是交叉口上游细胞密度逐步抬高。如果限制在晚上 22 点后新建的??那类问题不重要重要的是这个“限制发生在接收端”的特征决定了排错时永远先检查下游细胞密度。2.3 用 ctm_read_cells 检查细胞状态是否按规则演化跑仿真只盯着总延误不够延误升高后要回答“哪里先堵的”这就得读细胞级状态。ctm_read_cells.m负责取出某个 link 或 lane 的全部细胞车辆数。cells ctm_read_cells(link, L1); plot(cells, o-); xlabel(细胞编号从上游到下游); ylabel(细胞车辆数); grid on;读取结果里如果看到下游细胞车辆数持续逼近kjam * dx而上游细胞还在增加说明排队正在向上游蔓延。ctm_read_cells返回的数组顺序是从路段起点到终点细胞编号越小越靠上游。通过连续记录多个时刻的细胞状态堆叠成时空图可以直接观察红灯期间排队波向上游移动的速度这个速度理论上应该接近w。3. 用 ctm_add_link / ctm_add_lane 搭路段的三个关键参数3.1 先把道路拆成等长细胞dx 与 dt 的关系ctm_add_link并不要求你手工离散道路它只接收道路长度和基本图参数内部按dx vf * dt自动分细胞。这里有一个容易踩的坑dt和vf的取值必须一致。比如vf 11.140 km/hdt 2秒dx 22.2米那么 400 米长的路段会被分成约 18 个细胞而不是按 33.4 米分。一般我会把dt设为信号周期最小公因子的约数。两相位交叉口周期 60 秒dt 2秒可以直接整除如果信号周期带 5 秒的黄灯和 3 秒的全红dt 1秒更稳妥。dt选大了会把绿灯启亮时刻挪到错误的时间点对比配时方案时会产生系统性偏差。3.2 ctm_add_link 与 ctm_add_lane 参数表ctm_add_link.m创建物理路段ctm_add_lane.m为路段添加车道并指定其在交叉口的功能。一个最小可用示例L_WB ctm_add_link(name, WB, length, 400, ... vf, 16.7, kjam, 0.12, w, 5.0, dt, 2, lanes, 2); L_EB ctm_add_link(name, EB, length, 400, ... vf, 16.7, kjam, 0.12, w, 5.0, dt, 2, lanes, 2); ctm_add_lane(L_WB, pos, 1, type, lane_in); ctm_add_lane(L_WB, pos, 2, type, lane_nor); ctm_add_lane(L_EB, pos, 1, type, lane_out);ctm_add_link里lanes决定基础通行能力按几倍计算ctm_add_lane中pos是在该 link 内从左到右的车道编号type则决定这条车道的拓扑角色。lane_in用于从交叉口进口道进入的车辆lane_out用于出口道lane_nor是中间普通路段。函数必填参数作用常见错误ctm_add_linkname, length, vf, kjam, w, dt创建离散化路段忘记把vf换算成 m/sctm_add_lanepos, type给 link 附加车道角色pos重复导致车道覆盖ctm_set_queue车道对象 排队值初始化排队初始排队超过细胞容量ctm_set_queue在初始化时很有用。ini_4int_queues.m的场景是给你预设好的四个交叉口进口排队比如早高峰东进口排队 30 辆车、北进口排队 0 辆直接调用ctm_set_queue写入对应车道即可。这个初始排队值会直接影响仿真前 3 分钟的输出做配时对比时务必保证每组实验初始排队一致。3.3 lane_in / lane_out / lane_nor 在交叉口场景里的分工在同一个交叉口中进口道必须用lane_in因为它需要和信号相位绑定绿灯时向交叉口内部细胞放行红灯时无论发送量多大都置零。lane_out的作用是接收从交叉口驶出的车辆its容量如果不给够会出现绿灯亮了但车流被出口堵住的现象这在现实中恰好对应“下游路口排队回溢”。lane_nor用于路段中间区域不直接参与交叉口相位判断。构建四交叉口网络时中间连接路段两端都是普通细胞用lane_nor即可但和交叉口相邻的细胞必须切换成lane_in或lane_out。检查方法是通过ctm_read_lanes.m列出当前所有车道及其类型确认没有遗漏。lanes ctm_read_lanes(); for i 1:numel(lanes) fprintf(Lane %s: link%s, type%s\n, lanes(i).id, lanes(i).link, lanes(i).type); end上面的循环逐条打印仿真网络的每条车道属性跑通前先做这一步可以拦截大量“进口道没配 lane_in 导致绿灯不生效”的初级问题。4. 交叉口信号相位注册ctm_add_int 到 example_4intersection.m4.1 两相位交叉口的 movements 定义int_2phase.png表示两相位控制的最简单结构相位 1 放行东西进口直行相位 2 放行南北进口直行。用ctm_add_int创建交叉口对象再用ctm_add_phase注册相位表。I1 ctm_add_int(name, I1, phases, 2); ctm_add_phase(I1, 1, green, 30, movements, {WB - EB, EB - WB}); ctm_add_phase(I1, 2, green, 20, movements, {NB - SB, SB - NB});这里green是绿灯时长单位秒。movements里的字符串必须和已经创建的 link 名称精确对应格式是“上游 link 名 - 下游 link 名”中间不能有多余空格。相位注册好后可以用ctm_check_phases.m做一致性校验比如检查绿灯期间是否有互相冲突的流向同时放行。4.2 完整的最小仿真脚本一个完整的最小单交叉口仿真流程如下ctm_setup(); % 清理工作区并加载默认参数 % 创建两条相交道路的进出口段 WB ctm_add_link(name, WB, length, 200, vf, 16.7, ... kjam, 0.12, w, 5.0, lanes, 2); EB ctm_add_link(name, EB, length, 200, vf, 16.7, ... kjam, 0.12, w, 5.0, lanes, 2); NB ctm_add_link(name, NB, length, 200, vf, 16.7, ... kjam, 0.12, w, 5.0, lanes, 1); SB ctm_add_link(name, SB, length, 200, vf, 16.7, ... kjam, 0.12, w, 5.0, lanes, 1); % 交叉口与两相位 I1 ctm_add_int(name, I1, phases, 2); ctm_add_phase(I1, 1, green, 30, movements, {WB - EB}); ctm_add_phase(I1, 2, green, 20, movements, {NB - SB}); % 进口道布置初始排队 ctm_set_queue(WB_in, 25); ctm_set_queue(NB_in, 10); % 开始仿真 ctm_start(); for t 1:120 % 120 个时间步dt2 秒共 4 分钟 if mod(t, 50) 1 ctm_set_phase(I1, 1); elseif mod(t, 50) 31 ctm_set_phase(I1, 2); end ctm_simulation(advance); end total_delay ctm_read_total_delay(); ctm_stop();这个脚本的时序逻辑是每 50 步100 秒一个完整周期第 1 步切相位 1第 31 步切相位 2。ctm_set_phase改变的是交叉口内部连接器的开关状态绿灯相位会赋予对应方向连接器全部通行能力红灯相位则把流量强制为 0。ctm_read_total_delay返回整个网络累计的车辆延误单位是秒延误 实际行程时间 - 自由流行程时间。4.3 多交叉口切换ctm_switch_int 的参数语义example_4intersection.m的场景里有四个交叉口仿真循环里不能只对I1设相位。每一条ctm_set_phase调用之前需要先用ctm_switch_int把当前操作对象切到目标交叉口。ctm_switch_int(I2); ctm_set_phase(ctm_read_phases(active), 1); % 读取 I2 的相位对象后切换ctm_switch_int只接受交叉口 name 字符串不接受句柄。切换后所有ctm_set_phase都作用于新交叉口直到再次切换。多交叉口配时的常见错误是忘记切回上一交叉口导致某一路口始终执行同一个相位延误指标虚高。建议在每个循环迭代末尾调用ctm_read_phases()打印当前活跃相位做一次状态审计。4.4 仿真前自检ctm_check_cells 与 ctm_check_phases这个包自带两个检查函数运行后会在命令行输出警告或错误。ctm_check_cells检查每个细胞的车辆数是否小于容量如果出现溢出它会提示是哪个 link 的哪个细胞一般是dt不满足 CFL 条件或者kjam设得过大。ctm_check_phases检查相位表的 movements 是否有缺失路段引用。跑长仿真前先调这两个函数能省下事后翻数据的功夫。5. 从延误到配时对比ctm_read_total_delay 与 ctm_read_lane_outputs 的组合5.1 四类读出函数对应四个输出维度ctm_read_*.m系列是这个包的数据出口。ctm_read_total_delay给全网络累计延误ctm_read_lane_delays给单车道延误ctm_read_lane_outputs给车道累计通过车辆数ctm_read_cells给断面密度。组合使用才能定位到信号配时的具体问题。函数返回内容单位典型用途ctm_read_total_delay全网络累计延误秒配时方案横向对比ctm_read_lane_delays每个车道的延误向量秒找出瓶颈车道ctm_read_lane_outputs每个车道累计驶出车辆数veh计算通过量与绿灯利用率ctm_read_cells指定 link 各细胞车辆数veh排队长度与回溢判断ctm_read_lane_delays返回的是一个与 lane 顺序对齐的数组顺序和ctm_read_lanes返回的 lane id 顺序一致所以最好按 id 映射而不是按下标硬编码。5.2 配时方案 A/B 对比的代码模板对比两个配时方案时重置和读数的顺序容易弄混。正确顺序是跑完一组仿真停下来读数据保存结果再调用reset_ctm清空所有细胞状态换相位参数后重新从头仿真。function result run_plan(plan_id) % 参数由外部结构体 plan 指定plan.g1, plan.g2 global plan; ctm_setup(); % 建立基础路网略与 4.2 节相同 I1 ctm_add_int(name, I1, phases, 2); ctm_add_phase(I1, 1, green, plan.g1, movements, {WB - EB}); ctm_add_phase(I1, 2, green, plan.g2, movements, {NB - SB}); ctm_start(); total_steps 600; for t 1:total_steps if mod(t, (plan.g1 plan.g2)) 1 ctm_set_phase(I1, 1); elseif mod(t, (plan.g1 plan.g2)) plan.g1 1 ctm_set_phase(I1, 2); end ctm_simulation(advance); if t total_steps result.delay ctm_read_total_delay(); result.outputs ctm_read_lane_outputs(); ctm_reset_delay(); % 清空累计延误准备下一组 end end ctm_stop(); end代码里ctm_reset_delay只清零延误累计不清空细胞车辆数。如果要做多组对比在调用该函数前把之前的结果存入结构体数组否则上一组的延误会叠加到下一组造成第二组数据整体偏大。5.3 延误跳变里藏着什么信息把ctm_read_lane_delays的结果按时间戳连续采样并画成曲线会有明显的阶梯状红灯期间延误线性上升绿灯期间回降。如果曲线在某一时间点突然斜率变陡说明排队长到了进口道起点上游细胞开始满负荷如果曲线变成水平直线往往是延误累计到了上限车辆无法进入仿真区域此时需要检查初始排队是否超过容量或者进口道长度是否不足以容纳排队。6. 从单交叉口到 2×2 路网build_ctm_4int 和 merge/diverge 的工程技巧6.1 四个交叉口的拓扑连接顺序build_ctm_4int.m构建的是四条道路围成的 2×2 网格每个方向由三段 link 组成西进口到交叉口 1、交叉口 1 到交叉口 2、交叉口 2 到东出口。连接顺序不能随意换先建交叉口对象再建 link 并把它和上下游交叉口绑定最后才注册相位。如果先注册相位再绑 linkctm_check_phases会因为找不到 movements 里的 link 名而报错。6.2 link_merge 与 link_diverge 的接线时机link_merge用在两条上游道路汇入同一条下游道路的节点link_diverge用在一条上游道路分叉到两条下游道路。在网格路网里支路汇入主路的位置必须显式建 merge 连接器否则两条上游道路的车辆会同时进入同一下游细胞流量超过细胞接收能力后发生虚拟溢出。M ctm_add_link(name, M1, type, merge, ... upstream, {WB, SB}, downstream, EB);merge 连接器的upstream是 cell 数组顺序影响优先权排在前面的上游道路在竞争下游空间时占优。实际标定时我一般把主路放在前面支路放在后面这样仿真出的主路优先行为更接近真实驾驶人的让行规则。6.3 批量实验前先做三件事多交叉口网络参数多批量跑配置对比前建议在脚本开头固定三件事ctm_clean_all清理所有历史对象reset_ctm重置全局状态并把每一组实验的plan结构体存盘。用ctm_clean_outputs清空上一轮输出文件避免后读的数据混入旧批次的结果。把四个交叉口的东进口改成merge结构再配一条短匝道是最快验证路网级配时方案的方法。本文还有配套的精品资源点击获取
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门