4、交通需求建模:从出行需求到仿真车流

各位同学,欢迎来到第四讲。前面我们搭好了路网,就像铺好了跑道。但光有跑道不行,还得有车跑。车从哪里来?这就是我们今天要聊的——交通需求建模。

说白了,就是回答三个问题:谁要从哪里出发,到哪里去,以及怎么走。

我在做第一个SUMO项目时,就犯过一个低级错误:路网画得漂漂亮亮,结果忘了生成车流,仿真跑起来空空荡荡。嗯,那场面挺尴尬的。所以今天的内容,我建议你认真看。

核心知识点速览

  • 交通需求概念:OD矩阵、出行链
  • trips文件:只定义起终点,不指定路径
  • routes文件:包含完整路径(边序列)
  • duarouter:基于最短路径或动态分配
  • jtrrouter:基于转向概率的路径生成
交通需求建模核心流程 OD矩阵 起终点对 + 出行量 trips文件 只定义起终点 routes文件 完整路径序列 生成 路径分配 duarouter 最短路径/动态分配 jtrrouter 转向概率 OD矩阵 → trips文件 → 路径分配 → routes文件 → SUMO仿真 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矩阵怎么来的?」

嗯,常见来源有三种:

  1. 交通调查:车牌识别、手机信令、GPS轨迹
  2. 模型估算:用重力模型、增长系数法推算
  3. 假设生成:根据经验或规划数据手动设定

注意: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就是造物主的手。希望你能动手试试,把今天讲的命令敲一遍。

可可精品资料库