CBS多机器人路径规划(Conflict-Based Search)

本文介绍了多智能体路径规划问题(MAPF)以及CBS算法的工作原理。CBS通过两层规划解决冲突,底层使用如A*算法进行单机规划,顶层通过约束树解决冲突,生成无冲突的全局路径。算法在冲突发生时将约束分叉到子节点,确保最终找到全局最优解。此外,文章提到了CBS的优化变种ECBS,用于提高效率并提供有界次优结果。
摘要由CSDN通过智能技术生成

多机器人路径规划

多智能体路径规划 (Multi-Agent Path Finding, MAPF) 研究多智能体的路径规划算法,为多机系统规划无冲突的最优路径.
CBS(Conflict-Based Search) 是一种基于冲突的 MAPF 算法, CBS 算法给出 MAPF 问题的全局最优结果.

参考文献:

CBS 多机路径规划

CBS是一种中央规划算法(Centralized Planning for Distributed Plans),什么意思呢?也就是由一台主机作为中央控制器,在全局视角生成每一台机器人的路径并统筹解决冲突.除了中央规划算法,还有分布式规划算法(Distributed Planning for Centralized Plans,Distributed Planning for Distributed Plans)等,具体可以参考这篇综述 Survey of the Multi-Agent Pathfinding Solutions

CBS 是一族方法.算法的思想主要将多机规划分为两层底层执行带有约束的单机规划,例如用传统 A* 算法,顶层遍历底层的规划路径,检查路径之间是否有冲突,如果有冲突则施加约束重新进行底层单机规划,直到所有底层路径无冲突为止.

一些术语

先来讲讲CBS定义的一些术语:

  • p a t h path path: 一条机器人的路径,也就是我们平时用得最多的单机规划结果
  • s o l u t i o n solution solution: 多机系统中所有机器人的 p a t h path path 的集合( n n n p a t h path path),也就是 mapf 算法的全局规划结果
  • c o n f l i c t conflict conflict: 冲突.上述的 s o l u t i o n solution solution 中, n n n p a t h path path 之间可能会有冲突(没冲突当然皆大欢喜了).具体的描述形式为 ( a i , a j , v , t ) (a_i, a_j, v, t) (ai,aj,v,t) ,表示在时刻 t t t, a i a_i ai a j a_j aj 同时占据了顶点 v v v. 拿栅格地图来说,就是在时刻 t t t, a i a_i ai a j a_j aj 同时占据了矩阵的一个格子 m a t r i x ( i , j ) matrix(i, j) matrix(i,j).
  • c o n s t r a i n t s constraints constraints: 约束.一个约束 ( a i , v , t ) (a_i, v, t) (ai,v,t) ,表示在时刻 t t t, a i a_i ai 不能占据顶点 v v v.

顶层

CBS算法顶层使用约束树(Search the Constraint Tree (CT))数据结构来解决底层冲突,大概长这样:

在这里插入图片描述
其实就是一颗树(可以设计成二叉树或者多叉树),树的每个节点除了有指向子节点的指针,还包括:

    1. 约束( c o n s t r a i n t s constraints constraints) : 约束根据冲突( c o n f l i c t s conflicts conflicts)得到
    1. 当前计算的全局路径( s o l u t i o n solution solution, 注意这个可能包含冲突,一旦无冲突即为全局最优路径, 算法退出)
    1. 全局代价 c o s t cost cost
生成树

顶层算法中,最核心的一点就是树的生成,也就是说从父节点 N N N 该如何生成子节点 N N N-> c h i l d child child

假设现在有一个节点 N N N,节点内包括:

  • 当前节点的约束 c o n s t r a i n t s constraints constraints
  • 根据当前 c o n s t r a i n t s constraints constraints计算出来的全局路径 s o l u t i o n solution solution (怎么得到的?通过将约束施加到底层规划算法,例如 A A A*,得到一堆 p a t h path path, 把这些 p a t h path path 合起来就叫 s o l u t i o n solution solution. 怎么样将约束施加到底层算法呢?在后面来解释)
  • 根据 s o l u t i o n solution solution 计算出来的全局代价 c o s t cost cost
    在这里插入图片描述

