当前位置: 首页 > news >正文

基于RRT与人工势场混合算法的路径规划程序

程序基于RRT和人工势场混合算法的路径规划

前阵子帮实验室的小破车改路径规划模块,一开始纯用RRT跑出来的路总是像无头苍蝇乱晃,遇到个小障碍物就直接贴上去绕半天,有时候甚至会在障碍物旁边打转半小时都找不着路。后来翻了点资料混了人工势场,效果终于顺眼了点,今天就唠唠这个混合算法的事儿。

先简单掰扯下俩算法各自的尿性:RRT虽然说是不会陷局部最优,但胜在全靠瞎蒙采样,跑出来的路径歪歪扭扭不说,还经常绕远路;人工势场倒是思路挺直观,给目标加吸引力,给障碍物加斥力,看着就能把车往终点拽,但最大的坑就是容易陷局部极小——比如俩障碍物中间刚好卡个点,吸引力和斥力刚好抵消,车就原地不动了,或者目标离障碍物太近,斥力把车推得老远,根本到不了终点。

程序基于RRT和人工势场混合算法的路径规划

所以混合的思路其实也简单:用RRT的随机采样兜底,解决势场的局部最优问题;用势场的合力来引导采样方向,减少RRT的盲目性,让路径更顺一点。我写了个简化版的Python demo,咱边看代码边说。

首先是导入库和初始化参数,没啥好说的,就是定义了地图范围、起点终点和俩圆形障碍物,凑合用来看效果:

import numpy as np import matplotlib.pyplot as plt from matplotlib.patches import Circle MAP_RANGE = np.array([0, 10, 0, 10]) START = np.array([1, 1]) GOAL = np.array([9, 9]) # 俩障碍物,一个在中间,一个在左上 OBSTACLES = [Circle((5,5), 1), Circle((3,7), 0.8)]

然后整了个简单的节点类,用来存每个采样点的位置和父节点,方便最后回溯路径:

class RRTNode: def __init__(self, pos): self.pos = pos self.parent = None

接下来是找最近节点的函数,纯纯的欧氏距离比对,没啥花活:

def find_nearest_node(nodes, sample_pos): distances = [np.linalg.norm(node.pos - sample_pos) for node in nodes] return nodes[np.argmin(distances)]

碰撞检测也很直白,就是看采样点有没有进到障碍物的圆里面:

def check_collision(pos, obstacles): for obs in obstacles: ox, oy = obs.center r = obs.radius if np.linalg.norm(pos - np.array([ox, oy])) < r: return True return False

重头戏来了,先整个人工势场的力场计算函数。这里用的是最基础的势场公式,吸引力是朝着目标的,斥力是推开远离障碍物的,要是碰到障碍物直接返回无穷大的力,相当于直接放弃这个点:

def get_force_field(pos, goal, obstacles, k_att=1.0, k_rep=5.0, r_rep=2.0): # 吸引力:朝着目标的反方向,也就是拉着车往终点走 f_att = -k_att * (pos - goal) # 斥力:挨个算每个障碍物对当前点的斥力 f_rep = np.zeros(2) for obs in obstacles: ox, oy = obs.center r_obs = obs.radius obs_pos = np.array([ox, oy]) dist = np.linalg.norm(pos - obs_pos) # 已经撞到障碍物了,直接返回无效力 if dist <= r_obs + 1e-3: return np.array([np.inf, np.inf]) # 只有在斥力作用范围内才计算斥力 if dist <= r_rep: rep_mag = k_rep * (1/dist - 1/r_rep) * (1/(dist**2)) f_rep += rep_mag * (pos - obs_pos) total_force = f_att + f_rep # 如果合力太小,说明陷入局部最优了,加点随机扰动兜底 if np.linalg.norm(total_force) < 1e-3: total_force = np.random.uniform(-1,1,2) # 返回单位方向向量,方便后面沿着这个方向采样 return total_force / np.linalg.norm(total_force)

然后就是修改后的混合算法扩展函数,和纯RRT比起来,主要改了采样的部分:10%的概率还是随机采样,避免一直沿着一个方向走;剩下90%的概率就沿着势场算出来的合力方向采样,再加点小扰动,防止太死板:

def extend_hybrid(start, goal, obstacles, max_iter=1000, step_size=0.5, sample_bias=0.1): nodes = [RRTNode(start)] for _ in range(max_iter): # 随机采样兜底,防止势场坑住 if np.random.rand() < sample_bias: sample_pos = np.random.uniform(MAP_RANGE[:2], MAP_RANGE[2:]) else: # 沿着势场方向采样,再加点小偏移 current_pos = nodes[-1].pos force_dir = get_force_field(current_pos, goal, obstacles) # 沿着合力方向走5步的距离,再加个正态分布的小扰动 sample_offset = np.random.normal(force_dir * step_size * 5, scale=0.5) sample_pos = current_pos + force_dir * step_size *5 + sample_offset # 把采样点限制在地图范围内,别跑出界了 sample_pos[0] = np.clip(sample_pos[0], MAP_RANGE[0], MAP_RANGE[1]) sample_pos[1] = np.clip(sample_pos[1], MAP_RANGE[2], MAP_RANGE[3]) # 后面的步骤和纯RRT一样,找最近节点、走一步、检查碰撞 nearest = find_nearest_node(nodes, sample_pos) direction = sample_pos - nearest.pos if np.linalg.norm(direction) < 1e-6: continue direction = direction / np.linalg.norm(direction) * step_size new_pos = nearest.pos + direction if not check_collision(new_pos, obstacles): new_node = RRTNode(new_pos) new_node.parent = nearest nodes.append(new_node) # 到终点附近就直接收尾 if np.linalg.norm(new_pos - goal) < step_size: final_node = RRTNode(goal) final_node.parent = new_node nodes.append(final_node) return nodes, final_node return nodes, None

