[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 vehicle
          beforePickupTask = 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 diversion
          if (request.getFromLink() != vehicleEntry.start.link) { // add a new drive task
             VrpPathWithTravelData 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 task
             beforePickupTask = 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 task
          stopTask.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 stop
                DrtStopTask nextStopTask = stops.get(pickupIdx).task;
                if (stopTask.getTaskIdx() + 2 != nextStopTask.getTaskIdx()) {// there must a drive task in
                   // between
                   throw new RuntimeException();
                }
                if (stopTask.getTaskIdx() + 2 == nextStopTask.getTaskIdx()) {// there must a drive task in
                   // between
                   int driveTaskIdx = stopTask.getTaskIdx() + 1;
                   schedule.removeTask(schedule.getTasks().get(driveTaskIdx));
                }
             }

             Link toLink = request.getToLink(); // pickup->dropoff

             VrpPathWithTravelData 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 between
             if (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 between
                int driveTaskIdx = stayOrStopTask.getTaskIdx() + 1;
                //移除中间的行驶任务
                schedule.removeTask(schedule.getTasks().get(driveTaskIdx));
             }
          }
          //处理两种情况:一种是车辆已经在上客点(无需额外行驶),另一种是车辆需要行驶到上客点。
          if (stayTask != null && request.getFromLink() == stayTask.getLink()) {
             // the bus stays where it is
             beforePickupTask = 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 task
    double 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 pickup
    Link toLink = pickupIdx == dropoffIdx ? request.getToLink() // pickup->dropoff
          : stops.get(pickupIdx).task.getLink(); // pickup->i+1

    VrpPathWithTravelData 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 timings
    scheduleTimingUpdater.updateTimingsStartingFromTaskIdx(vehicleEntry.vehicle, taskIdx + 2,
          driveFromPickupTask.getEndTime());
    return pickupStopTask;
}

代码的整体逻辑
insertPickup用于在DVRP或DRT系统中安排一个上客任务。这个方法相当复杂,涉及多个步骤和条件判断,以下是它的逻辑概述:

上客逻辑
  1. 提取参数:从方法参数中提取插入数据insertionWithDetourData和请求request

  2. 获取车辆信息:从插入数据中获取VehicleEntry对象,进而获取车辆的行程schedule和停车点列表stops

  3. 确定上客索引和绕路数据:从插入数据中获取上客的索引pickupIdx和绕路数据detourData

  4. 检查行程状态:根据行程的当前状态scheduleStatus,确定是否需要创建新的上客任务。

  5. 处理已经开始的行程

    • 如果行程已经开始并且当前任务是上客或下客任务,并且车辆可以改道,那么会创建一个改道任务。
    • 如果行程已经开始并且当前任务是停留任务,会根据当前时间和请求的最早开始时间调整停留任务的结束时间。
  6. 创建上客任务

    • 如果上客点与当前任务或停留任务的地点相同,不需要创建新的上客任务,而是将请求添加到现有任务。
    • 如果需要创建新的上客任务,会计算开始时间并使用taskFactory创建一个新的DrtStopTask
  7. 添加上客请求:将接受的DRT请求添加到相应的上客任务。

  8. 创建从上客点出发的行驶任务:计算从上客点到下一个目的地的路径,并创建一个新的行驶任务。

  9. 更新行程:将新创建的上客任务和行驶任务添加到行程中。

  10. 更新时间:调用scheduleTimingUpdater更新行程中的任务时间。

  11. 返回结果:方法返回创建的DrtStopTask上客任务。

    关键点解释:

    • AcceptedDrtRequest:代表已接受的DRT请求。
    • InsertionWithDetourData:包含有关任务插入和绕路时间的数据结构。
    • VehicleEntry:包含车辆信息和停车点列表的数据结构。
    • Schedule:代表车辆行程的类。
    • Task:代表行程中的任务,可以是行驶任务、停留任务或上/下客任务。
    • DrtStopTask:特定类型的任务,代表DRT中的上客或下客任务。
    • taskFactory:用于创建任务的工厂类。
    • scheduleTimingUpdater:用于更新行程时间的类。

    这段代码展示了在DRT系统中如何根据当前车辆状态和请求要求,安排新的上客任务,并相应地更新车辆的行程。代码中包含了多个条件分支和异常处理,确保了在不同情况下都能正确安排任务。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值