生成子节点 N N N-> c h i l d child child :

  • 第一步: 遍历 s o l u t i o n solution solution, 搜索 p a t h path path 之间的冲突 c o n f l i c t conflict conflict
    • 没冲突:就找到全局最优结果,算法退出
    • 有冲突:假设现在找到了一个冲突 ( a i , a j , v , t ) (a_i, a_j, v, t) (ai,aj,v,t) ,表示在时刻 t t t, a i a_i ai a j a_j aj 同时占据了顶点 v v v
  • 第二步:解决冲突
    • ( a i , a j , v , t ) (a_i, a_j, v, t) (ai,aj,v,t) 表示在时刻 t t t, a i a_i ai a j a_j aj 同时占据了顶点 v v v, 怎么解决?打个比方,两伙人去饭店吃饭,只剩一桌空桌了,怎么办呢,很显然,在当前文明社会,来个石头剪刀布,输了的人等下一桌.虽然字母很抽象,但算法都来源于生活.
    • 好,按照生活常理,在时刻 t t t, a i a_i ai a j a_j aj 只能有一个占据顶点 v v v,也石头剪刀布?这样不好,计算机里面不是文明社会,谁也不服谁,容易打架.加一桌?不行,店就这么大,会挤到其他人.好在计算机能力强,不行的话我再开一家店吧,就在旁边立马复制一个一模一样的店,一样的味道,一样的人气,一样只空一桌, a i a_i ai 去这边, a j a_j aj去那边,谁也不亏,谁也不占便宜.
    • 注意,就在此时,父节点分叉了(fork),诞生出了两个子节点, a i a_i ai 去的子节点施加约束 ( a j , v , t ) (a_j, v, t) (aj,v,t) a j a_j aj 去的子节点施加约束 ( a i , v , t ) (a_i, v, t) (ai,v,t). 一个冲突 ( a i , a j , v , t ) (a_i, a_j, v, t) (ai,aj,v,t),拆成了两个约束 ( a i , v , t ) (a_i, v, t) (ai,v,t) ( a j , v , t ) (a_j, v, t) (aj,v,t)
  • 第三步:生成子节点
    • 对于一个冲突 ( a i , a j , v , t ) (a_i, a_j, v, t) (ai,aj,v,t), 给予 a i a_i ai a j a_j aj 同等机会, 节点 N N N 分叉成两个子节点, 每个子节点都继承父节点原有的约束,并施加每个子节点独有的约束,如下图:
    • 在这里插入图片描述
    • 将每个子节点的约束施加到底层规划算法,计算出一堆 p a t h path path, 把这些 p a t h path path 合起来变成 s o l u t i o n solution solution, 计算每个子节点的全局 c o s t cost cost, 然后在整个约束树找 c o s t cost cost 最小的节点 N N N, 回到第一步继续查找冲突, 如果有冲突,每个子节点继续分叉

底层

CBS的底层是单机搜索,理论上,可以用任意一种搜索算法,比如经典的 A A A* . 只不过这个 A A A* 除了空间维度,还需要搜索时间维度.顶层施加约束在底层的逻辑处理其实就是将约束当成一个虚拟障碍物,比如约束 ( a i , v , t ) (a_i, v, t) (ai,v,t) A A A* 算法中,只是一个空间 x 时间的立方体里的一个障碍物方块,与二维里的障碍物没有本质的区别.

关于时空 A*(space-time A*),可以参考这篇文章

例子

这是论文中的例子.

在这里插入图片描述
如图,有两只小老鼠 a 1 a_1 a1 a 2 a_2 a2 想要吃蛋糕, 分别从 S 1 S_1 S1 S 2 S_2 S2 出发,想要到达 G 1 G_1 G1 G 2 G_2 G2

我们按照步骤来生成顶层搜索约束树:

现在约束树暂时没有节点,先生成一个根节点,约束集为空, 在空约束集的约束下调用底层搜索,生成两个 p a t h path path:
a 1 a_1 a1: < S 1 , A 1 , C , G 1 > <S_1, A_1, C, G_1> <S1,A1,C,G1>
a 2 a_2 a2: < S 2 , B 1 , C , G 2 > <S_2, B_1, C, G_2> <S2,B1,C,G2>
计算一下所有 p a t h path path (也就是 s o l u t i o n solution solution) 的 c o s t cost cost: 3 + 3 = 6(起点不算)

现在的 r o o t root root 约束树如图:
在这里插入图片描述

