✅作者简介:热爱科研的Matlab仿真开发者,擅长数据处理、建模仿真、程序设计、完整代码获取、论文复现及科研仿真。
🍎 往期回顾关注个人主页:Matlab科研工作室
👇 关注我领取海量matlab电子书和数学建模资料
🍊个人信条:格物致知,完整Matlab代码获取及仿真咨询内容私信。
🔥 内容介绍
- 引言:为什么车辆路径规划需要 RRT+Dubins?
在自动驾驶、无人配送、智能巡检等场景中,车辆路径规划需满足两大核心需求:避障可行性与运动约束适配性。传统 RRT 算法虽能高效探索复杂环境并找到无碰撞路径,但生成的路径多为折线,无法适配车辆的非完整运动约束(如最小转弯半径、速度方向连续)—— 直接跟踪折线路径会导致车辆频繁启停、转向突变,甚至超出物理极限。
Dubins 曲线作为一种专门解决 “有最小转弯半径的移动机器人路径规划” 的理论工具,能生成光滑连续的最短路径(由直线 + 圆弧组成),恰好弥补 RRT 算法的短板。将 RRT 的 “避障探索能力” 与 Dubins 的 “运动约束适配能力” 相结合,可实现 “无碰撞 + 符合车辆动力学” 的高质量路径规划。本文将从原理、实现、实战三个维度,全面拆解该方案的落地逻辑。
- 核心基础:关键概念与技术铺垫
2.1 车辆运动约束与 Dubins 曲线原理
(1)车辆非完整运动约束
汽车、AGV 等轮式车辆属于非完整约束系统,核心约束包括:
最小转弯半径 R_min:受车轮转向角限制,无法实现零半径转弯;
速度方向连续:车辆运动方向与车身姿态一致,不能瞬间变向;
路径平滑性:转向角速度需在物理允许范围内(避免急刹急转)。
(2)Dubins 曲线核心原理
Dubins 曲线定义:在平面内,给定起点(x₀,y₀,θ₀)和终点(x_f,y_f,θ_f)(θ 为车身航向角),且满足最小转弯半径 R_min,最短的光滑路径由 3 段或 5 段基本曲线组成(直线 L、左转圆弧 L、右转圆弧 R),常见组合为 LRL、RLR、LRR、RLL、LLL、RRR(前两种为 5 段,后四种为 3 段)。
核心优势:
路径满足最小转弯半径约束,可直接被车辆跟踪;
路径长度最优(在给定约束下的最短路径);
曲率连续,转向平滑,降低车辆控制难度。
2.2 RRT 算法的适配改造思路
传统 RRT 算法的节点仅包含(x,y)位置信息,无法体现车辆航向角;扩展时采用直线步进,忽略最小转弯半径约束。为与 Dubins 曲线融合,需对 RRT 算法做两点关键改造:
节点状态扩展:每个节点包含(x,y,θ)三元组(位置 + 航向角),适配车辆姿态约束;
节点扩展方式优化:用 Dubins 曲线替代直线,连接当前节点与采样点,确保扩展路径符合最小转弯半径。
- 融合方案:RRT+Dubins 的核心逻辑
3.1 算法整体框架
RRT+Dubins 算法的核心思路是 “用 RRT 探索可行空间,用 Dubins 曲线连接节点,用碰撞检测过滤无效路径”,具体框架如下:
初始化:定义地图边界、障碍物信息、车辆参数(R_min)、起点 S(x₀,y₀,θ₀)、终点 G(x_f,y_f,θ_f);
构建随机树:以起点 S 为根节点,初始化随机树;
随机采样:在地图内随机生成采样点 P_rand(x_r,y_r,θ_r),需满足车辆运动范围约束;
最近节点查找:在随机树中找到与 P_rand 距离最近的节点 P_near(x_n,y_n,θ_n);
Dubins 路径生成:计算 P_near 到 P_rand 的 Dubins 曲线,作为候选扩展路径;
碰撞检测:检查 Dubins 曲线是否与障碍物碰撞,若安全则生成新节点 P_new(Dubins 曲线终点或步进终点);
目标判断:若 P_new 与终点 G 的距离小于设定阈值,计算 P_new 到 G 的 Dubins 曲线,若无碰撞则路径搜索成功;
路径优化:去除冗余节点,对最终路径进行 Dubins 曲线平滑优化。
Image
⛳️ 运行结果
Image
Image
Image
📣 部分代码
%%%%%%%%%%%%%%%%%%%%%%%%% DEFINE %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% The three segment types a path can be made up of
L_SEG = 1;
S_SEG = 2;
R_SEG = 3;
% The segment types for each of the Path types
DIRDATA = [ L_SEG, S_SEG, L_SEG ;...
L_SEG, S_SEG, R_SEG ;...
R_SEG, S_SEG, L_SEG ;...
R_SEG, S_SEG, R_SEG ;...
R_SEG, L_SEG, R_SEG ;...
L_SEG, R_SEG, L_SEG ];
%%%%%%%%%%%%%%%%%%%%%%%%% END DEFINE %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Generate the target configuration
types = DIRDATA(param.type, :);
param1 = param.seg_param(1);
param2 = param.seg_param(2);
param3 = param.seg_param(3);
r = param.r;
curve_cut = sum(param.seg_param(1:3))/segments;
t1 = 0:curve_cut:param1;
if( types(1) == L_SEG )
seg1 = [r* sin(param.p_init(3)+t1) ; r* -cos(param.p_init(3)+t1) ];
seg1 = [seg1(1,:) - r* sin(param.p_init(3)) + param.p_init(1) ;...
seg1(2,:) + r* cos(param.p_init(3)) + param.p_init(2) ];
elseif( types(1) == R_SEG )
seg1 = [r* sin(-param.p_init(3)+t1) ; r* cos(-param.p_init(3)+t1) ];
seg1 = [seg1(1,:) - r* sin(-param.p_init(3)) + param.p_init(1) ;...
seg1(2,:) - r* cos(-param.p_init(3)) + param.p_init(2) ];
end
mid_pt1 = dubins_segment( param1, param.p_init, types(1), r);
t2 = 0:curve_cut:param2;
if( types(2) == S_SEG )
seg2 = [r*t2* cos(mid_pt1(3)) ; r*t2*sin(mid_pt1(3))];
seg2 = [seg2(1,:) + mid_pt1(1) ;...
seg2(2,:) + mid_pt1(2) ];
elseif(types(2) == L_SEG)
seg2 = [r* sin(mid_pt1(3)+t2) ; r* -cos(mid_pt1(3)+t2) ];
seg2 = [seg2(1,:) - r* sin(mid_pt1(3)) + mid_pt1(1) ;...
seg2(2,:) + r* cos(mid_pt1(3)) + mid_pt1(2) ];
elseif(types(2) == R_SEG)
seg2 = [r* sin(-mid_pt1(3) + t2) ; r* cos(-mid_pt1(3) + t2) ];
seg2 = [seg2(1,:) - r* sin(-mid_pt1(3)) + mid_pt1(1) ;...
seg2(2,:) - r* cos(-mid_pt1(3)) + mid_pt1(2) ];
end
mid_pt2 = dubins_segment( param2, mid_pt1, types(2) , r);
t3 = 0:curve_cut:param3;
if( types(3) == L_SEG )
seg3 = [r* sin(mid_pt2(3)+t3) ; r* -cos(mid_pt2(3)+t3) ];
seg3 = [seg3(1,:) - r* sin(mid_pt2(3)) + mid_pt2(1) ;...
seg3(2,:) + r* cos(mid_pt2(3)) + mid_pt2(2) ];
elseif( types(3) == R_SEG )
seg3 = [r* sin(-mid_pt2(3)+t3) ; r* cos(-mid_pt2(3)+t3) ];
seg3 = [seg3(1,:) - r* sin(-mid_pt2(3)) + mid_pt2(1) ;...
seg3(2,:) - r* cos(-mid_pt2(3)) + mid_pt2(2) ];
end
path = [seg1, mid_pt1(1:2)', seg2, mid_pt2(1:2)', seg3];
end
%{
returns the parameter of certain location according to an inititalpoint,
segment type, and its corresponding parameter
%}
function seg_end = dubins_segment(seg_param, seg_init, seg_type, r)
L_SEG = 1;
S_SEG = 2;
R_SEG = 3;
if( seg_type == L_SEG )
seg_end(1) = seg_init(1) + r*( sin(seg_init(3)+seg_param) - sin(seg_init(3)) );
seg_end(2) = seg_init(2) - r*( cos(seg_init(3)+seg_param) - cos(seg_init(3)) );
seg_end(3) = seg_init(3) + seg_param;
elseif( seg_type == R_SEG )
seg_end(1) = seg_init(1) - r*( sin(seg_init(3)-seg_param) - sin(seg_init(3)) );
seg_end(2) = seg_init(2) + r*( cos(seg_init(3)-seg_param) - cos(seg_init(3)) );
seg_end(3) = seg_init(3) - seg_param;
elseif( seg_type == S_SEG )
seg_end(1) = seg_init(1) + cos(seg_init(3)) * seg_param * r;
seg_end(2) = seg_init(2) + sin(seg_init(3)) * seg_param * r;
seg_end(3) = seg_init(3);
end
end
🔗 参考文献
🎈 部分理论引用网络文献,若有侵权联系博主删除
🏆团队擅长辅导定制多种科研领域MATLAB仿真,助力科研梦: