news 2026/10/5 12:06:41

SUMO路网XML构建原理与工业级实践指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
SUMO路网XML构建原理与工业级实践指南

1. 为什么非得用XML写路网?——从“点选拖拽”到“精准控制”的思维切换

你打开SUMO的netedit,拖几条路、拉几个交叉口、点几下鼠标,5分钟就能画出一个像模像样的十字路口。这很爽,对吧?但当你需要建一个包含237个信号灯相位、每条车道精确到0.1米偏移、所有连接器带特定转向权重、且要和下游交通流仿真模型做毫秒级时间同步的城郊主干道网络时,netedit的图形界面会突然变得像一张布满毛刺的砂纸——越擦越糊,越改越乱。我去年帮一个物流园区做仿真优化,最初用netedit做了三天,导出的.net.xml文件里有47处手动调整后遗漏的laneID冲突,导致整个仿真在t=12.8秒时崩溃,报错信息只有一行:Error: Invalid lane ID 'E1_0' used in connection.。没人知道这个E1_0是从哪冒出来的,因为netedit根本不会告诉你它内部怎么编号。

这时候,XML就不是“一种语言”,而是你和SUMO底层引擎之间唯一能听懂彼此的“工程图纸”。netedit生成的.net.xml本质就是一份结构化说明书:它告诉SUMO,“这里有一条叫E1的边,长度326.7米,含3条车道,每条宽3.5米,起始坐标是(120.45, 30.22),终点是(120.89, 30.22),它的第0号车道连接到另一条边J2的第1号车道,转向权重设为0.83”。而XML的语法强制你把每一个坐标、每一条车道、每一个连接关系都明确定义出来。这不是繁琐,是可追溯、可版本管理、可自动化生成、可与GIS数据无缝对接的必然选择。

你可能会问:“那我直接手写XML,岂不是比用netedit还慢?”不。真正高效的路网构建,从来不是“纯手写”,而是“模板化+参数化+自动化”。比如,你要建一条标准城市快速路,它永远有:中央隔离带(1条虚拟lane)、双向各3条机动车道(每条3.75米)、最外侧1条公交专用道(3.5米)、两侧各1条非机动车道(2.5米)、人行道(3米)。把这些固定结构写成一个XML模板,只需替换其中的起点坐标、总长度、曲率半径三个参数,就能批量生成10条不同走向的快速路。我实测过,用Python脚本读取Excel里的道路列表(含名称、起点经纬度、终点经纬度、设计车速),自动填充模板并生成127个路段的.net.xml,耗时2.3秒。而用netedit逐条绘制,保守估计要17小时以上,且无法保证车道宽度一致性。

更关键的是,XML让你摆脱了图形界面的“所见即所得”幻觉。netedit里看起来整齐的交叉口,在底层可能藏着几十个未显式定义的连接器(connection),它们默认按某种启发式规则生成,一旦仿真中出现车辆在路口“悬停”或“鬼穿墙”,你根本无从排查。而手写XML时,你必须显式声明每一个connection,比如:

<connection from="E1" to="J2" fromLane="0" toLane="0" pass="0" priority="1"/> <connection from="E1" to="J2" fromLane="1" toLane="1" pass="1" priority="1"/> <connection from="E1" to="J2" fromLane="2" toLane="2" pass="1" priority="1"/>

这三行代码意味着:E1边的0号车道只能直行去J2的0号车道(pass="0"表示禁止变道),而1、2号车道允许变道(pass="1")。这种粒度的控制,图形界面永远做不到。所以,学XML不是为了取代netedit,而是为了在netedit画完初稿后,用XML做“外科手术式”的精准修正——这才是工业级仿真的真实工作流。

提示:别把XML当成编程语言来学。它没有循环、没有变量、没有函数。它就是一个高度结构化的“填空题”。你的任务不是写逻辑,而是准确填写SUMO要求的每一个字段。就像建筑工人看施工图,他不需要懂混凝土配比公式,但他必须清楚“此处钢筋直径12mm,间距150mm,锚固长度35d”。

2. netconvert:从“草图”到“可执行路网”的编译器原理

很多人以为netconvert只是一个“格式转换工具”,把OpenStreetMap的.osm文件转成.sumo.net.xml。这是巨大的误解。netconvert的本质,是一个路网语义编译器。它接收你提供的原始地理数据(osm、csv、shapefile)或手工XML描述,经过一系列严格的拓扑校验、几何计算、逻辑推导,最终输出一个符合SUMO内核运行规范的二进制就绪路网。这个过程,远比“转换”二字复杂得多。

举个最典型的例子:你在OpenStreetMap上画了一条路,标注为highway=primary。netconvert拿到这个信息后,并不会简单地把它变成一条边。它会做以下事情:

  1. 车道数推断:根据highway=primary这个标签,结合你配置的--default.lanenumber参数(默认是2),但它还会查表——SUMO内置了一个“道路等级-典型车道数”映射表。primary通常对应2-4车道,netconvert会进一步检查该路段的lanes=3这个附加标签,如果存在,就采用3;否则,它会分析该路段在OSM中的实际宽度(如果有width=12.5标签),除以标准车道宽3.5米,向下取整得到3条车道。

  2. 连接器自动生成:当两条highway=primary道路相交时,netconvert不会只生成一个简单的十字路口。它会:

    • 计算每条道路进入交叉口的“入口段”(approach)和“出口段”(exit);
    • 为每一对可能的转向组合(左转、直行、右转)生成独立的<connection>元素;
    • 根据道路等级,自动设置priority属性(主干道优先级高,支路优先级低);
    • 如果检测到某条转向路径存在几何冲突(比如左转轨迹会与对向直行车道重叠),它会自动插入一个“转弯缓存区”(turning loop),并在XML中生成额外的虚拟边(virtual edge)来规避。
  3. 几何精度校验:netconvert会对所有节点坐标进行浮点数精度归一化。它会把所有坐标统一缩放到小数点后6位(例如120.456789123→120.456789),并检查是否存在重复节点(距离小于1e-6米)。一旦发现重复,它会合并节点,并更新所有引用该节点的边(edge)的端点ID。这一步看似微小,却能避免后续仿真中因浮点误差导致的“车辆卡在节点上”的诡异问题。

所以,当你运行netconvert --osm-files map.osm -o network.net.xml时,你不是在“转换”,而是在“编译”。这个编译过程的输出,就是一份完全由机器验证过的、无歧义的路网定义。这也是为什么,我们强烈建议:任何通过netedit手工绘制的路网,最终都必须用netconvert重新“编译”一次。netedit保存的.net.xml文件,只是编辑状态的快照,它可能包含未清理的临时节点、冗余的连接器、甚至坐标精度错误。而netconvert的--xml-validation选项(默认开启)会强制执行全套校验,确保输出的.network.net.xml是“生产就绪”的。

我踩过的一个深坑是:在netedit里画了一条弯曲的环形匝道,觉得形状完美,直接保存为.net.xml。结果仿真跑起来,车辆在匝道末端集体“消失”。用sumo-gui --net-file network.net.xml加载后,发现匝道末端的最后一个节点,其坐标被netedit四舍五入成了(120.123456, 30.654321),而下游连接的道路起点坐标是(120.1234567, 30.6543219)。这两个点在图形界面上看起来完全重合,但在SUMO内核里,它们是两个不同的节点,导致连接器失效。用netconvert重新编译后,它自动将两个坐标归一化为(120.123457, 30.654322),问题瞬间解决。

注意:netconvert的--geometry.min-radius参数至关重要。它定义了道路曲线的最小允许曲率半径。如果你的路网中有大量小半径弯道(比如停车场内的U型掉头),必须把这个值调小(例如--geometry.min-radius 5),否则netconvert会强行把弯道“掰直”,生成一堆锯齿状的短线段,严重影响仿真精度和性能。

3. XML路网的核心骨架:从根节点到车道连接的逐层拆解

SUMO的路网XML文件,其结构严格遵循一个自顶向下的层级模型。理解这个骨架,是你能写出正确XML的前提。它不是随意堆砌的标签,而是一个精密的“树状工程蓝图”。我们从最顶层开始,一层层剥开:

3.1<net>:路网的容器与元数据

