[Vroom] 位置与矩阵 | 路由集成 | 抽象,解耦与通信
第三章:位置与矩阵
欢迎回到 VROOM !
在第一章:输入与问题定义中我们学习了如何描述整体问题框架,在第二章:任务与运输工具中我们聚焦于核心实体——任务(Jobs)与运输工具(Vehicles)。
现在我们将解析路径规划中不可或缺的关键要素:如何在任务执行位置与运输工具起止点之间建立移动关系,这正是位置(Locations)与矩阵(Matrices)的协同作用。
设想我们有一辆从仓库出发的配送卡车需要访问两个客户点。要规划最优路线,必须明确以下移动参数:
- 仓库至第一个客户点的耗时/距离
- 客户点之间的移动参数
- 返回仓库的移动参数
缺少这些位置间移动数据的支撑,任何路径优化算法都将失去计算基础。
位置(Locations)定义
在 VROOM 中,位置代表地图上对路径规划有重要意义的地理节点,包括:
- 运输工具的起始/终止位置
- 任务执行位置
通过 JSON 输入的坐标定义示例:
{"vehicles": [{"id": 1,"start": [2.3522, 48.8566], // 车辆1起始位置(巴黎仓库)"end": [2.3522, 48.8566], // 车辆1终止位置"capacity": [100],"profile": "car"}],"jobs": [{"id": 10,"location": [2.2945, 48.8584], // 任务10位置(埃菲尔铁塔)"service": 300,"delivery": [10]},{"id": 20,"location": [2.3490, 48.8641], // 任务20位置(卢浮宫)"service": 180,"delivery": [20]}]
}
该配置包含三个物理位置:
- 仓库坐标(2.3522, 48.8566)
- 任务10坐标(2.2945, 48.8584)
- 任务20坐标(2.3490, 48.8641)
VROOM 通过内部索引机制管理位置唯一性,相同坐标自动归并为同一位置。若使用预计算矩阵,可直接指定位置索引:
{"vehicles": [{ "id": 1, "start": 0, "end": 0, "profile": "car" } // 索引0表示仓库],"jobs": [{ "id": 10, "location": 1, "service": 300 }, // 索引1对应任务10{ "id": 20, "location": 2, "service": 180 } // 索引2对应任务20],"matrices": {// 矩阵数据需与索引0/1/2对应}
}
内部 vroom::Location
结构体实现:
// 简化版位置结构体(src/structures/vroom/location.h)
class Location {
private:Index _index; // 内部自动分配的索引OptionalCoordinates _coords; // 可选地理坐标bool _user_index; // 是否为用户定义索引public:explicit Location(Index index); // 用户索引构造器Location(const Coordinates& coords); // 坐标构造器// 获取索引/坐标等方法...
};
该结构支持坐标
与预定义
索引两种定位方式的无缝切换。
矩阵(Matrices)
矩阵是存储位置间移动成本的方阵数据结构,主要类型包括:
- 耗时矩阵(Duration):存储位置间移动秒数
- 距离矩阵(Distance):存储位置间移动米数
- 成本矩阵(Cost):自定义成本指标
对于 N 个唯一位置,矩阵为 N×N 结构。矩阵元素 [i][j] 表示从索引 i 位置到索引 j 位置的移动成本。
矩阵数据在 JSON 中的配置示例:
{"matrices": {"car": {"durations": [[0, 600, 900], // 仓库(0)到各点耗时[580, 0, 300], // 任务10(1)到各点耗时[880, 320, 0] // 任务20(2)到各点耗时],"distances": [[0, 5000, 7000], // 仓库(0)到各点距离[4800, 0, 2500], // 任务10(1)到各点距离[6800, 2700, 0] // 任务20(2)到各点距离]}}
}
矩阵特性说明:
- 非对称性:A→B 与 B→A 的耗时/距离可能不同(受单行道等因素影响)
- 索引强关联:用户定义索引必须与矩阵行列顺序严格对应
内部矩阵存储采用一维向量
优化访问效率:
template <class T> class Matrix {std::size_t n; // 矩阵维度std::vector<T> data; // 矩阵元素线性存储public:T* operator[](std::size_t i) { return data.data() + (i * n); // 模拟二维访问}// 其他方法...
};
数据处理全流程
VROOM 通过 vroom::io::parse
解析输入数据时执行以下关键步骤:
成本封装器(CostWrapper)是核心访问接口:
// 简化版成本访问逻辑
const auto& cost_wrapper = input.get_cost_wrapper(vehicle.profile);
Duration 耗时 = cost_wrapper.duration(起点索引, 终点索引);
Distance 距离 = cost_wrapper.distance(起点索引, 终点索引);
应用场景推演
以仓库(0)→任务10(1)→任务20(2)→仓库(0)的路线为例:
移动段 耗时(s) 距离(m)
0→1 600 5000
1→2 300 2500
2→0 880 6800
----------------------------
总计 1780 14300
VROOM 在优化过程中通过矩阵查询累计各路径成本,结合服务时间、时间窗等约束寻找全局最优解。
总结
位置与矩阵构成了移动成本计算的基础层。
通过精确配置位置关系与移动成本矩阵,或依赖路由引擎动态生成
(详见第四章:路由集成),VROOM 得以构建完整的成本评估体系。
理解此章内容将为后续学习路由集成机制奠定重要基础。
第四章:路由集成
在前几章中,我们学习了如何定义路径规划问题的核心要素:
需要执行的任务(第二章:任务与运输工具)以及任务执行位置
与运输工具
的起止点(第三章:位置与矩阵)。
我们了解到,获取位置间的移动耗时/距离数据对路径优化至关重要,这些信息存储在矩阵中。
但当用户仅
拥有坐标数据而缺乏
预计算矩阵时,VROOM 如何获取真实道路网络的移动参数?
这正是路由集成机制的核心价值。
连接现实世界的桥梁
路由集成是 VROOM 与专业地图服务交互的智能通道。不同于简单的直线距离计算,VROOM 通过集成以下主流路由引擎获取真实道路数据:
- OSRM(开源路由引擎)
- Openrouteservice (ORS)
- Valhalla
当用户在 JSON 输入中指定车辆配置(如"car"
)并提供地理坐标时,VROOM 将自动通过路由集成模块完成以下流程:
- 数据收集:
汇总
特定配置(如汽车)关联的所有坐标点 - 请求构建:
生成
路由引擎可识别的矩阵查询请求 - 服务交互:通过
HTTP 协议发送请求
至路由服务 - 结果解析:
提取响应
中的矩阵数据并转换为 VROOM 标准格式 - 路径可视化:在求解完成后获取道路级路径坐标串(用于地图展示)
路由服务配置机制
用户通过命令行参数指定路由引擎类型与服务地址,而非 JSON 输入。典型启动命令示例:
vroom --router osrm -m http://localhost:5000 < 输入文件.json
参数解读:
--router osrm
:选择 OSRM 引擎-m http://localhost:5000
:路由服务地址输入文件.json
:包含坐标与配置的输入文件
输入文件示例:
{"vehicles": [{"id": 1,"start": [2.3522, 48.8566], // 巴黎仓库坐标"end": [2.3522, 48.8566],"capacity": [100],"profile": "car" // 汽车配置方案}],"jobs": [{"id": 10,"location": [2.2945, 48.8584], // 埃菲尔铁塔坐标"service": 300,"delivery": [10]}]
}
内部实现架构
VROOM 通过抽象化的路由封装器(Wrapper)实现多引擎支持,其核心类结构如下:
基础封装器接口
class Wrapper {
public:std::string profile; // 支持的配置方案// 矩阵数据获取接口virtual Matrices get_matrices(const vector<Location>& locs) = 0;// 路径几何数据获取接口virtual void add_geometry(Route& route) = 0;
};
前文传送:[xiaozhi-esp32] 构建智能AI设备 | 开发板抽象层 | 通信协议层
(这个助手兼容多种硬件的架构设计,不同的硬件抽象为了统一的驱动,参考VFS的思想)
HTTP 通信基类
class HttpWrapper : public Wrapper {
protected:Server _server; // 服务端配置// 构建服务特定查询virtual string build_query(...) = 0;// 执行HTTP请求string run_query(const string& query) {// 实现HTTPS请求发送与接收}
};
具体引擎封装器(如OsrmRoutedWrapper
)继承自HttpWrapper
,实现服务特定的查询构建与结果解析逻辑。
数据处理全流程
相当于是借助第三方库的力量
应用场景
以包含仓库与两个配送点的汽车路径问题为例:
-
矩阵获取阶段:
- 封装器发送三个坐标点的矩阵查询至
OSRM
- 接收包含 3x3 耗时/距离矩阵的
JSON 响应
- 转换为
vroom::Matrices
对象供优化器使用
- 封装器发送三个坐标点的矩阵查询至
-
路径优化阶段:
- 求解器通过
CostWrapper
获取移动成本 - 生成最优路径序列(如仓库→任务A→任务B→仓库)
- 求解器通过
-
几何数据获取:
- 对最终路径发送路线查询请求
解析
响应中的 GeoJSON 路径坐标串- 写入解决方案供地图可视化使用
总结
路由集成机制使 VROOM 具备以下核心能力:
- 现实道路建模:考虑单行道、限速等实际路况
- 多运输模式支持:通过不同配置方案实现汽车/卡车/自行车等差异计算
- 动态数据更新:实时获取最新路况信息(需路由引擎支持)
- 可视化增强:提供导航级路径细节展示
通过解耦路由服务
与优化引擎
,VROOM 保持了架构的扩展性,可快速接入新型路由引擎。这种设计模式为处理复杂的物流路径问题提供了坚实基础。
在掌握路由集成机制后,我们将进入更具挑战性的约束条件处理环节:第五章:时间窗约束将解析如何在路径优化中纳入时效性要求。