12-AirSim+四旋翼仿真-RRT方法路径规划

之前已经实现了人工势场法避障的python仿真,人工势场法适用于局部避障,不依赖全局障碍物信息,根据实时检测到的障碍物即可进行避障。但其不能确保得到的路径最优,且存在局部极小值等问题。如果在已知部分障碍物信息的情况下,进行全局的路径规划,以局部避障方法作为辅助,可以得到更好的效果。

经过算法调研,了解到RRT方法(快速扩展随机树)和PRM方法(概率路线图方法)可以实现全局障碍物信息下的路径规划。PRM方法为,在地图中进行点采样,通过采样点获取可行路径;RRT方法为,从起始点开始进行节点扩展,每次有一定概率向目标点或一个随机点扩展,最终生成一棵包含可行路径的树。可通过A*等算法在当前采样点中寻找最优路径。

算法原理比较简单,接下来说明算法实现步骤,详细的代码见https://github.com/Kun-k/airsim_python/blob/main/code_python/python_avoid_RRT.py。

1.初始化地图

取一个二维数组作为输入地图,0表示该点为障碍物,1表示该点为可行点。读取一张图片并二值化,即可得到一个这样的数组。

# 读取地图图像并二值化
img = cv2.imread('6.png')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
retval, mymap = cv2.threshold(gray, 0, 255, cv2.THRESH_OTSU)
mymap = cv2.dilate(mymap, None, iterations=1).T
mymap = cv2.erode(mymap, None, iterations=4) / 255

为了便于记录和去重,生成一个与mymap相同尺寸的数组,用于储存各个位置的树节点。

tree_map = []  # 记录不同位置产生的节点,用于生成树时去重,0表示障碍物,None表示未访问,否则为已访问
for i in range(mapsize[0]):
    tree_map.append([])
    for j in range(mapsize[1]):
        if mymap[i][j] == 0:
            tree_map[i].append(0)
        else:
            tree_map[i].append(None)
2.设置航路点和参数

取下图所示的地图,分辨率为600x750,出发航路点为左下的[0,0]点,目标航路点为右上的[380,700]航路点。

6-flip

可设置的参数包括:

  1. 随机采样概率p_sample,在p_sample的概率下选择一个随机点作为采样点,否则以目标点作为采样点;
  2. 最大迭代次数maxlimit,若超出该迭代次数后仍未查找到路径,则认为查找失败,退出;
  3. 步长step,即每次向采样点移动的距离;
  4. 误差epsilon,与目标点的距离小于该值时认为查找到路径;
  5. 安全距离Q_save,选择航路点时,与障碍物的距离需大于安全距离。
3.点采样

p_sample的概率下选择一个随机点作为采样点,否则以目标点作为采样点,

# 获取采样点
sample = [0, 0]
while tree_map[sample[0]][sample[1]] is not None:
    if np.random.rand() < p_sample:
        sample = np.array([np.random.randint(0, mapsize[0]),
                           np.random.randint(0, mapsize[1])])
    else:
        sample = aim
4.树的生长

获取采样点后,在已搜索的点中,选择距离采样点最近的点作为当前的出发节点p_curr

# 在已搜索的点中,寻找距离采样点最近的点p_curr
min_dis = float('inf')
p_curr = start
for p_tmp in p_record:
    dis = distance(p_tmp, sample)
    if dis < min_dis:
        p_curr = p_tmp
        min_dis = dis

接下来由p_curr出发向采样点生长,生长的步长为step,得到点p_next

direction = (sample - p_curr) / np.linalg.norm((sample - p_curr))
p_next = p_curr + direction * step

