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

目录

  • 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 cellint 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 costunsigned 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 functionmat (np.ndarray): the matrix to transformdim (int): the dimension to transformReturns:df (np.array): the distance field along dim-dimension"""# initialzationk = 0n = mat.shape[dim]v, z = [0 for _ in range(n)], [0 for _ in range(n + 1)]z[0], z[1] = -self.INF, self.INF# envelopefor 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 -= 1s = ((f_get(v[k]) + v[k] ** 2) - (f_get(q) + q ** 2)) / (2 * (v[k] - q))k += 1v[k] = qz[k], z[k + 1] = s, self.INF# distance calculationk = 0edf = np.zeros((n, ))for q in range(n):while z[k + 1] < q:k += 1edf[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 fieldx/y (float): the query coordinateReturns:g(x, y): the gradient at (x, y)"""m, n = df.shapex, y = max(min(n - 1, x), 0), max(min(m - 1, y), 0)xi, yi = int(x), int(y)dx, dy = x - xi, y - yixi, 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.mzph.cn/diannao/28778.shtml

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

相关文章

深度学习之激活函数

激活函数&#xff08;Activation Function&#xff09;是一种添加到人工神经网络中的函数&#xff0c;旨在帮助网络学习数据中的复杂模式。在神经元中&#xff0c;输入的input经过一系列加权求和后作用于另一个函数&#xff0c;这个函数就是这里的激活函数。 1. 为什么需要激活…

鸿蒙开发:【线程模型】

线程模型 线程类型 Stage模型下的线程主要有如下三类&#xff1a; 主线程 执行UI绘制。管理主线程的ArkTS引擎实例&#xff0c;使多个UIAbility组件能够运行在其之上。管理其他线程的ArkTS引擎实例&#xff0c;例如使用TaskPool&#xff08;任务池&#xff09;创建任务或取消…

示例:WPF中应用MarkupExtention自定义IValueConverter

一、目的&#xff1a;应用MarkupExtention定义IValueConverter&#xff0c;使得应用起来更简单和高效 二、实现 public abstract class MarkupValueConverterBase : MarkupExtension, IValueConverter{public abstract object Convert(object value, Type targetType, object …

数字化转型对企业有什么价值?

数字化转型对企业有什么价值&#xff1f; 1. 信息共享 很多业务设计和管理规划&#xff0c;通常需要综合多个业务部门和业务专题的数据。 如果企业的数据和信息在位置分布上非常分散&#xff0c;就很难充分利用企业积累的数据资源&#xff0c;并将其用于有效的管理决策和业务…

零基础直接上手java跨平台桌面程序,使用javafx(七)用户操作界面探讨,这个系列结束

GUI&#xff0c;我们还是喜欢web。如果javafx有像wpf的WebView2差不多的功能&#xff0c;我们就开心了scene builder中拖出一个webview&#xff0c;然后再回到代码中。发现<?import javafx.scene.web.*?>是红色的&#xff0c;我们缺少配置。于是在pom.xml中添加JavaFX依…

Nodejs 第七十七章(MQ高级)

MQ介绍和基本使用在75章介绍过了&#xff0c;不再重复 MQ高级用法-延时消息 什么是延时消息? Producer 将消息发送到 MQ 服务端&#xff0c;但并不期望这条消息立马投递&#xff0c;而是延迟一定时间后才投递到 Consumer 进行消费&#xff0c;该消息即延时消息 插件安装 R…

[C++] 从零实现一个ping服务

&#x1f4bb;文章目录 前言ICMP概念报文格式 Ping服务实现系统调用函数具体实现运行测试 总结 前言 ping命令&#xff0c;因为其简单、易用等特点&#xff0c;几乎所有的操作系统都内置了一个ping命令。如果你是一名C初学者&#xff0c;对网络编程、系统编程有所了解&#xff…

学会python——读取大文本文件(python实例六)

目录 1、认识Python 2、环境与工具 2.1 python环境 2.2 Visual Studio Code编译 3、读取大文本文件 3.1 代码构思 3.2 代码示例 3.3 运行结果 4、总结 1、认识Python Python 是一个高层次的结合了解释性、编译性、互动性和面向对象的脚本语言。 Python 的设计具有很强…

电脑内存怎么看?5个秘诀,轻松查看内存!

“新买了一台电脑&#xff0c;想查看一下我电脑的内存&#xff0c;大家可以分享一下查看方法吗&#xff1f;” 当我们谈论电脑的性能时&#xff0c;内存无疑是一个不容忽视的关键组件。然而&#xff0c;对于许多普通用户来说&#xff0c;如何查看电脑内存的大小、类型以及使用情…

