轨迹优化 | 图解欧氏距离场与梯度场算法(附ROS C++/Python实现)

news2024/11/24 19:40:28

目录

  • 0 专栏介绍
  • 1 什么是距离场?
  • 2 欧氏距离场计算原理
  • 3 双线性插值与欧式梯度场
  • 4 仿真实现
    • 4.1 ROS C++实现
    • 4.2 Python实现

0 专栏介绍

🔥课程设计、毕业设计、创新竞赛、学术研究必备!本专栏涉及更高阶的运动规划算法实战:曲线生成与轨迹优化、碰撞模型与检测、多智能体群控、深度强化学习运动规划、社会性导航、全覆盖路径规划等内容,每个模型都包含代码实现加深理解。

🚀详情:运动规划实战进阶


1 什么是距离场?

距离场(Distance Field)也称为距离变换(Distance Transform),在图像处理和模式识别中是一种重要工具,其核心思想是将图像中每个像素点的值表示为到最近的目标像素的距离度量,更高维的情况依次类推。在计算机视觉领域,距离场是图像分割和配准的基础,在运动规划中则常用于地图建模(广义Voronoi图计算)和轨迹梯度优化。

在这里插入图片描述

给定点集 G \mathcal{G} G和其上的目标点集 Q ⊆ G Q\subseteq \mathcal{G} QG,可以定义函数

D I ( p ) = min ⁡ q ∈ G ( d ( p , q ) + I ( q ) ) \mathcal{D} _I\left( p \right) =\min _{q\in \mathcal{G}}\left( d\left( p,q \right) +I\left( q \right) \right) DI(p)=qGmin(d(p,q)+I(q))

计算任意一点 p p p Q Q Q的最近距离,其中距离度量 d ( ⋅ , ⋅ ) d\left( \cdot ,\cdot \right) d(,)定义了距离场的属性,指示函数

