news 2026/10/1 5:09:09

SUMO交通仿真实战:需求生成、sumocfg配置与输出解析

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
SUMO交通仿真实战:需求生成、sumocfg配置与输出解析

路网能跑通了、车也能动了,结果一看输出文件全是空的——这大概是每个用 SUMO 的人都会经历的第三阶段。前面两篇我们把 SUMO 装好、把路网从 OSM 或者手写节点的方式建出来了,net.net.xml躺在目录里看着挺像回事,可一旦开始跑仿真,要么路网上一辆车都没有,要么车全堵在路口表演行为艺术。这篇就专门讲这一段的破局思路:sumo 仿真环境从"能打开"到"能出数据"之间,到底缺了哪几块拼图。

这一篇的定位是系列里的"需求与运行"环节,核心围绕三件事展开:交通需求怎么从零生成、sumocfg配置文件到底该怎么写、仿真跑完之后那些 xml 输出怎么读。适合已经能把路网导入进来、但卡在"跑不出有意义结果"这一步的朋友,也适合想从 GUI 点点点升级到脚本批量化跑仿真的同学。整套流程我用下来是稳定的,但踩坑的地方相当多,尤其是需求生成脚本的参数和路由计算这两块,后面会重点展开。需要说明的是,文中涉及的参数取值和目录组织方式,是基于城市道路通行场景的常见实践补全的,你在具体项目里需要按自己的路网尺度微调。

1. 第三篇要解决的问题:从路网到仿真的最后一段路

1.1 前两篇留下的半成品到底缺什么

跑过前两篇的朋友应该有个共同体验:netconvert或netgenerate生成的net.net.xml是完整的,SUMO 能加载它、能画出来、能看到车道线和路口。但这时候你点下运行按钮,仿真时间在走,路上却一辆车没有。这不是软件坏了,而是 SUMO 的设计哲学决定的——路网和需求是彻底解耦的。

路网只描述"物理空间",也就是哪里有条路、这条路几条车道、限速多少、哪个路口和哪个路口相连。它完全不知道上面该有多少车、这些车从哪来、要到哪去。需求文件才负责描述"交通流",包括车辆从哪条边出发、走哪条路径、什么时候进入、开多快。这两者最后由一个配置文件粘在一起。所以缺的拼图就是两块:需求(车流)和配置(把路网、需求、时间、输出串起来的东西)。

很多人第一次用 SUMO 会想当然地觉得"路网建好了仿真就能跑",这个直觉来自其他一些集成度更高的仿真软件,它们把路网和交通流放在一个工程里管理。SUMO 走的是 UNIX 那套"每个工具只干一件事"的路线,好处是灵活、可脚本化、适合批量做场景,代价就是初次上手要理解这个拼装逻辑。我个人的体会是,一旦理解了这个解耦思想,后面做参数扫描和批量实验会非常舒服,因为你可以只换需求文件、复用同一个路网,跑几十组对比实验。

1.2 需求、路网、配置三者的依赖关系

先把依赖关系理清楚,后面所有操作都能对号入座。路网是基础,提供边(edge)的 id、车道数、连接关系(connection)。需求依赖路网,因为车辆出发时要说清楚在哪个 edge 上出现,路径也要沿着路网里真实存在的连接走,写错了 SUMO 直接报错说这条边不存在或者这两条边不连通。配置依赖前两者,它是仿真启动时的总入口,告诉 SUMO 去哪个文件加载路网、去哪个文件加载车辆、仿真从第几秒跑到第几秒、输出写到哪里。

这个依赖链有个很实际的推论:需求文件不能脱离路网单独生成。你没法先随便编一堆车的路径,再去找一个路网来配它。正确顺序永远是先有路网、再基于路网生成需求、最后写配置。我见过有同学从别处拷了一份routes.rou.xml过来,结果里面引用的 edge id 跟自己路网的完全对不上,一运行满屏报错,排查半天才发现是文件压根不配套。

还有一点容易被忽略:路口连接关系会影响路由可行性。比如路网里有条边 A 和边 B,物理上看着是接着的,但如果net.net.xml里的 connection 没定义 A 到 B 的转向,路由算法就不会给出经过 A→B 的路径。这个问题在手工编辑路网时特别常见,输出表现为"某几辆车找不到路径"或者"路径绕了很远"。所以生成需求前,建议先确认路网是连通的。