所有路网XML的根节点必须是<net>。它本身不定义任何几何实体,但它承载着整个路网的全局属性和命名空间声明:

<?xml version="1.0" encoding="UTF-8"?> <net version="1.8" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://sumo.dlr.de/xsd/net_file.xsd">

这里的version="1.8"指明了该文件遵循SUMO 1.8版的schema规范。这个版本号必须与你安装的SUMO版本严格匹配。如果你用SUMO 1.7生成的.net.xml,强行在1.8里加载,很可能因为新增的字段(如<edge>的type属性)缺失而报错。xsi:noNamespaceSchemaLocation指向官方XSD Schema文件,这是XML验证的依据。你可以用任何支持XSD验证的编辑器(如VS Code + XML Tools插件)来实时检查你的XML是否符合规范。

3.2<location>:地理坐标的锚定点

紧随<net>之后,几乎必有的是<location>块。它定义了整个路网的地理参考系:

<location netOffset="120.456789,30.234567" convBoundary="0.00,0.00,1000.00,1000.00" origBoundary="120.45,30.23,120.46,30.24" projParameter="+proj=utm +zone=50 +ellps=WGS84"/>
  • netOffset:这是最关键的一行。它表示路网原点(0,0)在WGS84地理坐标系(经纬度)中的位置。所有你在XML里写的<node>坐标,都是相对于这个原点的偏移量(单位:米)。例如,一个<node id="n1" x="100.0" y="200.0"/>,其真实地理坐标就是(120.456789 + 100.0/111319.9, 30.234567 + 200.0/111319.9)(这里111319.9是赤道附近1度经度约等于的米数,实际计算由projParameter完成)。
  • convBoundary:定义了路网在“投影平面坐标系”下的边界矩形(xmin,ymin,xmax,ymax),单位是米。SUMO用它来优化渲染和碰撞检测。
  • origBoundary:原始地理坐标的边界(经纬度),用于GIS软件识别。
  • projParameter:PROJ字符串,定义了如何将经纬度转换为平面坐标。+proj=utm +zone=50表示使用UTM第50带投影。如果你的路网横跨多个UTM带(比如从东经119°到东经121°),必须使用+proj=longlat(经纬度直角坐标)或+proj=merc(墨卡托),否则会出现严重的几何畸变。

3.3<node>:路网的“钉子”与拓扑基石

<node>是路网中最基础的几何单元,它代表一个空间点。所有道路(<edge>)都必须始于和终于某个<node>:

<node id="n1" x="0.0" y="0.0" type="traffic_light"/> <node id="n2" x="500.0" y="0.0" type="priority"/> <node id="n3" x="500.0" y="500.0" type="priority"/> <node id="n4" x="0.0" y="500.0" type="priority"/>
  • id:节点的唯一标识符,必须全局唯一,且不能包含空格或特殊字符(推荐纯字母数字)。
  • x,y:相对于netOffset的平面坐标(米)。
  • type:节点类型,决定了它在交叉口的行为。traffic_light表示此处有信号灯控制;priority表示主次路优先通行;unregulated表示无管制;dead_end表示死胡同。这个type属性直接驱动SUMO的车辆让行逻辑,绝不能随意设置。比如,一个type="priority"的节点,如果连接的两条边等级相同(都是highway=primary),SUMO会按“先到先得”原则处理,而不是你期望的“主路优先”。

3.4<edge>:道路的“骨架”与车道载体

<edge>定义了一条物理道路,它是连接两个<node>的线段。一条<edge>可以包含多条平行的<lane>:

<edge id="E1" from="n1" to="n2" priority="1" type="highway.primary"> <lane id="E1_0" index="0" speed="13.89" width="3.5" length="500.0"/> <lane id="E1_1" index="1" speed="13.89" width="3.5" length="500.0"/> <lane id="E1_2" index="2" speed="13.89" width="3.5" length="500.0"/> </edge>
  • id:边的ID,必须唯一。
  • from,to:引用<node>的ID,定义了边的起点和终点。
  • priority:边的优先级数值,数值越大,优先级越高。在交叉口,高优先级边上的车辆有路权。
  • type:边的类型,通常与OSM标签对应(highway.primary,highway.residential等),它关联到SUMO预设的车道数、速度限制等默认值。
  • <lane>:嵌套在<edge>内的子元素。每条<lane>代表一条物理车道:
    • id:车道ID,格式通常是<edge_id>_<index>,这是SUMO内部寻址的关键。
    • index:车道索引,从0开始。索引0通常是靠近路中心线的车道(左行国家则相反)。
    • speed:该车道的限速(m/s)。13.89m/s = 50 km/h。
    • width:车道宽度(米)。
    • length:车道长度(米),必须等于<edge>的几何长度。