需判断点p_next是否可行,这里进行三个限制

  1. p_next位置无障碍物

    if 0 <= p_next[0] < mapsize[0] and 0 <= p_next[1] < mapsize[1] \
            and tree_map[int(p_next[0])][int(p_next[1])] is None:  # 是否为非障碍物点
        flag = True  # True表示该点可行
    
  2. p_next周围安全距离的范围内无障碍物

    for dir in dir_save:  # 判断点是否为安全点
        p_search = p_next + dir
        if not (0 <= p_search[0] < mapsize[0] and 0 <= p_search[1] < mapsize[1]
                and tree_map[int(p_search[0])][int(p_search[1])] != 0):
            flag = False
            break
    
  3. p_currp_next的路径上是否存在障碍物

    d_next = distance(p_curr, p_next)
    direction = (p_next - p_curr) / np.linalg.norm((p_next - p_curr))
    for d_search in range(1, int(d_next) + 1):
        p_search = p_curr + d_search * direction
        if not (0 <= p_search[0] < mapsize[0] and 0 <= p_search[1] < mapsize[1]
                and tree_map[int(p_search[0])][int(p_search[1])] != 0):  # 是否为安全点
            flag = False
            break
    

如果同时满足这三个条件,则认为点p_next可行,p_currp_next的路径可行,此时在p_next产生一个新节点,其父节点为p_curr

parenttree = tree_map[int(p_curr[0])][int(p_curr[1])]
newtree = rtt_treenode(p_next, parent=parenttree,
                       cost_from_start=parenttree.cost + step,
                       dis_to_aim=distance(p_next, aim))
tree_map[int(p_next[0])][int(p_next[1])] = newtree
parenttree.children.append(newtree)
p_record.append(p_next)
e = newtree.dis
5.树生长的终止条件

当查找到一个节点,其与目标点的距离小于epsilon,则终止;或迭代次数超出设定值,终止。

6.最优路径搜索

这里使用A*算法进行搜索。

def A_star(root, eposilon):
    q = PriorityQueue()
    q.push(root)
    while q.size() != 0:
        node_curr = q.pop()
        for child in node_curr.children:
            # 生成树时已经去重,搜索时不再进行
            if child.dis < eposilon:  # 目标点
                return child.path()
            q.push(child)
    return []
7.结果展示

设置参数为

p_sample=0.1, maxlimit=5000, step=100, eposilon=50, Q_save=10

得到的结果为:

屏幕捕获_2022_07_22_12_03_40_89

在UE中设置障碍物,上图中的10个像素点对应UE中的1米,即步长为10米,误差为5米,安全距离为1米。将上图中得到的路径点调整为AirSim中的航路点后,使用之前完成的航路点跟踪算法,得到的飞行效果如下:

(航路点跟踪算法见 https://blog.csdn.net/k_kun/article/details/125726077?spm=1001.2014.3001.5501)

RRT方法路径规划AirSim+四旋翼仿真

设置不同的参数,将得到不同的效果。将采样概率修改为0.5后,得到树的随机性变大,分枝数变多:

image-20220726115721513

继续修改参数,将步长调整为20后,得到的树节点更加密集,计算量也更大。此时,AirSim仿真中两个航路点的距离为2米,距离过短,在较快速度下的飞行效果比较差。

image-20220726120122822

RRT方法参考来源:https://zhuanlan.zhihu.com/p/66047152

  • 5
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
MATLAB 是一个非常好的工具,可以用来模拟四旋翼无人机的运动。下面是一个基本的四旋翼无人机模型的示例: ```matlab % 定义四旋翼无人机的参数 m = 1.2; % 质量,单位 kg g = 9.81; % 重力加速度,单位 m/s^2 l = 0.25; % 旋翼到中心的距离,单位 m Jx = 0.034; % 绕 x 轴的惯性矩,单位 kg m^2 Jy = 0.045; % 绕 y 轴的惯性矩,单位 kg m^2 Jz = 0.097; % 绕 z 轴的惯性矩,单位 kg m^2 % 定义初始状态 x0 = 0; % x 位置,单位 m y0 = 0; % y 位置,单位 m z0 = 0; % z 位置,单位 m phi0 = 0; % 绕 x 轴的欧拉角,单位 rad theta0 = 0; % 绕 y 轴的欧拉角,单位 rad psi0 = 0; % 绕 z 轴的欧拉角,单位 rad u0 = 0; % x 方向速度,单位 m/s v0 = 0; % y 方向速度,单位 m/s w0 = 0; % z 方向速度,单位 m/s p0 = 0; % 绕 x 轴的角速度,单位 rad/s q0 = 0; % 绕 y 轴的角速度,单位 rad/s r0 = 0; % 绕 z 轴的角速度,单位 rad/s % 定义控制输入 % 这里我们简单地定义为固定的电机转速 w1 = 10; % 旋翼 1 的转速,单位 rad/s w2 = 10; % 旋翼 2 的转速,单位 rad/s w3 = 10; % 旋翼 3 的转速,单位 rad/s w4 = 10; % 旋翼 4 的转速,单位 rad/s % 定义仿真时间 tspan = [0 10]; % 定义初始状态向量 x0 = [x0 y0 z0 phi0 theta0 psi0 u0 v0 w0 p0 q0 r0]; % 定义控制输入向量 u = [w1 w2 w3 w4]; % 调用 ode45 函数进行求解 [t, x] = ode45(@(t, x) quadrotor_ode(t, x, u, m, g, l, Jx, Jy, Jz), tspan, x0); % 绘制无人机的轨迹 plot3(x(:,1), x(:,2), x(:,3)); ``` 其中,`quadrotor_ode` 函数用来计算四旋翼无人机的动力学方程。这个函数的代码如下: ```matlab function dxdt = quadrotor_ode(t, x, u, m, g, l, Jx, Jy, Jz) % 计算四旋翼无人机的动力学方程 % 解析状态向量 x1 = x(1); % x 位置,单位 m x2 = x(2); % y 位置,单位 m x3 = x(3); % z 位置,单位 m x4 = x(4); % 绕 x 轴的欧拉角,单位 rad x5 = x(5); % 绕 y 轴的欧拉角,单位 rad x6 = x(6); % 绕 z 轴的欧拉角,单位 rad x7 = x(7); % x 方向速度,单位 m/s x8 = x(8); % y 方向速度,单位 m/s x9 = x(9); % z 方向速度,单位 m/s x10 = x(10); % 绕 x 轴的角速度,单位 rad/s x11 = x(11); % 绕 y 轴的角速度,单位 rad/s x12 = x(12); % 绕 z 轴的角速度,单位 rad/s % 解析控制输入向量 w1 = u(1); % 旋翼 1 的转速,单位 rad/s w2 = u(2); % 旋翼 2 的转速,单位 rad/s w3 = u(3); % 旋翼 3 的转速,单位 rad/s w4 = u(4); % 旋翼 4 的转速,单位 rad/s % 计算四旋翼无人机的动力学方程 dxdt = zeros(12, 1); dxdt(1) = x7; dxdt(2) = x8; dxdt(3) = x9; dxdt(4) = x10 + sin(x4)*tan(x5)*x11 + cos(x4)*tan(x5)*x12; dxdt(5) = cos(x4)*x11 - sin(x4)*x12; dxdt(6) = sin(x4)/cos(x5)*x11 + cos(x4)/cos(x5)*x12; dxdt(7) = -g*sin(x5) + (cos(x4)*sin(x5)*cos(x6) + sin(x4)*sin(x6))*sum(w1, w2, w3, w4)/m; dxdt(8) = g*sin(x4)*cos(x5) - cos(x4)*cos(x5)*cos(x6)*sum(w1, w2, w3, w4)/m; dxdt(9) = g*cos(x4)*cos(x5)*cos(x6) - sin(x4)*cos(x5)*cos(x6)*sum(w1, w2, w3, w4)/m - g; dxdt(10) = (Jy - Jz)*x11*x12/Jx + l*(w2^2 + w4^2 - w1^2 - w3^2)/Jx; dxdt(11) = (Jz - Jx)*x10*x12/Jy + l*(w3^2 + w4^2 - w1^2 - w2^2)/Jy; dxdt(12) = (Jx - Jy)*x10*x11/Jz + (w1^2 + w2^2 - w3^2 - w4^2)/Jz; end ``` 这个函数使用了四元数来表示无人机的姿态,同时也考虑了旋翼的转速对无人机的动力学的影响。 你可以根据自己的需要来修改和扩展这个模型。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值