1.3 工程目录怎么组织才不返工

这一条是纯经验,教科书上不会写。我强烈建议在动手之前先把目录结构定下来,否则做到后面文件版本一多,你自己都分不清哪个是最新的。我的习惯是按阶段分目录,大致长这样:

project/ ├── net/ # 路网相关 │ └── net.net.xml ├── demand/ # 需求相关 │ ├── trips.trips.xml │ ├── routes.rou.xml │ └── vtypes.add.xml ├── config/ # 仿真配置 │ └── sim.sumocfg ├── output/ # 仿真输出 │ ├── tripinfo.xml │ ── summary.xml ── scripts/ # 脚本 └── run.py

这样做有几个好处。第一,路网和需求分离,换需求做对比实验时不用动路网。第二,输出单独放一个目录,方便批量清理,跑完一轮删掉 output 里的内容就行,不会误删源文件。第三,脚本独立出来,后面要做参数扫描或者接 TraCI,直接在这里扩展。

提示:全流程里所有的相对路径都是相对于你启动 SUMO 时的工作目录,不是相对于配置文件所在目录。这是个非常经典的踩坑点,配置文件里写net.net.xml而实际文件在net/子目录下,结果就是一句"could not access file"。

2. 交通需求建模:让路网上真正跑起车来

2.1 trips、flows、routes、OD 四种需求写法怎么选

SUMO 的需求描述有好几种格式,新手最容易在这里犯迷糊。我按抽象层次从低到高捋一遍,你按手头数据的精度选。

routes(路径级)是最直接的,每辆车明确写清楚出发时间、出发边、完整路径。适合你已经知道每辆车的确切路径,或者用于调试和教学。

<vehicle id="v0" depart="0.00"> <route edges="E0 E3 E7 E12"/> </vehicle>

trips(起讫级)只给出起点边和终点边,路径交给路由工具算。适合你有 OD 信息但没有具体路径的场景。

<trip id="t0" depart="0.00" from="E0" to="E12"/>

flows(流量级)是在 trips 基础上加了流量概念,用number或period指定一段时间内发多少车。做宏观流量验证时用它最省事。

<flow id="f0" begin="0" end="3600" number="600" from="E0" to="E12"/>

OD 矩阵是最宏观的,一个矩阵描述各交通小区之间的出行量。它需要配合od2trips转成 trips,再配合duarouter算路径。适合从交通调查数据出发做大尺度场景。

选型逻辑很简单:数据精度到什么程度,就用什么格式。只有路口转向流量,用 flows 或者 OD;有完整的路径调查,用 routes。我日常做场景验证大多用 flows,因为它写起来快、改起来方便,改一个 number 就能调流量强度。

2.2 randomTrips.py 关键参数逐个拆解

手上没有真实数据时,SUMO 自带的randomTrips.py是最常用的造数据工具,它在tools/目录下。这个脚本参数多得吓人,但真正天天用到的就那么十来个,我把最关键的几个按重要性排一下。

参数作用常用取值
-n指定路网文件net.net.xml
-o输出 trips 文件trips.trips.xml
-r输出路由文件(内部调用 duarouter)routes.rou.xml
--begin/--end仿真起止时间0/3600
--period平均发车间隔(秒)1表示每秒一辆
--fringe-factor边缘边吸引力倍数10让车多走边界
--seed随机种子42保证可复现
--trip-attributes给车辆附加属性departLane="best"

先说--period,这是控制流量强度的核心。它表示平均每隔多少秒产生一辆车,--period 1就是每秒一辆,一小时 3600 辆;--period 5就是每小时 720 辆。注意这里的"平均"是随机的,实际发车间隔是围绕这个均值的随机分布,不是精确等距。如果你想要精确的发车间隔,改用--insertion-rate或者直接在 flows 里写period。

--fringe-factor这个参数很有意思,值得单独说。SUMO 路网里的边分两类:边缘边(fringe edge)是只连了一头的、类似城市出入口的路,内部边是两头都连着的。默认情况下车辆起终点在城市内部随机撒,会导致大量短途出行、车刚上路就到终点了。把--fringe-factor调大,就会让边缘边作为起终点的权重更高,车就更倾向于从城市一头开到另一头,跑出长途出行。我做城市级仿真时一般设到 10 以上。

