碰撞检测——GJK算法

article/2025/9/25 1:50:11

目录

碰撞检测——GJK算法

1.GJK算法的原理及思想

1.1 Minkowski Sum(明可夫斯基和)

1.2 Simplex

1.3 support函数

1.4 构建Simplex

2. GJK算法步骤

3. GJK算法的优缺点分析

4. GJK算法与其他相关算法的比较分析

4.1 GJK算法和SAT算法的比较

4.2 GJK算法和EPA算法的比较

参考资料


碰撞检测——GJK算法

1.GJK算法的原理及思想

GJK算法是由 Gilbert,johnson和 Keerthi 3人在1988年共同开发的一类迭代算法。GJK算法的输入为两物体的顶点集,通过有限次数的迭代后,最后输出结果为两物体之间的欧氏距离。根据两物体之间 的欧氏距离,可进行碰撞检测。当两物体之间的距离等于或者小于零时,可判定两物体发生碰撞。基于距离跟踪的Gilbert-Johnson-Keerth(简称GJK)碰撞算法是通过支持映射来计算空间中两个凸体间距的渐进算法。

1.1 Minkowski Sum明可夫斯基和)

GJK算法中使用了明可夫斯基和的概念。假设有两个物体,它们的明可夫斯基和就是物体1上的所有点和物体2上的所有点的和集。用公式表示就是:

A + B = {a + b|a∈A, b∈B}

如果两个物体都是凸体,它们的明可夫斯基和也是凸体。

对于减法,明可夫斯基和的概念也成立,这时也可称作明可夫斯基差。

A – B = A + (-B) = {a + (– b)|a∈A, b∈B} = {a – b)|a∈A, b∈B}

如果两个形状重叠,进行Minkowski Sum后的形状包含原点。Minkowski Sum的运算是shape A的每个顶点和shape B的所有顶点求和(或求差)。所得到点的外包络即是运算所得形状。

eec058d49fa143a58789092e620e56e9.png

2bb8f37e53e84367bb0f95b329ff7776.png

1.2 Simplex

单纯形是代数拓扑中的基本概念,单纯形是三角形和四面体的一种泛化,一个k 维单纯形是指包含(k+1)个节点的凸多面体。不需要计算Minkowski Sum,只需要计算Minkowski Sum是否包含原点,如果包含原点则两个形状重叠,否则不重叠。在Minkowski Sum得到的形状中迭代的构建Simplex,判断Simplex是否包含原点,包含则重叠,否则不重叠。

1.3 support函数

support函数返回两个形状Minkowski Sum的一个点用以构建Simplex,shape1的点减去shape2的点可以得到一个Minkowski Sum的一个点,但我们不希望得到重复的点。使用support函数得到shape在一个direction(方向)上的最远点,然后在不同的方向上得到另一个不同的点。在direction上选择最远点可以使生成的Simplex的面积最大化以增加包含原点的几率增加算法收敛速度。使用这种方法得到的点在Minkowski Sum的边上,因此如果得到的点不包含原点则Minkowski Sum不包含原点。

Public Point support(Shape shape1, Shape shape2, Vector d) {// d is a vector direction(doesn't have to normalized)// get points on the edge of the shapes in opposite directionPoint p1 = shape1.getFarthestPointInDirection(d);Point p2 = shape2.getFarthestPointInDirection(d.negative());// perform the Minkowski SumPoint p3 = p1.subtract(p2);// p3 is now a point in Minkowski space on the edge of the Minkowski Differencereturn p3;
}

1.4 构建Simplex

首先选择三个点构造Simplex,如果包含原点,则两个多边形重叠,否则重新选择点构建新的Simplex。由support函数可知,需要一个direction来选择Minkowski Sum点,任意的direction都是可以的,但是选择两个多边形的中心点的向量方向是较优的选择。

