ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

Python实现无人机避障:人工势场法从公式到代码详解

Python实现无人机避障:人工势场法从公式到代码详解 做无人机避障的时候我第一个想到的路径规划算法就是这个——人工势场法APF。它不是新东西但它在很多轻量级场景里真的够用。如果你正在做无人机编队、巡检任务或者是想快速验证避障逻辑人工势场法都是一个起点友好、思路直观的算法。这篇博文就用Python带你从公式推导到代码落地完整实现一个无人机避障的人工势场算法并给出常见的调参经验和坑点复盘。先说一下适合谁来读。如果你熟悉Python基础语法了解一点numpy和matplotlib但对路径规划算法没有系统的认知这篇文章正合适。如果你已经跑通过A*或者RRT这类全局规划算法想要补一个轻量级的局部避障模块也可以直接跳到第3、4节看实现细节和参数整定。1. 人工势场法到底是什么它解决无人机避障的哪种问题1.1 从“力”的角度看避障人工势场法最早由Khatib在1986年提出核心思想特别朴素把地图抽象成一张势能场。目标点产生“引力”障碍物产生“斥力”无人机在引力场和斥力场的叠加作用下沿着势场下降的方向移动。这个思路和“水往低处流”很像——无人机永远在朝着势能更低的方向走而陷阱、死胡同在势能图上表现为局部低谷这也是后面要重点解决的局部极小值问题。放到真实无人机场景里这套方法最擅长处理的是局部动态避障也就是已知目标位置、遇到突发的障碍物时快速规划出一条安全的绕行路径。它不像A*那样需要事先构建完整的栅格地图也不像RRT那样需要大量采样和碰撞检测算法本身的运算量非常小非常适合部署在算力受限的飞控板或嵌入式边缘计算模块上。1.2 和DWA、A*、RRT相比人工势场法的优缺点很多初学者一上来就纠结“无人机避障到底用哪个算法”。我先把人工势场法放进坐标系里做个对比你就能看明白它的位置了。算法全局/局部计算量对环境建模的要求典型瓶颈A*全局中需要栅格地图地图更新慢动态场景会失效RRT / RRT*全局中高需要采样空间路径不平滑需要后处理DWA局部低需要速度采样空间容易陷入局部最优依赖全局引导人工势场法局部极低只需目标点和障碍物坐标局部极小值、目标不可达、参数敏感从表格能看出来人工势场法最大的优势是实时性和简洁性。传统上A*跑一次可能耗时几十毫秒甚至更久栅格越大地图越大而APF在每次控制周期内只要计算几个向量加法在Python这种解释型语言里也能跑到几十到上百赫兹更不用说如果用C或者直接部署在飞控里能达到什么水平。但它的问题也很明显没有全局视野。它只能“感知当前这一步”一旦势场建得有问题无人机就可能停在某个位置不动或者围着障碍物打转。所以工程上通常的做法是用全局规划算法A*、RRT先出一条参考路径再用人工势场法做局部避障两个算法配合使。2. 人工势场算法的数学建模与公式推导2.1 引力场和斥力场的定义人工势场法把整个空间定义为一个标量场 ( U(q) )无人机在某个位置 ( q ) 收到一个虚拟力 ( F(q) )这个力等于势场函数的负梯度[ F(q) -\nabla U(q) ]实际应用时我们会把 ( U(q) ) 拆成两部分目标点产生的引力势场 ( U_{att}(q) ) 和障碍物产生的斥力势场 ( U_{rep}(q) )所以总势场[ U(q) U_{att}(q) U_{rep}(q) ]总力就是两个力的矢量叠加[ F(q) F_{att}(q) F_{rep}(q) -\nabla U_{att}(q) - \nabla U_{rep}(q) ]下面分别展开。引力势场定义为目标点距离的二次函数[ U_{att}(q) \frac{1}{2} k_{att} \cdot \rho^2(q, q_{goal}) ]其中 ( \rho(q, q_{goal}) | q - q_{goal} | ) 是无人机当前位置到目标点的欧氏距离( k_{att} ) 是引力增益系数。对位置求导得到引力[ F_{att}(q) -k_{att} \cdot (q - q_{goal}) k_{att} \cdot (q_{goal} - q) ]注意这个方向的物理含义引力指向目标点大小和距离成正比。也就是说无人机离目标越远被拉向目标的力越大。这个设计很合理——远处能够快速接近目标近处能够慢慢收敛不会因为惯性冲过头。举个例子如果无人机在 (0, 0)目标在 (10, 0)( k_{att} 1 )那引力就是 (10, 0)方向朝正X轴如果它在 (5, 0)引力就是 (5, 0)方向不变但大小减半。斥力势场相比引力稍微复杂一点。最经典的定义是[ U_{rep}(q) \begin{cases} \frac{1}{2} k_{rep} \left( \frac{1}{\rho(q, q_{obs})} - \frac{1}{\rho_0} \right)^2, \text{若 } \rho(q, q_{obs}) \leq \rho_0 \ 0, \text{若 } \rho(q, q_{obs}) \rho_0 \end{cases} ]其中 ( q_{obs} ) 是障碍物位置( \rho_0 ) 是斥力的最大作用距离阈值半径。超过这个距离障碍物对无人机就没有影响了。( k_{rep} ) 是斥力增益系数。对位置求梯度得到斥力[ F_{rep}(q) \begin{cases} k_{rep} \left( \frac{1}{\rho} - \frac{1}{\rho_0} \right) \frac{1}{\rho^2} \frac{q - q_{obs}}{\rho}, \text{若 } \rho \leq \rho_0 \ 0, \text{若 } \rho \rho_0 \end{cases} ]展开写会更直观[ F_{rep}(q) k_{rep} \left( \frac{1}{\rho} - \frac{1}{\rho_0} \right) \frac{1}{\rho^2} \cdot \hat{n}_{obs} ]这里 ( \hat{n}{obs} \frac{q - q{obs}}{|q - q_{obs}|} ) 是从障碍物指向无人机的单位向量。所以斥力永远背离障碍物把无人机往外推。距离越近斥力越大距离到达 ( \rho_0 ) 边界时斥力为0。2.2 无人机运动模型和力的合成有了引力和斥力的表达式接下来把力转化成运动。最常见的做法是把无人机当成一个二维或三维空间内的质点用牛顿第二定律做近似运动。如果直接用力除以质量得到加速度再积分得到速度最后积分得到位置这就是最简的“力-加速度-速度-位移”链条。但在离散控制周期里工程上更常用的是速度指令法假设无人机有一个底层速度控制器直接计算期望速度[ v_{desired} \frac{F_{total}}{\gamma} ]其中 ( \gamma ) 是一个阻尼系数用来控制无人机对力的响应灵敏度。( \gamma ) 越大无人机对势场力的反应越“迟钝”移动越平滑( \gamma ) 越小无人机反应越“灵敏”但容易出现抖动和振荡。然后限制最大速度[ v_{desired} \begin{cases} v_{max} \cdot \frac{v_{desired}}{|v_{desired}|}, \text{若 } |v_{desired}| v_{max} \ v_{desired}, \text{否则} \end{cases} ]每次迭代更新位置[ q_{new} q_{old} v_{desired} \cdot dt ]这个模型里面有几个关键参数需要提前想清楚( k_{att} ): 引力增益控制无人机朝向目标点的强度。( k_{rep} ): 斥力增益控制无人机躲避障碍物的强度。( \rho_0 ): 斥力影响半径。这个参数必须大于无人机的安全半径通常根据无人机尺寸和传感器探测范围来定。( v_{max} ): 最大速度限制避免目标点很远时飞行速度无限增大。( dt ): 仿真步长也就是每个控制周期的间隔。( \gamma ): 阻尼系数影响速度响应的平滑度。这些参数之间是相互关联的调参时必须整体来看不能只盯着某一个参数。后面第4节我会专门讲怎么整定这些参数。3. 用Python从零实现无人机避障人工势场算法3.1 环境准备与依赖安装在写代码之前先把Python环境搞定。我默认你用的是Python 3.8以上的版本64位操作系统然后安装两个核心库numpy和matplotlib。pip install numpy matplotlib这里要说明一下为什么选numpy而不是纯Python的list。因为势场法的核心是大量的二维或三维向量运算如果每一个向量加法、点积、取模都用原生Python写循环仿真跑起来会很慢。numpy是C语言实现的向量化运算性能高得多。如果你碰巧是在一个全新的环境里可以先验证一下numpy是否安装成功import numpy as np print(np.__version__)能输出版本号就说明环境没问题。如果你在跑代码时遇到ModuleNotFoundError: No module named numpy那就要先确认pip安装到了哪个Python解释器。Windows下常见的问题是有多个Python版本pip装到了Python 3.7但运行代码时用的却是Python 3.11这样就会找不到模块。建议用python -m pip install numpy matplotlib而不是直接敲pip install这样安装目标一定是你当前Python对应的环境。3.2 程序整体结构和模块划分完整代码我放在下面不过先说明一下整体架构方便你后续二次开发。整个程序分成四个模块PotentialField2D类核心算法类包含引力计算、斥力计算、合力计算、单步更新逻辑。draw_map和draw_arrows函数可视化模块负责绘制环境、路径、力场箭头。主循环进行迭代仿真判断是否到达目标或超出最大迭代次数。用户输入区配置地图大小、目标点、障碍物列表、算法参数。之所以用类封装而不是把所有代码堆在一个脚本里是考虑到真实工程中你大概率要把这个类拿出去当作独立的“避障模块”集成到飞控系统里而不是每次重写一遍。封装成类之后可以通过对象属性很方便地调整参数也方便后续扩展成三维版本PotentialField3D。3.3 核心类实现下面是我整理的一份可以直接运行的完整代码。你复制到新的Python文件比如叫apf_demo.py里直接运行就能看到仿真结果。import numpy as np import matplotlib.pyplot as plt class PotentialField2D: 二维人工势场法路径规划器 用于无人机在静态障碍物环境下的局部避障 def __init__(self, k_att1.0, k_rep100.0, rho_05.0, gamma1.0, v_max2.0, dt0.1): self.k_att k_att self.k_rep k_rep self.rho_0 rho_0 self.gamma gamma self.v_max v_max self.dt dt def calc_attractive_force(self, position, goal): 计算引力指向目标点大小与距离成正比 F_att k_att * (goal - position) delta goal - position distance np.linalg.norm(delta) if distance 1e-6: return np.array([0.0, 0.0]) force self.k_att * delta return force def calc_repulsive_force(self, position, obstacles): 计算斥力远离障碍物距离越近力越大 只有当无人机进入障碍物影响半径内才计算 force np.array([0.0, 0.0]) for obs in obstacles: delta position - obs distance np.linalg.norm(delta) if distance self.rho_0 and distance 1e-6: magnitude self.k_rep * (1.0 / distance - 1.0 / self.rho_0) / (distance ** 2) direction delta / distance force magnitude * direction return force def calc_total_force(self, position, goal, obstacles): 计算无人机受到的合力 引力 所有障碍物的斥力 f_att self.calc_attractive_force(position, goal) f_rep self.calc_repulsive_force(position, obstacles) return f_att f_rep def step(self, position, goal, obstacles): 单步更新根据当前位置计算合力更新速度限制最大速度然后更新位置 f_total self.calc_total_force(position, goal, obstacles) # 速度 合力 / 阻尼系数 velocity f_total / self.gamma # 限制最大速度 speed np.linalg.norm(velocity) if speed self.v_max: velocity velocity / speed * self.v_max # 更新位置 new_position position velocity * self.dt # 返回新位置、速度、合力用于可视化 return new_position, velocity, f_total def main(): # 环境配置 map_size 100 # 地图范围 0~100 start np.array([10.0, 10.0]) goal np.array([90.0, 90.0]) obstacles [ np.array([40.0, 30.0]), np.array([35.0, 65.0]), np.array([70.0, 55.0]), np.array([20.0, 70.0]), np.array([60.0, 20.0]), np.array([75.0, 78.0]) ] # 算法参数 k_att 1.0 k_rep 500.0 rho_0 12.0 gamma 1.0 v_max 4.0 dt 0.05 max_iter 3000 goal_radius 1.0 # 到达目标点的判定半径 planner PotentialField2D(k_attk_att, k_repk_rep, rho_0rho_0, gammagamma, v_maxv_max, dtdt) # 开始仿真 positions [start.copy()] velocity np.array([0.0, 0.0]) current_pos start.copy() for i in range(max_iter): new_pos, velocity, force planner.step(current_pos, goal, obstacles) # 边界保护防止无人机飞出地图范围 new_pos np.clip(new_pos, 0, map_size) positions.append(new_pos.copy()) current_pos new_pos if np.linalg.norm(current_pos - goal) goal_radius: print(f第 {i 1} 次迭代到达目标最终位置{current_pos}) break if i max_iter - 1: print(f达到最大迭代次数 {max_iter}当前在{current_pos}) print(可能陷入了局部极小值建议调整参数或加入扰动) positions np.array(positions) # 绘图 plt.figure(figsize(8, 8)) # 障碍物画成圆圈 for obs in obstacles: circle plt.Circle(obs, 2.0, colorred, alpha0.6) plt.gca().add_patch(circle) # 起点终点标记 plt.scatter(start[0], start[1], colorgreen, s80, markero, labelstart) plt.scatter(goal[0], goal[1], colorblue, s80, marker*, labelgoal) # 路径 plt.plot(positions[:, 0], positions[:, 1], b-, linewidth1.5, labelpath) plt.xlim(0, map_size) plt.ylim(0, map_size) plt.grid(True, linestyle--, alpha0.3) plt.legend() plt.title(2D APF Path Planning for UAV Obstacle Avoidance) plt.xlabel(x) plt.ylabel(y) plt.gca().set_aspect(equal, adjustablebox) plt.show() if __name__ __main__: main()代码里的注释我写得很细这里再挑几个关键点重点说明。第一步初始化算法类时确定好运动模型。calc_attractive_force里我特别加了一个判断当距离小于1e-6时直接返回零向量。这是为了防止目标点和无人机重合时距离为0导致除零错误。实际环境中你可能永远也到不了“完全重合”的状态但做防御性编程总没错。第二步计算斥力时用for循环遍历所有障碍物。你可能想优化成向量化的方式把所有障碍物一次性丢进numpy计算。我这个代码里用的是循环因为Python的for循环在障碍物数量不多时几十个性能差异几乎可以忽略但代码更直观。如果你的场景有几百个障碍物那建议改成矩阵批量计算。第三步主循环里最重要的两个判断——到达目标和陷入局部极小值。到达目标的判断用了goal_radius它是一个半径阈值。实际飞控中GPS本身就有定位误差你不可能要求无人机精确飞到目标点坐标只要进入半径范围就算到达。如果你用的是厘米级定位模块这个值可以设小一点比如0.2。4. 运行结果分析与关键参数调优实战4.1 第一次运行的默认结果分析我使用上面的默认参数k_att1.0, k_rep500.0, rho_012.0, gamma1.0, v_max4.0, dt0.05跑一次正常情况下能看到一条从起点出发、绕过多个障碍物、最终到达目标点的蓝色路径。路径的大致特征是这样的在远离障碍物的开阔区域无人机主要受引力作用路径几乎是一条直线指向目标。当它进入某个障碍物的斥力影响范围时路径开始出现平滑的曲线绕开障碍物绕开之后引力重新占据主导路径再次折回目标方向。这里有一个比较重要的观察点路径不会“贴着障碍物边缘走”而是有一段缓冲距离。这是因为斥力场是一个连续函数距离靠近时斥力变大距离远了斥力减小形成了一种自然的“软碰撞检测”。从安全性来说这意味着无人机和障碍物之间始终会留有一段安全距离不会出现硬碰硬的瞬间规避。4.2 关键参数对路径的影响与整定方法我在调试过程中尝试过很多组参数这里把最重要的几组对比结果整理成表格并且给出建议的调整方向。参数调大调小实际场景建议k_att路径更直快速冲向目标但可能撞上障碍物路径更绕远反应迟钝从1.0开始配合k_rep一起调k_rep避障更激进安全距离更大但路径扭曲避障不足可能撞障碍物从300~1000之间开始试rho_0无人机提前感知障碍物路径平滑感知延迟路径离障碍物太近通常设为传感器探测距离的1/2~2/3gamma飞行更平滑路径更稳响应更快但会抖动保持1.0如遇抖动才调v_max收敛更快但路径更“冲”收敛变慢但路径更稳根据无人机实际飞行速度来别超过飞控限速实际调试次数多了以后我总结出一个经验先固定k_att再调k_rep和rho_0。因为目标点的引力决定了路径的总体趋势而障碍物斥力只影响局部绕行。如果一上来就同时调三个参数很难判断路径变化是哪个参数引起的。具体调试步骤可以这样做固定k_att 1.0gamma 1.0v_max 4.0。把rho_0设为障碍物所在环境的合理值。比如障碍物分布在间距大概20米的场景rho_0设为8~10米就够太大了会让无人机在很远处就开始绕路路径变得非常保守。从小到大调k_rep观察路径是否会出现撞障碍物的情况。会出现就增大不会出现就减小直到路径足够平滑又保持安全距离。最后微调v_max和dt确保路径收敛时间合理且没有明显震荡。4.3 参数不当导致的异常现象与修正参数没调好时会看到几种很典型的现象我列出来你跑仿真时如果遇到就知道怎么回事。第一个现象无人机在某个障碍物前方来回振荡走不出去。这大概率是gamma太小或者dt太大导致的。因为每次步进位置变化量太大进入了“过冲—拉回—再过冲”的循环。解决方法是减小dt或者适当增大gamma让速度响应变慢。第二个现象无人机从很远处就开始大幅度绕障碍物路径像蛇形。这大概率是rho_0太大。你可以把斥力影响半径缩小到目标点和障碍物之间距离的1/3左右。例如目标点距离你当前位置30米而你设置的rho_0是25米那无人机就会因为过早感知障碍物而偏离直线太多。第三个现象无人机最终停在某个位置不动既不前进也不后退。这就是经典的局部极小值问题也就是引力和斥力在某个点正好大小相等方向相反合力为零。我下面专门用一节来说怎么处理它。5. 人工势场法两大经典难题的解决方案5.1 局部极小值问题与“虚拟逃逸”策略人工势场法最出名的坑就是局部极小值。怎么判断飞机是不是陷入局部极小值一个简单有效的方法是记录连续几次迭代的位置变化如果位置变化幅度小于某个阈值比如连续20步位置变化都小于0.1米就可以判断它卡住了。解决方案有三种。第一种是加入随机扰动。当检测到陷入极小值就给速度指令加上一个随机向量。这个方法简单粗暴适合仿真环境缺点是扰动方向可能不理想在复杂障碍物环境中逃逸效率低。第二种是改进势场函数利用带角度的斥力场把无人机和目标点之间的相对位置也引入斥力计算。这样斥力不仅远离障碍物还会引导飞机朝目标方向绕过去。这是目前学术论文里比较常用的改进思路实现也不难在斥力公式里乘上一个与目标距离相关的权重项。第三种是与全局规划算法融合先用A*或RRT生成一条无碰撞的路径把这条路径的关键点当成“子目标点”然后依次用人工势场法去追踪每个子目标。这样即使某个局部区域有极小值无人机也只会卡在当前一小段路径里不会导致整体任务失败。我自己的项目里最常用第三种思路。因为纯APF在复杂场景下无论如何改公式都有翻车概率而和全局路径结合之后可靠性高很多。简单场景用纯APF复杂动态场景一定要加全局规划器兜底。实操时如果只用纯APF我推荐一个比较偏经验的逃逸策略在检测到极小值后沿着无人机当前到目标点的方向旋转90度给出一个侧向推力持续3~5步再恢复正常算法。实测下来这个“侧向挣脱”策略在大多数场景下都能成功逃出极小值需要的代码量也很少只需要在主循环里加一个极小值判断标志位临时修改合力方向。# 在detect_stuck返回True时执行逃逸策略 def escape_local_minimum(position, goal, escape_direction): # escape_direction perpendicular vector pointing to the side delta goal - position # 计算与到目标方向垂直的向量 perp np.array([-delta[1], delta[0]]) perp perp / (np.linalg.norm(perp) 1e-6) return perp * 0.5 delta * 0.15.2 目标不可达问题当目标本身被障碍物包围目标不可达问题的触发条件很典型目标点刚好在障碍物的斥力影响范围内无人机一旦靠近目标点斥力就变得很大而引力随着距离变近而减小最后在目标点附近形成平衡无人机停在目标点外永远无法真正到达。解决方法是修改斥力场公式给斥力乘以一个与“无人机到目标距离”成正比的权重因子[ F_{rep}(q) F_{rep}(q) \cdot \rho^n(q, q_{goal}) ]这样当无人机越来越接近目标时目标距离 ( \rho(q, q_{goal}) ) 越来越小斥力被同步削弱引力则保持主导从而保证无人机能够最终到达目标点。( n ) 通常取 2 左右效果比较好。如果在仿真中看到无人机在目标点旁边来回振荡或者到目标附近后转圈优先级最高的排查顺序是先看目标点是否落在某个障碍物的rho_0范围内如果是直接改用带目标距离权重的斥力公式。这是我在实际工程里遇到次数最多的一个坑建议你在一开始就把这个改进整合进代码里省得后面返工。下面是修改后的calc_repulsive_force函数在原有基础上乘了一个到目标的距离权重class APFWithGoalWeight(PotentialField2D): def calc_repulsive_force(self, position, goal, obstacles): force np.array([0.0, 0.0]) for obs in obstacles: delta position - obs distance np.linalg.norm(delta) if distance self.rho_0 and distance 1e-6: magnitude self.k_rep * (1.0 / distance - 1.0 / self.rho_0) / (distance ** 2) direction delta / distance # 引入目标距离权重解决目标不可达问题 goal_distance np.linalg.norm(goal - position) weight goal_distance ** 2 force weight * magnitude * direction return force注意改了斥力公式以后k_rep需要重新调大因为多了目标距离平方的因子斥力整体变小了。我在测试时通常把k_rep从500提高到2000左右才能保持原有的避障效果。6. 常见问题与排查技巧实录6.1 三维扩展从二维平面到三维空域无人机和普通移动机器人最大的区别在于它是在三维空间里运动的。虽然本文的代码是二维仿真但扩展到三维并不难核心改动只有几个地方。第一位置向量从二维变成三维把start、goal、obstacles全部从np.array([x, y])改成np.array([x, y, z])。第二斥力计算里求欧氏距离的部分会自动适配三维因为np.linalg.norm不受维度影响。第三绘图部分需要更换了matplotlib二维路径画图不能直接用要使用mpl_toolkits.mplot3d里的Axes3D画三维路径。三维场景下需要注意一个额外的物理约束无人机在垂直方向上的运动性能和水平方向不一样。比如固定翼无人机无法原地悬停转向四旋翼在垂直方向爬升和下降的速度限制也不同。所以三维APF里一般会引入非对称速度限制也就是水平方向和垂直方向分别设定v_max_horizontal和v_max_vertical。6.2 动态障碍物场景的处理方法人工势场法天然适合处理动态障碍物因为它的计算每一帧都是基于当前位置和障碍物位置重新计算的。只要你在循环里更新障碍物的坐标无人机就能自动响应障碍物移动。但直接使用会有一个问题——无人机对障碍物的速度太敏感障碍物稍微一移动无人机路径就剧烈抖动。工程上常用的处理方法是给障碍物的位置加一个低通滤波或者对速度指令做时间平滑# 对速度指令做指数滑动平均 smoothed_velocity alpha * raw_velocity (1 - alpha) * smoothed_velocityalpha取0.3~0.5可以让运动更平滑但alpha不能太小否则响应严重滞后避障效果变差。我一般从0.4开始调根据实际效果微调。如果你有视觉传感器比如单目相机或深度相机把视觉检测出来的障碍物坐标替换掉我代码里的静态障碍物列表就完成了一个简单的视觉避障闭环。要注意的是视觉检测有延迟实际部署时建议做一步预测也就是用卡尔曼滤波预测障碍物的下一帧位置再代入APF计算。6.3 从仿真到真机部署的几个坑仿真跑通了不等于真机就能飞。我说几个最容易踩的坑。第一真实无人机有惯性不是仿真里的理想质点。我的代码用的是速度指令模型假设无人机底层控制器能瞬间响应速度指令。但实际上无人机加减速都需要时间所以如果你直接从仿真里把v_max4.0的指令丢给飞控很可能出现转向过冲、路径偏离预期的情况。解决方案是在APF和飞控之间加一个速度平滑层或者轨迹跟踪控制器。第二传感器噪声和定位误差会直接影响势场计算。仿真里假设无人机位置是精确的但真实GPS有1~2米误差IMU有漂移。这会直接导致计算出的引力和斥力方向和大小都有偏差无人机可能在两个障碍物之间来回晃。第三算力问题。树莓派或者STM32上跑Python不是不行但性能有限。如果障碍物数量多、控制频率要求高比如50Hz以上建议用C重写核心计算或者把算法部署到专门的边缘计算模块上。第四安全冗余。任何时候都不要让纯APF作为唯一的安全保障。我个人的习惯是APF负责正常飞行时的平滑避障但一定会保留一个基于几何计算的最小安全距离检测模块。一旦无人机进入危险距离直接接管控制权执行急停或拉升。7. 代码改进方向与扩展建议如果你看完本文想要继续深入这里给你两条扩展路径。一条是算法层面的改进。人工势场法有很多成熟的变体比如谐波函数势场、流体力学势场、数值化拉普拉斯势场这些方法通过重新定义势场函数来消除局部极小值。还有把随机采样加入势场法形成的随机势场法通过在势场中引入可控的随机扰动来保证概率完备性。这些算法在论文里都有现成的推导适合做研究项目或者毕设课题。另一条是工程应用层面的扩展。你可以把这段代码封装成ROS节点用map_server加载地图用laser_scan或depth camera作为障碍物输入再结合move_base的全局路径规划器做一个完整的无人机自主导航系统。如果你用的是PX4或ArduPilot飞控可以通过MAVLink协议把期望速度指令发送给飞控执行代码量也不会增加太多。另外本文当前处理的是静态环境。当你要处理多个无人机协同避障时核心思路是把其他无人机也当作动态障碍物加入斥力场这样编队飞行时每架无人机都能自主保持间距、避免碰撞。我预研过这个方向效果还挺不错的但要注意无人机之间通信延迟会导致斥力计算的滞后实际使用时需要预留额外的安全距离余量。建议每架无人机的斥力影响半径至少多留20%~30%的余量航向和高度方向分别设置不同的斥力增益因为垂直方向的障碍物感知能力通常弱于水平方向应当更加保守。绕了这么一大圈个人体会是这套算法在“无人机避障”这个课题里的地位就像练武之人的扎马步——招式朴素但它支撑了大量上层应用。你把它吃透了再去看那些带神经网络、带深度学习的复杂避障方案会发现很多思路都是在人工势场的基础上做文章。参数整定和极小值处理的经验也是通用的不管以后换成哪类算法这些调试方法论都依然有效。
返回列表