路网生成基础:用netedit画个十字路口,再读懂.net.xml

各位同学,咱们今天聊点实在的。做自动驾驶仿真,第一步就是得有路。没有路,车往哪儿跑?对吧。

我个人习惯,不管项目多复杂,都从最简单的十字路口开始。你别小看这个十字路口,它包含了路网的所有核心要素:车道、连接、交叉口、信号灯。说白了,你把这个搞透了,后面再复杂的立交桥、环岛,都是这些元素的排列组合。

1. 用netedit画一个十字路口

netedit是SUMO自带的图形化路网编辑器。我第一次用的时候,觉得它界面有点糙,但用久了你会发现,它其实很高效。嗯,这里要注意,启动netedit之前,你得确保SUMO装好了,环境变量配好了。

打开终端,敲一行命令:

netedit

界面就弹出来了。别慌,咱们一步步来。

1.1 创建道路

在左侧工具栏,选「Edge」模式(就是那个像线段一样的图标)。然后在画布上点一下,拉出一条线,再点一下,一条路就出来了。我建议你先画一条横向的,再画一条纵向的,交叉在一起。

画完之后,你看到的是一条条「边」(Edge)。在SUMO里,一条边代表一段路,可以包含多个车道。默认每条边是双向的,各一个车道。

1.2 调整车道数和方向

双击一条边,弹出属性面板。这里你可以改:

  • numLanes:车道数。十字路口一般设2-3条。
  • speed:限速,单位m/s。城市道路一般13.89(约50km/h)。
  • oneway:是否单向。十字路口通常是双向,所以保持默认false。

我在项目中遇到过,有人把车道数设成1,结果仿真时车流一堵,整个路口就瘫痪了。你想想看,现实中的十字路口,哪个方向不得至少两条车道?一条直行加左转,一条右转,这是基本配置。

1.3 生成交叉口连接

路画好了,但车怎么转弯?这就需要「连接」(Connection)。在netedit里,切换到「Connection」模式,然后依次点击两条相交的边,软件会自动生成转向连接。

这里有个坑:连接必须覆盖所有可能的转向。直行、左转、右转、掉头,缺一个都不行。我曾经因为漏了左转连接,仿真时所有车都在路口直行,后面的车堵了三条街。排查了半天才发现是连接没配全。

1.4 添加信号灯

十字路口没有信号灯,那就是个环岛。在netedit里,选中交叉口,右键 -> 「Add TLS」(Traffic Light System)。然后你可以编辑信号相位。

默认的相位是两相位:南北直行+左转,然后东西直行+左转。我个人习惯改成四相位:南北直行、南北左转、东西直行、东西左转。这样更符合国内路口的实际情况。

小技巧: 信号灯的周期时长,我一般设90秒。太短了车流过不去,太长了司机等得烦躁。这个值是我跑了上百次仿真试出来的,你可以根据车流量微调。

2. 保存并导出路网文件

画完之后,按Ctrl+S保存。netedit会生成一个.net.xml文件。这就是SUMO的路网文件,所有仿真都基于它。

你可以在终端里用netconvert命令,把其他格式(比如OpenStreetMap的.osm)转成.net.xml。但今天咱们先不聊那个,先把手工画的搞明白。

3. 读懂.net.xml文件结构

好,现在打开那个.net.xml文件。别被它的长度吓到,其实核心结构就几块。我带你拆解一下。

3.1 文件头

<?xml version="1.0" encoding="UTF-8"?>
<net version="1.16" junctionCornerDetail="5" limitTurnSpeed="5.50">

这里声明了版本号和一些全局参数。junctionCornerDetail控制交叉口的圆角精度,值越大越圆滑,但文件也越大。我一般用5,够用了。

3.2 边(Edge)的定义

