关于分区排序的算法思考及实现

一、引言

   对于很多机器人产品,都需要去某个区域进行工作(不限于扫地机的清扫任务、无人机的覆盖式搜索、割草机的区域割草等等),对于一次完整的任务而言,首先会将工作空间切割成若干个小区域,然后一个个小区域的完成,从而实现一次完整的任务
    那么问题来了,如何进行小区域的排序是评判这次完整任优略的准则之一,好的排序可以提高效率、提升用户体验等等。


二、算法设计思路

1、用数据结构表征分区

假如有以下分区图,你如何用一种结构去描述呢?
在这里插入图片描述
我刚开始想到的就是以某个重要的区域作为根节点构建一颗多叉树,假如以1号区域作为根节点,则这棵多叉树可以表征为:
在这里插入图片描述
但大家可能一眼就看出来了,多叉树有一个很明显的问题,它是有向的,即parent节点指向child节点,而且多叉树的特性是一个parent可以有多个child,但是一个child只能有一个parent,所以它不能完整的表征分区关系(比如2-3是相连的,但多叉树无法进行表征);
进一步的思考,图结构是不是最合适呢?分区图本身就是一种图,图结构就再合适不过了,如下图所示:
在这里插入图片描述

2、构建分区之间的图

定义一个简单的图结构体GNode

typedef struct GNode {
    int element;   // 当前元素
    set<GNode *> neighbors;  // 所有的邻居
} GNode;

建立连接关系

// 建立连接关系
void setNeighbor(GNode *node, GNode *neighbor) {
    node->neighbors.insert(neighbor);
    neighbor->neighbors.insert(node);
}

思路是不是很清奇,构建所有分区的GNode指针,然后setNeighbor,分区之间的图结构就结束了。
大家可能也发现了,其中的关键是怎么知道哪两个GNode是相邻的呢?请看下文

3、计算分区之间的相邻关系

我们以一副实际地图为例:一个大区域被四根分割线分成5个小区域

在这里插入图片描述

为了输出两两相邻关系,我刚开的思路是这样的,先给区域编号,然后计算每个分割线的小垂线所经过的区域,准备实现时,发现这种方法貌似不太优雅,于是咨询了朋友,有了个更好的方案,如下:

第一步:分割线解耦。如下图所示,原始分割线的条数为4条,根据其交点关系变成互不耦合的6条。
思路:先判断哪些点是共线的,再将共线的点按照x值(或者y值)的从大到小(从小到大)排序,最后将共线组按照顺序前后组合即可得到6条分割线
在这里插入图片描述

第二步:求小区域轮廓。求取每个小区域的内轮廓。
思路:cv::findContours(map, contours, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_NONE);可以实现
在这里插入图片描述

第三步:轮廓匹配。根据处理后分割线的中心点每个轮廓的距离判断轮廓关系。即根据距离阈值进行两两配对。如下图所示1、4两区域分割线的中心点到两个区域轮廓的距离一定是最近的。
思路:cv::pointPolygonTest(contour, center_point, true)暴力求解每个点到轮廓的距离,根据距离阈值进行两两配对。
在这里插入图片描述

输出相邻关系:
neighbor: 0-1 、0-2 、1-0
neighbor: 1-2 、1-4
neighbor: 2-0 、2-4 、2-1 、2-3
neighbor: 3-2
neighbor: 4-2 、4-1

4、根据图结构输出分区顺序

输出分区顺序是需要根据需求来的,有了分区之间的图结构后,可玩性就很高了。我举例以其中一种bfs的方式进行输出

// BFS
void tranversalBfs(GNode *root) {
    deque<GNode *> node_deque;
    set<GNode *> node_set;
    // 先找到根结点的所有子结点
    for (const auto &p : root->neighbors) {
        node_deque.emplace_back(p);
        node_set.insert(p);
    }
    // 输出的结果 vector结构
    vec_results_.emplace_back(root_.element);
    // 为了去重  set结构
    set_results_.insert(root_.element);

    while (!node_deque.empty()) {
        for (const auto &p : node_deque) {
            cout << p->element << " - ";
        }
        cout << endl;

        // 取出最前面一个元素
        GNode *node = node_deque.front();
        node_deque.pop_front();
        vec_results_.emplace_back(node->element);
        set_results_.insert(node->element);

        // 找这个元素的下一个层级所有元素
        vector<GNode *> temp_vec;
        for (const auto &p : node->neighbors) {
            if (set_results_.count(p->element) == 0) {
                temp_vec.emplace_back(p);
            }
        }
        for (const auto &p : temp_vec) {
            // 如果当前元素等于上次最后一个,将其子放到最前面 最后一个赋予新值
            if (update_last_element) {
                node_deque.emplace_front(p);
                node_set.insert(p);
            } else {
                node_deque.emplace_back(p);
                node_set.insert(p);
            }
        }
    }
}
  • 12
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值