ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

如何用PyVRP建模装载与容量约束:多维载重与提货送货场景完整教程

如何用PyVRP建模装载与容量约束:多维载重与提货送货场景完整教程

【免费下载链接】PyVRP

Open-source, state-of-the-art vehicle routing problem solver in an easy-to-use Python package.

项目地址:https://gitcode.com/gh_mirrors/py/PyVRP
点击查看免费下载

PyVRP是一款开源、工业级的车辆路径问题(VRP)求解器,也提供开箱即用的 Python 包pyvrp。本文是一篇面向新手的完整教程,教你用 PyVRP 完成装载与容量约束的建模,并覆盖多维载重与提货送货(pickup & delivery)场景,帮助你快速落地配送、取送、逆向物流等真实业务。

快速上手:安装并导入 PyVRP

先在终端安装pyvrp:

pip install pyvrp

然后导入建模与绘图接口:

import pyvrp import pyvrp.plotting import pyvrp.stop

PyVRP 推荐使用高层的Model接口(源码位于 pyvrp/Model.py),你可以用业务语言直接描述location / client / depot / vehicle_type / edge五类对象。

⚠️重要提示:PyVRP 会把所有数值输入自动转成整数,(x, y) 坐标除外。如果你的数据带小数(如重量 1.5 kg),要先自行缩放成整数,再传入,否则会因截断出现意外行为。

理解"装载(load)":PyVRP 容量约束的核心概念

在 PyVRP 中,我们把客户提货与送货量统称为load(装载),并围绕它建立如下不变式:

车辆任意时刻的在车装载量 ≤ 该车辆类型的 capacity(容量)

  • 送货(delivery):货物从仓库出发,送达客户,车辆装载量逐步减少;
  • 提货(pickup):货物从客户处装车,运回仓库(逆向物流),车辆装载量逐步增加。

PyVRP 内部通过 LoadSegment 串联路线上的累计装载,因此"任意时刻不超容"是被严格保证的,而不是只在起点/终点检查。

核心参数速查表:

建模目标方法常用参数出处
客户送货/提货量(单维或多维)m.add_client(...)delivery,pickupModel.py#L237-L298
车辆类型与容量(单维或多维)m.add_vehicle_type(...)capacity,num_available,fixed_costModel.py#L428-L523
提货-送货配对(shipment)m.add_shipment(...)pickup_location,delivery_location,amountModel.py#L300-L345
起讫点之间的边m.add_edge(...)distance,durationModel.py#L386-L418

一、单维容量:最经典的 Capacitated VRP

先从一个最小可运行的例子开始:2 辆车、单车容量 10、2 个客户分别要送 4 与 5。

m = pyvrp.Model() m.add_vehicle_type(num_available=2, capacity=10) m.add_depot(location=m.add_location(0, 0)) m.add_client(location=m.add_location(0, 1), delivery=4) m.add_client(location=m.add_location(2.5, 0), delivery=5) for frm in m.locations: for to in m.locations: if frm != to: m.add_edge(frm, to, distance=1) # 简化:单位距离 res = m.solve(stop=pyvrp.stop.MaxRuntime(1)) # 求解 1 秒

由于总需求 4+5=9 小于单车容量 10,PyVRP 会建议只用一辆车跑完两条边——这就是最典型的 "CVRP 装载约束"。

二、混合提货与送货:同一客户同时 demand 和 return

现实业务里,客户既收货又退货(例如以旧换新、上门回收)非常常见。PyVRP 中Client同时暴露delivery和pickup两个字段,无需切换模型。

DELIVERIES = [4, 5] PICKUPS = [6, 0] m = pyvrp.Model() m.add_vehicle_type(num_available=2, capacity=10) m.add_depot(location=m.add_location(0, 0)) for idx, (d, p) in enumerate(zip(DELIVERIES, PICKUPS), start=1): m.add_client( location=m.add_location(idx, idx), delivery=d, pickup=p, )

💡直观理解:上图的灰色阴影条就是在车装载量随行驶距离的变化——它会先随送货减少、再随提货增加,但始终不超过 capacity。

三、多维载重:同时表达重量、体积与"技能"约束

PyVRP 支持任意维度的载重(load dimensions)。除了常见的重量 + 体积,你还可以用一个"容量为 0"的维度来表达兼容性/技能约束:

  • 若车辆在某维度容量为0,则它无法服务任何对该维度有需求的客户;
  • 若客户在某维度需求为>0,则必须由具备该维度容量的车辆服务。

示例:两种车辆——一种普通、一种"带特殊技能"(更贵);客户 A 需要特殊技能,客户 B 不需要。

m = pyvrp.Model() m.add_depot(location=m.add_location(0, 0)) # 车辆1: 容量10, 无特殊技能(=0) m.add_vehicle_type(1, capacity=[10, 0]) # 车辆2: 容量5, 有特殊技能(=1), 固定成本更高 m.add_vehicle_type(1, capacity=[5, 1], fixed_cost=10) m.add_client(location=m.add_location(0, 1), delivery=[5, 1]) # 需要技能 m.add_client(location=m.add_location(2.5, 0), delivery=[5, 0]) # 不需要技能

求解后你会发现:客户 1 被分给了更贵的那辆车——这是多维载重表达"技能/兼容性"约束的直接效果。🎯