<edge id="gneE0" from="gneJ0" to="gneJ1" priority="-1" numLanes="2" speed="13.89">
  <lane id="gneE0_0" index="0" allow="all" disallow="" speed="13.89" length="100.00"/>
  <lane id="gneE0_1" index="1" allow="all" disallow="" speed="13.89" length="100.00"/>
</edge>

每条边包含一个或多个车道(lane)。注意allow="all"表示所有车辆类型都能走。如果你只想让轿车走,不让卡车走,可以在这里限制。

我在项目中遇到过,某条路是公交专用道,结果忘了改allow属性,仿真时私家车全挤上去了,公交反而没路走。嗯,这种低级错误,犯过一次就记住了。

3.3 交叉口(Junction)的定义

<junction id="gneJ0" type="traffic_light" x="0.00" y="0.00" incLanes="gneE0_0 gneE0_1 gneE1_0 gneE1_1">
  <request index="0" response="0000" foes="0000" cont="0"/>
  <request index="1" response="0000" foes="0000" cont="0"/>
</junction>

交叉口类型有三种:

类型 说明
traffic_light 信号灯控制
priority 让行规则(比如支路让主路)
unregulated 无控制,谁先到谁先走

request标签里的response和foes是二进制掩码,表示哪些流向有冲突。这个有点复杂,你暂时不用深究,知道它是用来做冲突检测的就行。

3.4 连接(Connection)的定义

<connection from="gneE0" to="gneE1" fromLane="0" toLane="0" via="gneJ0_0" dir="s" state="M"/>

这个表示从边gneE0的车道0,连接到边gneE1的车道0,方向是直行(s),状态是M(表示信号灯控制)。

方向代码:

  • s:直行
  • l:左转
  • r:右转
  • t:掉头

状态代码:

  • M:信号灯控制
  • m:让行
  • y:减速让行
  • s:停车让行
注意: 如果你手工修改.net.xml文件,一定要保证连接的方向和实际道路几何一致。我曾经手滑把左转写成了直行,结果仿真时车流直接撞上了对向车道。这种错误在图形界面里不容易犯,但直接改XML就容易出问题。

4. 核心知识体系

下面这张图,是我自己总结的路网生成核心逻辑。你看一遍,基本就明白整个流程了。

路网生成核心知识体系 图形化绘制 netedit 工具 文件生成 .net.xml 导出 结构解析 XML 标签含义 创建道路 (Edge) 调整车道/方向 生成连接 (Connection) 添加信号灯 (TLS) 边 (edge) 标签 车道 (lane) 标签 交叉口 (junction) 标签 连接 (connection) 标签 属性含义 类型与状态码 冲突检测机制 三者循环迭代:绘制 → 导出 → 解析 → 修改 → 再导出

你看,整个流程其实是个闭环。你可以在netedit里画,然后看XML文件理解结构,再回去改,反复几次就熟了。

5. 避坑指南

最后,分享几个我踩过的坑:

  • 坐标对齐:画十字路口时,两条路的交叉点坐标必须精确重合。差一个像素,netedit就会生成两个交叉口,而不是一个。我曾经因为这个,仿真时车流在路口中间莫名其妙地消失了。
  • 车道数量一致:连接的两条边,车道数最好匹配。比如从3车道连接到2车道,你得指定哪条车道连哪条,否则默认配置可能乱套。
  • 信号灯相位别漏:每个方向都要有对应的绿灯相位。我见过有人只配了直行相位,左转车永远等不到绿灯,结果仿真跑了24小时,左转车道堵了10公里。

核心要点总结:

  1. netedit画路 -> 生成.net.xml -> 读懂结构,三步走
  2. 边(Edge)、车道(Lane)、交叉口(Junction)、连接(Connection)是四大核心元素
  3. 信号灯相位要覆盖所有转向方向
  4. 手工改XML时,注意连接方向与实际几何一致

好了,这一章的内容就到这儿。你先把十字路口画熟,把.net.xml文件读透。后面咱们再聊更复杂的路网结构。

可可精品资料库