3.5<connection>:交通流的“神经突触”

如果说<edge>是血管,<lane>是血细胞,那么<connection>就是决定血液流向的“瓣膜”。它定义了车辆如何从一条车道,合法地转移到另一条车道:

<connection from="E1" to="E2" fromLane="0" toLane="0" pass="0" priority="1" via=":n2_0_0"/> <connection from="E1" to="E2" fromLane="1" toLane="1" pass="1" priority="1" via=":n2_0_1"/> <connection from="E1" to="E2" fromLane="2" toLane="2" pass="1" priority="1" via=":n2_0_2"/>
  • from,to:引用源边和目标边的ID。
  • fromLane,toLane:引用具体的源车道和目标车道ID(注意,这里用的是index,不是id)。
  • pass:是否允许变道。0表示禁止,1表示允许。这是控制车辆行为的核心开关。
  • priority:该连接的优先级,影响车辆在冲突点的让行决策。
  • via:这是一个关键字段!它指向一个“内部节点”(internal node),格式为:n2_0_0。这个节点不是你定义的<node>,而是netconvert在编译时,为每个连接自动生成的虚拟节点,用于精确描述车辆的转向轨迹。你永远不应该手动修改或删除via属性,否则会导致转向路径计算失败。

理解这五层结构,你就掌握了XML路网的全部骨骼。剩下的,就是在这个骨架上,填充血肉——添加交通灯程序、定义公交线路、设置可变限速区域。但骨架错了,一切皆空。

4. 实战:从零开始构建一个带信号灯的T型交叉口

现在,让我们把前面所有的理论,落地到一个具体、可运行的案例:一个标准的T型交叉口,南北向为主干道(双向4车道),东西向为支路(双向2车道),交叉口处设有四相位信号灯。我们将全程手写XML,不依赖netedit,来展示真正的“精准控制”。

4.1 步骤一:规划坐标与节点

首先,确定交叉口的中心点为原点(0,0)。主干道(南北向)沿Y轴延伸,支路(东西向)沿X轴延伸。

  • 主干道南端节点n1:(0, -300)(距中心300米)
  • 交叉口中心节点n2:(0, 0)
  • 主干道北端节点n3:(0, 300)
  • 支路西端节点n4:(-200, 0)
  • 支路东端节点n5:(200, 0)

这些节点的type需要精心设置:

  • n2是信号灯控制的交叉口,type="traffic_light"
  • n1,n3,n4,n5都是普通端点,type="priority"(因为它们连接的是主干道,有路权)

4.2 步骤二:定义主干道与支路的边(Edge)

主干道分为两段:E1(南→中心),E2(中心→北)。支路也分为两段:E3(西→中心),E4(中心→东)。

<!-- 主干道南段 --> <edge id="E1" from="n1" to="n2" priority="3" type="highway.primary"> <lane id="E1_0" index="0" speed="16.67" width="3.5" length="300.0"/> <lane id="E1_1" index="1" speed="16.67" width="3.5" length="300.0"/> <lane id="E1_2" index="2" speed="16.67" width="3.5" length="300.0"/> <lane id="E1_3" index="3" speed="16.67" width="3.5" length="300.0"/> </edge> <!-- 主干道北段 --> <edge id="E2" from="n2" to="n3" priority="3" type="highway.primary"> <lane id="E2_0" index="0" speed="16.67" width="3.5" length="300.0"/> <lane id="E2_1" index="1" speed="16.67" width="3.5" length="300.0"/> <lane id="E2_2" index="2" speed="16.67" width="3.5" length="300.0"/> <lane id="E2_3" index="3" speed="16.67" width="3.5" length="300.0"/> </edge> <!-- 支路西段 --> <edge id="E3" from="n4" to="n2" priority="1" type="highway.residential"> <lane id="E3_0" index="0" speed="8.33" width="3.25" length="200.0"/> <lane id="E3_1" index="1" speed="8.33" width="3.25" length="200.0"/> </edge> <!-- 支路东段 --> <edge id="E4" from="n2" to="n5" priority="1" type="highway.residential"> <lane id="E4_0" index="0" speed="8.33" width="3.25" length="200.0"/> <lane id="E4_1" index="1" speed="8.33" width="3.25" length="200.0"/> </edge>

