SUMO交通信号自适应控制:五套算法仿真对比框架详解

发布时间:2026/10/29 0:39:53
SUMO交通信号自适应控制:五套算法仿真对比框架详解 简介一套基于SUMO仿真平台的多算法交通信号自适应控制项目面向智能交通方向的研究者、工程师及进阶学习者用于对比DQN、DDPG、韦氏算法、最大压力控制与自组织交通灯在交叉口信号优化中的实际表现。压缩包共65个文件核心为37个Python脚本模型训练、神经网络、信号控制、指标统计另有Shell训练脚本、SUMO路网配置sumocfg/xml、可视化输出图与说明文档整体仅2.94MB便于直接开展实验。已有46人学习/下载。资料内含单/双交叉口仿真场景、完整的强化学习环境封装、调参优化脚本及结果图表可支撑从算法建模到效果对比的完整流程其中DQN与DDPG侧重深度强化学习策略韦氏、最大压力与自组织方法提供了传统及分布式控制参照适合用于课程设计、毕业课题或个人研究能帮助快速理解各类控制策略的建模思路与运行机制。1. SUMO交通信号自适应控制为什么我把五套算法放进同一个仿真框架里对比一个十字路口车流早晚高峰差异大固定配时很容易出现一边空放、一边排长队的局面。把DQN、DDPG、最大压力、自组织交通灯和韦氏算法放到同一套SUMO路网里跑是验证“学习型控制是否真的比启发式控制强”最直接的做法。这份资源给的不是训练好的成品而是一整套可跑的实验框架net.xml定义路网sumocfg定义仿真参数src下的Python模块负责训练与指标统计根目录的Shell脚本负责启动训练和批量出图。适合两类人一类是交通工程方向的研究生需要快速拿强化学习和传统控制方法做对比实验另一类是刚接触强化学习落地的工程师想找一个能跑DQN与DDPG的仿真环境。我拆完之后的建议是先不要动任何算法文件把single.sumocfg跑通再往里面加自己的状态和奖励。注意这套资源按个人学习用途开放实验归实验别直接把里面的代码封装到商业项目里。2. 项目结构与算法边界从run.py到五套控制器的选型取舍2.1 目录与文件分工训练入口、路网与仿真模块各管一段拿到压缩包先不要急着运行先看目录。根目录的run.py是总入口负责解析算法类型、加载路网配置、拉起SUMO并启动训练或评估train_dqn.sh和train_ddpg.sh是Shell启动脚本封装了DQN和DDPG两种强化学习算法的训练命令hp_optimization.py做超参搜索gen_results.sh批量跑算法对比graph_results.py把结果画成图片graph_training.py画训练过程曲线。这个拆分思路很典型入口、训练、测评、画图分开方便在服务器上批量跑实验而不必每次都手工改参数。networks目录下是核心仿真配置single.sumocfg对应单交叉口场景double.sumocfg对应双交叉口场景single.net.xml和double.net.xml分别是两种场景的路网文件。拿到手先跑single单交叉口状态空间小训练一轮很快能确认环境通不通跑通了再切double看算法在多交叉口协同下是否还稳得住。src目录是真正的代码主体。sumosim.py控制仿真主循环learnerproc.py负责learner进程distprocs.py做分布式进程通信这是典型的“仿真与训练分离”设计。trafficmetrics.py统计平均等待时间、排队长度等指标trafficsignalcontrollers里放五种信号控制器rlagents里放强化学习训练器。用下面的命令快速核对一下你的解压目录跟我描述的是否一致cd 项目根目录 find . -maxdepth 2 -type f | sort-maxdepth 2限制递归深度避免刷屏-type f只看文件不看目录。如果发现缺了networks、src、run.py中的任何一个先补完整再往下走。我把主要文件的职责整理成表改代码前先对照它找位置文件/目录职责改动时看什么run.py命令行入口选择算法与仿真配置--algo、--cfg参数名networks/single.sumocfg单交叉口仿真参数SUMO版本兼容性src/sumosim.py仿真主循环与traci交互步长、仿真时长、状态采集频率src/trafficmetrics.py等待时间、排队长度统计reward的数据来源src/neuralnets.pyQ网络、Actor与Critic网络定义网络层数、激活函数src/trafficsignalcontrollers五种控制器实现与工厂注册新增算法时扩展的位置我自己看这类代码的习惯是先画一条数据流——车流从sumocfg生成sumosim.py每步读取路网状态交给trafficsignalcontrollers里的控制器决策信号灯trafficmetrics.py统计效果强化学习算法跨进程拿状态、给动作、收reward。把这条链路理清楚后面改任何一环都知道会影响什么。2.2 DQN与DDPG离散相位与连续动作的两条技术路线DQN处理的是离散动作空间。单交叉口场景里信号相位通常被离散成固定的几组南北直行、南北左转、东西直行、东西左转。DQN要做的是在某个状态下从这些相位中选择一个最优动作本质上是学一个Q值函数输出每个动作的期望累计收益再按epsilon-greedy策略做探索。项目里这一块放在neuralnets.py和nn_factory.py中nn_factory.py负责根据配置创建不同结构的网络neuralnets.py定义Q网络的层结构。如果你发现DQN训练不收敛我一般会先检查这里的状态输入是不是覆盖了排队长度、等待时间、车道占有率这几项核心信息而不是急着加网络层数。快速定位网络定义位置可以执行grep -n class.*Network\|def forward src/neuralnets.py | head -30grep把类名和forward方法行号打出来head -30限制输出条数。看到网络结构之后下一步要确认的是动作空间定义在哪个文件。通常动作数量决定Q网络输出维度如果动作定义和网络输出对不上训练直接报shape mismatch。DDPG面对的场景不一样。交通信号并不一定是只能离散切换也可以把绿灯时长当成连续输出动作。DDPG用Actor网络输出连续动作Critic网络评估当前状态下这个动作的Q值配合经验回放和目标网络来稳定训练过程。连续动作下信号灯时长可以平滑调整比如南北绿灯从20秒到25.3秒这对缓解信号切换带来的交通流冲击有帮助。项目里对应实现集中在rlagents目录learnerproc.py负责更新网络参数distprocs.py负责在仿真进程和训练进程之间传递状态、动作与奖励。需要注意DQN和DDPG虽然都叫强化学习踩坑点完全不同。DQN的典型问题是离散动作下的探索不足epsilon衰减太快就会过早锁定到一个次优相位DDPG的典型问题是连续输出容易震荡奖励尺度设计不好经常在训练中期出现动作突变。后面避坑章节会展开讲这两个问题在实际运行中怎么识别。2.3 韦氏、最大压力与自组织不依赖学习的启发式控制策略很多做RL交通控制的人容易忽视一个事实在没有与学习型控制器对比的情况下训练曲线再好看也没有说服力。max pressure、self-organizing和项目中的韦氏算法就是几个强启发式控制器用来当公平的baseline。最大压力控制是分布式控制里很经典的一族方法核心思路是计算交叉口各相位对应的“压力”压力可以近似为排队长度之差或者占用率之差每次选择压力最大的相位放行。它不学习、不调参只看当前局部状态在理论上对网络交通流有保障。在single和double场景下跑这个算法能得到一个相当有竞争力的参考线——如果DQN训练完连最大压力都打不过大概率不是代码bug是状态或reward设计出了问题。自组织交通灯的思路更简单每个信号灯根据本地检测到的车流情况独立决策车辆到达率达到某个阈值才切换相位否则保持当前相位。这样避免了全局优化带来的控制延迟在低流量场景下表现很好缺点是高流量下容易某个方向长时间不放行。韦氏这个命名容易让人联想到Webster配时公式但项目里的实现是按车辆轨迹分析来做的关注的是车辆在交叉口的转向、排队和通过轨迹从轨迹数据推断应该延长还是切换相位。它介于“固定配时”和“真正自学习”之间属于一种利用短期轨迹信息做动态绿灯延长的控制器。这三种控制器实现都在trafficsignalcontrollers目录下trafficsignalcontroller.py定义统一接口tsc_factory.py按名字注册和创建控制器。想确认目前注册了哪些控制器可以执行grep -n register\|__init__ src/trafficsignalcontrollers/tsc_factory.py | head -20如果后面想加自己的改进算法照着这个文件的注册方式加一个类即可不需要动sumosim.py主循环这一点对扩展实验非常友好。3. 环境准备与单交叉口首跑让SUMO先动起来再谈训练3.1 SUMO安装与Python绑定SUMO_HOME、PYTHONPATH和traci在Ubuntu/Debian系系统上安装SUMO最省事的方式是apt直接装sudo apt update sudo apt install sumo sumo-tools echo export SUMO_HOME/usr/share/sumo ~/.bashrc echo export PATH$PATH:$SUMO_HOME/bin ~/.bashrc echo export PYTHONPATH$SUMO_HOME/tools:$PYTHONPATH ~/.bashrc source ~/.bashrc sumo --version这段脚本做四件事安装SUMO主程序和工具集把SUMO_HOME指向程序目录把bin目录加入PATH方便命令行直接调用sumo和sumo-gui最关键的是把tools目录加入PYTHONPATH因为项目import traci依赖这里的Python库。如果你用Windows或者macOS路径会不一样但思路相同——只要能配置好这些环境变量sumo和Python这侧都能启动。配置结束后用sumo --version确认SUMO能运行再在Python侧验证traci。python -c import traci这条命令不打印任何信息才是好事import成功就说明traci绑定完成。如果这里报ModuleNotFoundError多半是PYTHONPATH没生效或者Shell环境没重开我一般会直接在当前终端source一遍.bashrc再测。如果你是拿vscode开发建议直接在vscode终端里配好环境再启动免得跑训练脚本时一会儿找不到sumo、一会儿找不到traci。项目对Python版本没有写死但强化学习训练涉及numpy和pytorch建议Python 3.8以上。requirements.txt里列了依赖安装时用pip install -r requirements.txt即可如果网络源慢换成国内pip镜像源会快很多这是Python安装依赖的常规步骤。3.2 跑通single.sumocfg从sumo-gui启动到traci接管环境准备好之后先用图形界面确认路网没有问题cd 项目根目录 sumo-gui -c networks/single.sumocfg执行后SUMO窗口里应该出现一个十字路口车辆按设定好的路线生成并行驶。这一步的意义是验证net.xml路网与sumocfg配置在当前SUMO版本下能被正常解析。如果连这一步都跑不出车后面所有训练都无从谈起。常见的不通过原因集中在版本差异和路径问题版本差异详见避坑章节。图形界面只是第一步项目真正运行是准备用run.py拉起SUMO通过traci做实时控制。模拟一下这个过程最小可用的traci连接代码如下import traci sumo_cmd [sumo-gui, -c, networks/single.sumocfg] traci.start(sumo_cmd) # 启动SUMO建立本地连接 for step in range(1000): traci.simulationStep() # 推进一个仿真步 # 在这一步读取状态、修改信号灯、收集指标 traci.close()traci.start()启动SUMO子进程simulationStep()每次推进一帧默认步长是100ms。在仿真步之间可以读取车辆位置、速度、排队长度也可以直接改信号灯相位。对强化学习项目来说这个循环就是agent与环境交互的接口sumosim.py本质上做的事情就是把这段循环拆到多进程里。跑通之后的判断标准不是“SUMO窗口打开”而是“车辆在路口正常排队、绿灯放行、没有车辆卡死不动的现象”。你可以在窗口里观察到车辆积压时排队长度增长、绿灯放行时车队消散说明仿真环境是活的状态。再进一步直接执行python run.py --algo maxpressure --cfg networks/single.sumocfg如果命令行能输出每个episode的平均等待时间说明项目自带的数据链路已经完全贯通。到这里这份SUMO交通信号自适应控制项目才算真正在你的机器上落地。3.3 用net.xml读懂路网参数edge、lane与junction之间的关系跑通之后建议打开single.net.xml认真读一遍。SUMO的路网核心是edge、lane、junction三类元素edge是一条道路lane是edge上的具体车道junction是道路之间的连接点。红绿灯逻辑在tlLogic节点里定义下面挂的是phase列表每个phase包含时长和灯色状态。元素作用看什么参数edge道路段id、functionnormal/source/sinklane具体车道speed限制、允许的车辆类型junction交叉口或路口连接点typetraffic_light表示有信号灯tlLogic信号灯方案phase列表、周期时长、红灯/绿灯状态connection车道间的转向连接左转、直行、右转关系理解这个结构对后面自定义场景很重要。比如你想把单交叉口改成三相位需要动tlLogic里的phase定义想加一条车道需要动edge和lane节点。用netedit可视化编辑比手改xml方便但手改能让人更清楚SUMO内部的数据组织方式。我一般会先用netedit打开single.net.xml再把对应节点在源码里对照着看一遍这样后续改路网时出错率明显下降。4. DQN与DDPG的复现路径训练脚本、超参搜索与结果绘图4.1 train_dqn.sh与train_ddpg.shShell入口打开训练流程根目录的train_dqn.sh是把DQN训练命令固化成脚本方便反复执行。项目同时提供了.zbak后缀的备份文件注意不要直接运行带.zbak的文件双击运行前先看一眼文件是否具备可执行权限。典型训练启动脚本长这样不同机器上你大概率需要改环境名和路径#!/bin/bash # 训练DQN的Shell启动脚本常见写法示例 source ~/anaconda3/etc/profile.d/conda.sh conda activate trafficrl python run.py \ --algo dqn \ --cfg networks/single.sumocfg \ --epochs 200 \ --steps 3600 \ --save-dir output/dqn作用是把Python入口、算法名、仿真配置、训练轮数集中放到一个可重复执行的命令里。trafficrl是虚拟环境名每个人机器上不一样我习惯先conda env list确认。--epochs控制训练轮数--steps控制每轮仿真步数步长100ms时3600步对应6分钟仿真时间。--save-dir指定模型和日志保存位置。DDPG对应的脚本结构一致把--algo换成ddpg最多再加几个DDPG特有的参数。如果执行过程中报“command not found”通常是bash脚本第一行的#!/bin/bash前面多了空格或者脚本没有执行权限chmod x train_dqn.sh就能解决。如果报Python模块缺失就是requirements.txt没装完。Shell脚本作为工程入口最大的价值不是炫技是把一组容易记错的参数固定下来每次训练跑同一套配置实验结果才可复现。4.2 hp_optimization.py网格搜索里的参数联动问题DQN这类算法的效果对学习率、折扣因子、batch size非常敏感手动调一轮动辄几小时。hp_optimization.py就是为了解决这个问题把一组候选参数组合依次跑一遍把每组参数对应的最终指标记录下来。常见实现逻辑可以抽象成下面这个简化版import itertools search_space { learning_rate: [1e-4, 3e-4, 1e-3], gamma: [0.90, 0.95, 0.99], batch_size: [32, 64, 128], } keys list(search_space.keys()) for values in itertools.product(*search_space.values()): params dict(zip(keys, values)) # 用 params 覆盖默认参数启动一轮训练 # 把这轮最终平均等待时间追加到结果文件 print(params)itertools.product会做笛卡尔积把三个超参的所有组合都枚举出来。实际项目里hp_optimization.sh会串行或并行调用这些组合结果写入pickle或文本文件供后续分析。参数组合数量增长很快上面三个参数三档取值就是27组训练单组训练就要几十分钟全部跑完要很久。所以我个人建议先跑小范围粗搜锁定learning_rate的数量级再缩小范围精调。gamma在交通信号控制场景里通常选0.9到0.99之间因为车流状态有一定持续性但又不至于需要特别长远地规划。超参这东西本身有几分玄学固定其余变量、一次只动一个参数比交替修改多个参数容易定位问题。import pickle data pickle.load(open(results.pkl, rb)) for entry in data: print(entry[params], entry[final_wait_time])这是读取已保存结果的示意代码项目里picklefuncs.py处理了这部分序列化逻辑。最终指标可以看final_wait_time这类字段对比不同超参组合时注意每个组合是否用了相同的训练轮数。轮数不一样指标之间没有可比性。hp_optimization.py产出的结果最终通过graph_results.py画成tsc_hp.png这类图你会看到不同超参组合的训练曲线或结果对比。这里容易犯的错是把所有组合的结果混在一起画曲线互相重叠看不出差异。我一般在代码里给每组参数加一个label前缀画图时按label分色问题就清楚多了。4.3 gen_results.sh与graph_results.py批量测评与对比图输出多算法对比实验最繁琐的是“重复跑同一套流程”gen_results.sh的作用就是把“跑所有算法”这个动作自动化。脚本内部大致是这样一个循环#!/bin/bash # 批量运行多种交通信号控制器 for algo in dqn ddpg maxpressure selforg webster; do python run.py --algo $algo --cfg networks/single.sumocfg done这里algo名字要和tsc_factory.py里注册的类名对应项目在命名上统一了算法名所以循环才能通用。跑完后所有的指标数据被写入本地文件再由graph_results.py读取。samples目录下能看到travel_time.png、intersection_moe.png等输出图travel_time.png重点关注它给出平均旅行时间随时间或训练轮数的变化intersection_moe.png对应多个评价指标的对比。看这些图的时候务必注意纵轴单位的可比性——旅行时间用秒、等待时间用秒、排队长度用辆横轴对齐后才不会把不同尺度的曲线强行比较。如果gen_results.sh里某个算法跑到一半崩了整个脚本默认会中断。我在实操中会在每个算法命令后加|| echo FAILED: $algo让脚本跳过失败的算法继续跑后面的最后统一看哪些失败。不要小看这个习惯批量跑实验时它能帮你省一整轮返工时间。5. 避坑与排查SUMO版本、路径与训练过程的五个常见问题5.1 打不开single.net.xml相对路径和工作目录不一致现象是运行sumo或run.py时报错提示Can not load network或Could not open network file。这个错我见得太多了绝大多数情况不是文件损坏而是当前Shell工作目录不在项目根目录sumocfg里用相对路径引用net.xmlshell没有按预期目录运行。解决方式很直接进到项目根目录再执行命令或者把所有命令里的相对路径改成绝对路径。排查时先用pwd确认当前位置再用ls确认networks目录是否真的存在。我现在的习惯是所有Shell脚本第一行加cd $(dirname $0)让脚本不管从哪里调用都固定到自身所在目录这个习惯从那次折腾一小时路径问题之后就再没改过。5.2 DQN训练不收敛奖励稀疏与epsilon衰减过快现象是训练几十轮之后平均等待时间不降反升或者一直围绕一个较高值抖动。先排除代码bug用baseline控制器最大压力或固定周期跑同一路网看等待时间的合理区间在哪里。如果baseline没问题问题就出在reward设计或者epsilon衰减过快奖励函数稀疏agent长期拿不到有效反馈或者epsilon从1.0衰减到0.05的速度太快探索不充分就早收敛了。解决思路是在trafficmetrics.py统计指标基础上把reward改成负等待时间或负排队长度并观察训练每个epoch的reward曲线确认有没有逐步上升。调参时也不要只看最后几个epoch要结合graph_training.py输出的训练过程曲线判断。5.3 .zbak文件不能直接跑备份与可执行权限的区分根目录里train_ddpg.sh.zbak、hp_optimization.sh.zbak这些文件是原始文件的备份。Linux下文件不是按后缀决定能否运行但脚本内里的命令路径和当前项目结构不一致时就会出错。明确一点要运行的是去掉.zbak的版本不是把.zbak当脚本用。如果项目里没有不带.zbak的同名文件就用cp train_dqn.sh.zbak train_dqn.sh恢复备份再改。查看文件类型可以用file train_dqn.sh.zbak确认内容再用head -n 5预览前几行是不是Shell脚本。保留.zbak文件是很多作者留下来的习惯性操作不要删它往往是最原始的可用版本。5.4 double.sumocfg不出车route定义和edge id对不上现象是double.sumocfg能启动路口长时间没有车辆生成。多数原因是sumocfg里车辆流定义与net.xml的edge id对不上或者车辆生成参数太稀疏前几百步看不到车。排查顺序先用sumo-gui加载double.sumocfg看GUI报错区域指示的是哪个文件然后打开sumocfg检查routes文件路径最后看vehiclegen.py里车辆生成相关的代码确认生成间隔是多少秒、每条route上分配多少辆车。项目用vehiclegen.py动态生成车流的好处是不同算法可以共用同一套车流输入但前提是edge id必须和路网里实际存在的id一致。grep -n route networks/double.sumocfg grep -n edge networks/double.net.xml | head -20第一条命令看sumocfg引用的route定义第二条命令列出net.xml里的edge id。对照net.xml里的edge id改掉route定义车辆马上就能恢复正常生成。5.5 DDPG输出NaN奖励尺度与学习率问题现象是DDPG训练中期动作输出变成NaNloss也跟着爆炸。DDPG对奖励尺度和学习率都非常敏感。当reward数值在几百上千量级Critic输出的Q值梯度会直接爆炸如果奖励绝对值过大训练快速发散的症状之一就是网络权重出现NaN。建议第一件事先在rlagent.py里把reward打印出来看取值范围如果波动过大用clip操作把reward限制在[-1,1]或先除以一个常数做归一化。第二件事把Actor和Critic的学习率降一个数量级DDPG稳定性比DQN差1e-3是危险区1e-4起步是常见做法。做完这两步再重启训练比反复调整网络结构有效得多。拿着一套打印了NaN的权重去分析任何训练过程都没有意义先让数值稳定下来再谈效果。6. 进阶改奖励函数与自定义车流把算法对比做出可信度真正拿这套SUMO项目做实验时值得改的就是三处reward、车流、评价口径。reward直接影响DQN和DDPG的学习方向。在rlagent.py里reward通常由trafficmetrics.py输出的指标组合而成常见形式是负的加权和reward -metrics[avg_waiting_time] - 0.5 * metrics[avg_queue_length]负等待时间让agent学会减少延误负排队长度是防止它只照顾绿灯方向而放任其他方向积压。0.5是惩罚权重取决于你更看重通行效率还是公平性。调权重时每次都固定其他变量只动一个权重否则多个变量一起改谁起的作用都说不清。车流方面我建议改vehiclegen.py把随机种子固定下来不然每轮训练的车流完全随机算法之间的差异会混入大量噪声。到达率、起始时间、不同进口道的流量比例都在这个文件里调整。固定种子之后跑一次固定周期信号作为车流基准值再跑最大压力最后跑DQN/DDPG这样对比才有意义。验证方法上我个人的一个习惯是每换一个算法版本就记录三组数字平均等待时间、平均旅行时间、每个交叉口的最大排队长度。graph_results.py的图可以看整体趋势但这三组数字要记在实验笔记里它们能快速告诉我一个算法是不是真的在进步而不是只在某个指标上刷得好。这条路走下来我对交通信号强化学习项目最大的感受是真正让你翻车的往往不是算法写不对而是环境没跑通、奖励没对齐、对比基座没立好。从那以后我每次拿到类似的交通控制工程项目都会强制先跑一轮固定周期信号作为baseline再跑最大压力最后才上DQN或DDPG。这套流程既可以复现别人项目里的结论也可以把自己新写的算法放到同一把尺子上量一量。希望帮到你。本文还有配套的精品资源点击获取

关于本文作者

来自尧图内容编辑团队

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

尧图内容编辑团队

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

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

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

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

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

网站改版的5个关键决策

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

获取专属建站方案

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

立即免费咨询