[Matsim]Matsim学习笔记-动态线路接乘客上车的逻辑
学习需求
matsim中动态线路场景模拟中核心的是三个功能:
1、拼车,生成新的插入点
2、生成接乘客上车的任务
3、生成送乘客下车的任务
本次学习第2个功能:接乘客上车的任务
学习笔记
接乘客上车在matsim中的代码是在扩展包
org.matsim.contrib.drt.scheduler
今天学习默认的调度器
DefaultRequestInsertionScheduler
接乘客上车的方法
var pickupTask = insertPickup(request, insertion);
下面是对insertPickUp方法的代码分析
insertPickUp
输入参数:
request:当前应答成功的订单请求
insertion:当前应答成功的订单的插入对象
输出结果
drtStopTask:接当前乘客的停车任务
源代码:
//用于在DVRP或DRT系统中安排一个上客任务(返回接乘客上车前的stopTask,即停车接乘客上车的任务)
DrtStopTask insertPickup(AcceptedDrtRequest request, InsertionWithDetourData insertionWithDetourData) {//当前订单的插入点,包含在哪上车,在哪下车var insertion = insertionWithDetourData.insertion;//当前订单的车辆VehicleEntry vehicleEntry = insertion.vehicleEntry;//当前订单的车辆的调度Schedule schedule = vehicleEntry.vehicle.getSchedule();//当前车辆的停靠点List<Waypoint.Stop> stops = vehicleEntry.stops;int pickupIdx = insertion.pickup.index;int dropoffIdx = insertion.dropoff.index;//绕路信息var detourData = insertionWithDetourData.detourData;//根据行程的当前状态scheduleStatus,确定是否需要创建新的上客任务ScheduleStatus scheduleStatus = schedule.getStatus();Task currentTask = scheduleStatus == ScheduleStatus.PLANNED ? null : schedule.getCurrentTask();//接乘客之前的任务Task beforePickupTask;//***************处理已经开始的行程****************************////如果行程已经开始并且当前任务是上客或下客任务,//找当前路径的改道点,如果找到改道点,重新规划路径//如果没有找到改造点,则会创建一个新的行驶任务。if (pickupIdx == 0 && scheduleStatus != ScheduleStatus.PLANNED && DRIVE.isBaseTypeOf(currentTask)) {LinkTimePair diversion = ((OnlineDriveTaskTracker)currentTask.getTaskTracker()).getDiversionPoint();if (diversion != null) { // divert vehiclebeforePickupTask = currentTask;VrpPathWithTravelData vrpPath = VrpPaths.createPath(vehicleEntry.start.link, request.getFromLink(),vehicleEntry.start.time, detourData.detourToPickup, travelTime);((OnlineDriveTaskTracker)beforePickupTask.getTaskTracker()).divertPath(vrpPath);} else { // too late for diversionif (request.getFromLink() != vehicleEntry.start.link) { // add a new drive taskVrpPathWithTravelData vrpPath = VrpPaths.createPath(vehicleEntry.start.link, request.getFromLink(),vehicleEntry.start.time, detourData.detourToPickup, travelTime);beforePickupTask = taskFactory.createDriveTask(vehicleEntry.vehicle, vrpPath, DrtDriveTask.TYPE);schedule.addTask(currentTask.getTaskIdx() + 1, beforePickupTask);} else { // no need for a new drive taskbeforePickupTask = currentTask;}}} else { // 如果行程已经开始并且当前任务是停留任务,会根据当前时间和请求的最早开始时间调整停留任务的结束时间DrtStayTask stayTask = null;DrtStopTask stopTask = null;if (pickupIdx == 0) {if (scheduleStatus == ScheduleStatus.PLANNED) {// PLANNED 表示车辆当前没有进行中的任务//从行程中获取第一个停留任务(DrtStayTask),初始化时的stay任务stayTask = (DrtStayTask)schedule.getTasks().get(0);//将停留任务的结束时间设置为其开始时间,这意味着停留任务将立即结束stayTask.setEndTime(stayTask.getBeginTime());} else if (STAY.isBaseTypeOf(currentTask)) {//中间过程中的持续进行的stay任务stayTask = (DrtStayTask)currentTask; double now = timer.getTimeOfDay();//如果停留任务的结束时间大于当前时间,将停留任务的结束时间设置为当前时间,这样新的任务就可以插入。if (stayTask.getEndTime() > now) { stayTask.setEndTime(now);}} else {//处理正在进行中的停车任务stopTask = (DrtStopTask)currentTask; }} else {//处理非第一个任务的上客stopTask = stops.get(pickupIdx - 1).task; }if (stopTask != null && request.getFromLink() == stopTask.getLink()) { // no detour; no new stop task// add pickup request to stop taskstopTask.addPickupRequest(request);double stopDuration = stopDurationEstimator.calcDuration(vehicleEntry.vehicle, stopTask.getDropoffRequests().values(), stopTask.getPickupRequests().values());stopTask.setEndTime(Math.max(stopTask.getBeginTime() + stopDuration, request.getEarliestStartTime()));if (pickupIdx == dropoffIdx) {// remove drive i->i+1 (if there is one)if (pickupIdx < stops.size()) {// there is at least one following stopDrtStopTask nextStopTask = stops.get(pickupIdx).task;if (stopTask.getTaskIdx() + 2 != nextStopTask.getTaskIdx()) {// there must a drive task in// betweenthrow new RuntimeException();}if (stopTask.getTaskIdx() + 2 == nextStopTask.getTaskIdx()) {// there must a drive task in// betweenint driveTaskIdx = stopTask.getTaskIdx() + 1;schedule.removeTask(schedule.getTasks().get(driveTaskIdx));}}Link toLink = request.getToLink(); // pickup->dropoffVrpPathWithTravelData vrpPath = VrpPaths.createPath(request.getFromLink(), toLink,stopTask.getEndTime(), detourData.detourFromPickup, travelTime);Task driveFromPickupTask = taskFactory.createDriveTask(vehicleEntry.vehicle, vrpPath,DrtDriveTask.TYPE);schedule.addTask(stopTask.getTaskIdx() + 1, driveFromPickupTask);// 更新后续任务的开始和结束时间scheduleTimingUpdater.updateTimingsStartingFromTaskIdx(vehicleEntry.vehicle,stopTask.getTaskIdx() + 2, driveFromPickupTask.getEndTime());}return stopTask;} else {StayTask stayOrStopTask = stayTask != null ? stayTask : stopTask;//检查是否有后续停车点 // remove drive i->i+1 (if there is one)if (pickupIdx < stops.size()) {// there is at least one following stop//获取下一个停车任务DrtStopTask nextStopTask = stops.get(pickupIdx).task;// check: if there is at most one drive task in betweenif (stayOrStopTask.getTaskIdx() + 2 != nextStopTask.getTaskIdx() //&& stayTask != null && stayTask.getTaskIdx() + 1 != nextStopTask.getTaskIdx()) {throw new RuntimeException();}//检查是否有行驶任务需要移除 if (stayOrStopTask.getTaskIdx() + 2 == nextStopTask.getTaskIdx()) {// removing the drive task that is in betweenint driveTaskIdx = stayOrStopTask.getTaskIdx() + 1;//移除中间的行驶任务schedule.removeTask(schedule.getTasks().get(driveTaskIdx));}}//处理两种情况:一种是车辆已经在上客点(无需额外行驶),另一种是车辆需要行驶到上客点。if (stayTask != null && request.getFromLink() == stayTask.getLink()) {// the bus stays where it isbeforePickupTask = stayTask;} else {// add drive task to pickup location// insert drive i->pickup// 需要创建一个新的行驶任务以将车辆从当前位置行驶到上客点VrpPathWithTravelData vrpPath = VrpPaths.createPath(stayOrStopTask.getLink(), request.getFromLink(),stayOrStopTask.getEndTime(), detourData.detourToPickup, travelTime);beforePickupTask = taskFactory.createDriveTask(vehicleEntry.vehicle, vrpPath, DrtDriveTask.TYPE);schedule.addTask(stayOrStopTask.getTaskIdx() + 1, beforePickupTask);}}}// insert pickup stop taskdouble startTime = beforePickupTask.getEndTime();int taskIdx = beforePickupTask.getTaskIdx() + 1;double stopDuration = stopDurationEstimator.calcDuration(vehicleEntry.vehicle, Collections.emptySet(), Collections.singleton(request));DrtStopTask pickupStopTask = taskFactory.createStopTask(vehicleEntry.vehicle, startTime,Math.max(startTime + stopDuration, request.getEarliestStartTime()), request.getFromLink());schedule.addTask(taskIdx, pickupStopTask);pickupStopTask.addPickupRequest(request);// add drive from pickupLink toLink = pickupIdx == dropoffIdx ? request.getToLink() // pickup->dropoff: stops.get(pickupIdx).task.getLink(); // pickup->i+1VrpPathWithTravelData vrpPath = VrpPaths.createPath(request.getFromLink(), toLink, pickupStopTask.getEndTime(),detourData.detourFromPickup, travelTime);Task driveFromPickupTask = taskFactory.createDriveTask(vehicleEntry.vehicle, vrpPath, DrtDriveTask.TYPE);schedule.addTask(taskIdx + 1, driveFromPickupTask);// update timingsscheduleTimingUpdater.updateTimingsStartingFromTaskIdx(vehicleEntry.vehicle, taskIdx + 2,driveFromPickupTask.getEndTime());return pickupStopTask;
}
代码的整体逻辑
insertPickup用于在DVRP或DRT系统中安排一个上客任务。这个方法相当复杂,涉及多个步骤和条件判断,以下是它的逻辑概述:
上客逻辑
-
提取参数:从方法参数中提取插入数据
insertionWithDetourData
和请求request
。 -
获取车辆信息:从插入数据中获取
VehicleEntry
对象,进而获取车辆的行程schedule
和停车点列表stops
。 -
确定上客索引和绕路数据:从插入数据中获取上客的索引
pickupIdx
和绕路数据detourData
。 -
检查行程状态:根据行程的当前状态
scheduleStatus
,确定是否需要创建新的上客任务。 -
处理已经开始的行程:
- 如果行程已经开始并且当前任务是上客或下客任务,并且车辆可以改道,那么会创建一个改道任务。
- 如果行程已经开始并且当前任务是停留任务,会根据当前时间和请求的最早开始时间调整停留任务的结束时间。
-
创建上客任务:
- 如果上客点与当前任务或停留任务的地点相同,不需要创建新的上客任务,而是将请求添加到现有任务。
- 如果需要创建新的上客任务,会计算开始时间并使用
taskFactory
创建一个新的DrtStopTask
。
-
添加上客请求:将接受的DRT请求添加到相应的上客任务。
-
创建从上客点出发的行驶任务:计算从上客点到下一个目的地的路径,并创建一个新的行驶任务。
-
更新行程:将新创建的上客任务和行驶任务添加到行程中。
-
更新时间:调用
scheduleTimingUpdater
更新行程中的任务时间。 -
返回结果:方法返回创建的
DrtStopTask
上客任务。关键点解释:
AcceptedDrtRequest
:代表已接受的DRT请求。InsertionWithDetourData
:包含有关任务插入和绕路时间的数据结构。VehicleEntry
:包含车辆信息和停车点列表的数据结构。Schedule
:代表车辆行程的类。Task
:代表行程中的任务,可以是行驶任务、停留任务或上/下客任务。DrtStopTask
:特定类型的任务,代表DRT中的上客或下客任务。taskFactory
:用于创建任务的工厂类。scheduleTimingUpdater
:用于更新行程时间的类。
这段代码展示了在DRT系统中如何根据当前车辆状态和请求要求,安排新的上客任务,并相应地更新车辆的行程。代码中包含了多个条件分支和异常处理,确保了在不同情况下都能正确安排任务。