注意priority的设置:主干道priority="3",支路priority="1",确保主干道车辆有绝对路权。

4.3 步骤三:定义所有必需的连接器(Connection)

T型交叉口有6种合法转向:主干道直行(2种)、主干道左转(2种)、支路直行(2种)。我们需要为每一种组合,显式定义<connection>。

<!-- 主干道南→北直行 (E1_0->E2_0, E1_1->E2_1, E1_2->E2_2, E1_3->E2_3) --> <connection from="E1" to="E2" fromLane="0" toLane="0" pass="0" priority="1"/> <connection from="E1" to="E2" fromLane="1" toLane="1" pass="0" priority="1"/> <connection from="E1" to="E2" fromLane="2" toLane="2" pass="0" priority="1"/> <connection from="E1" to="E2" fromLane="3" toLane="3" pass="0" priority="1"/> <!-- 主干道北→南直行 (E2_0->E1_0, E2_1->E1_1, E2_2->E1_2, E2_3->E1_3) --> <connection from="E2" to="E1" fromLane="0" toLane="0" pass="0" priority="1"/> <connection from="E2" to="E1" fromLane="1" toLane="1" pass="0" priority="1"/> <connection from="E2" to="E1" fromLane="2" toLane="2" pass="0" priority="1"/> <connection from="E2" to="E1" fromLane="3" toLane="3" pass="0" priority="1"/> <!-- 主干道南→西左转 (E1_0->E3_0, E1_1->E3_1) --> <connection from="E1" to="E3" fromLane="0" toLane="0" pass="0" priority="1"/> <connection from="E1" to="E3" fromLane="1" toLane="1" pass="0" priority="1"/> <!-- 主干道北→东左转 (E2_0->E4_0, E2_1->E4_1) --> <connection from="E2" to="E4" fromLane="0" toLane="0" pass="0" priority="1"/> <connection from="E2" to="E4" fromLane="1" toLane="1" pass="0" priority="1"/> <!-- 支路西→东直行 (E3_0->E4_0, E3_1->E4_1) --> <connection from="E3" to="E4" fromLane="0" toLane="0" pass="0" priority="1"/> <connection from="E3" to="E4" fromLane="1" toLane="1" pass="0" priority="1"/> <!-- 支路东→西直行 (E4_0->E3_0, E4_1->E3_1) --> <connection from="E4" to="E3" fromLane="0" toLane="0" pass="0" priority="1"/> <connection from="E4" to="E3" fromLane="1" toLane="1" pass="0" priority="1"/>

这里的关键是pass="0"。我们禁止所有变道,确保每条车道的转向路径是唯一的、可预测的。这对于信号灯配时至关重要。

4.4 步骤四:添加信号灯程序

最后,为节点n2添加一个四相位信号灯。SUMO的信号灯程序(<tlLogic>)定义了每个相位的绿灯时长和哪些连接器被允许通行。

<tlLogic id="n2" type="static" programID="myProgram" offset="0"> <!-- 相位1:主干道南北直行 --> <phase duration="30" state="GGGgrrrrGGGgrrrr"/> <!-- 相位2:主干道南北左转 --> <phase duration="15" state="yyyyGGGGyyyyGGGG"/> <!-- 相位3:支路东西直行 --> <phase duration="25" state="rrrrGGGgrrrrGGGg"/> <!-- 相位4:支路东西左转 --> <phase duration="10" state="GGGGyyyyGGGGyyyy"/> </tlLogic>

