
简介本资源是一套面向交通工程研究者、智能交通系统开发者及强化学习实践者的SUMO自适应信号控制实验平台聚焦于多算法对比验证——集成DQN与DDPG两种深度强化学习方法并融合韦氏配时、最大压力控制、自组织交通灯等经典策略解决城市交叉口动态信号优化问题。压缩包共54个文件含37个Python核心模块涵盖仿真接口、RL智能体、神经网络构建、交通指标计算等、5个Shell脚本用于训练调度、结果生成与环境清理、4张性能对比图及2个SUMO配置文件整体仅1.35MB轻量易部署。已有1321人学习下载资源结构清晰src目录封装完整可复用的控制器工厂与仿真流程samples提供可视化结果示例scripts支持一键训练与评估配套README与requirements.txt保障开箱即用。读者可直接运行对比五类算法在单/双路口场景下的延误、通行量与绿信比表现获得从建模、训练到评估的全流程实践支撑。 做交通信号控制研究绕不开仿真平台SUMO应该是这个领域出现频率最高的开源工具了。最近我整理了一个很有意思的项目在一个标准SUMO路网上把五种信号控制算法放进同一套环境里横向对比分别是深度强化学习里的DQN、DDPG经典配时里最常被引用的韦氏算法理论界评价很高的最大压力控制以及完全不需要训练的自组织交通灯。项目用Python写控制逻辑用Shell做批量实验调度最后打包成了一个下载后就能直接解压运行的zip工程文件名叫“SUMO自适应交通信号控制-DQN、DDPG、韦氏、最大压力、自组织交通灯_Python_Shell_下载.zip”。这个项目适合两类人看一类是刚接触智能交通或强化学习的同学拿来当入门实验范本能直观看到同一场景下不同算法的表现差异另一类是已经跑过一些仿真、想做算法对比但不想从零搭环境的研究者直接改配置文件就能复用这套流程。下面我会把整个项目的设计思路、算法原理、实操步骤、踩坑记录全部摊开讲内容尽量具体到指令和参数方便你照着复现。1. 项目整体设计与思路拆解1.1 为什么选SUMO作为仿真平台交通信号控制算法的验证必须依赖一个可复现、无风险、能自由控制场景参数的环境。SUMOSimulation of Urban MObility是德国航空航天中心开发的开源微观交通仿真平台它提供了路网编辑、车辆行为建模、TraCI接口这些核心能力。选它而不用商用软件主要看中三点第一跨平台且完全免费实验室和个人的机器都能跑第二TraCI允许外部程序在仿真运行过程中实时读取路网状态、修改信号灯相位这是实现强化学习闭环控制的前提第三路网和车辆需求文件都是XML文本格式方便用脚本批量生成正好能结合Shell做大规模实验。真实世界里不可能拿一个路口的红绿灯反复试错算法但仿真环境允许你跑一千次、一万次实验。SUMO的微观模型精度虽然不是最高但对信号控制这类偏向宏观策略的研究来说完全够用这也是学术界大量论文选择它的原因。项目里把SUMO当作“被控对象”Python进程通过TraCI和它通信形成“仿真环境-策略模块-评价模块”的闭环。1.2 五种算法同台竞技的定位这个项目最大的特点不是只实现单一算法而是把五类完全不同思想的控制策略放在同一个标准十字路口上对比。这个设计非常聪明因为它覆盖了信号控制领域的几大流派韦氏配时代表“固定配时离线优化”公式简单、应用广泛是绝大多数城市的现状。最大压力控制代表“实时响应分布式决策”理论上有稳定性保证。自组织交通灯代表“无模型启发式规则”不优化参数、不训练靠局部交互涌现出协调行为。DQN代表“值函数派深度强化学习”适合离散动作空间。DDPG代表“策略梯度派深度强化学习”适合连续动作空间。五者放一起既能看出传统方法与学习方法的代际差异也能看出无模型算法和强化学习之间的性价比差异。我特别建议你先跑一遍基线算法韦氏、最大压力、自组织再跑DQN和DDPG千万不要一上来就盯着深度强化学习否则你根本不知道模型学到的东西到底好在哪里、差在哪里。1.3 项目目录与模块分工项目压缩包解压后大致是这样的目录结构sumo_adaptive_signal_control/ ├── environments/ # SUMO路网与车辆需求文件 │ ├── single_intersection.net.xml │ └── flows.rou.xml ├── algorithms/ │ ├── webster.py # 韦氏配时 │ ├── max_pressure.py # 最大压力控制 │ ├── self_organizing.py # 自组织交通灯 │ ├── dqn_agent.py # DQN智能体 │ ├── ddpg_agent.py # DDPG智能体 │ └── trainer.py # 训练与评估公共入口 ├── utils/ │ ├── traci_connector.py # TraCI连接封装 │ └── metrics.py # 评估指标计算 ├── runs/ │ ├── run_all.sh # 批量运行脚本 │ └── seeds/ # 多随机种子结果存储 └── requirements.txtPython负责所有算法逻辑和TraCI通信Shell负责批量启动实验、收集结果。把仿真环境文件和算法代码分离是我后来反复调整实验时觉得最值得的一个设计换路网不用改算法代码换算法不用动路网文件。2. 五种信号控制算法的核心原理与选型依据2.1 韦氏配时经典中的经典韦氏算法是1965年前后提出的固定配时方法适用于孤立交叉口。它的核心是先把交叉口划分成若干个相位典型两相位或四相位统计每个相位的饱和流量比然后代入公式计算最优周期时长[ C_0 \frac{1.5L 5}{1 - Y} ]其中L是一个周期内所有相位的总损失时间启动延误黄灯时间Y是所有相位流量比的总和。算完周期后再按每个相位的流量比占总流量比的比例分配绿灯时间流量大的相位拿到的绿灯就多。这个算法的价值在于它是一个“下限参照物”。项目中把它作为定周期最优策略的代表意思是信号控制做得再花哨如果连考虑流量比例的韦氏配时都打不过那算法就是失败的。实际实现时要注意每个进道口的车道数会影响饱和流率如果路网里车道数不对称建议按车道组分别统计流量比不要简单用总流量代替。用Python实现韦氏配时并不复杂核心逻辑就是先离线读一遍历史流量数据计算C0和绿信比然后固定写入信号灯方案。它不依赖实时变化所以运行速度快、结果稳定但也正因如此它无法应对突发波动。2.2 最大压力控制用排队长度做实时决策最大压力控制Max Pressure Control最早起源于计算机网络中的背压路由思想后来被引入交通信号控制领域。它的核心不是计算配时方案而是在每个决策时刻实时计算各相位对应的“压力值”然后选择压力最大的相位放行。压力怎么定义对于一个相位假设它放行的车道集合是S对应下游能吸收车辆的车道集合是D那么压力可以写成[ P \sum_{i \in S} q_i - \sum_{j \in D} q_j ]其中q是排队长度或者占有率。直观理解就是如果放行方向排队车很多而下游接收能力很充足这个相位的压力就大应该优先放行如果下游已经堵满了即使这一侧排队很多放行反而会堵住下游压力会被下游状态“抵消”。这个算法最大的优势在于完全分布式、不需要预估OD矩阵、不需要离线标定参数理论上在网络层面有稳定性保证。实际跑SUMO时可以每5秒或每10秒做一次决策用路网中车道区检测器Lane Area DetectorE2返回的排队车辆数计算压力然后切换相位。我刚开始实现时犯过一个错误只统计了上游排队忽略了下游剩余容量结果在高流量场景里经常把车辆放到一个已经饱和的下游路段反而加剧拥堵。后来加上下游项效果立刻改善。这一点也是理解最大压力控制的关键。2.3 自组织交通灯局部规则涌现全局协调自组织交通灯Self-Organizing Traffic Light不依赖任何训练和全局优化它只是定义几条简单的局部规则让信号灯根据实时来车情况自行延长、切换绿灯典型代表是Gershenson提出的规则如果当前绿灯相位还有持续的车辆流到达就适当延长绿灯如果车辆流出现间隙或者另一方向排队严重就切换相位。实现方式大致是每个决策时刻检测当前绿灯相位放行的道路上是否有车辆即将通过停车线。如果有把绿灯时间延长一个固定步长比如3秒如果没有就切换到等待时间最长或排队最长的相位。同时设置最小绿灯时间和最大绿灯时间作为约束防止频繁切换造成绿灯时间碎片化也防止一个方向长时间霸占绿灯。这种算法的魅力在于它没有“最优周期”的概念却能根据车流波动自主调节在一些车流随机性很强的场景里效果甚至优于固定配时。而且它天然具备“绿波涌现”的潜力——当连续路口的信号灯都采用这种规则时适逢的车队会让每个路口都自动延长绿灯形成协调通行。不过它的局限也很明显完全没有全局视野每个路口只顾自己在路网非常密集、车流方向复杂的区域局部最优叠加起来不一定全局最优。作为对比实验里“零训练成本”的代表它性价比极高。2.4 DQN离散相位动作的深度强化学习DQN把信号控制建模成马尔可夫决策过程核心要素是状态、动作、奖励、环境转移。在这个项目中状态所有进道口车道的排队长度、车辆平均等待时间、当前相位编号和绿灯保持时间拼接成一个向量。动作切换相位离散动作比如“保持当前相位”“切换到相位2”。奖励用负的车辆总等待时间变化量或者负的平均排队长度希望智能体学会减少延误。环境SUMO的TraCI接口就是环境每个仿真步0.5秒或1秒返回一次观测。DQN的网络一般是几层全连接网络输入状态向量输出每个动作的Q值。训练时用经验回放Experience Replay和目标网络Target Network两个技巧来稳定学习。经验回放是把探索过程中的“状态-动作-奖励-下一状态”存进缓冲区训练时随机抽样打破样本间的时间相关性目标网络是让Q值的更新目标不随主网络频繁变化降低训练震荡。交通信号控制对DQN来说是个天然的离散决策问题因为相位的切换本身就是离散的。但这里面有个训练陷阱相位切换不是每一步都要发生连续切换会导致绿灯时间碎片化。所以实现时通常给动作空间加入“保持当前相位”这个选项或者设定最小绿灯时间让智能体只能在合适的时机切换。2.5 DDPG连续配时决策的连续控制方案DDPGDeep Deterministic Policy Gradient和DQN最大的区别是输出动作是连续值。在信号控制场景里DDPG的典型用法是直接输出当前相位需要延长的绿灯时间或者输出一个连续信号量表示切换倾向。DDPG属于Actor-Critic架构Actor网络根据状态输出动作Critic网络评估这个动作的Q值。训练时Actor根据Critic的梯度方向更新目标是找到能让Q值最大的动作Critic则用TD误差更新逼近真实的动作价值。为了让智能体探索实现中会在动作上叠加随机噪声通常用Ornstein-Uhlenbeck过程或高斯噪声。用DDPG控制信号灯的好处是可以做到“平滑调节”绿灯时间不是固定的15秒或30秒而是根据拥挤程度连续伸缩。这在交通流渐变时表现特别明显比如早晚高峰车流线性增长DDPG会给一个逐渐延长的绿灯序列而不是像离散动作那样一下从15跳到30。但DDPG的代价是训练稳定性差超参数敏感。学习率、软更新系数tau、噪声方差、奖励缩放都会影响收敛。我第一次跑的时候reward曲线震荡得完全没法看把critic学习率降到1e-4、actor学习率降到1e-4后情况才有了明显改善。后面会细说调参经验。3. 环境搭建与核心代码实操3.1 SUMO环境与Python工具链准备先搭环境。Ubuntu系统下最省事的方式是直接加SUMO的官方PPAsudo add-apt-repository ppa:sumo/stable sudo apt update sudo apt install sumo sumo-tools sumo-doc装完验证一下sumo --version sumo-gui --version如果看到版本号输出说明核心仿真器装好了。Windows用户可以去官网下载安装包装的时候勾选添加环境变量方便在命令行直接调用。Python端需要安装两个关键库traci用于连接SUMOnumpy做数值计算。PyTorch或TensorFlow按你习惯选一个项目里的DQN和DDPG用的是PyTorch安装命令pip install traci numpy torch注意traci的版本必须和SUMO主程序版本匹配否则连接时可能报协议错误。建议用pip安装最新版traci对应SUMO 1.14以上版本兼容性会好很多。3.2 路网与交通需求生成项目里用的路网是一个十字路口可以用SUMO自带的netedit图形编辑器手动画也可以直接写.net.xml文件。手动画的时候只需要画四条双向两车道的路段交叉处设置信号灯然后导出即可。如果你和我一样懒得开GUI推荐用命令行工具生成简单路网。先在本地创建一个plain文件目录然后写一个节点文件和边文件再用netconvert合并netconvert --node-filesnodes.nod.xml --edge-filesedges.edg.xml --output-filesingle_intersection.net.xml节点文件内容大致是nodes node idintersection x0 y0 typetraffic_light/ node idnorth x0 y500 typepriority/ node idsouth x0 y-500 typepriority/ node ideast x500 y0 typepriority/ node idwest x-500 y0 typepriority/ /nodes这里的typetraffic_light很关键它告诉netconvert在交叉口上自动生成信号灯。生成路网后用SUMO自带的randomTrips工具生成车辆需求python tools/randomTrips.py -n single_intersection.net.xml -r flows.rou.xml --period 2 --random \ --begin 0 --end 3600 --seed 42--period 2表示平均每2秒发一辆车--begin 0 --end 3600表示仿真前3600秒内生成车辆--seed 42确保随机种子可复现。想模拟高峰流量就把period调小到1甚至0.5。3.3 TraCI仿真控制循环的骨架TraCI是Python和SUMO通信的桥梁。启动SUMO时开启TraCI服务之后Python作为客户端连接进去每一步都能读取状态、下发控制指令。核心控制循环长这样import traci sumo_cmd [sumo, -c, sumo_config.sumocfg, --remote-port, 8813] traci.init(8813) # 连接已经启动的SUMO进程 while traci.simulation.getMinExpectedNumber() 0: # 1. 读取状态 state get_state() # 从TraCI获取排队长度、等待时间等 # 2. 用算法决定相位动作 action controller.act(state) # 3. 下发控制指令 traci.trafficlight.setPhase(0, action) # 4. 推进仿真一步 traci.simulationStep()实际项目中更推荐用traci.start()直接由Python启动SUMO子进程这样便于统一管理。标准做法是sumo_cmd [sumo-gui, -n, single_intersection.net.xml, -r, flows.rou.xml, --remote-port, 8813, --start] traci.start(sumo_cmd)注意如果跑批量实验应该用sumo而不是sumo-gui后者会打开可视化窗口仿真速度会慢很多。3.4 Shell脚本批量跑实验的思路单次实验跑完只能得到一个随机种子下的结果不能说明问题。项目里的run_all.sh就是为批量实验准备的核心思想是用for循环遍历算法和随机种子把每次实验的输出写到独立目录#!/bin/bash for algo in webster max_pressure self_organizing dqn ddpg; do for seed in 10 20 30 40 50; do echo Running $algo with seed $seed python train_and_eval.py \ --algo $algo \ --seed $seed \ --output runs/${algo}_seed${seed}.json done done跑完后再用Python写个汇总脚本把所有的json文件读进来画出平均等待时间和排队长度的对比曲线。这个流程虽然简单但能保证每个算法都在完全相同的交通需求下比较避免了随机性带来的偏差。4. 训练调参与效果对比4.1 奖励函数与状态设计强化学习算法能不能收敛一半取决于奖励函数设计。在信号控制里最自然的奖励是负等待时间但直接累加会导致数值过大训练不稳定。我的做法是计算这一步的等待时间增量再做缩放def compute_reward(prev_waiting, curr_waiting): delta prev_waiting - curr_waiting return delta / 100.0 # 缩放这样智能体学会的是“减少等待时间”而不是“绝对值最小”数值范围可控梯度更新稳定。状态向量则选取四方向进口道的排队长度和平均等待时间再加当前相位持续时长。排队长度可以直观反映拥挤程度等待时间能体现延误累积相位持续时长则帮助模型理解切换的时机约束。DDPG的奖励和状态可以复用同一套设计但动作需要做后处理。DDPG输出的连续值比如在[-1, 1]区间需要映射到实际的绿灯延长时间比如0到15秒。映射函数很简单extension (action 1) / 2 * 15 # [-1,1] - [0,15]不过要注意如果动作输出的是“延长秒数”那决策频率就不能太密否则绿灯时间会被切成无数个小片段反而造成启停损失。实际操作中建议每隔3秒或5秒做一次决策而不是每个仿真步都做。4.2 评估指标和数据处理评估算法好坏不能只看训练曲线要回到真实路网表现上。SUMO仿真结束后会输出tripinfo.xml文件里面记录了每辆车的出发时间、到达时间、行驶时间、等待时间。我通常提取三个指标平均等待时间每个信号周期内车辆在交叉口前等待的时间这是最直观的延误指标。平均行驶时间从车辆入场到出场总耗时反映了整体通行效率。排队长度停车线前排队车辆数的平均值排队越短代表通行越顺畅。用Python解析tripinfo.xml只需要几行import xml.etree.ElementTree as ET tree ET.parse(tripinfo.xml) trips tree.getroot() total_wait sum(float(trip.attrib[waitingTime]) for trip in trips) avg_wait total_wait / len(trips) # 所有车辆平均等待时间对比时我强烈建议跑多种流量条件低流量period3、中等流量period1.5、高流量period0.8。因为一种算法可能在低流量下表现很好在高流量下却完全崩溃只跑单一场景会得出片面结论。4.3 不同流量场景下的实验结果解读根据我跑过的实验可以给出一些参考性结论但建议你在自己的环境里复现在低流量下五种算法的差距很小因为车辆少信号灯几乎不需要等待。但韦氏配时和DDPG会略好于DQN因为DQN还在探索环境偶尔会做出无意义的切换DDPG则学会了给出一个基础绿灯时长。在中高流量下最大压力和自组织交通灯明显优于固定配时。固定配时死板地按周期切换一旦车流不均匀绿灯放空或红灯积压的情况很常见。最大压力能动态选择压力大的方向放行自组织能根据车流间隙自动延长时间两者都是实时响应适应性更强。在极高峰流量下DQN和DDPG经过充分训练后能接近甚至超过最大压力控制但训练时间会很长。DQN的离散动作在流量剧烈波动时切换不够平滑DDPG的连续延长机制更灵活。如果不做充分训练强化学习算法甚至可能出现比韦氏配时更差的结果这也是很多人跑RL交通信号项目时最容易困惑的地方——不是算法不行是训练不充分或奖励没设计好。5. 常见问题与排查实录5.1 仿真连接与运行环境类问题问题一TraCI连接报错“trying to connect to port 8813 failed”。这个问题九成是SUMO进程没有正常启动或者端口被占用。排查思路是先手动启动SUMO并指定端口再用另一个终端测试连接确认后再交给Python脚本管理。也可以在Python里捕获异常加上重试机制。问题二SUMO版本与traci不兼容。早期版本信号灯状态接口和现在差异很大如果你用的旧路网文件在新版本里打开报错建议直接用netconvert重新生成路网文件。这类问题在代码层面不好兼容最稳妥的办法是统一环境版本。问题三用sumo-gui跑强化学习训练太慢。训练过程中其实不需要可视化建议全部用sumo这个无界面模式。想观察智能体行为时单独跑一次eval模式再用sumo-gui打开。5.2 强化学习不收敛的排查路径如果你发现DQN或DDPG的reward曲线一直在震荡不下降按这个顺序排查第一检查状态归一化。排队长度可能从0到30变化直接喂给神经网络会引起梯度变化过大把状态除以最大值或者用mean-std归一化。第二检查奖励是否稀疏。如果每步只给-100或1这种大脉冲模型很难学习。把奖励改成连续、平滑的负等待时间增量训练会顺滑很多。第三调整探索参数。DQN的epsilon衰减太快智能体很快就停止探索可能永远发现不了“切换相位能减少延误”这个规律DDPG的噪声方差太大训练后期动作一直在乱抖。我一般把epsilon从1.0线性降到0.05经历大概300个回合DDPG的噪声方差则每回合乘0.995。第四尝试简化场景。从单交叉口两相位开始再扩展到四相位。如果简单场景都没法收敛不要贸然上复杂场景。5.3 对比实验公平性怎么保证做算法对比最怕的就是“赢了算法输了公平”。项目里用一套车辆需求文件但要注意生成车辆时有随机性同一个flow文件在不同seed下产生的车辆轨迹完全相同这才叫同条件对比。另一个公平性问题在于训练回合数。DQN和DDPG需要训练而韦氏、最大压力、自组织不需要训练。如果只给RL算法很少的训练回合它表现差并不能说明算法本身差。我的做法是先跑一个预训练让RL算法的训练曲线收敛到一个平台期然后再做评估对比这样才能保证“学了再比”。还有一点是信号灯相位设置的统一。所有算法的相位结构必须完全一致我统一用四相位南北直行、南北左转、东西直行、东西左转。不同算法只是在“什么时候切换、切换给哪个相位”上做不同决策。6. 一些个人体会与后续扩展想法实验做下来我的感受是信号控制问题没有银弹每种算法都有自己的适用区间。韦氏配时是下限保证适合需求稳定的场景最大压力在饱和流量下表现强悍而且无需训练工程落地价值极高自组织交通灯规则简单但效果稳定适合快速部署DQN和DDPG的潜力在于能拟合更复杂的策略分布但训练成本和对状态设计的敏感度都不低。如果后续想扩展可以在三个方向深入一是把单交叉口扩展到多个交叉口组成的路网测试多智能体强化学习比如MARL能否产生绿波协调二是给DDPG的状态加上更丰富的感知信息比如车辆轨迹预测三是把SUMO里的信号灯换成更精细的相位结构评估导向式控制算法在真实路口几何条件下的表现。我自己现在就在多交叉口路网上继续折腾这个项目等有新结论再写出来分享。本文还有配套的精品资源点击获取