--trip-attributes是新手最容易忽略但特别有用的参数。它的写法有点绕,需要转义引号:

python $SUMO_HOME/tools/randomTrips.py \ -n net/net.net.xml \ -r demand/routes.rou.xml \ --begin 0 --end 3600 \ --period 2 \ --fringe-factor 10 \ --seed 42 \ --trip-attributes "departLane=\"best\" departSpeed=\"max\""

departLane="best"表示让车选最合适的那条车道出发,而不是默认的第 0 条车道。这个参数的作用在下面"常见问题"里会展开,简单说就是避免车辆在出发边就发生挤兑。

注意:randomTrips.py内部调用 duarouter 时如果找不到路径会静默丢弃这些车,所以最终车辆数往往少于你按 period 算出来的理论值。想要知道实际生成了多少车,跑完后统计一下 rou 文件里的 vehicle 数量。

2.3 duarouter 路由计算与算法选择

如果randomTrips.py带了-r参数,它内部已经帮你调了duarouter,你不用手动再来一遍。但很多场景下我是分开跑的:先用randomTrips.py只生成 trips,再单独跑duarouter,这样方便在中途对 trips 做二次处理,也方便观察哪些 OD 对算不出路径。

duarouter \ -n net/net.net.xml \ --route-files demand/trips.trips.xml \ -o demand/routes.rou.xml \ --ignore-errors true \ --routing-algorithm dijkstra

--routing-algorithm有三个选项:dijkstra、astar、CH。Dijkstra 是最稳妥的,保证最短路径,速度对中小路网完全够用。A* 在有大范围路网时更快,但需要额外的启发式。CH(Contraction Hierarchies)是预处理型的,第一次跑会慢,之后查询极快,适合超大规模路网做反复查询。我一般在路网小于几千条边时直接用 Dijkstra,省心。

--ignore-errors true建议加上。路网里难免有些孤立的边,或者某些 OD 对之间确实不连通,不加这个参数 duarouter 遇到一个算不出路径的 trip 就可能中断整个流程。加上之后它会跳过这些 OD 对并给出警告,你跑完看警告数量就知道路网连通性有没有问题——警告特别多的时候,八成是路网本身有问题,而不是需求的问题。

duarouter 算出来的路径是不是物理上合理,还有个细节值得看:是否绕行。有时你会发现某辆车从相隔很近的两个点之间,路径却绕了大半个城市。这通常不是算法的问题,而是路网本身缺连接。排查方法是在 GUI 里加载这个路径文件,打开车辆轨迹显示,看它到底走了哪条线,然后回到路网里检查那几个关键路口的 connection。

2.4 车辆类型 vType:跟驰模型参数怎么定

需求文件里除了车的行程,还得定义车的"性格",也就是vType。这是需求建模里技术含量最高的部分,因为它直接决定仿真的行为真实度。

<vType id="car" vClass="passenger" accel="2.6" decel="4.5" sigma="0.5" length="5.0" minGap="2.5" maxSpeed="16.67" carFollowModel="Krauss" lcModel="LC2013" speedFactor="normc(1.0,0.1,0.2,2.0)"/>

逐个解释关键参数。accel是最大加速度,单位 m/s²,普通小汽车 2.6 左右是常见的标定值。decel是舒适减速度,取正值 4.5 表示减速能力,注意这里填正数,SUMO 内部处理方向。sigma是驾驶员不完美程度,0 表示完美跟驰、1 表示非常随机的驾驶行为,取 0.5 是比较折中的默认值。

maxSpeed单位是 m/s,16.67 对应 60 km/h。这里有个坑:这个值是车辆自身的限速,路网每条边也有一个限速,两者取小。很多人设了maxSpeed却发现车开得比预期慢,就是因为路段限速更低,把车卡住了。

speedFactor是我最推荐加的参数,用normc分布给每辆车分配一个期望速度因子。上面这行表示均值 1.0、标准差 0.1、截断在 0.2 到 2.0 之间的正态分布。有了它,仿真里车流速度就会自然分层,不会出现所有车速度一模一样的"机器人队伍"效果,通行能力和实际的差异会小很多。

