从滴滴模式看机器人租赁:撮合型租赁平台开发源码的调度系统设计思路

机器人租赁平台开发做到中期,客户提得最多的一个问题就是:"能不能像滴滴一样,订单进来自动派给最合适的服务商?"这个直觉是对的,但照抄网约车调度一定会翻车。我们团队在做调度系统那两个月里,反复推演过两种业务形态的差异,这篇文章把我们的抽象过程和评分函数、兜底机制的设计思路完整讲一遍。


一、为什么不能照抄滴滴:两种调度形态的本质差异

先说结论:网约车调度是"分钟级、人在路上"的实时匹配,机器人租赁调度是"天级、设备在固定网点"的预约型匹配。看起来都是"供需撮合",但底层逻辑差异很大。

维度 网约车调度 机器人租赁调度
资源位置 动态,在路上移动 静态,固定在服务网点/仓库
履约周期 分钟级(一趟行程几十分钟) 天级到月级(短则 1 天,长则 12 个月)
供需并发 实时抢单,秒级响应 预约制,订单提前数天甚至数月
资源状态 完成即释放 占用整个租期,档期互斥
调度约束 距离 + 接单意愿 档期空闲 + 场景匹配 + 交付半径
失败代价 派单失败重新排队 违约成本高(展会日期是硬约束)

其中最要命的是两条:

  1. 档期是区间占用。滴滴司机送完一单立刻可接下一单,而一台迎宾机器人被某展会租走 3 天,这 3 天内它对所有其他订单"不存在"。调度的输入不是"设备空闲与否"的布尔值,而是一段区间集合。
  2. 履约日期能提前但很难推迟。网约车晚接你 3 分钟感知不明显,但展会 10 号开幕,机器人 11 号才到,这单就是事故。所以调度系统必须把"履约确定性"排在"最优性"前面。

想清楚这两点,我们的调度系统就不是"派单引擎",而更像一个带约束的预约撮合系统

二、供需匹配模型:设备池、订单池、服务网点三要素

抽象之后,我们的调度问题只剩三个实体和它们之间的关系:

复制代码
服务网点 (ServicePoint)
  ├── 拥有 设备池 (DevicePool):若干台可租设备,各有档期与状态
  └── 覆盖 交付半径:可承接哪些区域的订单

订单池 (OrderPool):待履约订单,带场景标签、期望档期、交付地址

核心表结构我们是这样落的:

sql 复制代码
CREATE TABLE t_service_point (
  id BIGINT PRIMARY KEY AUTO_INCREMENT,
  name VARCHAR(64) NOT NULL,
  city_code VARCHAR(12) NOT NULL,
  lng DECIMAL(10,6) NOT NULL COMMENT '经度',
  lat DECIMAL(10,6) NOT NULL COMMENT '纬度',
  service_radius_km INT DEFAULT 50 COMMENT '交付半径',
  capacity_score DECIMAL(4,2) COMMENT '履约能力分',
  status VARCHAR(20) DEFAULT 'ACTIVE'
);

CREATE TABLE t_device (
  id BIGINT PRIMARY KEY AUTO_INCREMENT,
  sp_id BIGINT NOT NULL COMMENT '所属网点',
  model_id BIGINT NOT NULL COMMENT '机型',
  device_no VARCHAR(64) NOT NULL,
  status VARCHAR(20) NOT NULL COMMENT 'IDLE/RENTED/MAINTAIN/DISABLED',
  KEY idx_sp_model (sp_id, model_id, status)
);

CREATE TABLE t_dispatch_order (
  id BIGINT PRIMARY KEY AUTO_INCREMENT,
  order_id BIGINT NOT NULL,
  model_id BIGINT NOT NULL,
  start_date DATE NOT NULL,
  end_date DATE NOT NULL,
  deliver_lng DECIMAL(10,6),
  deliver_lat DECIMAL(10,6),
  scene_tag VARCHAR(32) COMMENT '场景标签:展会/迎宾/巡检',
  dispatch_status VARCHAR(20) DEFAULT 'PENDING',
  assigned_device_id BIGINT COMMENT '命中设备',
  version INT DEFAULT 0
);

调度的本质,就是给订单池里的每条 PENDING 记录,在设备池中找到一个"档期无冲突 + 场景匹配 + 交付成本可接受"的设备。 注意我们是"订单找设备",而不是"设备找订单"------因为租赁平台大多数时间设备供给是充裕的,瓶颈在订单履约确定性上。