四、提货送货(Shipment)配对:不经过仓库的取送

Client表达"客户与仓库之间"的取送,而Shipment(shipment / 货单)表达的是客户 A → 客户 B 的一对一取送,货物不经过仓库。这是快递、外卖、出租车调度、点对点搬家等场景的标配。

COORDS = [ (456, 320), (228, 0), (912, 0), (0, 80), (114, 80), (570, 160), (798, 160), (342, 240), (684, 240), (570, 400), (912, 400), ] # 5 组 (提货点, 送货点) 配对 PICKUP_DELIVERIES = [(1, 6), (2, 10), (4, 3), (5, 9), (7, 8)] m = pyvrp.Model() for x, y in COORDS: m.add_location(x, y) m.add_depot(location=m.locations[0]) m.add_vehicle_type(num_available=4, max_distance=3000) for pu, dv in PICKUP_DELIVERIES: m.add_shipment( pickup_location=m.locations[pu], delivery_location=m.locations[dv], # amount=... # 可选:给该 shipment 增加装载约束 ) for frm in m.locations: for to in m.locations: m.add_edge(frm, to, distance=abs(frm.x - to.x) + abs(frm.y - to.y)) res = m.solve(stop=pyvrp.stop.MaxRuntime(1))

读取解的窍门:打印res.best时会看到形如L0/U0的符号:

  • L<id>=Load(在某地提货)
  • U<id>=Unload(在某地送货)

PyVRP保证任何 shipment 的L一定出现在U之前,无需你手工校验。

📝amount参数:与delivery/pickup相同,amount可以是单个整数(单维)或整数列表(多维)。若希望 shipment 参与装载约束,给它赋值即可。

五、Reload(重载):用"回仓补货"缓解容量约束

当单车容量小于总需求时,除了增加车辆,你还可以允许车辆在路线中途回到仓库重载——PyVRP 把这个能力叫reload。

m = pyvrp.Model() depot = m.add_depot(location=m.add_location(456, 320)) m.add_vehicle_type( 1, capacity=15, reload_depots=[depot], # 允许在哪些仓库重载 max_reloads=2, # 每次路线最多重载 2 次 ) # ... add clients & edges ... res = m.solve(stop=pyvrp.stop.MaxRuntime(1))

🛻典型场景:城市末端配送(回仓换货)、垃圾分类清运(回处理场倒空)、快递网点多次补货。max_reloads可以精确控制"允许回仓几次"。

六、把结果可视化:用 plotting 模块验证你的装载约束

PyVRP 提供一组开箱即用的绘图函数(模块位于 pyvrp/plotting/),非常适合验证装载约束是否被正确建模:

import pyvrp.plotting # 1) 绘制整体路径解 pyvrp.plotting.plot_solution(res.best, m.data()) # 2) 绘制单条路线的"距离-时间-装载"综合图 route = res.best.routes()[0] pyvrp.plotting.plot_route_schedule(m.data(), route, load_dimension=0) # 3) 绘制客户需求量分布(按需求升序的柱状图) pyvrp.plotting.plot_demands(m.data(), dimension=0)

七、常见踩坑与最佳实践清单

  • ✅数据先转整数:所有delivery/pickup/capacity/amount/distance/duration都会被截断成int,带小数的场景先缩放(如把 kg 换成 100g)。
  • ✅容量维度数必须一致:一旦启用多维载重,Client.delivery/pickup与VehicleType.capacity的维度数要相同,否则求解会报错。
  • ✅用amount让 shipment 参与装载:默认 shipment 只有顺序约束、没有装载约束;加上amount=后才会占用车辆容量。
  • ✅用fixed_cost建模车型成本差异:当多维容量用于"技能"时,用fixed_cost表示"启用高技能车的额外成本"。
  • ✅求解参数先设短:pyvrp.stop.MaxRuntime(1)用来快速验证模型是否正确,正式跑批再换MaxRuntime(600)或NoImprovement(...)。
  • ⛔不要跳过 depot:每个Model至少要有一个add_depot(...),否则solve()会失败。

小结

本文带你走完了 PyVRP 建模装载与容量约束的完整闭环:

  1. 单维容量:Client.delivery/pickup+VehicleType.capacity组成最经典的 CVRP;
  2. 混合取送:同一客户可同时有 delivery 和 pickup;
  3. 多维载重:[重量, 体积, 技能, ...]任意扩展;
  4. 提货送货配对:add_shipment表达 A→B 直连取送,保证 L 先于 U;
  5. Reload 重载:允许车辆回仓补货,突破单车容量瓶颈;
  6. plotting 验证:用plot_solution与plot_route_schedule直观检查装载曲线。

掌握以上 6 步,你就能用 PyVRP 稳定解决绝大多数"装得下 / 送得完 / 取得了"的配送业务问题。🚀

【免费下载链接】PyVRP

Open-source, state-of-the-art vehicle routing problem solver in an easy-to-use Python package.

项目地址:https://gitcode.com/gh_mirrors/py/PyVRP
点击查看免费下载
上一篇:MkDocs 文档站点报错怎么办:7 类常见错误的快速定位方法
下一篇:从零实现 JavaScript 神经网络矩阵库(四):矩阵转置 transpose() 的源码级讲解

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

返回列表