最后就是回溯路径和画图的代码了,回溯就是从终点节点往父节点挨个找,然后反转一下变成从起点到终点的路径:

def get_path(final_node): path = [] current = final_node while current is not None: path.append(current.pos) current = current.parent return path[::-1] def plot_result(nodes, path): fig, ax = plt.subplots(figsize=(8,8)) ax.set_xlim(MAP_RANGE[0], MAP_RANGE[1]) ax.set_ylim(MAP_RANGE[2], MAP_RANGE[3]) # 画障碍物 for obs in OBSTACLES: ax.add_patch(obs) obs.set_facecolor('gray') obs.set_edgecolor('black') # 画起点终点 ax.scatter(START[0], START[1], c='green', s=100, label='起点') ax.scatter(GOAL[0], GOAL[1], c='red', s=100, label='终点') # 画RRT的搜索树 for node in nodes: if node.parent is not None: ax.plot([node.pos[0], node.parent.pos[0]], [node.pos[1], node.parent.pos[1]], c='blue', alpha=0.3) # 画最终路径 path_arr = np.array(path) ax.plot(path_arr[:,0], path_arr[:,1], c='red', linewidth=2, label='规划路径') ax.legend() plt.show()

跑起来的主函数也很简单:

if __name__ == "__main__": nodes, final_node = extend_hybrid(START, GOAL, OBSTACLES, max_iter=500) if final_node is not None: path = get_path(final_node) plot_result(nodes, path) else: print("害,没找到路径,调调参数再试试?")

说回实际效果,我一开始没加那个局部最优的扰动,结果跑到俩障碍物中间的时候直接卡死不动了,后来加了之后就会自动往旁边偏移一下绕过去。还有那个sample_bias参数,一开始设成0的话,跑出来的路径太直了,但遇到复杂一点的障碍物就容易陷进去,调到0.1左右刚好,既有势场引导的顺路径,又有RRT的随机兜底。

当然这个demo还是简化版的,实际用的话还要处理更多东西,比如动态障碍物、路径平滑之类的,但比起纯RRT和纯势场,这个混合算法已经好用太多了。当时改完之后,实验室的小破车终于能顺利绕开中间的障碍物开到终点,再也不会贴在墙上瞎转了,属实有点小成就感。

http://www.jsqmd.com/news/543002/

相关文章:

  • 手指划过屏幕放大模型界面,环氧树脂层和纤维基体在激光路径下呈现出清晰的物理场分布。突然发现这个双层材料烧蚀模型跑得格外顺畅——看来前几天通宵调参没白费
  • MAX30102血氧传感器避坑指南:如何解决I2C信号干扰问题(附Arduino代码)
  • LFM2.5-1.2B-Thinking-GGUF参数详解:如何通过temperature+top_p组合抑制幻觉输出
  • LyricsX:macOS平台的多源歌词同步与显示技术方案
  • BepInEx 技术入门指南:从架构理解到实践应用
  • 灵机一物AI智能电商小程序(已上线)-告别“人肉电商“:我们如何用 AI 数字员工,实现智能电商小程序自动化运营
  • HunyuanVideo-Foley部署案例:混合精度(FP16/AMP)推理性能实测报告
  • 从图像到数据:WebPlotDigitizer的高效图表数值提取指南
  • Mac上通过Docker Desktop快速部署MinIO对象存储实战指南
  • Gemma-3 Pixel Studio实操手册:集成企业微信机器人,实现移动端图片上传→自动回复结构化结果
  • Xinference-v1.17.1智能家居控制系统开发
  • OpenCV实战:用Python+SIFT+八点算法搞定双目视觉匹配(附完整代码)
  • 现代物流之智慧基石:基于西门子PLC的智能饲喂系统综合设计与实现
  • 隧道加热炉哪家好?隧道炉生产厂家哪家好?2026隧道炉生产定制厂家+加热炉生产厂家一站式定制指南 - 栗子测评
  • 大多数加密API都不够用:量化团队真正需要的数据到底是什么?
  • CMake 入门到实战笔记(通俗易懂,适合新手)
  • Django 学习日记(补充1)| 彻底吃透:自定义 JWT 认证 + 全局登录中间件
  • 2026年多模态AI前瞻:Qwen3-VL-2B开源生态发展潜力分析
  • 次元画室快速上手:用对话方式打造你的二次元角色
  • RTX 4090显卡福利:Qwen2.5-VL-7B-Instruct轻量化部署,支持对话历史管理
  • SDMatte+边缘精修教程:利用Alpha通道二次调整、PS中细化羽化与收缩参数
  • leetcode 困难题 1505. 最多 K 次交换相邻数位后得到的最小整数
  • WeMod Pro免费解锁终极指南:两种补丁方法完整对比与实战教程
  • 3个高级技巧:用ScintillaNET构建专业级文本编辑器的实战指南
  • SDMatte电商ROI测算:单图处理成本0.008元,较外包节省92%费用
  • 从一次线上OOM到MySQL锁表:我是如何用dmesg、jstack和jvisualvm揪出连环故障的
  • Miro收购Reforge,助力企业顺利迈向人工智能时代转型
  • FireRed-OCR保姆级教程:一键部署,精准提取表格公式转Markdown
  • Qwen3-VL历史文物识别:博物馆数字化管理部署解决方案
  • 77.基于matlab-GUI的图像分割分别包括超像素 (superpixels)分割 SLIC算法