Vector d = // choose a search direction
// get the first Minkowski Difference point
Simplex.add(support(A, B, d));
// negate d for the next point
d.negate();
// start looping
while (true) {// add a new point to the simplex because we haven't terminated yetSimplex.add(support(A, B, d));// make sure that the last point we added actually passed the originif (Simplex.geLast().dot(d) <= 0) {// if the point added last was not past the origin in the direction of d // then the Minkowski Sum cannot possibly contain the origin since// the last point added is on the edge of Minkowski Differencereturn false;} else {// otherwise we need to determine if the origin is in the current simplexif (Simplex.contains(ORIGIN)) {// if it does then we know there is a collisionreturn true;} else {// otherwise we cannot be certain so find the edge who is closest to the// origin and use its normal (in the direction of the origin) as the new// d and continue the loopd = getDirection(Simplex);}}
}

     

Simplex.geLast().dot(d) <= 0的解释:

Simplex最新得到的点是Minkowski Sum在给定方向上的最远点,如果这个点没有enclose原点,则Minkowski Sum不会包含原点。如下图,direction是(  1 , 0 ) (-1, 0)(-1,0),点p ( 1, - 4 ) p(1, 4)p(1, -4)是在direction上的最远点。点p在d上的投影是-1,原点在d dd上的投影是0,由于p是Minkowski Sum是最远点,所以其不会包含原点

b8272ed262764b30ae17c34fd4c428ab.png

当得到两个Minkowski Sum点时,如图-4,B是第一个点,A 是第二个点,A 和B是Minkowski Sum包络上的点,如果origin在R1或者R4区域,则Minkowski Sum不包含原点,Simplex.geLast().dot(d) <= 0返回true。由图可知,origin在R3区域内,因此需要计算AB的petualar vector应该指向R3。

AB=B−A

AO=O−A

perp=(AB×AO)×AB

如图-5,此时support函数得到新的Minkowski Sum点A,点B是第二点(图-4中的A ),点C 是第一个点(图-4中的B),origin可能存在于R3、R4和R5中,计算( A C × A B ) × A B生成ABperp,计算ABprep⋅(AO)判断origin是否在R4,计算(AB×AC)×AC生成ACperp,计算ACperp⋅(AO)判断origin是否在R3。

02e053b90daf4b38a638ffb05fa5c6f9.png

715dfcca043c48348c45515b796b6b63.png

 

  

Vector d = // choose a search direction
// get the first Minkowski Sum point
Simple.add(support(A, B, d));
// nagate d for the next point
d.negate();
// start loop
while (true) {// add a new point to the simplex because we haven't terminated yetSimple.add(support(A, B d));// make sure that the last point added actually passed the originif (Simple.getLast().dot(d) <= 0) {// if the point added last was not past the origin in the direction of d // then the Minkowski Sum cannot possibly contain the origin since// the last point added is on the edge of Minkowski Differencereturn false;} else {// otherwise determine if the origin is in the current simplexif (containsOrigin(Simple, d)) {// if it does, there is a collisionreturn true;}}
}public boolean containOrign(Simples s, Vector d) {// get the last point added to the simplexa = Simplex.getLast(); // the last point// compute AO (same thing as -A)ao = a.negate();if (Simplex.points.size == 3) {// in triangle case// get point B and Cb = Simplex.getB(); // the second pointc = Simplex.getC(); // the first point// compute the edgesab = b - a;ac = c - a;// compute the normalsabPerp = tripleProduct(ac, ab, ab);acPerp = tripltProdect(ab, ac, ac);// is the origin in R4if (adPerp.dot(ao) > 0) {// remove point CSimplex.remove(c);// set the new direction to ABPerpd.set(abPerp);} else {// is the origin in R3if (acPerp.dot(ao) > 0) {// remove point BSimples.remove(b);// set the new direction to ACPerpd.set(acPerp);} else {// otherwise origin in R5, there is a collisionreturn true;}}} else {// in line segment caseb = Simplex.getB();// compute ABab = b -a;// get the perp to AB in the direction of the originabPerp = tripleProduct(ab, ao, ab);// set the direction to ABPerpd.set(abPerp);}return false;
}

 

2. GJK算法步骤

1.选取初始方向,初始方向可以是两个多边形的中心点构成的矢量,也可以是两个多边形各自选取一个顶点构成的

矢量,还可以是给定的任意矢量;

