ORCA算法深度解析:从速度障碍法到多智能体互惠避碰实践
这是一篇关于《Reciprocal n-body Collision Avoidance》的论文阅读笔记也就是很多人常说的ORCA算法、RVO2库的出处。我断断续续把这篇文章读了三四遍每次在工程里碰到多智能体避碰的问题回来看都会有新的体会所以这篇笔记我会把论文里的数学推导、RVO2库的代码实现、以及在真实项目中踩过的坑揉在一起讲尽量做到“看完就能上手”。先说结论ORCA是目前多智能体局部避碰领域里工程落地最成功的算法之一RVO2作为它的官方开源实现被大量用在游戏AI、机器人导航、无人机编队、仓储机器人调度等场景里。这篇论文的核心贡献是提出了“最优互惠避碰”的概念——每个智能体只承担一半避让责任从而避免传统速度障碍法在全动态环境里的振荡问题。读完你会发现这个“一半责任”的想法虽然朴素但背后的几何推导和线性规划求解非常精巧。这篇文章适合这几类人看做机器人导航的工程师、游戏AI开发、搞群体仿真crowd simulation的研究生以及想搞清楚RVO2库内部原理的程序员。哪怕你完全没接触过速度障碍法我也会从最基础的VO开始讲一步步推到ORCA。1. 论文整体定位与核心问题1.1 这篇论文到底贡献了什么《Reciprocal n-body Collision Avoidance》是2009年发表在ISRRInternational Symposium on Robotics Research上的一篇论文作者是Jur van den Berg、Jamie Snape、Stephen J. Guy和Dinesh Manocha都来自UNC Chapel Hill的GAMMA小组。论文后来收录在Springer的STAR系列图书中正式刊出是2011年。这篇论文要解决的问题非常具体在多个移动智能体共存的动态环境中如何让每个智能体在不知道对方完整意图、只能观察对方当前速度的前提下快速算出自己的安全速度从而保证整个群体在宏观上无碰撞地移动。这个问题的难点在于“互惠”reciprocal。如果你的算法把其他智能体当成静态障碍物来处理那么当你避让的时候对方也在避让你双方的动作叠加起来反而容易产生新的碰撞风险或者来回振荡。ORCA的答案是约定双方各让一半。论文的主要贡献可以总结为三点。第一它给出了一个基于半平面约束的分布式避碰框架每个智能体只需要求解一个线性规划就能得到最优速度单步计算复杂度是O(n)的n是参与计算的邻居数量。第二它从理论上证明了如果所有智能体都遵循同样的策略那么系统在时间窗口τ内是无碰撞的——这个保证在当时的局部避碰方法里非常稀缺。第三它配套发布了RVO2开源库把算法做成了开箱即用的代码这也是它的影响力远超同领域论文的重要原因之一。1.2 多智能体避碰为什么难要理解ORCA的价值先得理解多智能体避碰为什么比单智能体避障难得多。单智能体避障环境是静态的障碍物不动你只需要考虑“我走哪条路不撞墙”。多智能体环境里每个障碍物都是活的它们有自己的意图也在做决策。你在避让对方对方也在避让你甚至还有第三方、第四方参与整个系统的行为是涌现出来的emergent不像静态场景那样可以精确预测。我打个比方。两个人面对面走在走廊里如果你俩都往自己的右边让那一下就错开了这就是“互惠避碰”。但如果两个人都往同一个方向让或者都站在原地等对方先动就会出现“你左我也左你右我也右”的尴尬局面。多智能体避碰算法的核心任务就是要把这种日常生活中的默契形式化成可计算的几何约束。早期的方法比如Fiorini和Shiller在1998年提出的速度障碍法Velocity ObstacleVO把这个问题变成了速度空间里的几何问题找出所有会导致碰撞的相对速度然后避开这个集合。但VO默认是“我避让、对方不动”的所以在一群运动智能体里直接用VO行为会非常保守且容易振荡。后面我会详细讲这个演进过程。2. 从VO到RVO再到ORCA算法演进脉络2.1 速度障碍VO把避碰变成几何裁剪速度障碍Velocity Obstacle的思想在1998年由Fiorini和Shiller提出。它的定义是这样的假设智能体A在位置p_A以速度v_A运动另一个智能体B在位置p_B以速度v_B运动。A和B的半径分别是r_A和r_B如果两者圆心的距离小于r_A r_B就算碰撞。那么对于A来说B的速度障碍是一个速度集合记为VO_B^τ(v_B)。这个集合里的每个速度v都会让A在时间τ内撞上B假设B保持当前速度v_B不变。数学表达式是VO_B^τ(v_B) { v | ∃t ∈ [0, τ] : t(v - v_B) ∈ D(p_B - p_A, r_A r_B) }其中D(p, r)表示圆心在p、半径为r的圆盘。这个式子看着抽象其实意思很直白v - v_B是A相对B的速度t(v - v_B)是t时刻A相对B的位移。如果这个位移落在以B当前位置为圆心、以r_A r_B为半径的圆盘内说明A和B在t时刻撞上了。把所有会导致碰撞的速度v都收集起来在速度空间里就得到一个锥形区域——锥形的顶点在v_B处两条侧边分别切过圆盘左右两侧。这就是速度障碍。有了VO之后A的可行速度集合就是整个速度空间减去VO这个禁区。如果A的期望速度v_pref恰好落在VO里A就需要在VO之外找一个速度离v_pref越近越好。这个“离期望速度最近的安全速度”的选择本质上是一个几何裁剪问题。VO的问题在于它默认B是“石头”A必须承担全部避让责任。如果B也在运动也用同样的方法避让A那双方的避让动作会叠加放大导致整个过程非常激进且不稳定。所以VO在静态环境避障里表现不错但在全动态多智能体环境里不太够用。2.2 互惠速度障碍RVO让双方分担责任2008年van den Berg等人在ICRA上发表了《Reciprocal Velocity Obstacles for Real-time Multi-agent Navigation》首次引入了“互惠”的概念。RVO的核心想法是A在选速度时假设B也会做出对等的避让反应所以A只需要考虑“如果B也动起来会不会更安全”。RVO的定义如下RVO_B^τ(v_A, v_B) { v | 2v - v_A ∈ VO_B^τ(v_B) }这里的2v - v_A可以理解为B的“镜像速度”如果A从v_A变到v假设B也做对称的变化那么B的新速度就是2v - v_A。A选择一个v使得在这个“对称假设”下的相对速度不在VO里因此双方都知道对方会让一步。RVO确实缓解了VO在动态环境里的振荡问题但并没有完全解决。它仍然可能出现“互相推诿”的局面——两个智能体各自绕到同一侧小幅来回摆动。业界后来也出现了HRVOHybrid Reciprocal Velocity Obstacles专门去修复RVO的振荡问题但ORCA换了一个更优雅的思路不再定义速度障碍的“镜像”而是直接构造一个半平面约束让双方各让一半。这个改动的效果我在下一节详细讲。2.3 ORCA的最优互惠避碰一人一半ORCA的全称是Optimal Reciprocal Collision Avoidance最优互惠避碰。“最优”体现在两个层面一是责任分配最优各承担一半二是速度选择最优在满足所有避碰约束的前提下选择最接近期望速度的那个速度。ORCA的核心步骤分两步。第一步对场景中的每个其他智能体B构造一个半平面约束。这个半平面是速度空间里一条直线划分出来的区域直线经过v_A 1/2 u法向朝外其中u是从A和B当前相对速度v_A - v_B指向VO边界最近点的向量。后面我会详细推导。第二步把所有半平面约束和最大速度边界一个以原点为圆心、maxSpeed为半径的圆盘求交集得到一个凸可行区域。在这个区域内找一个离期望速度v_pref最近的点就是A这一刻的最优速度。这是一个二维线性规划问题RVO2里用的是增广线性规划求解器。ORCA相比RVO最大的改进是从“速度集合的镜像变换”变成了“半平面的直接构造”。这个变化带来的好处非常明显可行域是凸的线性规划求解稳定高效半平面天然支持多智能体场景有多少个邻居就有多少个约束全部取交集即可而且由于避让责任被数学上严格地对半分整个系统不会出现“双方抢路”的振荡现象。注意ORCA的无碰撞保证是有前提条件的。一个是所有智能体都使用相同的ORCA策略另一个是智能体可以瞬时改变速度没有加速度限制还有一个是时间窗口τ内智能体的速度保持不变。工程上这些前提都会被打破所以实际效果往往达不到论文里的理论保证这个我后面会展开。3. ORCA核心推导逐个拆解3.1 半平面约束的构造过程这一节是整篇论文的精华我把构造过程逐步拆开。假设智能体A的当前位置是p_A速度是v_A智能体B的位置是p_B速度是v_B。两者的半径分别为r_A和r_B。A要构造自己对B的避碰约束。首先计算A相对于B的速度v_rel v_A - v_B。然后计算A相对于B的位置p_rel p_B - p_A以及合并半径r r_A r_B。以p_rel为圆心、r为半径画一个圆盘D(p_rel, r)这就是A和B发生碰撞时A相对B的位置集合。接下来看当前的相对速度v_rel落在哪里。如果v_rel在VO内部说明按照当前速度走下去A和B会在τ时间内撞上需要调整如果v_rel在VO外部说明当前是安全的但ORCA依然会构造一个约束防止新速度往危险方向偏。关键计算来了。在VO的边界上找一个点使得这个点到v_rel的欧氏距离最小。设这个最近点是w则向量u为u w - v_relu的意义是A和B的相对速度为了逃出VO或者为了远离VO边界保持安全至少需要改变的量。这个改变可以完全由A承担也可以完全由B承担还可以分摊。ORCA选择一人一半A负责u/2B负责u/2。于是A的新速度v_A需要满足v_A在v_A u/2的某一侧且沿着法向n的方向远离VO。写成公式就是ORCA_A|B^τ { v | (v - (v_A 1/2 u)) · n ≥ 0 }其中n是VO边界在w处的外法向量。这个不等式定义了一个半平面它把速度空间分成两部分满足条件的速度都在安全一侧不满足的在危险一侧。这里有一个特别容易被忽略的细节如果v_rel已经在VO之外u并不等于零它指向VO边界上离当前相对速度最近的点。半平面过v_A u/2法向朝向远离VO的方向这条约束会限制新速度不要往VO方向靠太多。这个“提前量”设计很巧妙它保证了算法的连续性——即使当前是安全的速度的调整也会被限制在一个安全走廊里不会因为避碰约束突然出现而产生跳变。对场景中的每一个智能体B都做同样的操作得到M个半平面。再加上自身的最大速度约束A的最终可行速度集合就是这些半平面和速度圆盘的交集。3.2 线性规划求解最优速度可行区域是凸的因为它是一堆半平面和一个凸圆盘的交集。在凸区域里找离期望速度v_pref最近的点是一个标准的凸优化问题展开到二维坐标系就是线性规划。RVO2的实现里求解器是这么做的先用v_pref初始化候选速度然后逐个检查每个半平面约束。如果某个约束被违反就把候选速度投影到该约束的边界直线上继续检查剩余约束。这个过程对应源码里的linearProgram2函数。如果所有约束都能满足就返回投影后的速度。如果中途发现某个约束把可行区域“压没”了约束之间的交集为空就进入linearProgram3在所有已经处理过的半平面约束里找出违背程度最小的那个解作为降级后的速度。说白了避碰约束全部无法满足时算法不是返回“无解”而是返回一个“尽量少撞”的速度让智能体不至于呆立原地。这个降级策略在密集场景里非常重要。想象一下四个智能体同时挤在一个走廊里无论怎么选速度都躲不开对方这时最优解可能是“大家减速、互相贴着擦过去”。如果求解器直接返回无解智能体就会停在原地更容易造成死锁。我把RVO2里求解器的行为总结成一张表情况求解行为运动效果v_pref本身满足所有约束直接返回v_pref智能体按期望速度直线运动v_pref不满足部分约束但可行域非空投影到边界平滑避让尽可能贴近期望方向所有约束交集为空最小化最大违背量降级避碰优先保证不彻底卡死这个求解过程是每个仿真步执行的。RVO2的doStep()会为每个智能体重新计算邻居集合、构造约束、求解速度、更新位置所以整个算法的计算量和邻居数量成正比而不是和场景总智能体数成正比。这也是ORCA能支持几千甚至上万个智能体实时运行的原因。3.3 关键参数与数学性质ORCA有几个关键参数直接决定了算法的行为和最终效果。这里我把每个参数的作用和调节倾向列一下。timeHorizon时间窗口这是最核心的参数。它决定了智能体提前多长时间开始考虑碰撞。timeHorizon越大智能体越“保守”早早就开始绕路timeHorizon越小智能体越“激进”快撞上了才调整。论文里的无碰撞保证跟这个τ直接相关理论上只要所有智能体都用同一个τ并且在τ时间内能完成速度切换就不会碰撞。工程上常用的范围是2到10秒具体看场景密度和智能体速度。neighborDist邻居距离只有在这个距离内的其他智能体才会被纳入避碰计算。这个参数要跟场景规模匹配。场景大、智能体密度低可以把neighborDist设大一些避免漏掉远处的潜在碰撞场景密集neighborDist设太大会导致约束过多、可行域被过度收缩智能体反而走不动。maxNeighbors最大邻居数限制参与计算的邻居数量上限主要用来控制计算量。RVO2的KdTree会按距离排序取最近的N个所以即使场景里有上万个智能体每个智能体也只看周围最近的10到20个。radius智能体半径碰撞检测用的圆形半径。这个半径应该略大于智能体的实际物理尺寸给控制误差留余量。maxSpeed最大速度智能体的速度上限ORCA求解出的速度不会超过这个值。这个值最好比期望速度的上限高出30%以上否则避碰时没有足够的调整空间。这些参数之间是联动的工程上通常要一起调。一个常见的坑是timeHorizon设得很大、同时maxSpeed又不够智能体会发现即使全速避让也达不到约束要求可行域经常为空于是频繁走降级解表现出来就是要么原地抖、要么擦碰频繁。后面我专门列了一套避坑清单。4. RVO2开源库实战4.1 代码结构概览RVO2是ORCA论文作者维护的官方开源实现C版本在GitHub上可以直接找到项目名就叫RVO2。核心代码量不算大主要文件就这几个RVO.h / RVO.cpp对外API定义RVOSimulator类Agent.h / Agent.cpp单个智能体的避碰逻辑包括邻居查找、约束构造、速度求解KdTree.h / KdTree.cpp空间划分和邻居查询RVO2用的是Kd树而不是网格因为每个智能体的查询半径是固定的Kd树在这种场景下效率很高Obstacle.h / Obstacle.cpp静态障碍物线段Vector2.h / Vector2.h二维向量工具类整个库的依赖非常少只用到了标准库编译很简单这对工程集成非常友好。我见过很多论文实现代码写得像天书RVO2的风格在学术界算是难得的干净。RVO2还有一个很贴心的设计官方仓库里自带Windows、Linux、macOS的工程文件社区还维护了C#、Java、Python、Unity、ROS的移植版本生态非常成熟。Python版本在pypi上可以直接pip安装库名就是rvo2C#版本在Unity游戏项目里经常见到。4.2 核心接口与配置参数RVO2的使用流程非常固定我总结为四步。第一步创建仿真器并设置全局参数#include RVO.h RVO::RVOSimulator sim; sim.setTimeStep(0.1f); sim.setAgentDefaults(10.0f, 10, 5.0f, 5.0f, 0.5f, 2.0f);setAgentDefaults的参数从左到右分别是neighborDist、maxNeighbors、timeHorizon、timeHorizonObst、radius、maxSpeed。注意timeHorizon和timeHorizonObst是分开的后者专门用于静态障碍物通常比动态智能体的timeHorizon略大一点因为静态障碍物不会自己让路。第二步添加智能体和静态障碍物// 添加智能体返回智能体ID size_t agentId sim.addAgent(RVO::Vector2(0.0f, 0.0f)); // 添加静态障碍物多边形顶点逆时针 std::vectorRVO::Vector2 obstacle; obstacle.push_back(RVO::Vector2(-1.0f, -1.0f)); obstacle.push_back(RVO::Vector2(1.0f, -1.0f)); obstacle.push_back(RVO::Vector2(1.0f, 1.0f)); obstacle.push_back(RVO::Vector2(-1.0f, 1.0f)); sim.addObstacle(obstacle); sim.processObstacles();静态障碍物以折线或闭合多边形的形式添加processObstacles()会预计算障碍物的切线和导航信息。这个函数必须在添加完所有静态障碍物之后调用而且静态障碍物添加后在仿真过程中不能修改否则KdTree会失效。第三步在每帧开始前设置期望速度sim.setAgentPrefVelocity(agentId, RVO::Vector2(1.0f, 0.0f));期望速度一般由上层规划器给出比如A*路径规划算出的下一个航路点方向乘以期望速率。ORCA本身只负责局部避碰不管全局导航。第四步调用doStep()推进一帧sim.doStep(); RVO::Vector2 newVel sim.getAgentVelocity(agentId); RVO::Vector2 newPos sim.getAgentPosition(agentId);doStep()内部会依次做三件事更新所有智能体的邻居集合KdTree查询、用ORCA约束求解每个智能体的新速度、按新速度更新位置。一个循环里反复执行第三、四步就得到了整个群体的避碰动画或运动轨迹。4.3 一个最小可运行示例我写一个最小的RVO2示例两个智能体面对面走过这是验证避碰算法最常见的测试场景。#include RVO.h #include cstdio int main() { RVO::RVOSimulator sim; sim.setTimeStep(0.1f); sim.setAgentDefaults(5.0f, 10, 3.0f, 3.0f, 0.5f, 3.0f); // 两个智能体一个在左边一个在右边相向而行 size_t a sim.addAgent(RVO::Vector2(-5.0f, 0.0f)); size_t b sim.addAgent(RVO::Vector2(5.0f, 0.0f)); for (int step 0; step 200; step) { sim.setAgentPrefVelocity(a, RVO::Vector2(1.5f, 0.0f)); sim.setAgentPrefVelocity(b, RVO::Vector2(-1.5f, 0.0f)); sim.doStep(); printf(step %d: posA(%.2f, %.2f), posB(%.2f, %.2f)\n, step, sim.getAgentPosition(a).x(), sim.getAgentPosition(a).y(), sim.getAgentPosition(b).x(), sim.getAgentPosition(b).y()); if (sim.getAgentPosition(a).x() sim.getAgentPosition(b).x()) { printf(agents passed each other at step %d\n, step); break; } } return 0; }运行这个例子你会看到两个智能体在接近时会自动横向错开一个稍微往上偏一个稍微往下偏擦肩而过之后继续朝目标走。这个“一个往上、一个往下”的对称性正是ORCA“一人一半”责任分配的直观体现——如果A往上避了B就会被约束推向下方形成互补轨迹。提示如果两个智能体在测试里出现了来回抖动而不是平滑错开的轨迹先检查timeHorizon和maxSpeed的比值。timeHorizon太大、maxSpeed又不够时智能体会试图提前太多去规避反而在快接近时发现约束无法满足产生抖动。5. 常见问题与调参避坑记录5.1 典型故障现象与原因我从自己的项目经历和社区问题里整理了四个最常遇到的现象。第一个是振荡oscillation。两个智能体在狭窄走廊里面对面反复横跳谁也不让谁。原因通常是timeHorizon过大导致双方都过早、过度地避让可行域被压得太小求解器频繁进入降级模式。另外如果上层规划器给的目标点会诱导智能体“互相穿过”比如让A和B的目标点几乎重合ORCA自身也会不断挣扎。解决办法是把timeHorizon调小一点同时检查全局路径是否合理。第二个是卡死deadlock。多个智能体在通道口挤成一团谁也动不了。这其实是局部方法的通病。ORCA本质上是一个反应式reactive方法它没有全局视野当可行域为空时只能选择最小违背速度在对称的密集场景里会收敛到静止状态。解决思路有两个方向一是给期望速度加扰动比如每帧在垂直于目标方向上加一点随机偏移打破对称性二是配合全局调度比如给智能体分配优先级或者时间槽从上层避免对称死锁。第三个是碰撞残留residual collisions。明明用了ORCA还是看到智能体擦碰。最常见的原因是控制延迟。ORCA假设智能体可以瞬时实现计算出的速度但真实机器人有加速度上限、转向延迟命令到执行之间有偏差。我自己的做法是在radius里加上一个“安全余量”比如物理半径0.3米ORCA半径设0.5米用余量吃掉延迟误差。另外把仿真步长从0.1秒降到0.05秒也能明显减少残留碰撞。第四个是性能问题。智能体数量超过几千之后即使有KdTree也会开始卡顿。此时先检查maxNeighbors——默认值是10但场景密度高的时候实际参与的邻居数量会被这个上限限制住。性能瓶颈往往更多在障碍物的KdTree构建和查询上。如果静态障碍物特别多考虑减少线段数量或者把远处的障碍物合并成粗粒度的凸包。5.2 调参速查表我把前面提到的参数整理成一个速查表调试的时候可以对着这个表逐项排查。参数调大的效果调小的效果常见取值timeHorizon更保守提前避让但易振荡和死锁更激进反应迟钝易碰撞3-8秒密集场景取小neighborDist考虑更远邻居更安全计算量略增忽略远处邻居可能漏避让3-10米视场景规模maxNeighbors更多约束更全局计算量增大计算快但可能漏掉关键邻居8-20radius更保守碰撞更少但通道利用率下降更激进容易擦碰物理半径×1.2~2.0maxSpeed调整空间大避让更从容调整空间小降级频繁期望速度上限×1.3以上在实际项目中我的调参顺序一般是先定radius和maxSpeed这两个跟机器人底盘动力学强相关再定timeHorizon根据场景里的预期相对速度来估算最后微调neighborDist和maxNeighbors。不要一上来就五六个参数一起改那样根本定位不到问题。5.3 工程落地中的扩展与改造ORCA的原始论文假设智能体是全向移动的holonomic可以瞬时获得任意方向的二维速度。但真实机器人很少满足这个假设尤其是差速底盘和汽车底盘。我见过三种主流改造方案。第一种是速度映射法。把ORCA算出的二维速度通过运动学映射成线速度和角速度。比如差速底盘可以用一个PD控制器跟踪ORCA的速度向量再通过转向角限制做平滑。这个方法实现简单但没法严格保证ORCA的碰撞避免性质因为底盘的横向速度跟不上。第二种是约束改造法。在ORCA的线性规划里加入动力学约束比如把最大速度和最大加速度约束成椭圆或者多边形区域。这样求解出来的速度天然符合底盘能力。RVO2-3D就是这么做的它把半平面约束扩展到三维速度空间把最大速度从圆改成球体。二维场景下也可以做类似改造把加速度约束写成半平面但要小心可行域可能不再凸需要额外处理。第三种是轨迹跟踪法。ORCA只负责生成期望速度底层的运动控制由另一个模块负责。这个方法在ROS里最常见比如navigation栈里用全局规划得到的路径点作为ORCA的导向底盘控制器再负责跟踪。好处是层次清晰ORCA可以很轻量地替换成其他避碰算法。另外一个常被忽视的点是“非合作目标”的处理。ORCA的互惠假设要求对方也是ORCA用户。如果场景里有人控制的角色、或者跑的是别的避碰算法的智能体ORCA的“一半责任”假设就失效了。我的建议是把这类非合作目标当成主动障碍物处理让ORCA全权避让而不是各让一半。6. 扩展应用与个人体会6.1 从论文到工业界的迁移路径ORCA从2009年发表到现在十几年过去依然是局部避碰的默认选项之一。在游戏行业很多3A大作的群体动画系统里都能看到RVO2的身影Unity和Unreal的生态里都有直接可用的移植版。在机器人行业它是仓储机器人、送餐机器人、扫地机器人这类室内低速移动场景的常见选择。在仿真领域行人疏散、群体行为研究也经常以ORCA作为底层控制器。为什么ORCA能穿越这么久的时间周期依然活跃我个人觉得有三个原因。第一计算量小实时性有保障这在算力受限的机器人嵌入式平台上很重要。第二接口简单输入一个期望速度输出一个安全速度跟任何上层规划器都容易对接。第三参数含义直观没有玄学的黑箱调参逻辑可以闭环。当然它也有明显的天花板。它解决的是“局部避碰”这个子问题不解决全局路径规划也不解决智能体之间的任务协调。如果目标点导致的路径交叉是本质性的比如所有智能体都要穿过同一个窄门ORCA只能让它们小心翼翼地挤过去效率并不会高。这时候需要的是上层调度比如排队、分批、分配时间窗口。6.2 我在实际实验中的体会最后聊一点个人感受。我第一次把RVO2跑起来的时候花了好一阵子才理解“半平面”这个设计的精妙之处。看论文的时候觉得公式推得很好但真正上手写代码、调参、看仿真结果之后才明白“一人一半”这个朴素想法在工程上的价值——它不只是数学上优雅而是真的解决了振荡和死锁这两个最烦人的实际问题。还有一点体会是论文里的保证是理想条件下的工程上不要指望原封不动地复现论文效果。我之前做过一个密集人群的仿真实验10个智能体在一个4x4米的区域里互相穿插理论上一人一半完全能避让但实测即使把timeHorizon调到2秒还是会出现局部抖动。原因是我的仿真步长是0.1秒而ORCA的假设是速度可以瞬时切换这个离散化就是误差来源。后来我把步长降到0.05秒抖动立刻小了很多。类似这种“理想和现实的差距”论文里不会写只有踩过才知道。如果让我给初学者一个建议那就是先用官方示例把RVO2跑通然后故意改坏参数观察各种故障现象振荡、死锁、碰撞建立起“参数-现象”的直觉再回去读论文的数学推导你会发现收获比直接硬啃公式大得多。算法这东西纸上推十遍不如跑起来看一眼。
上一篇/下一篇内容由系统自动关联
返回资讯列表 →