carFollowModel默认就是 Krauss,这是 SUMO 的招牌模型,参数少、稳定、够用。如果做精细的能耗或排放研究,可以换成 IDM,它的加减速曲线更平滑。换模型的时候要注意参数是跟着模型走的,IDM 有自己的参数集,直接把 Krauss 的参数套过去不会正常工作。

提示:vType既可以写在路由文件里,也可以单独放在一个additional文件里通过配置引入。我推荐后者,因为车辆类型在多个场景间通常是复用的,单独一个vtypes.add.xml方便维护。

3. 仿真配置与运行:从能跑到跑得对

3.1 sumocfg 文件结构拆解

sumocfg是 XML 格式,把所有输入输出串起来。它的结构是按功能分块,我按块说明。

<configuration> <input> <net-file value="net/net.net.xml"/> <route-files value="demand/routes.rou.xml"/> <additional-files value="demand/vtypes.add.xml"/> </input> <time> <begin value="0"/> <end value="3600"/> <step-length value="1.0"/> </time> <output> <tripinfo-output value="output/tripinfo.xml"/> <summary-output value="output/summary.xml"/> <vehroute-output value="output/vehroutes.xml"/> </output> <processing> <time-to-teleport value="300"/> <ignore-route-errors value="true"/> </processing> <random_number> <seed value="42"/> </random_number> </configuration>

input块里,route-files可以写多个文件用逗号隔开,这对多类车辆场景很实用。additional-files用来加载信号配时、公交站、检测器这些附加定义,它和主路网是两个入口,很多新手会忘了这个块导致信号配时没生效。

processing块里的time-to-teleport是必须理解的参数。SUMO 里如果一辆车在同一位置堵超过这个秒数,会被瞬移到下游,默认是 300 秒。这个机制本意是防止死锁,但它会悄悄改变你的拥堵统计结果。做拥堵研究时我一般把它设成 -1 关闭瞬移,让车真正堵在那里,否则你研究的拥堵其实是"瞬移前的拥堵",数据会失真。当然关掉之后要小心局部的死锁把整个仿真拖死,需要配合观察。

3.2 时间步长、随机种子与可复现性

step-length是仿真步长,默认 1 秒。这个值直接决定计算量和精度。步长 1 秒时,高速行驶的车在一步内移动十几米,跟驰模型的计算精度会打折。做高速场景或者细致的排放研究时,我会把它降到 0.1 甚至更小,代价是仿真时间成倍增长。

seed是随机种子,这个值的重要性怎么强调都不为过。SUMO 里车辆的发车时刻、speedFactor 的随机抽样、车道选择等等,全都受这个种子控制。同一个配置、同一个种子,跑两次结果应该完全一致。这个特性是做对比实验的生命线:改一个参数,其他全固定,这样结果差异才能归因到那个参数上。

我的习惯是每组对比实验跑 5 到 10 个不同种子,然后取平均值和方差。只跑一个种子就下结论,很容易被随机波动带偏。种子建议用固定的几个数字(比如 42、123、456),这样整个项目组跑出来的结果都可比。

注意:step-length改了之后,--period或者 flows 里的发车时刻不用改,因为它们是按仿真时间算的,不是按步数。但如果你在脚本里用步数当循环条件,要注意换算。

3.3 命令行运行与 GUI 调试的分工

sumo是无界面版本,sumo-gui是带界面版本,两者接受完全一样的参数。日常使用我的分工是:调试用 GUI,出数据用命令行。

命令行跑:

sumo -c config/sim.sumocfg --tripinfo-output output/tripinfo.xml

命令行跑的优势是快,没有渲染开销,批量跑几十组实验时差距非常明显。而且它能直接嵌进 shell 或者 Python 脚本里循环。

GUI 跑:

sumo-gui -c config/sim.sumocfg

GUI 的价值在于可视化排查。几个我觉得最实用的功能:把车辆颜色按速度映射,一眼就能看出哪段路在堵;打开"显示车辆路线"选项,能看到每辆车的完整轨迹,用来验证路由是否合理;调慢仿真速度,看路口转向的行为细节。