state字符串是核心。每一位对应一个<connection>,顺序与<connection>在XML中出现的顺序一致。G表示绿灯(允许通行),g表示黄灯(准备停止),r表示红灯(禁止通行),y表示黄灯。这个字符串必须与你定义的<connection>数量和顺序完全匹配,否则信号灯会失控。

将以上所有内容,连同<net>、<location>、<node>等,整合成一个完整的.net.xml文件,用netconvert编译,再用sumo-gui加载,你就能看到一个完全由你定义、毫秒级可控的T型交叉口仿真了。这个过程,没有任何“黑箱”,每一步都清晰可见。

经验技巧:在编写大型路网时,永远先写一个最小可行单元(如一个交叉口),用sumo-gui反复测试,确认所有连接器和信号灯都工作正常后,再复制、粘贴、修改,扩展成整个网络。切忌一上来就写几百个节点,最后报错时无从排查。

5. 常见XML陷阱与排错链路:从“文件加载失败”到“车辆行为异常”

即使你严格按照XSD Schema写了XML,SUMO在加载时仍可能报错。这些错误往往不是语法错误,而是语义错误。下面是我整理的最常遇到的5类问题,以及一套标准化的排错链路。

5.1 错误类型一:Error: Invalid node ID 'n1' used in edge 'E1'

表象:netconvert或sumo-gui启动时,第一行报错,指出某个节点ID不存在。

根因分析:这是最基础的引用错误。<edge from="n1" to="n2">中引用的n1,在<node>列表里根本没定义,或者ID拼写错误(比如写成了n01)。

排错链路:

  1. 用文本编辑器的“查找”功能,搜索n1,确认它只在<node id="n1">中定义过一次。
  2. 检查<node>标签是否闭合,有没有漏掉/>或</node>。
  3. 检查<node>是否被意外地写在了<edge>标签内部(XML不允许嵌套)。

避坑心得:在VS Code中安装“Auto Rename Tag”插件。当你修改一个<node id="n1">时,它会自动帮你修改所有引用n1的地方,彻底杜绝此类错误。

5.2 错误类型二:Warning: Connection 'E1_0->E2_0' is not usable (no common node)

表象:netconvert能成功生成.net.xml,但sumo-gui加载后,车辆在交叉口“凭空消失”或“撞墙”。

根因分析:<connection>的from和to边,没有共享同一个<node>。例如,E1的to="n2",E2的from="n3",那么E1和E2就没有物理连接,<connection>自然无效。

排错链路:

  1. 找到报错的<connection>,记下from和to的边ID。
  2. 分别找到这两个<edge>的定义,查看它们的from和to属性。
  3. 确认它们是否真的共享一个节点。例如,E1 to="n2",E2 from="n2",这才叫共享。

避坑心得:在定义<edge>时,养成习惯:先写好所有<node>,再写<edge>,并用编辑器的“折叠”功能,把<node>块暂时收起来,避免视觉干扰。写完所有<edge>后,再展开<node>,逐一核对。

5.3 错误类型三:Error: Lane 'E1_0' has invalid length

表象:netconvert报错,指出某条车道长度与边的几何长度不匹配。

根因分析:<lane length="500.0"/>的值,必须等于<edge>两个端点之间的欧氏距离。如果你的<node n1 x="0" y="0"/>和<node n2 x="300" y="400"/>,那么边长是500米,<lane>的length就必须是500.0,不能是500(缺少小数点)或500.0000001(浮点误差)。

排错链路:

  1. 用计算器算出两个端点的距离:sqrt((x2-x1)^2 + (y2-y1)^2)。
  2. 将结果精确到小数点后1位,填入<lane length="..."/>。
  3. 如果边是弯曲的(有shape属性),length必须是shape定义的曲线长度,这时需要用GIS软件或Python的shapely库计算。

避坑心得:对于直线边,永远用<edge>的length属性(如果netconvert生成的)或自己计算的精确值,不要凭感觉填写。SUMO对长度精度极其敏感。

5.4 错误类型四:Warning: No connections defined for node 'n2'

表象:路网能加载,但车辆到了交叉口就“卡住”,不转向。

根因分析:你定义了<node id="n2" type="traffic_light">,但没有为它定义任何<connection>。type="traffic_light"只是告诉SUMO“这里有个灯”,但灯管什么?必须由<connection>来定义。