2.根据初始方向,用Support函数得到第一个support点,并放到单纯形(simplex)中;

3.将初始方向取反,作为下一-次迭代方向;

4.循环开始:

a)根据迭代方向和Support函数计算出一个新的support点;

b)若新的support点与迭代方向的点乘结果小于0 ,说明在这个迭代方向上,已经无法找到一个能够跨越原

点的support点了。也就是说,无法组成一个能够包含原点的单纯形。 即两个多边形不相交,函数退出;

c)若新的support点能够跨越原点,则将其加到simplex中;

d)更新单纯形和迭代方向,并判断simplex是否包含原点。这时simplex有两个点或三个点:

若为两个点,则这两个点构成一条直线,以该直线的垂线朝向原点的方向,作为下一次迭代方向;

若为三个点,则需要判断这三个点组成的三角形是否包含原点。若不包含,则保留离原点最近的边上的两个点,同样以这两个点的直线的垂线朝向原点的方向,作为下一次迭代方向。

回到循环的开始步骤a。

3. GJK算法的优缺点分析

优点:

基于GJK模型的算法有快速,易实施,且适用于多种凸体的优点。

它是一种迭代方法,但是收敛速度非常快,如果使用最终的穿透/分离向量进行约束,则可以在几乎恒定的时间内运行。

它可以支持实现“support ”函数的任何形状。因此不需要增加特殊的代码或算法来处理弯曲的形状。

缺点:

仅能在凸包上运行,数学模型复杂(需要有一定的线性空间,线性代数的知识),且难以理解。

线段是一种十分简单的单形体,用GJK算法进行距离计算太繁琐。

4. GJK算法与其他相关算法的比较分析

4.1 GJK算法和SAT算法的比较

分离轴定理(Separating Axis Theorem,SAT)是一种可用于精确判断两个物体是否相交的算法,不仅适用于 Box(矩形),还适用于凸多边形。SAT用来检测两个凸多边形是否相交,也可以用于检测点是否在凸多边形内。凸多边形内的点的连线上的点都在凸多边形内,或者连线只和图多边形相交两次(边界处)。

GJK与SAT一样,仅在凸包上运行。GJK更具吸引力的功能之一是,它可以支持实现“support ”函数的任何形状。因此,与SAT不同,您不需要增加特殊的代码或算法来处理弯曲的形状。

GJK是一个迭代算法,但是如果事先给出穿透/分离向量,则它的收敛会很快,可以在常量时间内完成。在3D环境中,GJK可以取代SAT算法。GJK算法的最初目的是计算两个凸体之间的距离,在两个物体穿透深度比较小的情况下,可用它判定物体之间的碰撞。它也可以和别的算法相结合,用来检测两个物体之间深度穿透时候的碰撞情况。

4.2 GJK算法和EPA算法的比较

EPA,扩展多边形算法(Epanding Polytop Algorithm) ,用来计算两个多边形碰撞的穿透深度和方向,可用于将两个发生碰撞的多边形分离。当碰撞发生时,原点到最近的闵可夫斯基差集多边形的边(下文称作“差集最近边”)的距离,就是穿透深度,原点到该边的垂直向量就是穿透向量的方向。因此,核心问题就转换成了,如何求得距离原点最近的差集边。当碰撞检测完毕后,我们会得到一个单形体(Simplex),该单形体可能包含两个或三个support点,将这些support点首尾相连构成封闭的多边形。EPA算法每次迭代的时候,从这个多边形中选择一条最近的边,沿着该边的法线方向(原点到边的垂线方向),向外扩展。直到某条边无法向外扩展时,则该边就是差集最近边。

EPA算法和GJK求最近距离算法很相似,都是在找一个差集最近边。不过EPA用于发生碰撞的情况下,GJK求最近距离用于没有发生碰撞的情况下。理论上EPA也可以用于求两个多边形的最近边,只不过EPA收敛的速度没有GJK算法快。

参考资料

参考资料:http://www.dyn4j.org/2010/04/gjk-gilbert-johnson-keerthi/
版权声明:本文为CSDN博主「小作坊钳工」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:http://t.csdn.cn/6Vmyo

游蓝海的这篇文章:http://t.csdn.cn/dxWdx

由上述文章参考并修改

 

 


http://chatgpt.dhexx.cn/article/9vySKB49.shtml

相关文章

QPM 之悬浮窗设置信息

QPM 开源地址&#xff1a;https://github.com/ZhuoKeTeam/QPM 更多实用信息&#xff1a; 手机的基本信息AndroidManifest.xml 信息App 中所有的 SharePreference 信息可配置的开关网络接口 手机基础信息 再也不用 去手机的复杂界面查看各种数据&#xff1b;再也不用 下载 辅…

QPM访谈问题

QPM&#xff1a;Quantitative Project Management&#xff0c;量化项目管理 一、访谈问题 1、请简要介绍贵司的QPM管理的管理方式&#xff1f; 依据公司整体商业目标和CMMI要求&#xff0c;制定和部署了《量化项目管理过程》和相关的指南、模板。 在项目执行中&#xff0c;…

QPM 性能监控组件——总篇

QPM &#xff08;Quality Performance Monitor&#xff09; 是一个质量性能监控组件&#xff0c;可以很方便的查看当前 App 的性能和常用数据。目前主要运行在 Android 平台上&#xff0c;通过集成 QPM 组件&#xff0c;可以在 App 中通过悬浮窗可视化相关实时数据。意在帮助广…

IDEA快捷键显示参数提示

一、IDEA显示参数的提示 1、理想状态是这样 2、某度了半天 都是让画show parameter 那个勾的&#xff0c;不是我想要的 IDEA默认的快捷键提示是 " ctrl p"&#xff0c;但是我改成eclipse的快捷键后就没有提示了 File---->settings---->keymap 需要给ecli…

Idea设置代码自动提示快捷键

使用eclipse都习惯使用快捷键ALT/ 来代码自动提示&#xff0c;后来使用IntelliJ Idea这个快捷键并不管用&#xff0c;十分不便&#xff0c;这里记录如何使更改idea代码自动提示快捷键。 哪个是代码自动提示快捷键 File–》Settings–》KeyMap(快捷键ctrlalts)进入快捷键设置界…

IDEA提示方法参数的快捷键

在写Java方法的时候有时想让软件提示一下方法的参数&#xff0c;解决方法是将鼠标放置到方法括号里&#xff0c;按下ctrlp&#xff0c;即可显示方法参数

idea 设置代码提示快捷键

Settings->Keymap 搜索框内搜索 completion Main Menu-Code-Code Completion-Basic 设置成对应的快捷键&#xff0c;如 Alt/

IDEA的使用—常用快捷键

IDEA常用快捷键 具体详解视频见于&#xff08;【零基础 快速学Java】韩顺平 零基础30天学会Java&#xff09; 文章目录 前言一、IDEA快捷键是什么&#xff1f;二、IDEA快捷键的设置 1.快捷键12.快捷键23.快捷键3总结 前言 IDEA快捷键能够提升编程速度以及防止出现书写代码时的低…

idea快捷键设置(Idea常用快捷键大全)

目录 友情提醒第一章、IDEA常用快捷键1.1&#xff09;快捷键&#xff1a;查找/提示类1.2&#xff09;快捷键&#xff1a;修改代码类1.3&#xff09;快捷键&#xff1a;光标移动类 第二章、Idea如何修改快捷键2.1&#xff09;已知快捷键&#xff0c;通过搜索快捷键查找2.2&#…

idea快捷键最全最新最好