跳舞电动机器人单片机方案

这款机器人形状智能电子玩具是一款集娱乐、教育和互动于一身的高科技产品。它的主要功能包括&#xff1a; 1、智能对话&#xff1a;机器人可以进行简单的对话&#xff0c;回答用户的问题&#xff0c;提供有趣的互动体验。 2、前进、后退、左转、右转、滑行&#xff1a;机器人…

BERT报错记录

一、加载数据集下载失败 报错&#xff1a; TimeoutError: [WinError 10060] 由于连接方在一段时间后没有正确答复或连接的主机没有反应&#xff0c;连接尝试失败。urllib3.exceptions.NewConnectionError: <urllib3.connection.HTTPSConnection object at 0x00000241F9AD4…

Element UI 一键校验多表单(v-for循环表单,异步校验规则,v-for 中的 ref 属性,避坑 forEach 不支持异步 await )

需求描述 表单为数组 v-for 循环得到的多表单&#xff0c;如可自由增删的动态表单表单中存在异步校验规则&#xff0c;如姓名需访问接口校验是否已存在点击提交按钮&#xff0c;需一键校验所有表单&#xff0c;仅当所有表单都通过校验&#xff0c;才能最终提交到后台 效果预览 …

亚马逊新店如何实现高效流量转化?自养号测评深度解析与实用策略

在亚马逊平台上&#xff0c;自养号测评是一种通过卖家自行控制的海外买家账号对商品进行评价的方法&#xff0c;旨在提高商品的排名和流量。 亚马逊的自养号测评是指卖家通过使用在海外真实环境注册的买家账号&#xff0c;代替真实买家对商品进行测评。账号由卖家自己管理&…

电子传真怎么在国产系统上使用?一文看懂网络传真信创方案

国产化浪潮正在逐步深入&#xff0c;越来越多的企业开始关注如何在国产系统上高效、安全地使用办公软件&#xff0c;电子传真系统也不例外。 作为网络电子传真领域的重要品牌&#xff0c;EastFax也对原Windows电子传真系统进行了信创改造&#xff0c;全面支持国产化操作系统、…

串扰(一)

一、说明 串扰应该算比较常见的信号完整性问题了&#xff0c;一般是指由于走线较近&#xff0c;传输信号时在临线上产生耦合噪声的现象。串扰的原因是由于电场和磁场的耦合&#xff0c;我们经常用耦合电容和耦合电感模型进行问题分析。 本文是基于被攻击线阻抗匹配的情形下计…

Vatee万腾平台,让智能更懂你

在数字化浪潮席卷全球的今天&#xff0c;智能科技已经渗透到我们生活的方方面面。然而&#xff0c;真正的智能不仅仅是技术的堆砌&#xff0c;更是对人性需求的深刻理解和满足。Vatee万腾平台&#xff0c;正是这样一个让智能更懂你的平台&#xff0c;它以其独特的方式&#xff…

PyCharm配置教程,手把手教你如何配置

文章目录 引言1. 安装 PyCharm1.1 下载和安装1.2 初次启动 2. 基本配置2.1 设置界面2.2 常用配置项 3. 项目配置3.1 创建新项目3.2 配置解释器 4. 虚拟环境配置4.1 创建虚拟环境4.2 使用已有虚拟环境4.3 管理依赖 5. 插件和扩展5.1 安装插件5.2 推荐插件 6. 调试配置6.1 配置调…

【计算机毕业设计】234基于微信小程序的中国各地美食推荐平台

&#x1f64a;作者简介&#xff1a;拥有多年开发工作经验&#xff0c;分享技术代码帮助学生学习&#xff0c;独立完成自己的项目或者毕业设计。 代码可以私聊博主获取。&#x1f339;赠送计算机毕业设计600个选题excel文件&#xff0c;帮助大学选题。赠送开题报告模板&#xff…

可穿戴设备:苹果“吃老底”、华为“忙复苏”、小米“再扩容”

配图来自Canva可画 随着产品功能的创新&#xff0c;可穿戴设备不再被简单地视为手机的延伸&#xff0c;而是被当成一种独立的、具有独特功能和优势的产品&#xff0c;受到了越来越多人的青睐。 一方面&#xff0c;技术的进步使得可穿戴设备在功能、性能和使用体验上得到显著提…

Golang | Leetcode Golang题解之第160题相交链表

题目&#xff1a; 题解&#xff1a; func getIntersectionNode(headA, headB *ListNode) *ListNode {if headA nil || headB nil {return nil}pa, pb : headA, headBfor pa ! pb {if pa nil {pa headB} else {pa pa.Next}if pb nil {pb headA} else {pb pb.Next}}retu…