GUI 里还有一个隐藏技巧:延迟时间设置。在界面上把 delay 设成 100ms 以上,你可以肉眼看清每辆车的换道过程和路口排队形成过程。对于要写报告或者给非技术人员演示的场景,这个功能救过我好几次。

3.4 输出文件解读:tripinfo、summary、edgeData

仿真跑完,输出目录里会有一堆 xml,第一次看很容易懵。我挑三个最常用的说清楚它们各自能回答什么问题。

summary.xml是每个时间步一条记录,包含当前在路网上的车辆数、平均速度、总行驶里程、总等待时间等汇总指标。做宏观趋势分析、画仿真过程曲线用它。它粒度粗但数据量小,适合看整体。

tripinfo.xml是每完成一次旅程一条记录,包含这辆车的行驶时间、等待时间、行驶距离、平均速度等。做单车级别的统计分析、算平均延误用它。这是做交通评价最常用的文件。

<tripinfo id="v0" depart="12.00" arrival="245.30" duration="233.30" routeLength="1850.40" waitingTime="38.20" timeLoss="62.10" avgSpeed="7.93"/>

timeLoss这个字段特别值得关注,它表示这辆车相对理想自由流状态损失了多少时间,本质就是延误。做信号配时优化时,这个值就是你要优化的目标函数。waitingTime只统计速度接近零的时间,和timeLoss是两回事,别混用。

edgeData.xml是每条边一条记录,统计这条边上的车流量、平均速度、密度、排队长度等。做路段级的通行能力分析、画热力图用它。这个文件默认不输出,需要在配置里显式加<edgeData>定义或者用--edgedata-output参数。

输出文件粒度主要用途
summary.xml每时间步整体趋势、过程曲线
tripinfo.xml每辆车延误统计、行程时间分析
edgeData.xml每条边路段流量、密度、排队
vehroute.xml每辆车路径核查、路由验证

4. 进阶玩法:TraCI 接口与信号配时

4.1 TraCI 能做什么,为什么值得学

TraCI 全称 Traffic Control Interface,是 SUMO 提供的一个在仿真运行过程中实时读写状态的接口。前面所有内容都是"配置好一次性跑完",TraCI 让你能在每一仿真步里做决策。它的典型用途有三类:信号灯的动态控制、车辆路径的实时重规划、以及把 SUMO 当成一个环境来训练控制算法。

为什么值得学?因为静态配置能做的实验是有上限的。比如你想验证一个自适应信号控制策略,用静态的tlLogic根本没法表达"根据排队长度动态切换相位"这个逻辑,必须用 TraCI 在运行中读取检测器数据、计算、再下发控制指令。这套流程跑通之后,SUMO 就从"交通仿真软件"变成了"交通控制实验平台",玩法完全不一样了。

Python 是本领域使用最广泛的接入方式,因为 SUMO 官方的traci库就是 Python 的,而且配合 numpy、pandas 做数据分析特别顺。下面给一个最小可运行的模板。

4.2 Python 控制实战:一个最小可运行脚本

import traci import sumolib # 启动仿真,注意 sumoBinary 用无界面版本提速 sumo_binary = sumolib.checkBinary("sumo") traci.start([sumo_binary, "-c", "config/sim.sumocfg"]) step = 0 while traci.simulation.getMinExpectedNumber() > 0: traci.simulationStep() step += 1 # 示例:读取某条边的排队车辆数 queue = traci.edge.getLastStepHaltingNumber("E0") # 示例:读取所有车辆的当前速度 for veh_id in traci.vehicle.getIDList(): speed = traci.vehicle.getSpeed(veh_id) # 示例:仿真中期动态改一辆车的路线 if step == 500: traci.vehicle.changeTarget("v0", "E20") traci.close()

这段代码里有几个点值得展开。sumolib.checkBinary是官方推荐的方式,它会去环境变量里找 SUMO 的安装路径,比硬编码路径可移植性好得多。getMinExpectedNumber()返回的是路网上还有多少辆车加上还有多少辆没发出来,用这个当循环条件比固定跑 3600 步更合理,仿真会自然结束。

traci.vehicle.getIDList()每步都要调用的话,在大规模场景下会有性能开销。如果你的场景车辆上万,建议不要每步全量遍历,改成只查询你关心的边或者区域,或者每隔几步查一次。

