灾害应急物流调度这几年被提得越来越频繁,但真正让我觉得“这事不简单”的,是我在一个应急路径规划项目里碰到的一组真实矛盾:物资就那么多、车就那么多、受灾点哪个都不能放下不管。网上搜“车辆路径规划”“NSGA2”,大多数资料讲的是最小化总成本或总距离,可灾害场景里最关键的往往不是总里程最小,而是那个“最惨的点”到底有多惨。我最后用NSGA2做了个多目标车辆路径规划方案,两个目标分别是受灾点缺货量最大值最小,以及需求点最晚送达时间最小。这篇文章把我从建模到实现、再到调试踩坑的完整过程梳理一遍,希望能给做应急物流、灾后救援路径规划相关的读者一些可复现的参考。
1. 为什么“缺货量最大值”和“最晚送达时间”要放在一起优化
1.1 应急场景下,最需要盯住的不是平均,而是“底线”
普通物流路径规划通常把总行驶距离或总运输成本作为目标,因为商业物流在意效率,绕一点点路、超出几分钟可能都能接受。但灾害初期不一样:道路可能中断,运力严重不足,物资是按箱、按吨抢出来的时间。这时候如果你只优化“总缺货量”,算法很容易给出一种比较危险的方案:因为总缺货量是求和,系统为了总量最小,大概率会优先满足那些需求量很大的点,而对于需求很小的点,就算整个放弃它,对总量的影响也不大,于是这些点就可能完全断供。
举个我构造过的例子:假设有5个受灾点,其中4个各缺20单位物资,剩下1个缺50单位。方案A把这4个点各补上一点,让它们都只缺20,另一个缺50,最大缺货量是50;方案B把50单位的那个点彻底放弃,让它缺50,另外4个点缺0,这时总缺货量反而更小。可站在灾民的角度,那个缺50的点等于完全没有任何物资进来,这显然违背了救援的公平性原则。所以灾害应急里更稳妥的目标是“最大缺货量最小”,用最优化术语叫min-max,保证最差的那个受灾点也不要差到底。
最晚送达时间同理。延迟的平均值再小,也不能掩盖某个偏远需求点的物资在48小时后才到的事实。两个目标都用“最大值”来评价,本质上是在说:灾害场景下我们不仅要整体最好,还要让“最差”也尽量可接受。
1.2 这两个目标存在真实的冲突,不能简单合并
若只让最大缺货量最小,算法会倾向于让车辆尽量覆盖那些需求量大的、可能路径更偏的点,于是车辆绕行距离大幅上升,部分受灾点等待时间变长;若只让最晚送达时间最小,车辆就会优先跑离仓库近、路况好的点,那些远且需求量大的点就可能暂时不被派车,最大缺货量迅速恶化。这两个目标之间的冲突非常明显,不是一个“谁更重要”就一定压过另一个的问题,更适合用多目标优化生成一组帕累托解,让决策者根据当前灾害阶段来决定偏向公平还是偏向时效。
这也是我最终选定NSGA2的原因之一:它一次运行可以给出一整条帕累托前沿,而不是只给一个单点解。后面我会细讲为什么不是加权和,也不是简单的分层序列法。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 应急VRP建模:目标、决策变量、约束条件都得重新定义
2.1 问题假设与符号说明
我这个项目做成单配送中心、多辆同型车、单周期的调度模型。虽然真实灾害可能涉及多级中转和多波次配送,但先把单周期问题讲透,后续扩展多波次会比较容易。
模型的基本符号这样定义:
- 配送中心编号为0,受灾点集合为D,共有N个受灾点;
- 可用的车辆有K辆,每辆最大载重为Q;
- 受灾点i的物资需求量为d_i;
- 两点之间的行驶时间为t_ij,服务点i需要的装卸时间为s_i。
决策变量本质上就是K条路线,每条路线由一串受灾点编号组成,车辆从配送中心出发,依次访问这些点,最后回到配送中心。因为一辆车不可能同时服务两个点,所以每个受灾点最多只能出现在一条路线里。
2.2 目标1:最大缺货量最小
实际灾害中有一个关键约束:物资总量或者总运力是有限的。这里我统一假设单辆车最大装载Q,一共K辆,所以单次调度最多能送出的物资总量是K×Q。如果所有受灾点的总需求大于KQ,就一定会有需求点得不到足量物资,缺口就产生了。
那么如何判定缺货量?我用了一个相对直观的规则:一个受灾点只要被某条路线覆盖,车辆就尽量带足它的需求量;一个点如果没能被任何路线覆盖,它的缺货量就是自己的需求d_i。于是目标函数变成:
Z1 = max(所有未被覆盖受灾点的需求量),若所有点都被覆盖则Z1为0。
你可能已经发现,这个定义没考虑“部分覆盖”的情况。确实,单周期全额配送模型里,缺货来源主要是覆盖决策,不是部分装载。若要支持一辆车给多个点配送但物资不够分的情况,那要把问题扩展成允许拆分配送的SDVRP,编码和求解都会复杂很多。我在实验阶段为了聚焦NSGA2实现,先做了这个简化,效果已经能体现双目标权衡了。
2.3 目标2:需求点最晚送达时间最小
对于所有被覆盖到的受灾点,设车辆到达该点的时刻为A_i。目标函数定义为:
Z2 = max(所有被覆盖受灾点的到达时间)。
之所以不考虑未被覆盖点的到达时间,是因为它们没有实际服务行为。为了不让算法利用这个空子把所有偏远点都变成“未覆盖”,Z1会给它们极其严厉的惩罚。如果希望完全杜绝,可以把未覆盖点的“虚拟到达时间”设为一个很大的惩罚值,相当于宁可作为缺货点也不能让它被无限期拖延。但实验里我发现Z1的min-max形式已经能把这种倾向压制住,所以没增加额外惩罚项。
2.4 约束条件
我整理的约束如下:
- 每条路线都从配送中心出发,最后回到配送中心;
- 每条路线上所有受灾点的需求量总和不超过车辆载重Q;
- 使用车辆数不超过K;
- 每个受灾点最多被一条路线服务一次;
- 已经覆盖的受灾点,服务完成时间要符合实际行驶过程,不能出现时间倒流。
第四个约束在编码阶段很容易满足,只要每个编号在染色体里只出现一次就行。真正起作用的是第二条约束,它直接限制了一条路线能覆盖哪些点,也导致我们需要在NSGA2的每一代里做容量合法的路径解码。
3. 为什么选NSGA2,而不把两个目标加权成一个数
3.1 加权和的缺点在应急场景里暴露得很明显
许多刚接触多目标优化的人会习惯性想:
