You need to enable JavaScript to run this app. OR-Tools v8.2 Python实现多行程CVRP:约束车辆返回depot前须到独立卸货点卸货
优惠活动
大模型
产品
解决方案
定价
更多

OR-Tools v8.2 Python实现多行程CVRP:约束车辆返回depot前须到独立卸货点卸货

适配OR-Tools v8.2 Python的多行程带容量VRP实现方案

核心目标是修正官方cvrp_reload示例的路线缺陷,强制车辆返回depot前必须完成卸货,不得携带载重回depot。

核心约束逻辑拆解

对比官方示例的不足,需要额外落地两个强制规则:

  • 车辆抵达depot终点时,实时载重必须为0
  • 单条完整路线内,至少到访1次独立卸货点
  • 保留原有业务逻辑:车辆收集需求点货物达到容量上限时,自主前往卸货点清空载重后继续收集

分步实现流程

1. 基础维度配置

容量维度配置

  • 按常规CVRP逻辑配置车辆最大载重上限,每个需求点对应固定的收集量
  • 参考官方cvrp_reload的逻辑,配置卸货点载重清零规则:只要路线经过独立卸货点,就将当前累计载重直接重置为0,清空后继续收集剩余需求
  • 记得给独立卸货点添加可选访问配置,调用routing.AddDisjunction([unloading_node_id], penalty=0),允许求解器在路线中多次插入卸货点行程,不要把卸货点设置成单次必须访问的节点

卸货计数维度配置

也就是你之前推测可用的count_dimension用法:

  • 新建一个累计计数维度,规则为:路线每经过一次卸货点,累计计数+1,经过其他节点时计数不变
  • 维度设置时把最大累计值设为足够大的数(比如100,远大于单条路线可能的最大卸货次数),避免求解约束冲突

2. 核心强制约束添加

遍历每一辆车的路线终点(即depot节点),给求解器加两个硬约束:

  • 从容量维度取车辆抵达depot时的载重变量值,强制要求该值等于0,不允许带载重入depot
  • 从计数维度取车辆抵达depot时的累计计数值,强制要求该值≥1,不允许全程一次卸货点都没去就直接回depot

3. 细节注意事项

  • 不要把depot节点纳入载重清零逻辑的触发范围,否则车辆会直接从最后一个需求点回depot时就清空载重,完全绕过卸货点
  • 卸货点要配置正确的时间窗:如果有运营时段限制,要和需求节点一样给卸货点配时间窗,避免求解器安排在非运营时段安排卸货行程
  • 车辆的最大行程时间约束要覆盖多次往返卸货点的路程,不要把总时间设得过短,否则求解器会判定问题不可行

关键代码片段参考

# 初始化需求回调,读取每个节点的固定收集量
def demand_callback(from_index):
    from_node = routing.IndexToNode(from_index)
    return demands[from_node]

# 创建容量维度,绑定车辆最大载重能力
CAPACITY_DIM = "Capacity"
routing.AddDimensionWithVehicleCapacity(
    demand_callback,
    slack_max=0,
    vehicle_capacity=vehicle_capacities,
    fix_start_cumul_to_zero=True,
    dimension_name=CAPACITY_DIM
)
cap_dim = routing.GetDimensionOrDie(CAPACITY_DIM)

# 配置卸货点载重清零逻辑
def cap_transit_callback(from_index, to_index):
    to_node = routing.IndexToNode(to_index)
    # 如果下一个节点是独立卸货点,清空当前累计载重
    if to_node == unloading_node_id:
        return -cap_dim.CumulVar(from_index)
    return 0
cap_dim.SetTransitionCallback(cap_transit_callback)

# 创建卸货计数维度
COUNT_DIM = "UnloadCounter"
def count_callback(from_index):
    from_node = routing.IndexToNode(from_index)
    return 1 if from_node == unloading_node_id else 0
routing.AddDimension(
    evaluator=count_callback,
    slack_max=0,
    cumul_max=100,
    fix_start_cumul_to_zero=True,
    dimension_name=COUNT_DIM
)
count_dim = routing.GetDimensionOrDie(COUNT_DIM)

# 添加核心硬约束
for vehicle_id in range(manager.GetNumberOfVehicles()):
    end_route_index = routing.End(vehicle_id)
    # 回depot时载重必须为0
    solver.Add(cap_dim.CumulVar(end_route_index) == 0)
    # 回depot前至少经过1次卸货点
    solver.Add(count_dim.CumulVar(end_route_index) >= 1)

效果说明

加上上述约束后,求解器会自动避开官方示例中「从最后一个需求点直接回depot」的低成本路线选择,强制在回depot前插入最后一次卸货行程,最终生成的路线会完全匹配合规示例:0 > 2 > 3 > 4 > 1 (unload) > 5 > 6 > 7 > 1 (unload) > 0。


内容的提问来源于stack exchange,提问作者mert

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.30 04:06:03