排错链路:

  1. 找到<node id="n2">。
  2. 搜索所有<connection from="..." to="...">,确认是否有from或to指向n2所连接的边。
  3. 如果有,再检查这些<connection>的fromLane和toLane索引,是否超出了对应边的车道数。

避坑心得:为每个type="traffic_light"的节点,建立一个检查清单:1)该节点连接的所有边;2)每条边的所有车道;3)为每一对可能的转向,写一个<connection>。用Excel表格管理,比在XML里硬找可靠得多。

5.5 错误类型五:Error: Invalid tlLogic ID 'n2' for node 'n2'

表象:信号灯不亮,或者所有相位同时为绿灯。

根因分析:<tlLogic id="n2">的id,必须与<node id="n2">的id完全一致。此外,<node>的type必须是traffic_light,否则SUMO会忽略这个<tlLogic>。

排错链路:

  1. 确认<node id="n2" type="traffic_light">已正确定义。
  2. 确认<tlLogic id="n2">的id与之完全相同。
  3. 确认<tlLogic>的state字符串长度,等于该节点所有<connection>的数量。用`grep "<connection" your
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/5 12:06:22

MT4/MT5加载EA失败的五大核心原因与排查链

1. 为什么“加载EA”这个动作&#xff0c;90%的人卡在第一步就失败了你点开MT4或MT5&#xff0c;双击桌面图标&#xff0c;界面弹出来——看起来一切正常。你把下载好的.mq4或.ex4文件拖进软件窗口&#xff0c;没反应&#xff1b;右键“文件→打开数据文件夹”&#xff0c;找到…

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

高精度定位技术全解析:RTK、PPP-RTK与GNSS/INS组合导航

高精度定位技术这几年的热度&#xff0c;从测量测绘行业一路烧到智能驾驶、低空经济、机器人和工程机械。2025年再回头看&#xff0c;行业的竞争点已经从“谁能拿到厘米级精度”&#xff0c;换成了“谁的厘米级表现能一直稳定&#xff0c;在树荫、高架、隧道边还能扛得住”。去…

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

SPM数据处理高频报错排查与实用解决指南

1. 从SPM启动那一刻开始&#xff1a;界面卡死与路径暗坑用SPM做FMRI数据处理&#xff0c;很多人第一步就会卡住——不是数据的问题&#xff0c;而是SPM压根起不来&#xff0c;或者起来之后各种报错。我最早接触SPM的时候&#xff0c;光是把界面打开就折腾了整整一个下午&#x…

作者头像 李华
网站建设 2026/10/5 12:03:11

线性回归从原理到实战:手写实现、sklearn流程与常见坑排查

很多人第一次接触机器学习&#xff0c;不是被神经网络拉进坑的&#xff0c;而是被一行“linear代码线性回归”拉进坑的。十几行代码跑完&#xff0c;屏幕上跳出斜线穿过散点图&#xff0c;当时觉得“就这&#xff1f;”。但后来回头看&#xff0c;线性回归模型把机器学习的完整…

作者头像 李华
网站建设 2026/10/5 12:03:02

EKF与UKF电力系统动态状态估计对比及IEEE 39节点系统实践

把基于EKF&#xff08;扩展卡尔曼滤波&#xff09;和UKF&#xff08;无迹卡尔曼滤波&#xff09;的电力系统动态状态估计完整做一遍&#xff0c;选的是IEEE 39节点系统&#xff0c;从模型搭建、算法推导、仿真数据生成到结果对比&#xff0c;一路踩坑一路填坑&#xff0c;最后总…

作者头像 李华
网站建设 2026/10/5 12:01:09

企业智能体平台落地实战:工作流编排、RAG检索与权限治理的五种路径

1. 企业智能体平台落地的真实困境过去一年多&#xff0c;我参与过三个不同规模的企业智能体平台项目&#xff0c;从几十人的创业团队到上千人的集团公司都有。一个非常普遍的现象是&#xff1a;演示阶段效果惊艳&#xff0c;POC 阶段勉强过关&#xff0c;一到真实业务场景就各种…

作者头像 李华