三要素之间的关系链也值得画清楚:订单不直接找设备,而是先按"机型 + 期望档期 + 交付地址"匹配到一组候选网点,再在网点内的设备池里挑具体设备。两段式匹配的好处是,当某网点没有可用设备时,系统可以自然地把订单流转给相邻网点,而不是报一句"无可用设备"把客户挡在门外。我们在业务分析阶段发现,将近三成的"无货"其实是"本网点无货、邻网点有货",两段式匹配直接把这批订单救了回来。

网点维度还有一个容易被忽视的设计点:库存分布要带"能力属性"。同样是迎宾机器人,有的网点有配套的技术驻场人员,有的网点只能做纯设备交付;有的网点有充电仓储条件可以承接长租,有的只适合短租快转。这些属性会影响调度结果------一个需要驻场技术支持的大型展会订单,即使邻网点的距离更近,也可能应该派给稍远但有驻场能力的网点。我们把网点能力建模成结构化标签,与订单要求做匹配过滤,这在后续上线驻场服务品类时省掉了一次架构调整。

另外有一个我们踩过的坑值得记录:设备池一定要区分"平台直营设备"和"商家入驻设备"两个来源。商家的设备有自主档期编排权,调度引擎只能"建议分配",不能像直营池那样"强制占用"。这个区别决定了后面评分函数里要有商家维度的因子。

三、就近调度评分函数:三个因子的权衡

调度候选集的生成逻辑是先粗筛后精排:

sql 复制代码
-- 第一步:粗筛候选设备(机型匹配 + 档期无冲突 + 状态可用)
SELECT d.id, d.sp_id
FROM t_device d
JOIN t_schedule s ON s.device_id = d.id
    AND s.start_date < #{endDate} AND s.end_date > #{startDate}
