案例 064 雨天续航打八折
案例 064 雨天续航打八折
摘要:暴雨把满电续航从 \(48.0\) km 压到 \(38.4\) km,同一场雨又把单量从 \(30\) 单抬到 \(39\) 单,两头一起挤。本文把"续航随决策变化"的背包写成一条右端含整变量的约束 \(\sum_i d_i x_i \le 38.4 + 35.0 n\),在 \(39\) 个 0-1 变量加 1 个换电整变量的模型上,比较按晴天上限排程(雨天硬撑)、按雨天续航保守排程、动态降档(换电补程)三种做法。结论很硬:晴天排程把 \(45.7\) km 塞进 \(48.0\) km 的电池,只剩 \(2.3\) km 余量,续航折扣只要低于 \(0.951\) 就会崩;暴雨下它要推车 \(7.3\) km、耗时 \(109.0\) min,全天 \(483.8\) min 远超 \(430.0\) min 上限。保守排程只能送 \(28\) 单;动态降档换电 \(1\) 次、多花 \(6.0\) min,送到 \(35\) 单,比保守多 \(7\) 单,代价是 \(3.0\) 元。
关键词:容量随决策变化;续航折扣;换电补程;两维背包;推车风险
1 问题提出
1.1 问题是什么
雨天的两个变化同向恶化:电池可用里程打八折,\(E = 48.0 \times 0.80 = 38.4\) km;订单却多三成,从 \(30\) 单涨到 \(39\) 单。表 1 是三种应对方式的实测结果,先摆数字。
表 1 三方案在暴雨日的结果对照
| 方案 | 订单池 /单 | 可完成 /单 | 总里程 /km | 换电 /次 | 全天耗时 /min | 推车 /km | 净收益 /元 |
|---|---|---|---|---|---|---|---|
| A 晴天排程硬撑 | 30 | 30 | 45.7 | 0 | 483.8 | 7.3 | 212.3 |
| B 雨天保守 | 39 | 28 | 38.4 | 0 | 332.4 | 0.0 | 197.8 |
| C 动态降档 | 39 | 35 | 50.3 | 1 | 429.8 | 0.0 | 246.4 |
晴天那套排程单看没问题:\(30\) 单、\(45.7\) km、装进 \(48.0\) km 的电池刚好还剩 \(2.3\) km。可雨天一到,这 \(45.7\) km 就超了 \(7.3\) km——不是超一点,是超出 \(18.9\)%。
1.2 为什么要优化
第一,余量是隐藏的。晴天排程看起来"排满",其实余量只有 \(2.3\) km,占总续航的 \(4.9\)%。任何超过 \(4.9\)% 的续航衰减都会让这套排程失效,而雨天衰减是 \(20.0\)%——差了一个数量级,垮得毫无悬念。
第二,容量不是参数,是决策。换电一次补 \(35.0\) km,花 \(6.0\) min 和 \(3.0\) 元。容量写成 \(E_0\lambda + \Delta E\, n\) 之后,\(n\) 进了右端,约束不再是背包,而是"背包 + 扩容"的混合问题。
第三,时间也是硬约束。多送一单不只是多耗电,还多占 \(6.2\) min 左右。雨天在线 \(430.0\) min,能量够了时间也未必够——两个维度一起卡,才是真实现场。
1.3 Gurobi 如何求解
模型有 \(39\) 个 0-1 变量 \(x_i\) 和 1 个整数变量 \(n \in \{0,1,2\}\),约束 \(2 + 1\) 条,规模极小。关键是把"容量随决策变"这条非线性约束线性化:原式 \(\sum_i d_i x_i \le 38.4(1 + n)\) 右端是变量乘积,改写为 \(\sum_i d_i x_i \le 38.4 + 35.0\,n\) 之后完全线性,Gurobi 直接分支定界,单次求解 \(0.039\) 秒。参数扫描共 \(62\) 次求解,全部在 3 秒内跑完。
2 数学模型
2.1 符号说明
\(I\) 为当日订单集合,\(|I| = 39\);\(d_i\) 为单均往返里程(km),\(w_i\) 为配送费(元),\(t_i\) 为占用时长(min);\(E_0 = 48.0\) 为晴天满电续航,\(\lambda\) 为续航折扣,\(\Delta E = 35.0\) 为单次换电补充里程,\(\tau = 6.0\) 为换电耗时,\(W = 430.0\) 为在线上限,\(c = 3.0\) 为换电机会成本;\(x_i \in \{0,1\}\),\(n \in \{0,1,2\}\)。
2.2 目标函数
收益减换电成本。换电省下的不是钱,是单量——\(c\) 取 \(3.0\) 元是把它折成"三次换电抵一单"的量级,避免模型为了 1 块钱无限换电。
2.3 约束条件
续航约束。这是本题的核心:右端含决策变量 \(n\),容量随决策变。原始写法 \(E_0\lambda(1+n)\) 是双线性,改成加法形式后线性。\(\Delta E\) 略小于 \(E_0\lambda\)(\(35.0\) 对 \(38.4\)),因为换上的电池不是满电、且要留出回柜的余量。
时间约束。\(t_i = d_i / v + 6.2\),\(v = 16.0\) km/h 是雨天车速。换电同时吃电和时间,这是它唯一的成本。
换电上限。商圈周边换电柜就那几个,一天最多跑 \(2\) 次。
2.4 规模与性质
\(39\) 个 0-1 变量、1 个整数变量、3 条约束,是一维背包加一维"可扩容容量"的变形。它是弱 NP-难:\(n\) 只有 3 个取值,固定 \(n\) 后退化为标准 0-1 背包,可用价值密度 \(\theta_i = w_i / d_i\) 贪心近似;Gurobi 的分支定界在根节点就几乎收敛。
3 Gurobi 求解实现
3.1 建模要点
x = m.addVars(NN, vtype=GRB.BINARY)
n = m.addVar(vtype=GRB.INTEGER, lb=0, ub=nmax)
m.addConstr(gp.quicksum(d[i] * x[i] for i in range(NN)) <= E0 * lam + de * n)
m.addConstr(gp.quicksum(tt[i] * x[i] for i in range(NN)) + TAU * n <= wcap)
一个实现细节:右端含变量时不要写成 \(E_0\lambda(1+n)\)。Gurobi 会把乘积判为非线性,转 QCP 甚至报错;而 \(E_0\lambda + \Delta E n\) 是纯线性的,且语义更清楚——"每换一次电,可用里程加 \(\Delta E\)"。
3.2 求解结果
三方案各解一次:A 用 \(\lambda=1\)、\(n\) 锁 0、时间不限,得到"晴天视角下最优"的那套排程;B 用 \(\lambda=0.80\)、\(n\) 锁 0;C 放开 \(n \le 2\)。
表 2 续航折扣主扫描(单块电池,方案 B 口径)
| 折扣 \(\lambda\) | 雨天续航 /km | 可完成 /单 | 缺口 /单 | A 推车 /km | A 超时 /min |
|---|---|---|---|---|---|
| 0.70 | 33.6 | 25 | 14 | 12.1 | 125.8 |
| 0.75 | 36.0 | 27 | 12 | 9.7 | 89.8 |
| 0.80 | 38.4 | 28 | 11 | 7.3 | 53.8 |
| 0.85 | 40.8 | 30 | 9 | 4.9 | 17.8 |
| 0.90 | 43.2 | 31 | 8 | 2.5 | 0.0 |
| 0.95 | 45.6 | 32 | 7 | 0.1 | 0.0 |
| 1.00 | 48.0 | 34 | 5 | 0.0 | 0.0 |
4 结果分析
4.1 结构:余量只有 \(4.9\)%

