前言与参考

这一部分仅为路径规划源码及论文GPIR的一个小部分,但是有代码实现,第一次看的时候有些懵,所以特此记录;主要是设置好了栅格地图后,添加了障碍物后,对其的欧式距离计算和梯度计算等。原代码中为了加速,不同以往的直接每个点逐个计算遍历,而是分了两个步骤同时使用多线程并行计算进行加速

参考文献:

  1. 2002论文:a general algorithm for computing distance transforms in linear time
  2. 完整GPIR 路径规划源码 github链接
  3. 仅sdf_test 的debug及测试代码 gitee链接
  4. openmp并行计算:介绍与简单测试链接

通常在计算图/栅格距离时,复杂度为 \(O(m\times n)\) 也就是pixel of image OR dim of map,其中在计算欧式距离时因为还有一些额外的计算操作所以在计算大图时会有些耗时,此处根据参考1论文中的方法提出一种可以使用多线程并行计算的方式,使得理论上复杂度为 \(O(m\times n/p)\) 其中p为线程数量,可以通过结果表得知,在较为大的pixel or dim下,加速较为明显

现在的主要应用可能是在加速路径规划时的栅格地图及距离图时效果较为明显,效果预览(灰黑为grid图,彩色为离障碍物距离图,最右边为距离梯度示意图)

以下我们进行简洁伪代码及实际代码对应,及整个过程的详细讲解

简洁过程