持续更新(如果文档中没有的&#xff0c;麻烦在评论中添加) 常用快捷键 返回最顶头&#xff1a; home 返回最末尾&#xff1a;end AltInsert 可以新建类&#xff0c;文件&#xff0c;get或set方法&#xff0c;此快捷键又名创造一切 编辑区和文件区的跳转。 …

IntelliJ IDEA 设置代码提示或自动补全的快捷键

对于中国的Java开发者来说&#xff0c;可能使用Eclipse的人最多。 使用Idea的程序员也不少, 而且每个人都在鼓吹其好用之处。 试用半个月&#xff0c;感觉各有千秋&#xff0c;关键看熟练程度和配置是否好用。 自动提示快捷键 有时候希望使用自动补全&#xff0c;因为不偷懒…

【idea】idea 常用快捷键(每个都有操作演示)

【IDEA】 idea 常用快捷键&#xff08;每个都有操作演示&#xff09; IDEA 一款非常优秀的开发工具&#xff0c;本篇博客总结一些在 IDEA 中常用的快捷键&#xff0c;旨在提高开发效率&#xff0c; 下面的 “关键字” 指的是在快捷键搜索框中输入的内容&#xff0c;演示语言为…

Intellij IDEA 快捷键

官方快捷键资料&#xff1a;https://resources.jetbrains.com/storage/products/intellij-idea/docs/IntelliJIDEA_ReferenceCard.pdf 关闭SQL语法检查&#xff1a;https://blog.csdn.net/qq_35478963/article/details/76392947关闭field injection is not recommended警告&am…

IDEA常用快捷键及修改快捷键

IDEA常用快捷键 快捷键功能AltEnter导入包&#xff0c;自动修正代码CtrlY删除光标所在行CtrlD复制光标所在行的内容&#xff0c;插入光标位置下面CtrlAltL格式化代码Ctrl/单行注释CtrlShift/选中代码注释&#xff0c;多行注释&#xff0c;再按取消注释AltIns自动生成代码&…

IDEA快捷键大全及修改IDEA快捷键

✨✨个人主页:沫洺的主页 &#x1f4da;&#x1f4da;系列专栏: &#x1f4d6; JavaWeb专栏&#x1f4d6; JavaSE专栏 &#x1f4d6; Java基础专栏&#x1f4d6;vue3专栏 &#x1f4d6;MyBatis专栏&#x1f4d6;Spring专栏&#x1f4d6;SpringMVC专栏&#x1f4d6;SpringBoot专…

基础开始IntelliJ IDEA 设置代码提示或自动补全的快捷键 (附IntelliJ IDEA常用快捷键)

修改方法如下&#xff1a; 点击 文件菜单(File) –> 点击 设置(Settings… CtrlAltS), –> 打开设置对话框。 在左侧的导航框中点击 KeyMap。 接着在右边的树型框中选择 Main menu –> Code –> Completion. 接着需要做两件事&#xff1a; 1. 移除原来的Cycle Ex…

Idea智能提示快捷键

修改智能提示快捷键&#xff1a; 原文地址&#xff1a;https://blog.csdn.net/gaoqiao1988/article/details/73299670 Idea(File –>Settings)(CtrlAltS), –> 打开设置对话框。 在左侧的导航框中点击 KeyMap。 接着在右边的树型框中选择 Main menu –> Code –>…

IntelliJ IDEA设置代码自动提示的快捷键

前言&#xff1a;使用 eclipse 都习惯使用快捷键ALT/ 来代码自动提示&#xff0c;后来使用IntelliJ Idea这个快捷键并不管用&#xff0c;十分不便&#xff0c;这里记录如何使更改idea代码自动提示快捷键。 打开Settings设置 [ 快捷键 Ctrl Alt S ] File ––> Settings –…

IDEA常用快捷键总结

文章目录 IDEA常用快捷键总结1. 根据psvm或者main快速生成主函数2. 根据sout快速生成打印语句3. 查找的快捷键4. 万能键AltEnter5. for循环的快捷键6. CtrlN 搜索类7. CtrlShiftN 强力搜索8. CtrlH 查看类的继承关系9. Alt7 快速查看类的结构信息10. CtrlR 文本替换11.下面是总…

IDEA常用快捷键及设置方法

一、IDEA默认快捷键&#xff08;持续更新&#xff09; 1、自己常用快捷键&#xff1a;&#xff08;部分有更改&#xff09; 说明快捷键替换所有当前关键字shift F6删除当前行ctrl d复制当前行到下一行ctrl ↓补全代码alt /添加注释或取消ctrl /自动导包&#xff08;需要先…