图 1 续航—里程曲线与晴天排程的击穿点
三条斜线是三种雨强下的"剩余续航 = 起始续航 − 已跑里程"。晴天排程落在 \(45.7\) km,暴雨线在这一点已经是 \(-7.3\),落在横轴以下。直线斜率都是 \(-1\),所以缺口只取决于起点的下移量:起点掉 \(9.6\) km,缺口就是 \(7.3\) km。

图 2 订单池全景:里程—配送费散点(气泡 = 占用时长)
\(39\) 单铺在里程 \(0.94\)—\(1.79\) km、配送费 \(6.37\)—\(7.74\) 元之间。雨天新增的 \(9\) 单(方块)集中在短里程、高单价一侧——这是雨天加价的结果,也是"该保哪些单"的答案:单位里程收益 \(\theta_i\) 高的单必须优先保住。
4.2 结果:三方案的取舍

图 3 三方案的可完成单量与缺口
A 的两根柱子一样高——\(30\) 单看着"全送到了",可代价藏在图外:推车 \(7.3\) km、超时 \(53.8\) min。B 老老实实按 \(38.4\) km 排,只排得出 \(28\) 单,缺口 \(11\) 单。C 把池子里的 \(39\) 单全排上,最后送到 \(35\) 单,靠的是换电 \(1\) 次撑出的 \(73.4\) km 容量。

