4、交通需求建模:从出行需求到仿真车流
各位同学,欢迎来到第四讲。前面我们搭好了路网,就像铺好了跑道。但光有跑道不行,还得有车跑。车从哪里来?这就是我们今天要聊的——交通需求建模。
说白了,就是回答三个问题:谁要从哪里出发,到哪里去,以及怎么走。
我在做第一个SUMO项目时,就犯过一个低级错误:路网画得漂漂亮亮,结果忘了生成车流,仿真跑起来空空荡荡。嗯,那场面挺尴尬的。所以今天的内容,我建议你认真看。
核心知识点速览
- 交通需求概念:OD矩阵、出行链
- trips文件:只定义起终点,不指定路径
- routes文件:包含完整路径(边序列)
- duarouter:基于最短路径或动态分配
- jtrrouter:基于转向概率的路径生成
4.1 交通需求概念:你到底想模拟什么?
交通需求,说白了就是人或者车想要移动的愿望。你想想看,早高峰为什么堵?因为大家都想从家到公司。这个「从家到公司」就是最原始的交通需求。
在SUMO里,我们把这个需求抽象成几个要素:
- 出发时间:什么时候走?
- 起点:从哪条路、哪个区域出发?
- 终点:要去哪里?
- 车辆类型:是小轿车、公交车还是货车?
我在项目中遇到过最头疼的事,就是甲方只给了「早高峰有5000辆车」这么一句话。没有OD矩阵,没有时间分布,你根本没法做。所以,需求建模的第一步,永远是搞清楚你的数据源。
我的经验:如果实在没有真实OD数据,可以用人口普查数据+用地性质来估算。比如住宅区是出发地,商业区是目的地。虽然粗糙,但比瞎猜强。
4.2 OD矩阵:交通需求的「户口本」
OD矩阵,全称Origin-Destination Matrix。它就是一个表格,记录了每个起点到每个终点的出行量。
举个例子,假设我们有三个区域:A(住宅区)、B(商业区)、C(工业区)。OD矩阵可能长这样:
| 起点\终点 | A | B | C |
|---|---|---|---|
| A | 50 | 300 | 150 |
| B | 200 | 30 | 80 |
| C | 100 | 60 | 20 |
这个表告诉我们:从A到B有300辆车,从B到A有200辆车。对角线上的数字(A到A、B到B)表示区域内出行,通常可以忽略或单独处理。
你可能会问:「OD矩阵怎么来的?」
嗯,常见来源有三种:
- 交通调查:车牌识别、手机信令、GPS轨迹
- 模型估算:用重力模型、增长系数法推算
- 假设生成:根据经验或规划数据手动设定
注意:OD矩阵的单位是「出行次数」,不是「车辆数」。一个人可能换乘多次,但SUMO里我们通常直接对应到车辆。如果你有5000次出行,但只有4000辆车,那就需要做合并或调整。
4.3 trips文件:只告诉车「去哪」,不告诉「怎么走」
有了OD矩阵,下一步就是生成trips文件。trips文件长什么样?我直接给你看代码:
<routes>
<trip id="veh1" depart="0.00" from="edge1" to="edge5" type="passenger"/>
<trip id="veh2" depart="2.00" from="edge2" to="edge8" type="passenger"/>
<trip id="veh3" depart="4.50" from="edge3" to="edge6" type="truck"/>
</routes>
看到没?每个trip只有三个关键属性:出发时间(depart)、起点边(from)、终点边(to)。至于中间走哪条路,trips文件不管。
我个人习惯把trips文件叫做「半成品」。它表达了需求,但还没告诉SUMO怎么执行。就像你告诉司机「去火车站」,但没告诉他走哪条路。
生成trips文件,SUMO提供了几个工具:
- randomTrips.py:随机生成,适合测试
- od2trips.py:从OD矩阵生成,最常用
- flowrouter.py:基于流量分配生成
举个例子,用od2trips.py把上面的OD矩阵转成trips:
od2trips.py -n my_net.net.xml -d my_od.csv -o my_trips.xml
这里-n指定路网,-d指定OD矩阵文件,-o输出trips文件。注意,OD矩阵文件格式有要求,我建议你查一下官方文档,别踩坑。
避坑指南:我曾经用od2trips.py时,忘记检查路网中的边ID是否和OD矩阵中的一致。结果生成了一堆无效trip,仿真跑起来车全卡在起点不动。后来我养成了一个习惯:先跑一下sumo-gui my_net.net.xml,手动确认边ID。
4.4 routes文件:完整的「导航路线」
routes文件是trips文件的升级版。它不光告诉车去哪,还告诉车具体走哪条路。看代码:
<routes>
<vehicle id="veh1" depart="0.00">
<route edges="edge1 edge4 edge7 edge5"/>
</vehicle>
<vehicle id="veh2" depart="2.00">
<route edges="edge2 edge5 edge8"/>
</vehicle>
</routes>
注意区别:<vehicle>标签里嵌套了<route>,里面是一串边ID序列。这就是车辆要走的完整路径。
你想想看,如果路网有1000条边,手动写routes文件?那不得累死。所以我们需要工具来自动生成。
4.5 duarouter:最短路径的「老司机」
duarouter是SUMO里最常用的路径分配工具。它的工作原理很简单:给定起点和终点,在路网上找一条最短路径。
用法也很直接:
duarouter -n my_net.net.xml -t my_trips.xml -o my_routes.xml
这里-n是路网,-t是输入trips文件,-o是输出routes文件。
duarouter默认使用旅行时间作为权重。也就是说,它会选择时间最短的路径,而不是距离最短的。这一点很重要,我见过有人搞混了。
duarouter还支持一些高级功能:
- --weight-files:自定义权重,比如避开拥堵路段
- --repair:自动修复无效路径(比如起点终点不在路网上)
- --ignore-errors:跳过错误,继续处理
我的经验:duarouter默认是静态分配,所有车都走最短路径。但现实中,如果大家都走同一条路,那条路就会堵。所以更高级的做法是用动态用户均衡(DUE),SUMO里可以用duarouter --routing-algorithm d来启用。不过那是进阶内容了,今天先不展开。
4.6 jtrrouter:基于转向概率的「随机漫步」
jtrrouter和duarouter的思路完全不同。它不计算最短路径,而是在每个路口根据转向概率随机选择下一步。
什么时候用jtrrouter?
- 你不知道具体的OD矩阵,但知道每个路口的转向比例
- 你想模拟随机漫游的交通流(比如景区内的观光车)
- 你只有交叉口的流量数据,没有起终点数据
jtrrouter需要一个额外的文件:转向概率文件(turns.xml)。长这样:
<turns>
<interval begin="0" end="3600">
<junction id="J1">
<fromEdge id="edge1" toEdge="edge2" probability="0.3"/>
<fromEdge id="edge1" toEdge="edge3" probability="0.7"/>
</junction>
</interval>
</turns>
这个文件告诉jtrrouter:在路口J1,从edge1来的车,30%转向edge2,70%转向edge3。
用法:
jtrrouter -n my_net.net.xml -t my_trips.xml -x my_turns.xml -o my_routes.xml
注意,jtrrouter的输入也是trips文件,但它只关心起点,终点其实可以忽略——因为路径完全由转向概率决定,最终车会走到哪里,是随机的。
警告:jtrrouter有个坑——如果转向概率设置不合理,车可能会在路网里「鬼打墙」,永远到不了终点。我曾经设置了一个环形路口的概率,结果车一直在转圈。所以建议你设置一个最大路径长度参数--max-edges-factor,比如限制每辆车最多走100条边。
4.7 实战对比:duarouter vs jtrrouter
我整理了一个对比表,方便你理解两者的区别:
| 对比项 | duarouter | jtrrouter |
|---|---|---|
| 输入数据 | OD矩阵 / trips文件 | trips文件 + 转向概率 |
| 路径生成逻辑 | 最短路径(旅行时间) | 随机转向(概率驱动) |
| 适用场景 | 已知起终点,路网结构清晰 | 已知转向比例,起终点模糊 |
| 结果确定性 | 确定(相同输入,相同输出) | 随机(每次运行可能不同) |
| 典型应用 | 城市交通仿真、通勤分析 | 景区交通、应急疏散、随机漫游 |
我个人建议:90%的场景用duarouter就够了。jtrrouter更适合做研究或特殊场景。如果你刚开始学,先把duarouter玩熟。
4.8 一个完整的实战流程
最后,我给你一个完整的命令流程,从OD矩阵到routes文件:
# 1. 生成trips文件(从OD矩阵)
od2trips.py -n my_net.net.xml -d my_od.csv -o my_trips.xml
# 2. 检查trips文件(可选)
sumo-gui my_net.net.xml -t my_trips.xml
# 3. 用duarouter生成routes文件
duarouter -n my_net.net.xml -t my_trips.xml -o my_routes.xml --repair
# 4. 运行仿真
sumo -n my_net.net.xml -r my_routes.xml
嗯,就这么简单。但实际项目中,你可能会遇到各种问题。比如OD矩阵格式不对、边ID不匹配、路径分配不合理。别慌,多试几次,看看SUMO的报错信息,慢慢就熟了。
最后一个小技巧:如果你不确定生成的routes文件对不对,可以用sumo-gui打开,然后按「Play」按钮。看着车跑起来,你就知道有没有问题了。视觉检查永远是最直观的。
好了,这一讲的内容就到这里。交通需求建模是整个仿真的灵魂,OD矩阵是骨架,trips和routes是血肉,duarouter和jtrrouter就是造物主的手。希望你能动手试试,把今天讲的命令敲一遍。