提示:TraCI 脚本里所有的距离单位是米、速度单位是 m/s、时间单位是秒。到这一步千万别再想着用什么 km/h,界面上显示的是 km/h,接口里返回的全是 m/s,单位搞混是初学者最常犯的错。

4.3 tlLogic 信号配时方案的手写与调试

信号配时是仿真里绕不开的东西,静态的tlLogic定义长这样:

<tlLogic id="C0" type="static" programID="0" offset="0"> <phase duration="42" state="GGrrGGrr"/> <phase duration="3" state="yyrryyrr"/> <phase duration="30" state="rrGGrrGG"/> <phase duration="3" state="rryyrryy"/> </tlLogic>

state字符串的每一位对应一个车道或者一个连接,G是绿灯、y是黄灯、r是红灯。这个字符串的长度必须和这个路口被信号控制的车道数严格匹配,写长了写短了 SUMO 都会报错。新手最容易在这里翻车。

怎么知道该写多长?在 GUI 里点开这个路口的信号灯信息面板,或者用netconvert --sumo-net-file配合检查连接数。更稳的办法是先用netconvert自动生成信号配时(加--tls.default-type参数),把生成结果当模板改,比自己从零数位数靠谱得多。

offset参数用于做干线协调。相邻信号灯设置不同的 offset,让车流能一路绿灯通过,这就是所谓的绿波带。这是做干线优化的核心手段,调 offset 的过程用 TraCI 自动化做效率最高,手动调基本不可能找到最优解。

配时字段含义调试要点
duration相位持续秒数总和为一个周期
state各位灯色位数必须与连接数一致
offset周期偏移用于干线协调绿波
programID方案编号多方案切换时索引

5. 踩坑实录:常见报错与排查思路

5.1 报错速查表

下面这些是我实际踩过、并且反复见到别人踩的报错,整理成速查表。

报错信息关键词根本原因解决方向
could not access file相对路径错位检查工作目录,统一用相对项目根的路径
Edge 'xxx' is not known需求里的边 id 与路网不符比对 rou 文件和 net 文件的 id
No connection between edge路网缺转向连接用 netconvert 重建或手工补 connection
Vehicle 'xxx' has no valid route路由算不出路径检查连通性,用 duarouter 单独验证
Invalid position for vehicle出发车道越界用小车道数或改 departLane
The traffic light 'xxx' has no programtlLogic 未定义补 additional 文件里的信号定义

could not access file出现频率最高。它的本质是 SUMO 不关心你的配置文件放在哪,它只关心你启动命令时所在的目录。解决办法是要么每次都从项目根目录启动,要么在脚本里用os.chdir切到项目根,再做相对路径引用。我现在的习惯是脚本一开头就切目录,从不依赖命令行位置。

5.2 仿真结果不合理的几种典型症状

程序不报错不代表结果对。下面几种症状我遇到太多次,都是"能跑但结果离谱"的类型。

第一种是所有车速度偏低。八成是maxSpeed和路段限速的双重限制在起作用,或者sigma设太大导致跟驰行为过于保守。排查方法是单独放一辆车在空旷路段跑,看它能不能跑到期望速度。

第二种是大量车在路口莫名消失。这通常是time-to-teleport在起作用,车堵太久被瞬移了。把它设成 -1 再看,如果瞬间出现严重排队甚至死锁,说明路口的通行能力设计有问题,需要检查信号配时或者转向连接。

第三种是通行量明显低于理论值。检查车辆插入是否成功,有没有出现"无法插入"的警告。发车太密集时,插入边被占满,后面的车会插入失败被丢弃,实际流量就上不去。解决办法是给出发边留足够车道、用departLane="best"、或者降低发车频率。

第四种是tripinfo 里 timeLoss 全都巨大。这往往不是仿真问题,而是路网限速设置不合理,或者你把begin时间设成负数导致车在外面排队了很久。先确认基础时空参数,再看行为。

5.3 性能与规模的取舍经验

