如何在OR-Tools VRP中实现乘客最大乘车时间软约束?
解决OR-Tools VRP中统一目的地的乘客乘车时长软约束问题
针对你提出的统一目的地VRP场景(如校车接送),以及乘车时长≤MaxRideTime的软约束需求,结合OR-Tools的特性,给出以下解决方案:
问题解答
相关建模经验
该场景属于统一目的地VRP(UD-VRP),是VRP的常见变体,核心约束是控制乘客从接载到抵达终点的时长。这类需求通常通过累积时间维度+自定义约束/惩罚项来实现,在通勤接送、物流配送(统一配送中心)等场景广泛应用。能否直接为(终点值-接载值)添加软惩罚
OR-Tools Routing API本身不支持直接对两个累积变量的差值设置软约束,但可以通过底层CP-SAT约束+目标函数惩罚项间接实现:直接引用Duration维度的累积变量(接载时间和终点到达时间),构建差值约束,将违规部分转化为惩罚加入目标函数。是否需要big-M约束
不需要。因为可以直接通过RoutingModel获取每个节点的累积时间变量(CumulVar),这些变量本身是线性的,差值约束可以直接用CP-SAT的线性不等式表达,无需引入big-M辅助变量。更优方法推荐
推荐两种高效实现方式,优先选择方案一:- 方案一:基于现有Duration维度添加软约束惩罚
利用已有的Duration维度,为每个乘客节点添加软约束:车辆到达终点的时间 - 乘客接载时间 ≤ MaxRideTime,违规则按单位时长施加惩罚。这种方式无需新增维度,代码改动最小。 - 方案二:自定义追踪乘车时长的维度
创建一个自定义维度,在乘客节点处记录当前时间,在终点处计算与该时间的差值,设置软上限。这种方式更直观,但需要额外的维度配置。
- 方案一:基于现有Duration维度添加软约束惩罚
代码修改示例(方案一)
在现有代码中,添加软约束的部分(建议放在添加完Duration维度之后):
# 定义乘车时长上限和惩罚系数 MAX_RIDE_TIME = 60 * 60 # 60分钟,单位秒 RIDE_TIME_PENALTY = 100 # 每超1秒的惩罚值 # 获取Duration维度 duration_dim = routing.GetDimensionOrDie('Duration') # 遍历所有乘客节点(假设data['pickup_nodes']是乘客节点列表,需根据你的数据模型调整) for pickup_node in data['pickup_nodes']: pickup_index = manager.NodeToIndex(pickup_node) # 遍历所有车辆,为每个车辆的终点添加约束(因为每个车辆对应一个终点) for vehicle_idx in range(data['num_vehicles']): end_node = data['ends'][vehicle_idx] end_index = manager.NodeToIndex(end_node) # 获取接载时间和终点到达时间的累积变量 pickup_time_var = duration_dim.CumulVar(pickup_index) end_time_var = duration_dim.CumulVar(end_index) # 构建约束:end_time_var - pickup_time_var <= MAX_RIDE_TIME + slack_var # 其中slack_var >=0,惩罚项为RIDE_TIME_PENALTY * slack_var slack_var = routing.solver().IntVar(0, routing.solver().infinity(), f"slack_{pickup_node}_{vehicle_idx}") routing.solver().Add(end_time_var - pickup_time_var <= MAX_RIDE_TIME + slack_var) # 将惩罚项加入目标函数 routing.AddCost(RIDE_TIME_PENALTY * slack_var)
注意事项
- 需确保
data['pickup_nodes']正确包含所有乘客节点的ID,根据你的_create_data_model逻辑调整。 - 惩罚系数
RIDE_TIME_PENALTY需要根据实际业务调整:如果希望优先满足乘车时长约束,设置较大的惩罚值(比如远大于路径成本的单位权重);如果允许少量违规,设置较小值。 - 若车辆的终点是同一个节点(比如所有车都送同一个学校),可以简化为只针对该终点节点处理,无需遍历所有车辆。
额外优化建议
- 如果乘客节点数量较多,为避免约束过多影响求解速度,可以将惩罚项调整为分段惩罚:比如超过MaxRideTime的前10分钟惩罚系数低,超过部分惩罚系数高,平衡求解效率和约束优先级。
- 可以结合
routing.AddDisjunction为乘客节点设置可选性(如果允许部分乘客不被接载),但需配合相应的惩罚。
内容的提问来源于stack exchange,提问作者Harshaadhithya Kalaikumar
相关产品推荐
相关产品推荐

