CBS(Conflict-Based-Search)原理和代码实现

CBS(Conflict-Based Search)算法是一种用于多智能体路径规划(‌MAPF)的算法。关于MAPF问题的定义、冲突类型、目标函数等基础知识可以参考这篇文章:https://zhuanlan.zhihu.com/p/613639712

因为MAPF的状态空间随agent数量呈指数增长,所以也不能保证多项式时间复杂度能验证一个结果,是一个NP-hard问题,无法得到最优解。agent数量多时通常使用次优求解器

CBS算法

全称Conflict-Based-Search,CBS分为两层,下层对单个agent进行调度,上层解决多agent之间的冲突问题

原论文:Sharon G, Stern R, Felner A, et al. Conflict-based search for optimal multi-agent pathfinding[J]. Artificial intelligence, 2015, 219: 40-66.

论文地址:https://www.sciencedirect.com/science/article/pii/S0004370214001386

关键定义

  • path : 一条机器人的路径,也就是我们平时用得最多的单机规划结果

  • solution : 多机系统中所有机器人的 path 的集合( n 条 path ),也就是 mapf 算法的全局规划结果

  • conflict : 冲突.上述的 solution 中, n 条 path 之间可能会有冲突(没冲突当然皆大欢喜了).具体的描述形式为 (​) ,表示在时刻 t , ​ 和 ​ 同时占据了顶点 v . 拿栅格地图来说,就是在时刻 t , ​ 和 ​ 同时占据了矩阵的一个格子 matrix(i,j) .

  • constraints : 约束.一个约束 (,v,t) ,表示在时刻 t , 不能占据顶点 v .

  • 约束树 CT

CT的每个节点,包含:

  • constraint,也就是某agent在某个时刻不能经过某个节点

  • solution,也就是能够满足这个约束的一种策略

  • cost,当前策略的成本,比如所有agent要走的路径之和

步骤

1.先计算每个agent的最短路径

2.检测冲突,如果没有冲突则算法结束,返回结果

3.如果有冲突,选择一个冲突创建两个子问题:如下图,发现1号和2号在t=2时存在冲突,那么两个子问题就是让2号在t=2时不经过冲突点和让1号在t=2时不经过冲突点

4.最终形成一个搜索树,每个节点表示一个路径规划状态,算法在树中搜索得到无冲突的路径

局限性

计算复杂性可能会在处理大规模问题时变得很高。

尽管CBS被证明是完备和最优的,但在实践中,它可能会花费较长时间来找到最优解。

对于一些问题,CBS生成的路径可能会相对较长,因为它主要关注冲突解决而不是路径优化。这可能导致生成的解决方案在路径长度上不够优秀。

CBS的性能可能受到特定问题实例的影响。在某些问题上表现良好,而在另一些问题上可能效果不佳。

CBS代码实现(python)

源代码:GitHub - msaudulhassan/mapf: Python implementation of Space-time A*, Prioritized Planning, & Conflict-based Search for Multi-agent Path Finding (MAPF) 这个链接里面有Space-time A*, Prioritized Planning, & Conflict-based Search for Multi-agent Path Finding的python实现

下面是关于源代码的解读:

CBS上层

对应的py文件:mapf-main/cbs.py

CBS上层有一个open_list,这个列表用来记录冲突节点树

open_list的节点:

root = {'cost': 0,  # 当前的花销,即所有agent都走完花费的最少时间步
        'constraints': [],  # 限制条件
        'paths': [],    # 给每个agent单独规划出的路径
        'collisions': []}   # 冲突,每条冲突记录了冲突发生的agent、位置、时间戳

冲突检测:

使用A*规划好若干agent的路径之后,对这些路径进行冲突检测

冲突有两类,顶点冲突vertex_collision和边冲突edge_collision。顶点冲突就是某一时刻t,两个agent同时到达同一位置边冲突就是两个agnet不会发生位置互换,例如agent_1在​时刻位于​,agent_2在​时刻位于​,而agent_1在下一时刻​位于​,agent_2在下一时刻​位于​

当冲突发生时,记录发生冲突的两个agent、冲突发生的位置、时间戳(不是指现实世界的时间戳,而是记录agent行动步数的时间戳)

算法:

  1. 创建第一个节点,即所有agent在没有约束下使用下层算法得到各自的路径,将这个节点加入open_list

  2. 从open_list中弹出一个节点,检测它的冲突,如果无冲突则返回当前每个agent的路径;有冲突则根据冲突得到对不同agent在某一时刻的约束,这些约束每一个都产生一个子节点

  3. 重复2步骤,直到open_list为空

CBS下层的A*实现

在CBS下层对单个agent使用A*来规划路径,与单独A*相比,需要添加上对路径的限制constraints

对应py文件:mapf-main/single_agent_planner.py

计算heuristics

def compute_heuristics(my_map, goal)

使用Dijkstra构建从goal出发的最短路径树:

使用open_list存储待扩展节点,也就是已发现但未被探索的节点。这是一个list,其中元素为cost、goal以及{cost,goal}组成的一个tuple。例如:(0, (1, 5), {'cost': 0, 'loc': (1, 5)})

使用closed_list存储已探索节点,确保这些节点不会被重复处理。这是一个dict,元素的key为某个非障碍物的位置,value为由这个点的cost和value组成的dict

算法:

  1. 根据goal以及它的cost构建open_list节点,作为open_list的初始元素

  2. 选出open_list堆最上面的节点,在它的4个方向上进行探索,如果探索节点是障碍物或超出地图边界,则进入下一循环;如果没问题则在之前cost基础上+1,并检查这个节点是否被探索过(是否在closed_list),如果被探索过则更新closed_list,如果没有被探索过则在closed_list中直接创建一个新的node

  3. 重复2步骤,直到open_list为空

  4. 将closed_list节点中的节点存入h_values作为返回内容

这个函数被调用的位置:初始化CBS等算法时,获得每个agent在不同位置时距离终点的启发式。本以为这个地方使用的是曼哈顿距离,但是仔细一看还真不是

寻路

def a_star(my_map, start_loc, goal_loc, h_values, agent, constraints)

参数agent是该智能体的序号

关键结构:

  • node节点:包含loc,g_val,h_val,parent和timestamp,记录agent在当前位置下的实际成本、启发式、父节点和时间戳

  • frontier:相当于open_list,栈中存储的节点元素为总成本、启发式、位置和对应节点构成的tuple

  • explored_set:相当于closed_list,元素key为节点位置+节点时间戳,value为节点本身

算法:

  1. 构建约束表,初始化frontier和explored_set,相当于open_list和closed_list

  2. 创建agent初始位置的节点,并加入到frontier和explored_set

  3. 从frontier弹出节点,检查这个节点的5个动作(除了上下左右还有STAY)之后的节点是否是终点,如果是则退出循环返回path;否则判断是否出界/是否是障碍物/是否和约束相违背,如果没有,则更新frontier和explored_set,更新策略和上面的计算heuristics相同

  4. 循环3步骤,直到frontier为空或到达终点

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值