路网生成基础:用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)。然后你可以编辑信号相位。
默认的相位是两相位:南北直行+左转,然后东西直行+左转。我个人习惯改成四相位:南北直行、南北左转、东西直行、东西左转。这样更符合国内路口的实际情况。
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:停车让行
4. 核心知识体系
下面这张图,是我自己总结的路网生成核心逻辑。你看一遍,基本就明白整个流程了。
你看,整个流程其实是个闭环。你可以在netedit里画,然后看XML文件理解结构,再回去改,反复几次就熟了。
5. 避坑指南
最后,分享几个我踩过的坑:
- 坐标对齐:画十字路口时,两条路的交叉点坐标必须精确重合。差一个像素,netedit就会生成两个交叉口,而不是一个。我曾经因为这个,仿真时车流在路口中间莫名其妙地消失了。
- 车道数量一致:连接的两条边,车道数最好匹配。比如从3车道连接到2车道,你得指定哪条车道连哪条,否则默认配置可能乱套。
- 信号灯相位别漏:每个方向都要有对应的绿灯相位。我见过有人只配了直行相位,左转车永远等不到绿灯,结果仿真跑了24小时,左转车道堵了10公里。
核心要点总结:
- netedit画路 -> 生成.net.xml -> 读懂结构,三步走
- 边(Edge)、车道(Lane)、交叉口(Junction)、连接(Connection)是四大核心元素
- 信号灯相位要覆盖所有转向方向
- 手工改XML时,注意连接方向与实际几何一致
好了,这一章的内容就到这儿。你先把十字路口画熟,把.net.xml文件读透。后面咱们再聊更复杂的路网结构。