最后聊聊规模。SUMO 跑多大路网、多少车是可行的,这个问题没有标准答案,但我可以给一些经验值。普通笔记本跑几千辆车、几千条边的路网,实时比大概在 1:5 到 1:20 之间,也就是仿真 1 小时实际算几分钟。上到几万辆车,就需要考虑关掉不必要的输出、加大 step-length、用 CH 路由这些优化手段了。

输出文件的体积经常比计算时间更让人头疼。netstate-dump这种每步全量车辆状态的输出会爆炸式增长,除非做轨迹研究,否则别开。我一般只保留 tripinfo 和 summary,需要路段数据时再加 edgeData,按需输出。

还有一个容易忽略的点:仿真跑不完不一定是卡死。如果路网上有严重的瓶颈,车辆持续累积,getMinExpectedNumber()一直大于 0,仿真永远不会自然结束。这种情况要么设个仿真时间上限强行结束,要么去解决那个瓶颈。我个人的习惯是在脚本里加一个硬性的步数上限作为兜底,防止批处理任务挂在某一组参数上整夜不结束。

踩过几次坑之后我逐渐形成一个习惯:任何一组新参数,先跑 600 秒看趋势,没问题再拉长到完整时长。600 秒足够暴露需求生成、路由、信号配时这三块的基础问题,能省下大量全时长跑废的时间。规模这件事没有银弹,真的是靠一轮轮试出来的手感。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/1 5:09:05

深数据驱动投放实战:从用户画像到千人千面的方法拆解

做投放这些年&#xff0c;我有个很深的感触&#xff1a;预算烧得最多的项目&#xff0c;往往不是策略最复杂的&#xff0c;而是数据用得最浅的。一个通投素材扔给全量用户&#xff0c;后台看起来覆盖了几十万人群&#xff0c;实际上一大半人根本不在乎。标题里的“深数据”三个…

作者头像 李华
网站建设 2026/10/1 5:07:26

电子病历命名实体识别实战:BERT+CRF与BIOES标签体系

简介&#xff1a;面向医疗NLP研究与工程落地场景&#xff0c;这套基于BERT模型的电子病历命名实体识别源码&#xff0c;可帮助开发者与研究者从非结构化病历文本中抽取疾病、药物、诊疗手段等关键实体&#xff0c;为临床决策与医疗信息化提供基础能力。压缩包共38个文件&#x…

作者头像 李华
网站建设 2026/10/1 5:07:04

Madeira:异步任务调度与事件回执分发服务的设计与实践

当我第一次把Madeira这个词填进仓库目录名的时候&#xff0c;只是觉得它读起来很有辨识度。后来有人在 PR 评论区里追问&#xff1a;这个项目为什么叫 Madeira&#xff1f;我想了想&#xff0c;给出的答案倒也简单&#xff1a;马德拉酒要经历漫长的熟成过程才有味道&#xff0c…

作者头像 李华
网站建设 2026/10/1 5:06:23

JSTL依赖配置全解:版本对齐、Maven配置与部署排查

JSTL 标签库这个东西&#xff0c;属于那种"平时不用觉得无所谓&#xff0c;一旦用上就再也不想回去写脚本片段"的存在。它的依赖配置本身并不复杂&#xff0c;但在 web 项目里翻车的概率高得离谱——jar 放进去了页面还是报The absolute uri ... cannot be resolved&…

作者头像 李华
网站建设 2026/10/1 5:05:47

200K上下文实战指南:Qwen2.5+TGI+PDF2Markdown长文本处理全栈方案

1. 这不是“平替”&#xff0c;是重新定义长文本处理边界的实战方案最近在几个技术社群里&#xff0c;频繁看到有人发截图&#xff1a;“升级&#xff01;ChatGPT4.0最强平替&#xff0c;可处理200k上下文”——标题很抓眼球&#xff0c;但点进去发现要么是模糊的演示视频&…

作者头像 李华
网站建设 2026/10/1 5:04:39

企业AI落地关键:QuickBlue应用底座如何连接、编排与治理

咱们直接聊点实际的&#xff1a;最近我团队在给几家制造业和零售客户搭AI应用底座&#xff0c;几乎每一家都会问同一个问题——“我们到底要不要自己弄一个QuickBlue这样的东西&#xff1f;还是直接调大模型API就完事了&#xff1f;”我的回答永远是&#xff1a;如果你只想做个…

作者头像 李华