好,接下来正式按步骤:

  • 第一步:遍历 s o l u t i o n solution solution, 搜索 p a t h path path 之间的冲突 c o n f l i c t conflict conflict
    • 很明显有冲突:一眼找到两条 p a t h path path 中的第 2 步(起点不算)都到 C C C, 生成冲突 ( a 1 , a 2 , C , 2 ) (a_1, a_2, C, 2) (a1,a2,C,2) ,表示在时刻 2 2 2, a 1 a_1 a1 a 2 a_2 a2 同时占据了顶点 C C C
  • 第二步:解决冲突
    • 一个冲突 ( a 1 , a 2 , C , 2 ) (a_1, a_2, C, 2) (a1,a2,C,2),拆成两个约束 ( a 1 , C , 2 ) (a_1, C, 2) (a1,C,2) ( a 2 , C , 2 ) (a_2, C, 2) (a2,C,2)
  • 第三步:生成子节点
    • 对于冲突 ( a 1 , a 2 , C , 2 ) (a_1, a_2, C, 2) (a1,a2,C,2), 给予 a 1 a_1 a1 a 2 a_2 a2 同等机会, 节点 r o o t root root 分叉成两个子节点, 每个子节点都继承父节点原有的约束,并施加每个子节点独有的约束
    • 将每个子节点的约束 ( a 1 , C , 2 ) (a_1, C, 2) (a1,C,2) ( a 2 , C , 2 ) (a_2, C, 2) (a2,C,2) 施加到底层规划算法,计算出一堆 p a t h path path, 把这些 p a t h path path 合起来变成 s o l u t i o n solution solution, 如下图:
      在这里插入图片描述
    • 计算每个子节点的全局 c o s t cost cost,发现都是 7 7 7,由于搜索的时候一般从左边开始, 所以先考察左子节点, 回到第一步,遍历左子节点的 s o l u t i o n solution solution, 发现无冲突,说明找到了全局最优路径,算法返回.

总结

到这里,可以发现,CBS就是一个算法框架,顶层解决冲突,底层进行搜索.

优化 CBS 算法,就是考虑最优性(Optimal)和完全性(Complete), 来优化顶层约束树的生成方式和底层搜索算法.

例如 ECBS, 就是一种有界次优的 CBS 算法, 通过优化约束树进行节点分叉的启发函数和底层搜索算法给出有界次优结果,在可接受的情况下大大降低时间复杂度,有兴趣可以看看.

