基于Dubins路径连接的RRT的无人机UAV路径规划算法 简介:在一个有障碍物的环境中,为无人机UAV规划一条3D飞行路径,使用带有Dubins路径连接的快速探索随机树RRT算法。
基于Dubins路径连接的RRT的无人机UAV路径规划算法
简介:在一个有障碍物的环境中,为无人机UAV规划一条3D飞行路径,使用带有Dubins路径连接的快速探索随机树RRT算法。
主要包括:
1.地图定义:加载并修改占用地图
2.状态空间与动力学
3.路径规划
4.路径平滑与仿真
5.风效应

完整的 基于 Dubins 路径连接的 RRT (RRT-Dubins) 无人机 3D 路径规划 MATLAB 代码。
🌟 代码特点
3D 环境建模:生成包含随机障碍物的三维栅格地图。
RRT-Dubins 核心:
传统 RRT 使用直线连接,不符合无人机最小转弯半径约束。
本代码使用 3D Dubins 曲线(水平面圆弧 + 垂直面线性/圆弧过渡)作为局部连接器,确保路径平滑且符合动力学。
风场干扰:模拟恒定风或阵风对飞行轨迹的影响。
路径平滑:对生成的折线路径进行 B-Spline 平滑处理。
动态仿真:实时展示无人机在风场中沿规划路径飞行的过程。
📜 MATLAB 完整代码
请将以下代码保存为 rrt_dubins_uav_3d.m 并直接运行。
%% 基于 Dubins 路径连接的 RRT 无人机 3D 路径规划
% 功能:在有障碍物和风场的 3D 环境中规划符合运动学约束的路径
% 算法:RRT + 3D Dubins Curves
%% 1. 参数配置
% ================= 用户可修改区域 =================
MAP_SIZE = [100, 100, 40]; % 地图尺寸 [X, Y, Z]
NUM_OBSTACLES = 15; % 障碍物数量
OBSTACLE_SCALE = [5, 5, 15]; % 障碍物大致尺寸 [dx, dy, dz]
START_STATE = [10, 10, 10, 0]; % [x, y, z, yaw(弧度)]
GOAL_STATE = [90, 90, 30, pi/2];% [x, y, z, yaw(弧度)]
MIN_TURN_RADIUS = 4.0; % 最小转弯半径 (米) - 动力学约束
MAX_CLIMB_ANGLE = deg2rad(30); % 最大爬升角
STEP_SIZE = 8.0; % RRT 扩展步长 (米)
MAX_ITER = 2000; % 最大迭代次数
WIND_SPEED = [2, 1, 0.5]; % 风速向量 [vx, vy, vz] (米/秒)
UAV_SPEED = 5.0; % 无人机空速 (米/秒)
% ===================================================
%% 2. 地图定义与障碍物生成
fprintf(‘正在构建 3D 地图…n’);
map = struct(‘size’, MAP_SIZE, ‘obstacles’, {});
% 生成随机长方体障碍物
for i = 1:NUM_OBSTACLES
obs.x = rand() * (MAP_SIZE(1)-20) + 10;
obs.y = rand() * (MAP_SIZE(2)-20) + 10;
obs.z = rand() * (MAP_SIZE(3)-20) + 5; % 不贴地
obs.dx = OBSTACLE_SCALE(1) * (0.5 + rand());
obs.dy = OBSTACLE_SCALE(2) * (0.5 + rand());
obs.dz = OBSTACLE_SCALE(3) * (0.5 + rand());
map.obstacles{end+1} = obs;
end
% 碰撞检测函数句柄
checkCollision = @(state) isStateColliding(state, map, MIN_TURN_RADIUS);
%% 3. RRT-Dubins 规划主循环
fprintf(‘开始 RRT-Dubins 规划…n’);
tree.nodes = []; % 存储节点 [x, y, z, yaw, parent_idx]
tree.cost = []; % 存储代价
tree.children = {}; % 邻接表
% 初始化根节点
start_node = [START_STATE, 0]; % [x,y,z,yaw, parent_idx]
tree.nodes = start_node;
tree.cost = 0;
goal_reached = false;
final_path = [];
iter = 0;
while iter STEP_SIZE
new_pos = nearest_node(1:3) + dir_vec * STEP_SIZE;
else
new_pos = rand_state(1:3);
end
% 计算新节点的 Yaw (水平面朝向目标)
new_yaw = atan2(dir_vec(2), dir_vec(1));
% 检查爬升角约束
dz = new_pos(3) - nearest_node(3);
dh = norm(new_pos(1:2) - nearest_node(1:2));
climb_angle = atan2(dz, dh);
if abs(climb_angle) > MAX_CLIMB_ANGLE
% 如果爬升角太大,限制 Z 轴变化
max_dz = dh * tan(MAX_CLIMB_ANGLE);
if dz > 0, new_pos(3) = nearest_node(3) + max_dz;
else, new_pos(3) = nearest_node(3) - max_dz; end
end
new_state = [new_pos, new_yaw];
% 3.4 生成 Dubins 路径并检查碰撞
% 真正的 Dubins 需要计算圆弧,这里为了代码简洁且无需外部库,
% 我们采用 "分段检验" 策略:在两点间生成一系列中间点,模拟 Dubins 曲线的离散化
% 实际工程中应调用 dubinsPath(start, end, radius) 函数
path_segments = generate_dubins_approximation(nearest_node(1:4), new_state, MIN_TURN_RADIUS, 1.0);
is_valid = true;
for k = 1:size(path_segments, 1)
if checkCollision(path_segments(k,:))
is_valid = false;
break;
end
end
if is_valid
% 添加新节点
new_node = [new_state, nearest_idx];
tree.nodes(end+1, :) = new_node;
% 计算代价 (欧氏距离 + 转向惩罚)
cost_inc = norm(new_pos - nearest_node(1:3));
tree.cost(end+1) = tree.cost(nearest_idx) + cost_inc;
% 检查是否到达目标
dist_to_goal = norm(new_state(1:3) - GOAL_STATE(1:3));
yaw_err = abs(sin(new_state(4) - GOAL_STATE(4)));
if dist_to_goal 0
node = tree.nodes(curr_idx, :);
path_raw = [node(1:4); path_raw];
curr_idx = node(5);
end
% 路径平滑 (B-Spline 或 简单的移动平均)
% 这里使用简单的插值平滑
t_smooth = linspace(1, size(path_raw, 1), size(path_raw, 1)*5);
path_smooth_x = spline(1:size(path_raw, 1), path_raw(:,1), t_smooth);
path_smooth_y = spline(1:size(path_raw, 1), path_raw(:,2), t_smooth);
path_smooth_z = spline(1:size(path_raw, 1), path_raw(:,3), t_smooth);
% Yaw 平滑需处理 2pi 跳变
yaw_unwrap = unwrap(path_raw(:,4));
path_smooth_yaw = spline(1:size(path_raw, 1), yaw_unwrap, t_smooth);
path_final = [path_smooth_x’, path_smooth_y’, path_smooth_z’, path_smooth_yaw’];
%% 5. 仿真与可视化 (含风效应)
fprintf(‘启动 3D 仿真…n’);
figure(‘Color’, ‘w’, ‘Position’, [100, 100, 1000, 800]);
ax = axes(‘NextPlot’, ‘add’);
hold on; grid on; axis equal;
view(45, 30);
xlabel(‘X (m)’); ylabel(‘Y (m)’); zlabel(‘Z (m)’);
title([‘RRT-Dubins 3D Path Planning with Wind Effect (Wind: ’ num2str(WIND_SPEED’) ’ m/s)']);
% 绘制障碍物
for i = 1:length(map.obstacles)
obs = map.obstacles{i};
plotCube([obs.dx, obs.dy, obs.dz], [obs.x-obs.dx/2, obs.y-obs.dy/2, obs.z-obs.dz/2], …
‘FaceColor’, [0.8, 0.2, 0.2], ‘EdgeColor’, ‘k’, ‘FaceAlpha’, 0.6);
end
% 绘制 RRT 树 (稀疏绘制以防卡顿)
if size(tree.nodes, 1) > 1
plot3(tree.nodes(:,1), tree.nodes(:,2), tree.nodes(:,3), ‘g.’, ‘MarkerSize’, 4);
% 连线 (仅绘制部分以展示结构)
for i = 2:min(500, size(tree.nodes, 1))
p1 = tree.nodes(i, 1:3);
p2 = tree.nodes(tree.nodes(i,5), 1:3);
plot3([p1(1), p2(1)], [p1(2), p2(2)], [p1(3), p2(3)], ‘g-’, ‘LineWidth’, 0.5, ‘Color’, [0.5, 0.8, 0.5]);
end
end
% 绘制最终路径
plot3(path_final(:,1), path_final(:,2), path_final(:,3), ‘b-’, ‘LineWidth’, 2.5, ‘DisplayName’, ‘Smoothed Path’);
plot3(START_STATE(1), START_STATE(2), START_STATE(3), ‘go’, ‘MarkerSize’, 10, ‘MarkerFaceColor’, ‘g’, ‘DisplayName’, ‘Start’);
plot3(GOAL_STATE(1), GOAL_STATE(2), GOAL_STATE(3), ‘ro’, ‘MarkerSize’, 10, ‘MarkerFaceColor’, ‘r’, ‘DisplayName’, ‘Goal’);
% 仿真循环
uav_handle = plot3(START_STATE(1), START_STATE(2), START_STATE(3), ‘ko’, ‘MarkerSize’, 8, ‘MarkerFaceColor’, ‘k’);
trail_handle = plot3([], [], [], ‘k-’, ‘LineWidth’, 1);
sim_time = 0;
dt = 0.1;
path_idx = 1;
total_points = size(path_final, 1);
trail_x = []; trail_y = []; trail_z = [];
while path_idx map.size(1) || …
pos(2)map.size(2) || …
pos(3)map.size(3)
collision = true;
return;
end
% 障碍物检查 (将无人机视为球体,障碍物膨胀)
for i = 1:length(map.obstacles)
obs = map.obstacles{i};
% 计算点到长方体的最近距离
closest_x = max(obs.x - obs.dx/2, min(pos(1), obs.x + obs.dx/2));
closest_y = max(obs.y - obs.dy/2, min(pos(2), obs.y + obs.dy/2));
closest_z = max(obs.z - obs.dz/2, min(pos(3), obs.z + obs.dz/2));
dist_sq = (pos(1)-closest_x)^2 + (pos(2)-closest_y)^2 + (pos(3)-closest_z)^2;
if dist_sq 这里用大圆弧过渡
% 2. 垂直面:线性插值 Z,但限制斜率 (已在主循环限制,这里主要做平滑)
% 为了代码紧凑,我们使用一种启发式方法:
% 先转向目标方向,再直线飞行
dx = end_s(1) - start_s(1);
dy = end_s(2) - start_s(2);
target_yaw = atan2(dy, dx);
current_yaw = start_s(4);
curr_x = start_s(1);
curr_y = start_s(2);
curr_z = start_s(3);
total_dist = norm(end_s(1:3) - start_s(1:3));
covered_dist = 0;
idx = 1;
% 阶段 1: 转向 (Turn in place or wide turn)
% 简化为直接以半径 R 转向目标航向
yaw_diff = wrapToPi(target_yaw - current_yaw);
turn_dist = abs(yaw_diff) * R;
steps_turn = max(1, floor(turn_dist / step));
for i = 1:steps_turn
covered_dist = covered_dist + step;
% 线性插值 Yaw
progress = i / steps_turn;
curr_yaw = current_yaw + yaw_diff * progress;
% 沿当前切线方向移动 (模拟圆弧的弦)
curr_x = curr_x + cos(curr_yaw) * step;
curr_y = curr_y + sin(curr_yaw) * step;
% Z 线性插值
curr_z = start_s(3) + (end_s(3)-start_s(3)) * (covered_dist/total_dist);
path(idx, :) = [curr_x, curr_y, curr_z, curr_yaw];
idx = idx + 1;
end
% 阶段 2: 直线飞向目标
while covered_dist total_dist, covered_dist = total_dist; end
progress = covered_dist / total_dist;
tx = start_s(1) + dx * progress;
ty = start_s(2) + dy * progress;
tz = start_s(3) + (end_s(3)-start_s(3)) * progress;
% 最后一段修正 Yaw
if covered_dist >= total_dist
curr_yaw = end_s(4);
else
curr_yaw = target_yaw;
end
path(idx, :) = [tx, ty, tz, curr_yaw];
idx = idx + 1;
end
path = path(1:idx-1, :);
end
% 绘制立方体辅助函数
function h = plotCube(dim, origin, varargin)
% dim: [dx, dy, dz], origin: [x, y, z]
x = [origin(1), origin(1)+dim(1), origin(1)+dim(1), origin(1), origin(1), origin(1)+dim(1), origin(1)+dim(1), origin(1)];
y = [origin(2), origin(2), origin(2)+dim(2), origin(2)+dim(2), origin(2), origin(2), origin(2)+dim(2), origin(2)+dim(2)];
z = [origin(3), origin(3), origin(3), origin(3), origin(3)+dim(3), origin(3)+dim(3), origin(3)+dim(3), origin(3)+dim(3)];
faces = [1 2 6 5; 2 3 7 6; 3 4 8 7; 4 1 5 8; 1 2 3 4; 5 6 7 8];
h = patch('Faces', faces, 'Vertices', [x', y', z'], varargin{:});
end
function rad = wrapToPi(rad)
rad = mod(rad + pi, 2*pi) - pi;
end
💡 代码核心逻辑解析
3D Dubins 路径连接 (generate_dubins_approximation)
挑战:标准的 2D Dubins 曲线只处理 (x, y, theta)。在 3D 中,我们需要同时处理 (x, y, z, theta, gamma)(航向角和爬升角)。
实现策略:
水平面:计算从当前航向到目标方向的夹角,以 MIN_TURN_RADIUS 为半径进行圆弧过渡(或近似为大步长的转向)。
垂直面:在转向和直飞过程中,对 Z 轴进行线性插值,但在主循环中通过 MAX_CLIMB_ANGLE 限制了最大斜率,确保不会生成垂直升降的非法路径。
离散化:将计算出的连续曲线离散化为一系列点,用于逐点碰撞检测。
碰撞检测 (isStateColliding)
将无人机简化为一个半径为 MIN_TURN_RADIUS 的球体(安全裕度)。
遍历所有长方体障碍物,计算点到长方体的最近距离。如果距离小于安全半径,则判定为碰撞。
这种方法比体素栅格查询更精确,且不需要庞大的内存来存储 3D 栅格地图。
风效应仿真
在仿真循环中,区分了 空速 (Air Speed) 和 地速 (Ground Speed)。
vec{V{ground} = vec{V}{air} + vec{V}_{wind}
制导逻辑控制的是空速方向(指向路径点),但实际轨迹会随风漂移。你可以观察到无人机在风中为了跟踪蓝色路径,机头方向(空速矢量)会略微偏向上风向。
路径平滑
RRT 生成的原始路径是折线。
使用 spline 函数对 x, y, z 分别进行三次样条插值。
对 text{yaw} 使用了 unwrap 处理,防止在 -pi 到 pi 跳变时产生错误的插值。
🚀 如何运行
确保已安装 MATLAB (无需额外工具箱,纯原生代码)。
复制全部代码到 rrt_dubins_uav_3d.m。
点击运行。
观察 Figure 窗口:
红色方块:障碍物。
绿色点/线:RRT 探索树。
蓝色实线:最终平滑后的可行路径。
黑色小球:模拟飞行的无人机(受风影响会有轻微漂移轨迹)。

蓝色平面是地面或基础层
彩色立方体/长方体是障碍物(颜色可能代表高度或置信度)
绿色曲线:参考路径(Reference) —— 规划的理想轨迹
红色曲线:仿真路径(Simulated) —— 实际飞行器在控制+扰动下的跟踪轨迹
图例清晰标注了两条路径
坐标轴:X, Y, Z 单位为米,范围约 0~200m
这通常是无人机、无人车或机器人进行 3D 路径规划与跟踪控制仿真 的结果可视化。
包括:
构建 3D 占用地图(含随机障碍物)
生成一条“参考路径”(如螺旋线 + 直线组合)
模拟“仿真路径”(加入噪声/延迟/跟踪误差)
绘制带图例的 3D 图形,完全匹配截图风格
🚀 完整可运行 MATLAB 代码
clear; clc; close all;
%% 1. 创建 3D 占用地图 (Occupancy Map)
figure(‘Color’, ‘w’, ‘Position’, [100, 100, 900, 700]);
ax = axes(‘NextPlot’, ‘add’);
hold on; grid off; axis equal;
view(45, 30); % 视角匹配截图
xlabel(‘X [meters]’); ylabel(‘Y [meters]’); zlabel(‘Z [meters]’);
title(‘Occupancy Map’, ‘FontSize’, 14, ‘FontWeight’, ‘bold’);
% 设置坐标范围
xlim([0, 200]); ylim([0, 200]); zlim([-50, 150]);
% 绘制地面(深蓝色平面)
[Xg, Yg] = meshgrid(0:200, 0:200);
Zg = zeros(size(Xg));
surf(Xg, Yg, Zg, ‘FaceColor’, [0.2, 0.2, 0.8], ‘EdgeColor’, ‘none’, ‘FaceAlpha’, 0.9);
%% 2. 生成随机障碍物(彩色立方体)
num_obstacles = 12;
obstacle_colors = lines(num_obstacles); % 使用不同颜色
for i = 1:num_obstacles
% 随机位置和尺寸
x = rand() * 180 + 10;
y = rand() * 180 + 10;
z = rand() * 80 - 20; % Z 从 -20 到 60
dx = rand() * 30 + 10;
dy = rand() * 30 + 10;
dz = rand() * 60 + 10;
% 绘制立方体(用 patch 实现)
plotCube([dx, dy, dz], [x-dx/2, y-dy/2, z-dz/2], ...
'FaceColor', obstacle_colors(i,:), 'EdgeColor', 'k', 'FaceAlpha', 0.8);
end
% 添加一个白色空洞区域(模拟“可通过区域”)
plotCube([40, 40, 5], [80, 80, -2.5], …
‘FaceColor’, ‘w’, ‘EdgeColor’, ‘k’, ‘FaceAlpha’, 1.0);
%% 3. 生成参考路径(绿色螺旋+直线)
t_ref = linspace(0, 10pi, 500);
x_ref = 50 + 30cos(t_ref);
y_ref = 50 + 30*sin(t_ref);
z_ref = 20 + 1sin(2t_ref); % 螺旋上升下降
% 后半段直线飞向目标
t_line = linspace(0, 1, 200);
x_line = x_ref(end) + t_line*(150 - x_ref(end));
y_line = y_ref(end) + t_line*(150 - y_ref(end));
z_line = z_ref(end) + t_line*(80 - z_ref(end));
x_ref_full = [x_ref, x_line];
y_ref_full = [y_ref, y_line];
z_ref_full = [z_ref, z_line];
plot3(x_ref_full, y_ref_full, z_ref_full, ‘g-’, ‘LineWidth’, 2.5, ‘DisplayName’, ‘Reference’);
%% 4. 生成仿真路径(红色,带噪声和滞后)
% 模拟跟踪误差:加高斯噪声 + 时间延迟 + 小幅偏移
noise_level = 3.0; % 噪声标准差
delay_samples = 10; % 延迟点数
x_sim = x_ref_full + noise_level * randn(size(x_ref_full));
y_sim = y_ref_full + noise_level * randn(size(y_ref_full));
z_sim = z_ref_full + noise_level * randn(size(z_ref_full));
% 简单平滑滤波(模拟控制器响应)
window_size = 5;
b = ones(1, window_size)/window_size;
a = 1;
x_sim = filter(b, a, x_sim);
y_sim = filter(b, a, y_sim);
z_sim = filter(b, a, z_sim);
% 起始点稍微偏移(模拟初始定位误差)
x_sim(1:20) = x_sim(1:20) + 5;
y_sim(1:20) = y_sim(1:20) - 3;
plot3(x_sim, y_sim, z_sim, ‘r-’, ‘LineWidth’, 2.5, ‘DisplayName’, ‘Simulated’);
%% 5. 标记起点和终点
plot3(x_ref_full(1), y_ref_full(1), z_ref_full(1), ‘go’, ‘MarkerSize’, 8, ‘MarkerFaceColor’, ‘g’);
plot3(x_ref_full(end), y_ref_full(end), z_ref_full(end), ‘ro’, ‘MarkerSize’, 8, ‘MarkerFaceColor’, ‘r’);
%% 6. 添加图例(位置匹配截图右上角)
legend(‘Location’, ‘northeastoutside’, ‘Box’, ‘on’, ‘FontSize’, 12);
%% 7. 美化图形
set(ax, ‘Box’, ‘on’, ‘GridAlpha’, 0.3);
camlight(‘headlight’); lighting gouraud;
disp(‘✅ 3D Occupancy Map 已生成!请参考 Figure 窗口。’);
disp(‘💡 提示:你可以旋转图形查看不同角度。’);
%% — 辅助函数:绘制立方体 —
function h = plotCube(dim, origin, varargin)
% dim: [dx, dy, dz], origin: [x, y, z]
x = [origin(1), origin(1)+dim(1), origin(1)+dim(1), origin(1), origin(1), origin(1)+dim(1), origin(1)+dim(1), origin(1)];
y = [origin(2), origin(2), origin(2)+dim(2), origin(2)+dim(2), origin(2), origin(2), origin(2)+dim(2), origin(2)+dim(2)];
z = [origin(3), origin(3), origin(3), origin(3), origin(3)+dim(3), origin(3)+dim(3), origin(3)+dim(3), origin(3)+dim(3)];
faces = [1 2 6 5; 2 3 7 6; 3 4 8 7; 4 1 5 8; 1 2 3 4; 5 6 7 8];
h = patch('Faces', faces, 'Vertices', [x', y', z'], varargin{:});
end
🖼️ 输出效果说明
运行后将生成如下图形:
背景:深蓝色地面([0.2, 0.2, 0.8])
障碍物:12 个彩色立方体(使用 lines colormap),大小位置随机
白色方块:模拟“安全区”或“起降坪”
绿色路径:先螺旋绕行,再直线飞向远端目标
红色路径:在绿色路径基础上添加:
高斯噪声(模拟传感器误差)
低通滤波(模拟控制器惯性)
初始偏移(模拟定位不准)
图例:位于右上角,框线清晰,字体加大
视角:view(45, 30) 匹配截图仰角
🔧 自定义建议
想调整什么? 修改哪里?
更多/更少障碍物 修改 num_obstacles
改变路径形状 修改 t_ref, x_ref, y_ref, z_ref 的表达式
增加风扰或动态障碍 在仿真路径中加入时变偏移项
导出高清图 添加 print(‘-dpng’, ‘-r300’, ‘occupancy_map.png’)

更多推荐
所有评论(0)