WHERE d.model_id = #{modelId}
  AND d.status = 'IDLE'
  AND s.id IS NULL
  AND d.sp_id IN (
    SELECT id FROM t_service_point
    WHERE ST_Distance_Sphere(POINT(lng, lat), POINT(#{lng}, #{lat})) / 1000
          <= service_radius_km
  )
LIMIT 50;

粗筛出的候选设备再进入精排打分。我们的评分函数是三因子加权和:

复制代码
Score(device) = W1 × 距离分 + W2 × 档期匹配度分 + W3 × 商家服务分

三个因子分别归一化到 0~100 再加权,权重通过运营后台可配置:

因子 含义 初始权重 说明
距离分 设备网点到交付地址的球面距离映射 0.40 50km 内线性衰减,超过按 0 计
档期匹配度 期望档期与设备空闲区间的贴合程度 0.35 完全覆盖 100 分;需要转场 buffer 的按扣分
商家服务分 网点历史履约质量 0.25 逾期率、损坏率、投诉率综合计算

核心打分代码大致是:

java 复制代码
public double score(DeviceCandidate c, DispatchOrder order) {
    double distScore = distanceScore(c.getDistanceKm());   // 50km 线性衰减
    double scheduleScore = scheduleScore(c, order);        // 档期贴合度
    double serviceScore = c.getMerchantScore();            // 已离线算好

    // 档期匹配度低于 60 直接一票否决:交付确定性优先
    if (scheduleScore < 60) return -1;

    return weights.getDistance() * distScore
         + weights.getSchedule() * scheduleScore
         + weights.getService()  * serviceScore;
}

有几个设计决策值得展开:

1. 档期匹配度拥有一票否决权。 一台距离 2km 但档期要"掐点"的设备,不如一台距离 15km 但前后都有富余 buffer 的设备。我们的档期分算法是:交付日之前是否有维护窗口、归还之后是否有转场 buffer、冲突的预占单是否处于 15 分钟未支付可释放状态,逐项扣分。

2. 商家服务分离线算,不要在线算。 每天凌晨跑一次批任务,把每个网点的逾期率、设备损坏率、工单响应时效折算成 0~100 的分数落库。调度时直接读数,避免在线聚合拖垮响应时间。

3. 权重必须可配置 + 可灰度。 我们上线初期距离权重给到 0.5,结果发现部分展会客户投诉"设备状态差",运营把服务分权重提到 0.3 后投诉率才降下来。权重是业务策略,不是技术参数,必须交给运营。

评分最高的设备进入"预占确认"流程------这里直接复用我们档期系统已有的预占-确认两阶段机制,调度引擎不重复造轮子。

还要交代一下整个调度链路的耗时预算:粗筛 SQL 控制在 50ms 以内(地理半径过滤走索引 + 经纬度粗筛,不在线上做精确球面计算),精排打分是纯内存计算几乎不耗时,整体调度决策 P99 控制在 200ms 内。调度不是秒杀级的并发场景,订单量再大也是"每秒几笔"的量级,不要为了调度性能上缓存和预计算,先把索引和 SQL 写对------这是我们压测后的一致结论。真正需要异步化的是调度之后的履约编排(通知网点、生成配送任务),这些走消息队列解耦即可。

四、调度补偿与人工兜底:机器解决不了的 20%

自动化调度再完善,也必然有一批订单机器处理不了。我们统计上线后前三个月的数据,大约 12%~18% 的订单最终靠兜底流程消化。所以兜底不是异常处理,是系统的正式组成部分

兜底分两层:

第一层:自动补偿任务。 每 10 分钟扫描一次 PENDING 超过 30 分钟的调度单,按降级策略重试:

java 复制代码
// 降级链:候选设备逐步放宽
List<RelaxStrategy> fallbackChain = List.of(
    new RelaxByScene(),        // 放宽场景标签,用"同能力机型"替代
    new RelaxByRadius(),       // 交付半径 50km → 100km → 200km
    new RelaxByDate(),         // 档期允许 ±1 天浮动(需客户确认)
);

放宽策略触发时,调度单会打上标记,给客户的报价和确认流程也要跟着变(比如跨城要加运费),所以这一步必须生成"变更确认单"而不是静默改单。

第二层:人工调度工作台。 对自动补偿三轮都失败的订单,转入人工池。工作台要做的是把决策需要的信息一次性给到运营:候选设备列表及其分数明细、冲突原因、客户履约紧急度、历史沟通记录。我们的经验是人工兜底的效率取决于信息呈现,而不是操作自由度------运营最需要的是"为什么派不出来",其次是"系统建议我怎么办"。

最后是一套双向反馈机制:人工改派的结果要回流到评分模型里(人工否决了系统推荐,说明评分函数在某些场景失真),每周复盘一次否决日志,据此调整权重和粗筛规则。调度系统不是上线即结束,它是被这批"人工纠偏数据"持续喂出来的。

度量调度质量,我们看了三个指标:自动调度成功率 (无需人工介入的比例,上线后从 82% 爬到 89%)、履约准时率 (设备在承诺时间前到达现场的比例,稳定在 98% 以上)、调度平均耗时(订单创建到设备分配完成的时长,中位数 4 分钟,主要耗时在商家确认环节)。这三个指标拼起来才完整:只看成功率会误判(把该转人工的硬留在自动链路上),只看准时率会忽略成本,只看耗时又可能牺牲准确性。

五、最佳实践清单

  1. 先做业务形态的差异化分析,再决定调度模型------预约型撮合和实时派单是两套系统;
  2. 评分函数三因子起步(距离/档期/服务分),权重交给运营配置,支持灰度;
  3. 档期匹配度设一票否决线,履约确定性永远优先于最优性;
  4. 商家服务分离线批算,调度链路只读不写;
  5. 自动补偿按"放宽策略链"执行,放宽必出变更确认单;
  6. 人工兜底工作台以"解释为什么"为核心,人工否决日志要回流优化模型。

我们在设备租赁与资产管理类系统的定制开发上有多年落地经验,撮合调度、档期预占、服务网点体系这些模块都经过真实业务验证。如果你也在规划机器人租赁或类似平台,欢迎交流。

相关推荐
szarron1 小时前
国产手持式频谱分析仪选型攻略:TFN RC系列 vs HTOOL SA8T频谱分析仪 专业参数对比(军工/路测/调试全覆盖)
开发语言·前端·状态模式
代码村新手1 小时前
Linux-vim的使用和配置
linux
开开心心就好1 小时前
免费桌签打印工具,支持批量导入名字
前端·javascript·人工智能·docker·jupyter·智能手机·语音识别
涛涛ing1 小时前
2026 年 9 月,整个 npm 生态的「心脏」都被 Rust 换了
前端
IT_陈寒1 小时前
明明设了默认值,为什么我的JavaScript函数参数还是undefined?
前端·人工智能·后端
CappuccinoRose1 小时前
Web Components 基础
前端·javascript·交互·web component·shadow dom
晴天161 小时前
前端 Monorepo 入门分享:从概念到实践
前端
吴声子夜歌1 小时前
编写Shell脚本——启动项目
linux·运维·shell
闲云野鹤在人间1 小时前
云计算入门|网络与存储云服务知识梳理
linux·运维·服务器·网络·centos·云计算·php