图 4 按价值密度排序的累计配送费阶梯
三条阶梯的斜率就是价值密度:C 一路爬到 \(249.4\) 元才停,B 在 \(38.4\) km 竖线处被截断在 \(197.8\) 元。B 与 C 的差额 \(51.6\) 元,就是"不肯换电"的代价;而换电只花 \(3.0\) 元。

图 5 续航折扣主灵敏度:可完成单量与推车里程
折扣从 \(1.00\) 掉到 \(0.70\),保守排程的可完成单量从 \(34\) 单滑到 \(25\) 单,几乎线性。点线标出临界折扣 \(0.951\)——只要折扣低于它,晴天排程必然推车;粉区是"无论怎么排都跑不完"的折扣段。

图 6 续航折扣与订单池规模的二维灵敏度
热图把两个同向恶化的变量摆在一起:右下角(折扣高、单量少)最宽松,左上角(折扣低、单量多)最紧。从 \(22\) 单到 \(32\) 单,格子颜色跨了 \(10\) 单——折扣与单量各恶化两成叠起来,可完成的单量掉了近三成。

图 7 在线时长上限对可完成单量的边际效应
在线时长从 \(350.0\) 放到 \(510.0\) min,可完成单量从 \(29\) 升到 \(39\),最后触到 \(39\) 单的池顶——雨天真正稀缺的不是电,是时间。换电次数整段都停在 \(1\) 次——容量早就不是瓶颈,卡住的是时间:\(73.4\) km 的容量全程富余,多给的每一分钟都直接变成单量。

图 8 时间—里程—单量的帕累托前沿与不可行区
横轴是全天地面里程,纵轴是单量。粉色区(\(> 38.4\) km)是单块电池物理上到不了的区域。前沿从左上往右下走:要更多单,就得跑更多路,也就必须换电。B 卡在 \(38.4\) km 的竖直墙上,C 越过墙靠的是 \(\Delta E\)。

图 9 推车里程的蒙特卡洛分布与累计概率
把折扣看成随机变量(均值 \(0.84\)、标准差 \(0.08\)),A 的推车里程分布右偏,均值 \(5.4\) km,推车概率 \(91.4\)%——几乎必然。这意味着 A 不是"风险方案",是"必败方案"。

图 10 三方案的全天耗时构成
堆叠条把 \(430.0\) min 的账算清楚:A 的骑行 \(171.3\) min 加推车 \(109.0\) min,总量 \(483.8\) min 直接冲破上限 \(53.8\) min;C 多花 \(6.0\) min 换电,总量 \(429.8\) min 仍在 \(430.0\) min 之内。换电买的是里程,花的是时间,而时间这里还有富余——所以这笔交易划算。
5 结论
-
续航折扣临界值是 $0.951。晴天满负荷排程只剩 \(2.3\) km 余量(\(4.9\)%),任何超过 \(4.9\)% 的衰减都会击穿它;暴雨的 $20.0% 衰减是它的 \(4.1\) 倍。
-
硬撑不是"少送几单",是"推车 \(7.3\) km、超时 \(53.8\) min"。推车速度只有 \(4.0\) km/h,\(7.3\) km 要 \(109.0\) min,比送完剩下的单还费时间。
-
保守排程的代价是 \(11\) 单。单块电池在 \(38.4\) km 内只能覆盖 \(28\) 单,占池子的 \(71.8\)%。
-
动态降档用 \(3.0\) 元和 \(6.0\) min 换回 \(7\) 单,折合每单 \(0.4\) 元成本,远低于单均 \(7.09\) 元的配送费。
-
真正该盯的不是"今天下不下雨",是"余量够不够"。晴天排程把电池用掉 \(95.1\)%,这种"刚好够"的排法在任何扰动下都会崩;把电池用量压到 \(76.1\)% 以内、或者干脆把换电写进计划,才是稳态做法。
参考文献
[1] Gurobi Optimization LLC. Gurobi Optimizer Reference Manual[Z]. 2024.
[2] Kellerer H, Pferschy U, Pisinger D. Knapsack Problems[M]. Berlin: Springer, 2004.
[3] Martello S, Toth P. Knapsack Problems: Algorithms and Computer Implementations[M]. Chichester: John Wiley & Sons, 1990.
[4] 司守奎, 孙玺菁. 数学建模算法与应用[M]. 3 版. 北京: 国防工业出版社, 2021.

浙公网安备 33010602011771号