论文中总结

  1. In a first phase each column \(C_x\) is separately scanned. (defined by points \((x , y)\) with x fixed) 其中距离由最近的点决定
  2. In a second phase each row \(R_y\) is separately scanned. (defined by points \((x , y)\) with y fixed). 对于\(R_y\)上的每个点,最小的EDT值为\((x-x')^2+G(x',y)^2\) 其中 \((x',y)\)沿row \(R_y\)

可以看到伪代码对应的第一和第二阶段,和实际代码中的 完全对应

void SignedDistanceField2D::EuclideanDistanceTransform(
std::array<int, 2> dim,
std::function<bool(const int x, const int y)> is_occupied,
DistanceMap* output_map) {
int inf = dim[0] + dim[1] + 10; std::vector<std::vector<int>> g(dim[0], std::vector<int>(dim[1], 0));
omp_set_num_threads(1);
{
#pragma omp parallel for
// column scan phase 1====
for (int x = 0; x < dim[0]; ++x) {
g[x][0] = is_occupied(x, 0) ? 0 : inf; for (int y = 1; y < dim[1]; ++y) {
g[x][y] = is_occupied(x, y) ? 0 : 1 + g[x][y - 1];
}
for (int y = dim[1] - 2; y >= 0; --y) {
if (g[x][y + 1] < g[x][y]) g[x][y] = 1 + g[x][y + 1];
}
}
} // row scan phase 2====
omp_set_num_threads(1);
{
#pragma omp parallel for
for (int y = 0; y < dim[1]; ++y) {
int q = 0, w;
std::vector<int> s(dim[0], 0);
std::vector<int> t(dim[0], 0); auto f = [&g, &y](int x, int i) -> double {
return (x - i) * (x - i) + g[i][y] * g[i][y];
}; for (int u = 1; u < dim[0]; ++u) {
while (q >= 0 && f(t[q], s[q]) > f(t[q], u)) {
--q;
} if (q < 0) {
q = 0;
s[0] = u;
} else {
w = 1 + std::floor((u * u - s[q] * s[q] + g[u][y] * g[u][y] -
g[s[q]][y] * g[s[q]][y]) /
(2 * (u - s[q])));
if (w < dim[0]) {
++q;
s[q] = u;
t[q] = w;
}
}
} for (int u = dim[0] - 1; u >= 0; --u) {
output_map->SetValue(u, y, map_resolution_ * std::sqrt(f(u, s[q])));
if (u == t[q]) --q;
}
}
}
}

详细讲解

因为论文比较久远有些符号的使用并不像现在这样,可以点击上面原文进行观看 EDT全称: Euclidean Distance Transform,首先定义一下久远的符号 \(\operatorname{MIN}(k:P(k):f(k))\) 是指 \(k\)在 \(P(k)\)范围内使得 \(f(k)\) 最小的值

正常的距离计算通常需要逐次计算距离 \(dt[x,y]=\sqrt{EDT(x,y)}\)

\[\operatorname{EDT}(x, y)=\operatorname{MIN}\left(i, j: 0 \leq i<m \wedge 0 \leq j<n \wedge b[i, j]:(x-i)^{2}+(y-j)^{2}\right)
\]

为了所有的EDT,MDT, CDT的计算方便后续和代码中的G 如下:

\[\begin{aligned}
\operatorname{EDT}(x, y) &=\operatorname{MIN}\left(i: 0 \leq i<m:(x-i)^{2}+G(i, y)^{2}\right)\\
\text{where } G(i, y)&=\operatorname{MIN}(j: 0 \leq j<n \wedge b[i, j]:|y-j|)
\end{aligned}\tag{2}
\]

然后就进入了第一阶段,可以看出第一阶段的column scan是可以直接并行的(仔细对着代码看看就能看出来了),因为每个column和其他直接互不影响,然后column下在往下直接for row,第一阶段算的是G值,比如示例test中的如果把对角线都设为1,其他给0(假设>0即 被占据),原始占据图与G图如下:

比较难以理解的应该是第二阶段,但是第二阶段其实就是上面公式(2)所示,直接手推一下,取min,如上图右边,只是论文的大部分在介绍如何优雅的写出这样的并行for

第二阶段首先y是fixed,也就是一个column看下来,所以简化一下公式二就是把y去掉

\[\operatorname{DT}(x, y)=\operatorname{MIN}(i: 0 \leq i<m: f(x, i)) \tag{3}
\]

其中 \(f(x,i)\) 的定义,因为全文同时对其他的距离函数也进行了说明,所以原文中分了出来,但是我们在这里仅看着欧式距离计算哈,可以看到和公式(2)的区别就是y被藏起来了,因为y已经是fixed了

\[f(x, i)= \begin{cases}(x-i)^{2}+g(i)^{2} & \text { for EDT } \\ |x-i|+g(i) & \text { for MDT } \\ |x-i| \max g(i) & \text { for CDT }\end{cases}
\]

申明下图Fig 2. \(F_i\) 就是 点 \((i,g(i))\) 的曲线连接而成,我们想要的是min 最小的 所以是solid line 实线的部分

假设这个实线的一系列点(从左到右)的index是:\(s[0],s[1],\dots,s[q]\)

对应给定了upper bound \(u >0\) 定义使 \(x\) 最小的新 index \(h\) 通常来说x是有大于1的minimizer,定义拿到的最小的那个 \(0 \le h<u\) 使得对于所有的\(i\) 都有:\(f(x, h) \leq f(x, i)\)

\[H(x, u)=\operatorname{MIN}(h: 0 \leq h<u \wedge \forall(i: 0 \leq i<u: f(x, h) \leq f(x, i)): h)
\]

由此定义处\(s(u)\) 即从左到右的scan,拿到minimizers

\[\begin{aligned}
S(u) &=\{H(x, u) \mid 0 \leq x<m\} \\
T(h, u) &=\{x \mid 0 \leq x<m \wedge H(x, u)=h\} \text { if } 0 \leq h<u
\end{aligned}
\tag{4}
\]

为了找到 \(x^*\) 我们引入 \(\text{Sep}\) ,\(\text{Sep}(i,u)\) 指的是第一个不小于水平坐标相交点\(F_u\)和\(F_i\) 的整数,公式表示也就是:

\[F_{i}(x) \leq F_{u}(x) \quad \Leftrightarrow \quad x \leq \operatorname{Sep}(i, u)
\]

由此可以获取\(x^*=\text{Sep}(s[l^*],u)\), 然后\(\text{Sep}\)这个取决于我们想要计算的距离是什么,比如EDT的话就是:

\[\begin{aligned}
& F_{i}(x) \leq F_{u}(x) \\
\Leftrightarrow &\left\{\text { definition of } F_{i}, F_{u}\right\} \\
&(x-i)^{2}+g(i)^{2} \leq(x-u)^{2}+g(h)^{2} \\
\Leftrightarrow &\{\text { calculus; } i<u ; x \text { is an integer }\} \\
& x \leq\left(u^{2}-i^{2}+g(u)^{2}-g(i)^{2}\right) \text { div }(2(u-i))
\end{aligned}
\]

总结完就是:

\[\operatorname{Sep}(i, u)=\left(u^{2}-i^{2}+g(u)^{2}-g(i)^{2}\right) \operatorname{div}(2(u-i)) \tag{5}
\]

但是感觉好像... 理论还有漏的地方,暂时就先这样吧... 后面有机会再补充.. 因为感觉超出了我的...能力范围,总感觉卡在哪里了,可能就是卡在了这for 套for 再套while里把,对着伪代码写出来 emm能用 ok;用ab的话:调用就行了, hhhh

代码对应

然后回看伪代码和代码,所有的标和变量名称基本保持了一致

  omp_set_num_threads(4);
{
#pragma omp parallel for
for (int y = 0; y < dim[1]; ++y) {
int q = 0, w;
std::vector<int> s(dim[0], 0);
std::vector<int> t(dim[0], 0); auto f = [&g, &y](int x, int i) -> double {
return (x - i) * (x - i) + g[i][y] * g[i][y];
};//公式2中求距离的 for (int u = 1; u < dim[0]; ++u) {
while (q >= 0 && f(t[q], s[q]) > f(t[q], u)) {//公式4对比大小
--q;
} if (q < 0) {
q = 0;
s[0] = u;
} else {
w = 1 + std::floor((u * u - s[q] * s[q] + g[u][y] * g[u][y] -
g[s[q]][y] * g[s[q]][y]) /
(2 * (u - s[q])));//公式5 +1
if (w < dim[0]) {
++q;
s[q] = u;
t[q] = w;
}
}
} for (int u = dim[0] - 1; u >= 0; --u) {
output_map->SetValue(u, y, map_resolution_ * std::sqrt(f(u, s[q])));
if (u == t[q]) --q;
}
}
}

结果展示

如果我们设一个100x100的 然后对角线设为1的话,得到的图,其中左边是grid map右边是距离转了jet color图,蓝色即代表离被占据(也就是黑色那个1)越近的距离

打印数字的话10x10的grid如下(即欧式距离 每个格的分辨率为1)

测试包内包括了Bilinear进行grid 梯度求解,后面再补充进来,结果如图:

【基础计算】ESDF栅格距离图计算并行加速版的更多相关文章

  1. 明风:分布式图计算的平台Spark GraphX 在淘宝的实践

    快刀初试:Spark GraphX在淘宝的实践 作者:明风 (本文由团队中梧苇和我一起撰写,并由团队中的林岳,岩岫,世仪等多人Review,发表于程序员的8月刊,由于篇幅原因,略作删减,本文为完整版) ...

  2. MaxCompute 图计算用户手册(上)

    概要 ODPS GRAPH是一套面向迭代的图计算处理框架.图计算作业使用图进行建模,图由点(Vertex)和边(Edge)组成,点和边包含权值(Value),ODPS GRAPH支持下述图编辑操作: ...

  3. 图计算 on nLive:Nebula 的图计算实践

    本文首发于 Nebula Graph Community 公众号 在 #图计算 on nLive# 直播活动中,来自 Nebula 研发团队的 nebula-plato 维护者郝彤和 nebula-a ...

  4. 关于图计算&图学习的基础知识概览:前置知识点学习(Paddle Graph Learning (PGL))

    关于图计算&图学习的基础知识概览:前置知识点学习(Paddle Graph Learning (PGL)) 欢迎fork本项目原始链接:关于图计算&图学习的基础知识概览:前置知识点学习 ...

  5. 关于图计算和graphx的一些思考[转]

    原文链接:http://www.tuicool.com/articles/3MjURj “全世界的网络连接起来,英特纳雄耐尔就一定要实现.”受益于这个时代,互联网从小众的角落走到了历史的中心舞台.如果 ...

  6. MaxCompute 图计算开发指南

    快速入门step by step MaxCompute Studio 创建完成 MaxCompute Java Module后,即可以开始开发Graph了. 代码示例 在examples目录下有gra ...

  7. Spark入门实战系列--9.Spark图计算GraphX介绍及实例

    [注]该系列文章以及使用到安装包/测试数据 可以在<倾情大奉送--Spark入门实战系列>获取 .GraphX介绍 1.1 GraphX应用背景 Spark GraphX是一个分布式图处理 ...

  8. GraphX 图计算实践之模式匹配抽取特定子图

    本文首发于 Nebula Graph Community 公众号 前言 Nebula Graph 本身提供了高性能的 OLTP 查询可以较好地实现各种实时的查询场景,同时它也提供了基于 Spark G ...

  9. 图计算引擎分析——Gemini

    前言 Gemini 是目前 state-of-art 的分布式内存图计算引擎,由清华陈文光团队的朱晓伟博士于 2016 年发表的分布式静态数据分析引擎.Gemini 使用以计算为中心的共享内存图分布式 ...

  10. 开源图计算框架GraphLab介绍

    GraphLab介绍 GraphLab 是由CMU(卡内基梅隆大学)的Select 实验室在2010 年提出的一个基于图像处理模型的开源图计算框架.框架使用C++语言开发实现. 该框架是面向机器学习( ...

随机推荐

  1. 1.13~1.14&&放假寄

    1.13 3点就放了,手机在机房就能拿到,我为了给手机充会电又多留了一会(事实证明这挺对的) 因为我们是 化微机的班,老师收手机都放在一个箱子里,要有人负责把剩下的手机搬到教室,我走得晚还被当成免费劳 ...

  2. 【深度学习】基础--NumPy

    因为深度学习会应用到我们大学时候学习的数学知识---线性代数.(矩阵当年想起来还是挺有意思的,有考研的经历都有感觉) 而在计算机里面如何展示矩阵的计算和应用,就需要运用到NumPy,是Python的一 ...

  3. 《最新出炉》系列入门篇-Python+Playwright自动化测试-43-分页测试

    1.简介 分页测试,这种一般都是公共的方法系统中都写好了,这种一般出现是数据展示比较多的时候,会采取分页的方法,而且比较固定,一般是没有问题的,因此它非常适合自动化测试,但是如何使用playwrigh ...

  4. Pytorch入门—Tensors张量的学习

    Tensors张量的学习 张量是一种特殊的数据结构,与数组和矩阵非常相似.在PyTorch中,我们使用张量来编码模型的输入和输出,以及模型的参数. 张量类似于NumPy的ndarrays,只是张量可以 ...

  5. 使用可视化工具redis-desktop-manager管理查询缓存。

    AnotherRedisDesktopManager https://gitee.com/qishibo/AnotherRedisDesktopManager/releases 下载windows版本 ...

  6. Swift中的nil

    Swift中的nil和OC中的nil不一样.OC中的nil表示不存在的对象,你无法给NSInteger类型的变量赋值nil,但是Swift中的nil表示不存在,可以给任何Optional的变量或者常量 ...

  7. LocalDateTime 时间偏移量的处理

    一.代码处理块 // 当前系统时间两年后的时间 LocalDateTime expirationTime = LocalDateTimeUtil.offset(LocalDateTime.now(), ...

  8. nim 7. nimble--制作包

    1. nim的包管理工具: nimble nim的包管理工具,是nimble. 在安装nim的时候,已经自带了nimble. nible通常需要使用git服务器存储包,因此,本地需要git命令的支持. ...

  9. 网络拓扑—DHCP服务配置

    目录 DHCP服务搭建 相关配置细节前提 安装DHCP服务 DHCP服务搭建 相关配置细节前提 系统:Windows Server 2003 IP网段:10.0.0.0/24 三台机子: 普通PC机 ...

  10. RESTful风格openapi接口设计+openapi远程服务调用

    我们平常开发一般只使用GET.POST方法.而对于HTTP给出的PUT.DELETE等其他方法都没使用.以RESTful风格设计接口就能全部用上这些方法. 按照RESTful理查德森成熟度模型改造接口 ...