多机器人路径规划(Multi-Agent Path Finding, MAPF) 这里展示了 CBS 算法在 ros 上的实现效果,有兴趣可以看看.

  • 16
    点赞
  • 103
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
CBS算法是一种针对多机器人路径规划问题的算法,其核心思想是将多机器人的路径规划问题分解为单机器人的路径规划问题,并利用冲突检测和冲突消解来解决整个问题。下面是一个基于C++语言实现的多机器人CBS算法的示例代码: ```c++ #include <iostream> #include <vector> #include <queue> using namespace std; // 定义机器人结构体 struct Robot { int id; // 机器人编号 int start; // 起点编号 int goal; // 终点编号 vector<int> path; // 路径 }; // 定义冲突结构体 struct Conflict { int time; // 冲突时间 int robot1; // 机器人1编号 int robot2; // 机器人2编号 int location; // 冲突位置 }; // 定义节点结构体 struct Node { vector<Robot> robots; // 机器人集合 vector<Conflict> conflicts; // 冲突集合 int cost; // 路径长度 bool operator<(const Node &n) const { return cost > n.cost; } }; // 定义地 vector<vector<int>> map; // 定义机器人集合和终点集合 vector<Robot> robots; vector<int> goals; // 定义冲突检测函数 bool detect_conflict(int time, int robot1, int robot2, vector<Robot> &robots) { int loc1 = robots[robot1].path[time]; int loc2 = robots[robot2].path[time]; if (loc1 == loc2) { return true; } for (int i = time + 1; i < robots[0].path.size(); i++) { int next_loc1 = robots[robot1].path[i]; int next_loc2 = robots[robot2].path[i]; if (next_loc1 == next_loc2) { return true; } if (next_loc1 == loc2 && next_loc2 == loc1) { return true; } loc1 = next_loc1; loc2 = next_loc2; } return false; } // 定义冲突消解函数 void resolve_conflict(int time, int robot1, int robot2, vector<Robot> &robots) { Robot &r1 = robots[robot1]; Robot &r2 = robots[robot2]; int loc1 = r1.path[time]; int loc2 = r2.path[time]; if (loc1 == loc2) { return; } // 交换位置 if (r1.goal == loc2 && r2.goal == loc1) { swap(r1.goal, r2.goal); swap(r1.path, r2.path); return; } // 机器人1等待 if (r1.goal == loc2) { r1.path.insert(r1.path.begin() + time, loc1); return; } // 机器人2等待 if (r2.goal == loc1) { r2.path.insert(r2.path.begin() + time, loc2); return; } // 交叉 for (int i = time + 1; i < r1.path.size(); i++) { if (r1.path[i] == loc2 && r2.path[i] == loc1) { r1.path.insert(r1.path.begin() + i, loc1); return; } if (r2.path[i] == loc1 && r1.path[i] == loc2) { r2.path.insert(r2.path.begin() + i, loc2); return; } } } // 定义A*算法函数 int astar(int robot, int start, int goal) { vector<int> g(map.size(), INT_MAX); vector<int> f(map.size(), INT_MAX); vector<int> parent(map.size(), -1); priority_queue<pair<int, int>, vector<pair<int, int>>, greater<pair<int, int>>> pq; g[start] = 0; f[start] = g[start] + heuristic(start, goal); pq.push({f[start], start}); while (!pq.empty()) { int curr = pq.top().second; pq.pop(); if (curr == goal) { int cost = 0; while (parent[curr] != -1) { int prev = parent[curr]; cost += distance(curr, prev); curr = prev; } return cost; } for (int i = 0; i < map[curr].size(); i++) { int next = map[curr][i]; int dist = distance(curr, next); int h = heuristic(next, goal); if (g[curr] + dist < g[next]) { g[next] = g[curr] + dist; f[next] = g[next] + h; parent[next] = curr; pq.push({f[next], next}); } } } return INT_MAX; } // 定义CBS算法 int cbs() { Node n; n.robots = robots; for (int i = 0; i < robots.size(); i++) { n.cost += astar(i, robots[i].start, robots[i].goal); } priority_queue<Node> pq; pq.push(n); while (!pq.empty()) { Node curr = pq.top(); pq.pop(); if (curr.conflicts.empty()) { return curr.cost; } Conflict c = curr.conflicts[0]; vector<Node> children; for (int i = 0; i < 2; i++) { Node child = curr; int robot = (i == 0 ? c.robot1 : c.robot2); int goal = (i == 0 ? robots[c.robot1].goal : robots[c.robot2].goal); for (int j = c.time; j < child.robots[0].path.size() && child.robots[robot].path[j] != goal; j++) { child.robots[robot].path[j] = child.robots[robot].path[c.time]; } child.robots[robot].path.erase(child.robots[robot].path.begin() + c.time + 1, child.robots[robot].path.end()); child.robots[robot].goal = goal; child.cost = 0; for (int j = 0; j < robots.size(); j++) { child.cost += astar(j, child.robots[j].start, child.robots[j].goal); } children.push_back(child); } for (int i = 0; i < children.size(); i++) { bool valid = true; for (int j = 0; j < children[i].robots.size(); j++) { for (int k = j + 1; k < children[i].robots.size(); k++) { if (detect_conflict(c.time, j, k, children[i].robots)) { valid = false; Conflict new_c = {c.time, j, k, children[i].robots[j].path[c.time]}; children[i].conflicts.push_back(new_c); } } } if (valid) { pq.push(children[i]); } } } return INT_MAX; } int main() { // 初始化地 map = vector<vector<int>>(10, vector<int>(10, 1)); // 初始化机器人和终点 Robot r1 = {0, 1, 9, {1}}; Robot r2 = {1, 9, 1, {9}}; Robot r3 = {2, 1, 9, {1}}; Robot r4 = {3, 9, 1, {9}}; robots.push_back(r1); robots.push_back(r2); robots.push_back(r3); robots.push_back(r4); goals = {9, 1, 9, 1}; // 调用CBS算法 int cost = cbs(); cout << "Total cost: " << cost << endl; return 0; } ``` 在上述代码中,我们首先定义了机器人和冲突的结构体,然后定义了冲突检测和冲突消解的函数,以及A*算法的函数。最后,我们定义了CBS算法的函数,并在主函数中调用该函数来解决多机器人路径规划问题。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

zjyspeed

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值