I Q ( q ) = { 0 , i f q ∈ Q ∞ , o t h e r w i s e I_Q\left( q \right) =\begin{cases} 0, \mathrm{if} q\in Q\\ \infty , \mathrm{otherwise}\\\end{cases} IQ(q)={0,ifqQ,otherwise

常用的 d ( ⋅ , ⋅ ) d\left( \cdot ,\cdot \right) d(,)

  • 欧氏距离,此时称为欧氏距离变换(Euclidean Distance Transform, EDT)
  • 曼哈顿距离,此时称为曼哈顿距离变换(Manhattan Distance Transform, MDT)
  • 切比雪夫距离,此时称为切比雪夫距离变换(Chebyshev Distance Transform, CDT)

2 欧氏距离场计算原理

n n n维距离场可以通过一维距离场迭代计算得到,因此只需要讨论一维EDT的计算即可。如下图左侧所示为初始计算轴的EDT计算,右侧所示为更一般的情况,此时障碍物处的采样函数叠加了前轴计算信息。

在这里插入图片描述

注意到 O \mathcal{O} O定义了一系列以障碍物 q ∈ O q\in \mathcal{O} qO为顶点的抛物线,而 ∀ p ∈ G \forall p\in \mathcal{G} pG在系列抛物线形成的下包络的投影组成了距离场 D f ( p ) \mathcal{D} _f\left( p \right) Df(p),而下包络的计算与抛物线交点有关。联立两条抛物线 ( s − q ) 2 + f ( q ) = ( s − r ) 2 + f ( r ) \left( s-q \right) ^2+f\left( q \right) =\left( s-r \right) ^2+f\left( r \right) (sq)2+f(q)=(sr)2+f(r)可得

s = ( f ( r ) + r 2 ) − ( f ( q ) + q 2 ) 2 r − 2 q s=\frac{\left( f\left( r \right) +r^2 \right) -\left( f\left( q \right) +q^2 \right)}{2r-2q} s=2r2q(f(r)+r2)(f(q)+q2)

即任意两条抛物线有且仅有一个交点 s s s。设 K \mathcal{K} K为实际组成下包络的抛物线集合, v ( k ) v\left( k \right) v(k)表示其中第 k k k条抛物线的顶点, z ( k ) z\left( k \right) z(k)表示第 k k k条和第 k − 1 k-1 k1条抛物线的交点,区间 [ z ( k ) , z ( k + 1 ) ) \left[ z\left( k \right) ,z\left( k+1 \right) \right) [z(k),z(k+1))表示第 k k k条抛物线的下包络范围。在遍历求解下包络过程中,对于新的抛物线 e e e,其与 K \mathcal{K} K中最新的一条抛物线 k k k的交点 s s s有两种情况:

  • s > z ( k ) s>z\left( k \right) s>z(k),则将 e e e添加到 K \mathcal{K} K中并更新 v ( k ) v\left( k \right) v(k) z ( k ) z\left( k \right) z(k)
  • s ⩽ z ( k ) s\leqslant z\left( k \right) sz(k),则第 k k k条抛物线不参与构成下包络,应从 K \mathcal{K} K中删除并重新计算 e e e与新的第 k k k条抛物线的交点直至 s s s z ( k ) z(k) z(k)右侧;

算法流程如表所示

在这里插入图片描述

3 双线性插值与欧式梯度场

在求解梯度过程中,需要计算离散距离场的线性插值函数。以二维环境的双线性插值为例,设已知离散空间有四点坐标为 A ( x 1 , y 1 ) A\left( x_1,y_1 \right) A(x1,y1) B ( x 2 , y 1 ) B\left( x_2,y_1 \right) B(x2,y1) C ( x 1 , y 2 ) C\left( x_1,y_2 \right) C(x1,y2) D ( x 2 , y 2 ) D\left( x_2,y_2 \right) D(x2,y2),对其中任意一点 P P P,首先在 x x x方向上插值

{ f ( R 1 ) = f ( x , y 1 ) = x 2 − x x 2 − x 1 f ( A ) + x − x 1 x 2 − x 1 f ( B ) f ( R 2 ) = f ( x , y 2 ) = x 2 − x x 2 − x 1 f ( C ) + x − x 1 x 2 − x 1 f ( D ) \begin{cases} f\left( R_1 \right) =f\left( x,y_1 \right) =\frac{x_2-x}{x_2-x_1}f\left( A \right) +\frac{x-x_1}{x_2-x_1}f\left( B \right)\\ f\left( R_2 \right) =f\left( x,y_2 \right) =\frac{x_2-x}{x_2-x_1}f\left( C \right) +\frac{x-x_1}{x_2-x_1}f\left( D \right)\\\end{cases} {f(R1)=f(x,y1)=x2x1x2xf(A)+x2x1xx1f(B)f(R2)=f(x,y2)=x2x1x2xf(C)+x2x1xx1f(D)

再基于插值点 R 1 R_1 R1 R 2 R_2 R2进行 y y y方向的插值

f ( P ) = f ( x , y ) = y 2 − y y 2 − y 1 f ( R 1 ) + y − y 1 y 2 − y 1 f ( R 2 ) f\left( P \right) =f\left( x,y \right) =\frac{y_2-y}{y_2-y_1}f\left( R_1 \right) +\frac{y-y_1}{y_2-y_1}f\left( R_2 \right) f(P)=f(x,y)=y2y1y2yf(R1)+y2y1yy1f(R2)

展开可得矩阵形式

f ( x , y ) = 1 ( x 2 − x 1 ) ( y 2 − y 1 ) [ x 2 − x x − x 1 ] T [ f ( A ) f ( C ) f ( B ) f ( D ) ] [ y 2 − y y − y 1 ] f\left( x,y \right) =\frac{1}{\left( x_2-x_1 \right) \left( y_2-y_1 \right)}\left[ \begin{array}{c} x_2-x\\ x-x_1\\\end{array} \right] ^T\left[ \begin{matrix} f\left( A \right)& f\left( C \right)\\ f\left( B \right)& f\left( D \right)\\\end{matrix} \right] \left[ \begin{array}{c} y_2-y\\ y-y_1\\\end{array} \right] f(x,y)=(x2x1)(y2y1)1[x2xxx1]T[f(A)f(B)f(C)f(D)][y2yyy1]

在这里插入图片描述

对于离散栅格坐标而言, x 2 − x 1 = y 2 − y 1 = 1 x_2-x_1=y_2-y_1=1 x2x1=y2y1=1,设 Δ x = x − x 1 \varDelta x=x-x_1 Δx=xx1 Δ y = y − y 1 \varDelta y=y-y_1 Δy=yy1,则插值函数简化为

f ( Δ x , Δ y ) = [ 1 − Δ x Δ x ] T [ f ( A ) f ( C ) f ( B ) f ( D ) ] [ 1 − Δ y Δ y ] f\left( \varDelta x,\varDelta y \right) =\left[ \begin{array}{c} 1-\varDelta x\\ \varDelta x\\\end{array} \right] ^T\left[ \begin{matrix} f\left( A \right)& f\left( C \right)\\ f\left( B \right)& f\left( D \right)\\\end{matrix} \right] \left[ \begin{array}{c} 1-\varDelta y\\ \varDelta y\\\end{array} \right] f(Δx,Δy)=[1ΔxΔx]T[f(A)f(B)f(C)f(D)][1ΔyΔy]

则函数 f f f在任意一点的梯度为

∇ f ( Δ x , Δ y ) = [ ∂ f ( Δ x , Δ y ) ∂ Δ x ∂ f ( Δ x , Δ y ) ∂ Δ y ] = [ [ − 1 1 ] T [ f ( A ) f ( C ) f ( B ) f ( D ) ] [ 1 − Δ y Δ y ] [ 1 − Δ x Δ x ] T [ f ( A ) f ( C ) f ( B ) f ( D ) ] [ − 1 1 ] ] \nabla f\left( \varDelta x,\varDelta y \right) =\left[ \begin{array}{c} \frac{\partial f\left( \varDelta x,\varDelta y \right)}{\partial \varDelta x}\\ \frac{\partial f\left( \varDelta x,\varDelta y \right)}{\partial \varDelta y}\\\end{array} \right] =\left[ \begin{array}{c} \left[ \begin{array}{c} -1\\ 1\\\end{array} \right] ^T\left[ \begin{matrix} f\left( A \right)& f\left( C \right)\\ f\left( B \right)& f\left( D \right)\\\end{matrix} \right] \left[ \begin{array}{c} 1-\varDelta y\\ \varDelta y\\\end{array} \right]\\ \left[ \begin{array}{c} 1-\varDelta x\\ \varDelta x\\\end{array} \right] ^T\left[ \begin{matrix} f\left( A \right)& f\left( C \right)\\ f\left( B \right)& f\left( D \right)\\\end{matrix} \right] \left[ \begin{array}{c} -1\\ 1\\\end{array} \right]\\\end{array} \right] f(Δx,Δy)=[Δxf(Δx,Δy)Δyf(Δx,Δy)]=[11]T[f(A)f(B)f(C)f(D)][1ΔyΔy][1ΔxΔx]T[f(A)f(B)f(C)f(D)][11]

4 仿真实现

4.1 ROS C++实现

核心代码如下所示

GradientLayer::updateCosts(
  nav2_costmap_2d::Costmap2D & master_grid, int min_i, int min_j,
  int max_i,
  int max_j)
{
  if (!enabled_) {
    return;
  }

  unsigned char * master_array = master_grid.getCharMap();
  unsigned int size_x = master_grid.getSizeInCellsX(), size_y = master_grid.getSizeInCellsY();
  // Fixing window coordinates with map size if necessary.
  min_i = std::max(0, min_i);
  min_j = std::max(0, min_j);
  max_i = std::min(static_cast<int>(size_x), max_i);
  max_j = std::min(static_cast<int>(size_y), max_j);

  // Simply computing one-by-one cost per each cell
  int gradient_index;
  for (int j = min_j; j < max_j; j++) {
    // Reset gradient_index each time when reaching the end of re-calculated window
    // by OY axis.
    gradient_index = 0;
    for (int i = min_i; i < max_i; i++) {
      int index = master_grid.getIndex(i, j);
      // setting the gradient cost
      unsigned char cost = (LETHAL_OBSTACLE - gradient_index*GRADIENT_FACTOR)%255;
      if (gradient_index <= GRADIENT_SIZE) {
        gradient_index++;
      } else {
        gradient_index = 0;
      }
      master_array[index] = cost;
    }
  }
}

4.2 Python实现

欧氏距离场核心代码

def compute(self, f_get, mat: np.ndarray, dim: int) -> np.array:
	"""
	Compute distance field along one-dimension baseon sample funciton f_get.
	
	Parameters:
		f_get (function): sample function
		mat (np.ndarray): the matrix to transform
		dim (int): the dimension to transform
	
	Returns:
		df (np.array): the distance field along dim-dimension
	"""
	# initialzation
	k = 0
	n = mat.shape[dim]
	v, z = [0 for _ in range(n)], [0 for _ in range(n + 1)]
	z[0], z[1] = -self.INF, self.INF

	# envelope
	for q in range(1, n):
		s = ((f_get(v[k]) + v[k] ** 2) - (f_get(q) + q ** 2)) / (2 * (v[k] - q))
		while s <= z[k]:
			k -= 1
			s = ((f_get(v[k]) + v[k] ** 2) - (f_get(q) + q ** 2)) / (2 * (v[k] - q))
		k += 1
		v[k] = q
		z[k], z[k + 1] = s, self.INF

	# distance calculation
	k = 0
	edf = np.zeros((n, ))
	for q in range(n):
		while z[k + 1] < q:
			k += 1
		edf[q] = (q - v[k]) ** 2 + f_get(v[k])
	
	return edf

欧氏梯度场核心代码:

def gradient(self, df: np.ndarray, x: float, y: float) -> np.array:
	"""
	To obtain the gradient at (x, y) in the distance field through bilinear interpolation.
	
	Parameters:
	   df (np.ndarray): the distance field
	   x/y (float): the query coordinate
	
	Returns:
	   g(x, y): the gradient at (x, y)
	"""
	m, n = df.shape
	x, y = max(min(n - 1, x), 0), max(min(m - 1, y), 0)
	
	xi, yi = int(x), int(y)
	dx, dy = x - xi, y - yi
	
	xi, yi = max(min(n - 1, xi), 0), max(min(m - 1, yi), 0)
	xp, yp = max(min(n - 1, xi + 1), 0), max(min(m - 1, yi - 1), 0)
	bl, br = df[yi, xi], df[yi, xp]
	tl, tr = df[yp, xi], df[yp, xp]
	
	return np.array([
	   (1 - dy) * (br - bl) + dy * (tr + tl),
	   -((1 - dx) * (tl - bl) + dx * (tr - br))
	])

效果如下所示
在这里插入图片描述

一个实际地图的案例如下

在这里插入图片描述

完整工程代码请联系下方博主名片获取


🔥 更多精彩专栏

  • 《ROS从入门到精通》
  • 《Pytorch深度学习实战》
  • 《机器学习强基计划》
  • 《运动规划实战精讲》

👇源码获取 · 技术交流 · 抱团学习 · 咨询分享 请联系👇

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1830793.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Easyui Datagrid 解决页面加载重复请求2次后端问题

现象 在页面刚刚打开时&#xff0c;页面加载完毕&#xff0c;显示查询数据&#xff0c;连续请求网络2次&#xff0c; 第一次是只携带了 分页参数&#xff1b; 第二次携带分页和查询参数 问题出现原因 html代码中利用class声明了datagrid&#xff0c;导致easyUI解析class代…

【Android】安卓开发的前景

人不走空 &#x1f308;个人主页&#xff1a;人不走空 &#x1f496;系列专栏&#xff1a;算法专题 ⏰诗词歌赋&#xff1a;斯是陋室&#xff0c;惟吾德馨 目录 &#x1f308;个人主页&#xff1a;人不走空 &#x1f496;系列专栏&#xff1a;算法专题 ⏰诗词歌…

PyQt5 生成py文件不能运行;pushButton点击事件;QTextEdit 获取输入框内容

目录 cant open file c.pyuic: c.pyuic $FileName$ -o $FileNameWithoutExtension$.p PyQt5 生成py文件不能运行 pushButton点击事件 QTextEdit 获取输入框内容 整体运行代码: Creating a Qt Widget Based Application | Qt Creator Manual cant open file c.pyuic: c.…

【论文阅读】-- DeepVisualInsight: 深度分类训练时空因果关系的时间旅行可视化

中文标题 摘要引言动机举例相关工作时间旅行可视化的属性符号定义邻居保护属性边界距离保持属性逆投影保持属性暂时保存属性 方法 δ \delta δ-边界估计(k)-BAVR综合体建设逆投影保持时间连续性 评估案例分析结论参考文献 摘要 了解深度学习模型的预测在训练过程中是如何形成…

Anritsu 安立 MS2720T 手持式频谱分析仪

Anritsu 安立 MS2720T 手持式频谱分析仪 频谱分析仪功能特点 频率范围&#xff1a; 9 kHz ~ 9 GHz、13 GHz 和 20 GHz测量&#xff1a; 占用的带宽、信道功率、ACPR、C/I、发射波罩、杂散发射、场强适用于 LTE(FDD & TDD)、CDMA、W-CDMA、WiMAX、GSM 和 TD-SCDMA 的 3G 和…

vue简介实例

先看样式 再看代码 <div v-else class"relative mt-4 h-44 cursor-pointer overflow-hidden rounded-xl"><divclass"absolute flex h-44 w-full blur-lg":style"{ backgroundImage: url(${currentSongList.list[0]?.coverImgUrl}) }"…

【Stable Diffusion教程】AI绘画工具SD如何安装使用?三种方法带你轻松上手!(附安装包和云端部署教程)

大家好&#xff0c;我是向阳 AI绘画专业工具Stable Diffusion在哪里用怎么安装&#xff1f;这一期给大家介绍三种使用SD的方法&#xff0c;无论你有没有专业显卡都能轻松上手SD哦&#xff5e; 一、SD本地部署秋葉安装包安装方法 如果你有进一步的需求&#xff0c;想要学习SD…

Linux下Shell脚本基础知识

主要参考视频&#xff1a; 这可能是B站讲的最好的Linux Shell脚本教程&#xff0c;3h打通Linux-shell全套教程&#xff0c;从入门到精通完整版_哔哩哔哩_bilibili 主要参考文档&#xff1a; Shell 教程 | 菜鸟教程 (runoob.com) Bash Shell教程 (yiibai.com) 先用视频入门&…

Python数据库编程指南:连接与操作SQLite与MySQL

目录 一、引言 二、SQLite数据库连接与操作 &#xff08;一&#xff09;安装SQLite库 &#xff08;二&#xff09;建立数据库连接 &#xff08;三&#xff09;执行SQL语句 &#xff08;四&#xff09;注意事项 三、MySQL数据库连接与操作 &#xff08;一&#xff09;安…

使用 Dapper 创建 Blazor Server SPA

介绍 Blazor 是 Microsoft 构建的一个新框架&#xff0c;用于使用 .NET 代码库创建交互式客户端 Web UI。 Dapper 是一个微型 ORM&#xff08;对象关系映射器&#xff09;&#xff0c;可帮助将本机查询输出映射到领域类。它是由 StackOverflow 团队构建并作为开源发布的高性能…

编写工具调用windeployqt+ldd为msys2 Qt应用程序生成完整发布包

文章目录 概要整体架构流程技术名词解释技术细节1. 界面设计2. 递归枚举文件3. 运行windeployqt4. 运行ldd并拷贝文件5. 驱动流程 小结完整工程链接 概要 在windows下&#xff0c;动态链接库一直是发布Qt程序最为头痛的问题。在msys2环境下&#xff0c;尤其如此。msys2的winde…

ArcGIS Pro SDK (三)Addin控件 3 事件功能类

22 ArcGIS Pro 放置处理程序 22.1 添加控件 22.2 Code 放置处理程序可以实现文件拖动放置、TreeVIew、ListBox等控件拖动放置功能&#xff0c;此处新建一个停靠窗并添加一个TreeVIew&#xff0c;实现节点拖动事件&#xff1b; TestDropHandlerDockpane.xaml <UserContro…

开源项目推荐

这个资源列表集合了.NET开发领域的优秀工具、库、框架和软件等&#xff0c; 如果您目前研究开源大模型项目&#xff0c;请参考热门开源大模型项目推荐链接如下&#xff1a;https://blog.csdn.net/hefeng_aspnet/article/details/139669116 欢迎各位小伙伴收藏、点赞、留言、评论…

一文学会消息中间件的基础知识

什么是消息队列 队列数据结构 我们都学习过数据结构与算法相关的内容,消息队列从数据结构来看,就是一个由链表或是数组构成的一个先进先出的数据容器。由链表实现还是数组实现都没关系,它只要满足数据项是先进先出的特点,那么就可以认为它是一个队列结构。队列是只允许在…

个人云服务器已经被安全合规等卡脖子 建议不要买 买了必定后悔 安全是个大问题 没有能力维护

我的想法 自己买一个云服务器&#xff0c;先自己边做边学习&#xff0c;向往硅谷精神&#xff0c;财富与自由。如果能赚钱&#xff0c;就开个公司。这次到期就放弃了。 我前前后后6年花6000多元买云服务器。业余花了无数的精力&#xff0c;从2018到现在 &#xff0c;也没有折…

618购物节数码好物到底怎么选?盘点几款可闭眼入的数码好物分享

随着618购物节的热烈氛围逐渐升温&#xff0c;数码产品的选购成为了许多消费者关注的焦点。面对市场上层出不穷的新品和优惠&#xff0c;如何选择一款既符合自己需求又物超所值的数码好物&#xff0c;成为了不少人的难题&#xff0c;今天&#xff0c;我们就为大家带来几款精心挑…

QT Redis 中的实现发布/订阅功能(全网最全的教程)

Redis 介绍 Redis发布/ 订阅系统 是 Web 系统中比较常用的一个功能。简单点说就是 发布者发布消息&#xff0c;订阅者接收消息&#xff0c;这有点类似于我们的报纸/ 杂志社之类的 实现代码 #ifndef MAINWIDGET_H #define MAINWIDGET_H#include <QWidget> #include <…

【C++提高编程-09】----C++ STL之常用排序算法

&#x1f3a9; 欢迎来到技术探索的奇幻世界&#x1f468;‍&#x1f4bb; &#x1f4dc; 个人主页&#xff1a;一伦明悦-CSDN博客 ✍&#x1f3fb; 作者简介&#xff1a; C软件开发、Python机器学习爱好者 &#x1f5e3;️ 互动与支持&#xff1a;&#x1f4ac;评论 &…

基于单片机的无线遥控自动翻书机械臂设计

摘 要&#xff1a; 本设备的重点控制部件为单片机&#xff0c;充分实现了其自动化的目的。相关研究表明&#xff0c;它操作简单便捷&#xff0c;使残疾人在翻书时提供了较大的便利&#xff0c;使用价值性极高&#xff0c;具有很大的发展空间。 关键词&#xff1a; 机械臂&…

Django后台忘记管理员的账号

使用命令启动项目&#xff1a; python manage.py runserver输入后缀/admin&#xff0c;进入后台管理员&#xff0c;如果此时忘记你先前设置的用户名与密码怎么办&#xff1f; 终端输入&#xff1a; python manage.py shell 输入以下内容&#xff0c;并查看返回结果&#xff…