通过本节,你将会:1.明白什么是出行需求// 2.学会用Matsim生成随机的出行需求
在之前章节讨论如何实现一个小型仿真时,我们提出Matsim的仿真除了需要路网文件network.xml,还需要出行需求文件,通常命名为plans.xml。究竟什么是出行需求呢?在交通流仿真中,我们需要在路网中添加许多交通工具,而这些交通工具的起始位置、所经过的道路、最后到达的目的地,都与操控车辆的人有关。每个人会依据自己的一日计划,确定出行的时间,并自己规划一条路线,参与城市的交通行为。 我们利用Matsim的语言重新描述这一过程:每个现实中的人相当于仿真中的代理(agent),每个人都有自己一天的出行计划(plan),例如:7点从家里出发去单位,下午5点从单位回家。在地点的迁移过程中,便产生交通行为。为了在城市路网中实现交通行为,代理必须要为自己规划一条合理的路线(route),同时还需要知道自己所乘坐的交通工具(leg)。这便是基于多代理(Multi-Agent)的交通流仿真(Transport Simulation)。
幸运的是,我们不必提供以上全部的信息,Matsim会在仿真中为我们计算好。因此我们所需要提供的出行需求,称为初始需求(initial demand),应包含以下信息:
代理在特定位置所进行的\textbf{活动(activity)}活动的\textbf{坐标(coord)}和活动的\textbf{结束时间(end time)}代理当前活动结束后,到下一活动的交通工具这些信息作为仿真的输入,被定义在出行需求文件(plans.xml,如下图所示)中,而Matsim将在仿真期间,自动计算每次交通行为的路线、并动态规划交通工具在道路上的行驶速度、到达时间、通过多次重复(iteration)的不断计算和优化,最后展示出一个最接近真实情况的城市交通。
plans.xml对初始需求的定义
Matsim为我们做了很多工作,其中就包括写了一些接口,让我们很方便地利用Matsim的各种功能,当然,前提是我们还要自己编几行代码喔。 交通流仿真时一群代理的行为,因此我们要为一个人口群体(population)中的每个人创造属于自己的出行需求。最简单的,需要告诉每个人在哪里居住,几点开始出发到哪里上班,最后几点下班回家。我们用随机的方法产生一些人口。注意,本节的重点是如何利用Matsim提供的接口,来产生我们需要的文件,所以我们用随机的方法。而至于如何产生接近真实的出行需求,则不在本节的讨论范围内,也许会留给以后。 首先,我们需要为每个代理的活动地点分配坐标,该坐标应与路网形成映射关系。例如,这个坐标可以随机选取路网中的某个地点。相关代码请见CreatePopulationUtils.setMaxMinCoord(Network network),具体见下方代码。 其次,我们要为每个代理创建出行计划,我们简单设定每个人的一天都是从家出发去工作,然后再从工作地点回家,并且设定每个人的出发时间符合高斯分布,相关代码请见CreatePopulationUtils.createOnePerson(Scenario scenario...)。 另外,我们需要一个接口去读取我们之前创建的\filenameFormate{network}路网文件。在Matsim中,已经为我们写好一个类\filenameFormate{MatsimNetworkReader},我们可以直接调用。
综合上面的分析,我们创建一个类CreatePopulationUtils,并将代码补充完整,可以得到以下完整的代码,它可以实现从路网文件中随机产生出行需求:
package MyCode; import java.util.Random; import org.matsim.api.core.v01.Coord; import org.matsim.api.core.v01.Id; import org.matsim.api.core.v01.Scenario; import org.matsim.api.core.v01.network.Network; import org.matsim.api.core.v01.network.Node; import org.matsim.api.core.v01.population.Activity; import org.matsim.api.core.v01.population.Leg; import org.matsim.api.core.v01.population.Person; import org.matsim.api.core.v01.population.Plan; import org.matsim.api.core.v01.population.Population; import org.matsim.core.config.Config; import org.matsim.core.config.ConfigUtils; import org.matsim.core.network.io.MatsimNetworkReader; import org.matsim.core.scenario.ScenarioUtils; import org.matsim.core.population.io.PopulationWriter; public class CreatePopulationUtils { public static Coord coordSW; // 西南角节点坐标(最小x,y) public static Coord coordNE; // 东北角节点坐标(最大x,y) private static void setMaxMinCoord(Network network){ // 设定坐标边界 coordSW, coordNE for (Id<Node> nodeId : network.getNodes().keySet()){ Coord nodeCoord = network.getNodes().get(nodeId).getCoord(); if(coordSW == null||coordNE == null){ coordSW = nodeCoord; coordNE = nodeCoord; continue; } coordSW = new Coord(coordSW.getX()<nodeCoord.getX()?coordSW.getX():nodeCoord.getX(), coordSW.getY()<nodeCoord.getY()?coordSW.getY():nodeCoord.getY()); coordNE = new Coord(coordNE.getX()>nodeCoord.getX()?coordNE.getX():nodeCoord.getX(), coordNE.getY()>nodeCoord.getY()?coordNE.getY():nodeCoord.getY()); } } private static Coord getRandomCoord(){ // 获得随机坐标 double x, y; x = coordSW.getX()+(coordNE.getX() - coordSW.getX())*Math.random(); y = coordSW.getY()+(coordNE.getY() - coordSW.getY())*Math.random(); return new Coord(x, y); } public static void main(String[] args){ Config config = ConfigUtils.createConfig(); Scenario scenario = ScenarioUtils.createScenario(config); // 读取network.xml到内存中 new MatsimNetworkReader(scenario.getNetwork()).readFile("F:/MyFileDir/network.xml"); setMaxMinCoord(scenario.getNetwork()); fillScenario(scenario); // plans.xml文件位置 String filePath = "F:/MyFileDir/plans.xml"; new PopulationWriter(scenario.getPopulation()).write(filePath); System.out.println("Done writing file to"+filePath); } private static Population fillScenario(Scenario scenario) { Population population = scenario.getPopulation(); // 创建500个代理 for (int i = 0; i < 500; i++) { Coord coord = getRandomCoord(); Coord coordWork = getRandomCoord(); createOnePerson(scenario, population, i, coord, coordWork); } return population; } private static void createOnePerson(Scenario scenario, Population population, int i, Coord coord, Coord coordWork) { Person person = population.getFactory().createPerson(Id.createPersonId("p_"+i)); Plan plan = population.getFactory().createPlan(); Activity home = population.getFactory().createActivityFromCoord("home", coord); home.setEndTime(randomTime(8*60*60, 60*60)); plan.addActivity(home); Leg hinweg = population.getFactory().createLeg("car"); plan.addLeg(hinweg); Activity work = population.getFactory().createActivityFromCoord("work", coordWork); work.setEndTime(randomTime(19*60*60, 3*60*60)); plan.addActivity(work); Leg rueckweg = population.getFactory().createLeg("car"); plan.addLeg(rueckweg); Activity home2 = population.getFactory().createActivityFromCoord("home", coord); plan.addActivity(home2); person.addPlan(plan); population.addPerson(person); } private static int randomTime(int normalTime, int variance){ // 产生符合高斯分布的随机时间 Random rand = new Random(); return (int)(variance*rand.nextGaussian()+normalTime); } }再次强调一遍,由于我们需要利用Matsim提供的接口实现以上功能,所以大家在学习过程中一定要规规矩矩的,按照Java标准,在你的Matsim项目中添加以上代码。到此为止,我们学会使用Matsim的接口,可以方便地为每个代理创建自己的出行计划。